524 lines
16 KiB
C++
524 lines
16 KiB
C++
/**
|
|
* Copyright (c) 2021 OceanBase
|
|
* OceanBase CE is licensed under Mulan PubL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PubL v2.
|
|
* You may obtain a copy of Mulan PubL v2 at:
|
|
* http://license.coscl.org.cn/MulanPubL-2.0
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PubL v2 for more details.
|
|
*/
|
|
|
|
#include "lib/file/file_directory_utils.h"
|
|
|
|
#include "lib/ob_define.h"
|
|
#include "lib/allocator/ob_malloc.h"
|
|
#include "lib/ob_errno.h"
|
|
#include "common/ob_smart_call.h"
|
|
#include "lib/utility/ob_hang_fatal_error.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
#include <sys/statvfs.h>
|
|
|
|
|
|
namespace oceanbase
|
|
{
|
|
namespace common
|
|
{
|
|
|
|
//return true if filename is exists
|
|
int FileDirectoryUtils::is_exists(const char *file_path, bool &result)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
result = false;
|
|
struct stat64 file_info;
|
|
if (OB_ISNULL(file_path) || OB_UNLIKELY(strlen(file_path) == 0)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", KCSTRING(file_path), K(ret));
|
|
} else {
|
|
result = (0 == stat64(file_path, &file_info));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//return true if file is accessible
|
|
int FileDirectoryUtils::is_accessible(const char *file_path, bool &result)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
result = false;
|
|
if (OB_ISNULL(file_path) || OB_UNLIKELY(strlen(file_path) == 0)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", KCSTRING(file_path), K(ret));
|
|
} else {
|
|
if (0 == access(file_path, R_OK)) {
|
|
result = true;
|
|
} else {
|
|
LIB_LOG(WARN, "access file failed", KERRMSG, K(file_path));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//return ture if dirname is a directory
|
|
int FileDirectoryUtils::is_directory(const char *directory_path, bool &result)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
result = false;
|
|
struct stat64 file_info;
|
|
if (NULL == directory_path || strlen(directory_path) == 0) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", KCSTRING(directory_path), K(ret));
|
|
} else {
|
|
result = (0 == stat64(directory_path, &file_info) && S_ISDIR(file_info.st_mode));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int FileDirectoryUtils::is_link(const char *link_path, bool &result)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (NULL == link_path || strlen(link_path) == 0) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", KCSTRING(link_path), K(ret));
|
|
} else {
|
|
struct stat64 file_info;
|
|
result = (0 == lstat64(link_path, &file_info) && S_ISLNK(file_info.st_mode));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//create the give dirname, return true on success or dirname exists
|
|
int FileDirectoryUtils::create_directory(const char *directory_path)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
mode_t umake_value = umask(0);
|
|
umask(umake_value);
|
|
mode_t mode = (S_IRWXUGO & (~umake_value)) | S_IWUSR | S_IXUSR;
|
|
|
|
if (NULL == directory_path || strlen(directory_path) == 0) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", KCSTRING(directory_path), K(ret));
|
|
} else if (::mkdir(directory_path, mode) != 0) {
|
|
if (EEXIST == errno) {
|
|
ret = OB_SUCCESS;
|
|
} else {
|
|
ret = OB_IO_ERROR;
|
|
LIB_LOG(WARN, "create directory failed.",
|
|
KCSTRING(directory_path), K(errno), KERRMSG, K(ret));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//creates the full path of fullpath, return true on success
|
|
int FileDirectoryUtils::create_full_path(const char *fullpath)
|
|
{
|
|
|
|
int ret = OB_SUCCESS;
|
|
struct stat64 file_info;
|
|
int64_t len = 0;
|
|
if (NULL == fullpath || (len = strlen(fullpath)) == 0) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", KCSTRING(fullpath), K(ret));
|
|
} else {
|
|
ret = ::stat64(fullpath, &file_info);
|
|
if (0 == ret) {
|
|
if (!S_ISDIR(file_info.st_mode)) {
|
|
ret = OB_ENTRY_EXIST;
|
|
LIB_LOG(WARN, "file is exists but not a directory.", KCSTRING(fullpath), K(ret));
|
|
} else {
|
|
ret = OB_SUCCESS;
|
|
}
|
|
} else {
|
|
ret = OB_SUCCESS;
|
|
// path not exists.
|
|
char dirpath[MAX_PATH + 1];
|
|
strncpy(dirpath, fullpath, len);
|
|
dirpath[len] = '\0';
|
|
char *path = dirpath;
|
|
|
|
// skip leading char '/'
|
|
while (*path++ == '/');
|
|
|
|
while (OB_SUCC(ret)) {
|
|
path = strchr(path, '/');
|
|
if (NULL == path) {
|
|
break;
|
|
}
|
|
|
|
*path = '\0';
|
|
if (OB_FAIL(create_directory(dirpath))) {
|
|
LIB_LOG(WARN, "create directory failed.", KCSTRING(dirpath), K(ret));
|
|
} else {
|
|
*path++ = '/';
|
|
// skip '/'
|
|
while (*path++ == '/')
|
|
;
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(create_directory(dirpath))) {
|
|
LIB_LOG(WARN, "create directory failed.", KCSTRING(dirpath), K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//delete the given file, return true if filename exists
|
|
// return OB_SUCCESS on success;
|
|
int FileDirectoryUtils::delete_file(const char *filename)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
struct stat64 file_info;
|
|
if (NULL == filename || strlen(filename) == 0) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", KCSTRING(filename), K(ret));
|
|
} else {
|
|
ret = ::stat64(filename, &file_info);
|
|
if (0 != ret) {
|
|
ret = OB_FILE_NOT_EXIST;
|
|
LIB_LOG(WARN, "file is not exists.", KCSTRING(filename), K(ret));
|
|
} else if (S_ISDIR(file_info.st_mode)) {
|
|
ret = OB_FILE_NOT_EXIST;
|
|
LIB_LOG(WARN, "file is directory, use delete_directory.",
|
|
KCSTRING(filename), K(ret));
|
|
} else if (0 != unlink(filename)){
|
|
ret = OB_IO_ERROR;
|
|
LIB_LOG(WARN, "unlink file failed.",
|
|
KCSTRING(filename), K(errno), KERRMSG, K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//delete the given directory and anything under it. Returns true on success
|
|
int FileDirectoryUtils::delete_directory(const char *dirname)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool is_dir = false;
|
|
if (NULL == dirname || strlen(dirname) == 0) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", KCSTRING(dirname), K(ret));
|
|
} else if (OB_FAIL(is_directory(dirname, is_dir))) {
|
|
LIB_LOG(WARN, "check if directory failed.", KCSTRING(dirname), K(ret));
|
|
} else if (!is_dir) {
|
|
ret = OB_FILE_NOT_EXIST;
|
|
LIB_LOG(WARN, "file path is not a directory.", KCSTRING(dirname), K(ret));
|
|
} else if (0 != rmdir(dirname)) {
|
|
ret = OB_IO_ERROR;
|
|
LIB_LOG(WARN, "rmdir failed.",
|
|
KCSTRING(dirname), K(errno), KERRMSG, K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//return the size of filename
|
|
int FileDirectoryUtils::get_file_size(const char *filename, int64_t &size)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
struct stat64 file_info;
|
|
if (NULL == filename || strlen(filename) == 0) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", KCSTRING(filename), K(ret));
|
|
} else {
|
|
ret = ::stat64(filename, &file_info);
|
|
if (0 != ret) {
|
|
ret = OB_FILE_NOT_EXIST;
|
|
LIB_LOG(WARN, "file is not exists.", KCSTRING(filename), K(ret));
|
|
} else if (S_ISDIR(file_info.st_mode)) {
|
|
ret = OB_FILE_NOT_EXIST;
|
|
LIB_LOG(WARN, "file is not a file.", KCSTRING(filename), K(ret));
|
|
} else {
|
|
size = file_info.st_size;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int FileDirectoryUtils::is_valid_path(const char *path, const bool print_error)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
|
|
if (NULL == path) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "path must not null", K(ret));
|
|
} else {
|
|
for (int64_t i = 0; OB_SUCC(ret) && '\0' != path[i]; ++i) {
|
|
if (!isalnum(path[i]) && '_' != path[i] && '/' != path[i] && '.' != path[i]) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
if (print_error) {
|
|
LIB_LOG(WARN, "invalid path", K(ret), K(i), K(path[i]), KCSTRING(path));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int FileDirectoryUtils::is_empty_directory(const char *directory_path, bool &result)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
DIR *dir = NULL;
|
|
struct dirent *entry = NULL;
|
|
int64_t num = 0;
|
|
if (NULL == directory_path) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "directory path is NULL, ", K(ret));
|
|
} else if (NULL == (dir = opendir(directory_path))) {
|
|
ret = OB_ERR_SYS;
|
|
LIB_LOG(WARN, "Fail to open dir, ", K(ret), K(errno), KCSTRING(directory_path));
|
|
} else {
|
|
while(NULL != (entry = readdir(dir))) {
|
|
++num;
|
|
}
|
|
if (2 == num) {
|
|
result = true;
|
|
} else {
|
|
result = false;
|
|
}
|
|
}
|
|
|
|
if (NULL != dir) {
|
|
closedir(dir);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int FileDirectoryUtils::open(const char *pathname, int flags, mode_t mode, int &fd)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (NULL == pathname || strlen(pathname) == 0) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", KCSTRING(pathname), K(ret));
|
|
} else {
|
|
fd = ::open(pathname, flags, mode);
|
|
if (fd < 0) {
|
|
ret = OB_IO_ERROR;
|
|
LIB_LOG(WARN, "Fail to open", K(ret), K(errno), KCSTRING(pathname), K(mode), K(flags));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int FileDirectoryUtils::close(const int fd)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (fd < 0) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", K(fd), K(ret));
|
|
} else {
|
|
if (0 != ::close(fd)) {
|
|
ret = OB_IO_ERROR;
|
|
LIB_LOG(WARN, "fail to close", K(ret), K(errno), K(fd));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int FileDirectoryUtils::symlink(const char *oldpath, const char *newpath)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (NULL == oldpath || strlen(oldpath) == 0 || NULL == newpath || strlen(newpath) == 0) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "path must not null", K(ret), KCSTRING(oldpath), KCSTRING(newpath));
|
|
} else {
|
|
if (0 != ::symlink(oldpath, newpath)) {
|
|
ret = OB_IO_ERROR;
|
|
LIB_LOG(WARN, "fail to symlink", K(ret), K(errno), KCSTRING(oldpath), KCSTRING(newpath));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int FileDirectoryUtils::unlink_symlink(const char *link_path)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool is_link_file = false;
|
|
if (NULL == link_path || strlen(link_path) == 0) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", KCSTRING(link_path), K(ret));
|
|
} else if (OB_FAIL(is_link(link_path, is_link_file))) {
|
|
LIB_LOG(WARN, "fail to exectue is_link", K(ret), KCSTRING(link_path));
|
|
} else if (is_link_file) {
|
|
if (0 != ::unlink(link_path)) {
|
|
ret = OB_IO_ERROR;
|
|
LIB_LOG(WARN, "fail to unlink", K(ret), K(errno), KERRMSG, KCSTRING(link_path));
|
|
}
|
|
} else {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LIB_LOG(WARN, "not a symlink", K(ret), KCSTRING(link_path));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int FileDirectoryUtils::dup_fd(const int fd, int &dup_fd)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (fd < 0) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid arguments.", K(fd), K(ret));
|
|
} else if (0 > (dup_fd = ::dup(fd))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LIB_LOG(WARN, "fail to dup", K(ret), K(fd), K(errno), KERRMSG);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int FileDirectoryUtils::get_disk_space(
|
|
const char *path,
|
|
int64_t &total_space,
|
|
int64_t &free_space)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
struct statvfs svfs;
|
|
total_space = 0;
|
|
free_space = 0;
|
|
|
|
if (OB_ISNULL(path)) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LIB_LOG(WARN, "invalid args", K(ret), KP(path));
|
|
} else if (OB_FAIL(statvfs(path, &svfs))) {
|
|
ret = OB_IO_ERROR;
|
|
LIB_LOG(WARN, "get svfs fail", K(ret), KCSTRING(path), K(errno), KERRNOMSG(errno));
|
|
} else {
|
|
// Remove the space reserved by the root user
|
|
total_space = (svfs.f_blocks + svfs.f_bavail - svfs.f_bfree) * svfs.f_bsize;
|
|
free_space = svfs.f_bavail * svfs.f_bsize;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int FileDirectoryUtils::delete_directory_rec(const char *path)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
DIR *dir = NULL;
|
|
struct dirent *entry = nullptr;
|
|
if (NULL == (dir = opendir(path))) {
|
|
if (ENOENT != errno) {
|
|
ret = OB_FILE_NOT_OPENED;
|
|
LIB_LOG(WARN, "fail to open dir", K(ret), K(path));
|
|
} else {
|
|
ret = OB_ENTRY_NOT_EXIST;
|
|
LIB_LOG(WARN, "dir does not exist", K(ret), K(path));
|
|
}
|
|
} else {
|
|
char current_file_path[OB_MAX_FILE_NAME_LENGTH] = {'\0'};
|
|
while ((entry = readdir(dir)) != NULL && OB_SUCC(ret)) {
|
|
bool is_dir = false;
|
|
MEMSET(current_file_path, '\0', OB_MAX_FILE_NAME_LENGTH);
|
|
if (0 == strcmp(entry->d_name, ".") || 0 == strcmp(entry->d_name, "..")) {
|
|
// do nothing
|
|
} else if (0 >= snprintf(current_file_path, OB_MAX_FILE_NAME_LENGTH, "%s/%s", path, entry->d_name)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LIB_LOG(WARN, "snprintf failed", K(ret), K(current_file_path), K(path), K(entry->d_name));
|
|
} else if (OB_FAIL(FileDirectoryUtils::is_directory(current_file_path, is_dir))) {
|
|
LIB_LOG(WARN, "is_directory failed", K(ret), K(entry->d_name));
|
|
// delecte directory recursive
|
|
} else if (true == is_dir && OB_FAIL(SMART_CALL(delete_directory_rec(current_file_path)))) {
|
|
LIB_LOG(WARN, "delete directory failed", K(ret), K(entry->d_name), K(path));
|
|
// delete normal file
|
|
} else if (false == is_dir && OB_FAIL(FileDirectoryUtils::delete_file(current_file_path))) {
|
|
LIB_LOG(WARN, "delete_file failed", K(ret), K(current_file_path));
|
|
}
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
LIB_LOG(WARN, "delete directory rec failed", K(ret), K(path));
|
|
} else if (OB_FAIL(delete_directory(path))) {
|
|
LIB_LOG(WARN, "delete_directory failed", K(ret), K(path));
|
|
}
|
|
if (NULL != dir) {
|
|
closedir(dir);
|
|
dir = nullptr;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int FileDirectoryUtils::delete_tmp_file_or_directory_at(const char *path)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
DIR *dir = NULL;
|
|
struct dirent *entry = nullptr;
|
|
if (NULL == (dir = opendir(path))) {
|
|
ret = OB_ERR_SYS;
|
|
LIB_LOG(WARN, "opendir failed", K(path));
|
|
} else {
|
|
auto check_is_tmp_file = [](const char* file_name) -> bool {
|
|
return NULL != strstr(file_name, ".tmp");
|
|
};
|
|
char current_file_path[OB_MAX_FILE_NAME_LENGTH] = {'\0'};
|
|
while ((entry = readdir(dir)) != NULL && OB_SUCC(ret)) {
|
|
bool is_dir = false;
|
|
MEMSET(current_file_path, '\0', OB_MAX_FILE_NAME_LENGTH);
|
|
if (0 == strcmp(entry->d_name, ".") || 0 == strcmp(entry->d_name, "..")) {
|
|
// do nothing
|
|
} else if (0 >= snprintf(current_file_path, OB_MAX_FILE_NAME_LENGTH, "%s/%s", path, entry->d_name)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LIB_LOG(WARN, "snprintf failed", K(ret), K(current_file_path), K(path), K(entry->d_name));
|
|
} else if (OB_FAIL(FileDirectoryUtils::is_directory(current_file_path, is_dir))) {
|
|
LIB_LOG(WARN, "is_directory failed", K(ret), K(entry->d_name));
|
|
} else if (true == check_is_tmp_file(current_file_path)) {
|
|
if (true == is_dir && OB_FAIL(delete_directory_rec(current_file_path))) {
|
|
LIB_LOG(WARN, "delete_directory_rec failed", K(ret), K(entry->d_name), K(path));
|
|
} else if (false == is_dir && OB_FAIL(FileDirectoryUtils::delete_file(current_file_path))) {
|
|
LIB_LOG(WARN, "delete_file failed", K(ret), K(current_file_path));
|
|
} else {
|
|
}
|
|
} else if (true == is_dir && OB_FAIL(delete_tmp_file_or_directory_at(current_file_path))) {
|
|
LIB_LOG(WARN, "delete_tmp_file_or_directory_at failed", K(ret), K(current_file_path));
|
|
} else {
|
|
}
|
|
}
|
|
}
|
|
if (NULL != dir) {
|
|
closedir(dir);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int FileDirectoryUtils::fsync_dir(const char *dir_path)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int fd = ::open(dir_path, O_DIRECTORY | O_RDONLY);
|
|
if (-1 == fd) {
|
|
ret = OB_IO_ERROR;
|
|
LIB_LOG(WARN, "::open failed", K(ret), K(dir_path), K(errno));
|
|
} else if (-1 == ::fsync(fd)) {
|
|
ret = OB_IO_ERROR;
|
|
LIB_LOG(WARN, "::fsync failed", K(ret), K(dir_path), K(errno));
|
|
} else {
|
|
}
|
|
if (-1 != fd) {
|
|
::close(fd);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
}//end namespace common
|
|
}//end namespace oceanbase
|