core/common/FileSystemUtil.h (162 lines of code) (raw):
/*
* Copyright 2022 iLogtail Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <sys/stat.h>
#include <memory>
#include <string>
#include <vector>
#if defined(__linux__)
#include <dirent.h>
#elif defined(_MSC_VER)
#include <Windows.h>
#endif
#include <filesystem>
#include <functional>
#include "DevInode.h"
#include "ErrorUtil.h"
#include "LogtailCommonFlags.h"
// Filesystem utility.
namespace logtail {
constexpr size_t kDefaultMaxFileSize = 1024 * 1024; // 1 MB
// Separator of path, use std::string to concat with const char *.
extern const std::string PATH_SEPARATOR;
// PathJoin concats base and sub (by adding necessary path separator).
// NOTE: the implementation is not elegant for better performance (backward).
inline std::string PathJoin(const std::string& base, const std::string& sub) {
if (!base.empty() && base.back() != PATH_SEPARATOR[0]) {
return base + PATH_SEPARATOR + sub;
}
return base + sub;
}
std::string ParentPath(const std::string& path);
// CheckExistance checks whether the dir or file specified by @path is exist or not.
bool CheckExistance(const std::string& path);
bool Mkdirs(const std::string& dirPath);
// Mkdir @dirPath.
// NOTE: access errno directly for more information instead of GetErrno().
bool Mkdir(const std::string& dirPath);
// For Mkdir Only.
inline bool IsEEXIST(int e) {
#if defined(__linux__)
return (EEXIST == e);
#elif defined(_MSC_VER)
return (ERROR_ALREADY_EXISTS == e);
#endif
}
bool IsRelativePath(const std::string& path);
// . -> /usr/local/ilogtail/.
// ./a.txt -> /usr/local/ilogtail/./a.txt
std::string AbsolutePath(const std::string& path, const std::string& basepath);
// /usr/local/ilogtail/. -> /usr/local/ilogtail
// /usr/local/ilogtail/./a.txt -> /usr/local/ilogtail/a.txt
std::string NormalizePath(const std::string& path);
// FSeek, FTell that can handle file large than 2GB on 32bits OS.
int FSeek(FILE* stream, int64_t offset, int origin);
int64_t FTell(FILE* stream);
// TrimLastSeperator removes last path separator unless @path is equal to '/'.
void TrimLastSeperator(std::string& path);
long GetPageSize();
size_t GetBlockSize(const std::filesystem::path& path);
enum class FileReadResult {
kError = -1, // 发生错误
kOK = 0, // 文件成功读取完毕
kTruncated = 1 // 文件被截断(超过最大大小限制)
};
// ReadFileContent reads up to maxFileSize content of @fileName to @content.
// Cannot garantee the content is complete if the file is changed during reading.
FileReadResult
ReadFileContent(const std::string& fileName, std::string& content, uint64_t maxFileSize = kDefaultMaxFileSize);
int GetLines(std::istream& is,
bool enableEmptyLine,
const std::function<void(const std::string&)>& pushBack,
std::string* errorMessage);
int GetLines(const std::filesystem::path& filename,
bool enableEmptyLine,
const std::function<void(const std::string&)>& pushBack,
std::string* errorMessage);
int GetFileLines(const std::filesystem::path& filename,
std::vector<std::string>& res,
bool enableEmptyLine = true,
std::string* errorMessage = nullptr);
// OverwriteFile overwrides @fileName with @content.
bool OverwriteFile(const std::string& fileName, const std::string& content);
bool WriteFile(const std::string& fileName, const std::string& content, std::string& errMsg);
// IsAccessibleDirectory checks if the @dirPath is a existent directory and accessible.
// Accessibility means that we can iterate the contents of it.
bool IsAccessibleDirectory(const std::string& dirPath);
// GetAllFiles gets all files that match @filePattern in @dirPath, save their names into @allFiles.
// @filePattern: supports wildcard, such as *.log. If no wildcard in it, just return one file
// named @filePattern.
// @return true if succeeds.
bool GetAllFiles(const std::string& dirPath, const std::string& filePattern, std::vector<std::string>& allFiles);
// By default, fopen on Windows will make the opened file locked (even in read-only mode),
// which will affect users' actions such as deletion, renaming, rotating on that file.
// So we offer a series of functions to solve this problem.
// Functions will not check the @mode param, make sure what you pass is right.
FILE* FileReadOnlyOpen(const char* filePath, const char* mode = "r");
FILE* FileWriteOnlyOpen(const char* filePath, const char* mode = "w");
FILE* FileAppendOpen(const char* filePath, const char* mode = "a");
// Logtail will ignore files with special suffix.
bool IsValidSuffix(const std::string& filename);
std::string GetFdPath(int fd);
#ifdef _MSC_VER
typedef int mode_t;
#endif
void Chmod(const char* filePath, mode_t mode);
namespace fsutil {
class Entry {
public:
enum class Type { UNKNOWN, DIR, REG_FILE };
Entry() {}
Entry(const std::string& name, Type type, bool isSymbolic) : mName(name), mType(type), mIsSymbolic(isSymbolic) {}
std::string Name() const { return mName; }
bool IsDir() const { return Type::DIR == mType; }
bool IsRegFile() const { return Type::REG_FILE == mType; }
bool IsSymbolic() const { return mIsSymbolic; }
// It's caller's job to deal unknown entry type.
operator bool() const { return !mName.empty(); }
private:
std::string mName;
Type mType = Type::UNKNOWN;
bool mIsSymbolic = false;
};
class Dir {
Dir(const Dir&) = delete;
Dir& operator=(const Dir&) = delete;
public:
Dir(const std::string& dirPath);
~Dir();
bool Open();
// Utility function to decide why Open failed.
inline static bool IsENOENT(int e) {
#if defined(__linux__)
return (ENOENT == e);
#elif defined(_MSC_VER)
return (ERROR_PATH_NOT_FOUND == e || ERROR_FILE_NOT_FOUND == e);
#endif
}
inline static bool IsENOTDIR(int e) {
#if defined(__linux__)
return (ENOTDIR == e);
#elif defined(_MSC_VER)
return (ERROR_DIRECTORY == e);
#endif
}
inline static bool IsEACCES(int e) {
#if defined(__linux__)
return (EACCES == e);
#elif defined(_MSC_VER)
return (ERROR_ACCESS_DENIED == e);
#endif
}
// ReadNext reads next entry (FILE or DIR).
// @param resolveWithStat: if true, use stat to check entry type when the entry
// type from filesystem directory iteration API is not FILE or DIR,
// eg. LNK type under Linux.
// @return: the entry of the file. If it is sure that we reached the end, an
// 'false' entry will be returned. Otherwise, we will use UNKNOWN type entry
// to indicate some errors, it is caller's job to handle this (eg. skip it).
// TODO: Most error happened when the entry is symbolic, maybe we can ignore
// this and let caller to call something else to deal it.
Entry ReadNext(bool resolveWithStat = true);
void Close();
private:
std::string mDirPath;
#if defined(__linux__)
DIR* mDir;
#elif defined(_MSC_VER)
HANDLE mFind;
// Because FindFirstFile will return first entry, we have to cache it.
Entry mCachedEntry;
#endif
bool IsOpened() const;
};
#if defined(__linux__)
typedef struct stat RawStatType;
#elif defined(_MSC_VER)
typedef struct _stat64 RawStatType;
#endif
class PathStat {
std::string mPath;
RawStatType mRawStat;
public:
PathStat();
~PathStat();
RawStatType* GetRawStat() { return &mRawStat; }
// stat wrappers ::stat.
static bool stat(const std::string& path, PathStat& ps);
bool IsDir() const;
bool IsRegFile() const;
// lstat wrappers Linux ::lstat and implements it with ::stat on Windows.
static bool lstat(const std::string& path, PathStat& ps);
bool IsLink() const;
// fstat wrappers ::fstat.
// @resolvePath only works on Windows.
// If it is true, fstat will get corresponding file path according to @file and
// set it to @ps.mPath, however, if this procedure failed, false will be returned
// although the call to ::fstat succeeded.
// **Use it when you want to call GetDevInode() or GetLastWriteTime().**
// **Disable it if you only want to call GetMtime() or GetFileSize().**
static bool fstat(FILE* file, PathStat& ps, bool resolvePath = true);
static bool fstat(int fd, PathStat& ps, bool resolvePath = true);
#if defined(_MSC_VER)
static bool fstat(HANDLE hFile, PathStat& ps, bool resolvePath = true);
#endif
// For Windows, GetDevInode and GetLastWriteTime have to call other system APIs to
// get information (by mPath), this will spend extra costs.
DevInode GetDevInode() const;
void GetLastWriteTime(int64_t& sec, int64_t& nsec) const;
// GetMtime and GetFileSize return st_mtime and st_size in struct stat. They needn't
// to call another system APIs.
time_t GetMtime() const;
int64_t GetFileSize() const;
// GetMode returns st_mode.
int GetMode() const { return static_cast<int>(mRawStat.st_mode); }
};
} // namespace fsutil
bool ReadFile(const std::string& filepath, std::string& content);
} // namespace logtail