文件夹是否有修改的检查
linux 文件夹是否有修改的检查
实现某个功能时,需要检查某个文件和对应的文件夹是否有修改:
记录 inode + 修改时间 + 文件大小确保文件没有被修改
在 Linux 中:
- 每个文件由一个 inode number(inode 节点号) 唯一标识
- 这个唯一性 只在同一个文件系统(filesystem / 分区)内保证
static string ensureTrailingSlash(const std::string& path) {
if (path.back() != '/')
return path + "/caches/";
return path + "caches/";
}
string
findValidImage(const string& layout, const abLoadOption& opt) {
if (!s_cachePaths.empty()) {
for (const auto& it : s_cachePaths) {
string path = ensureTrailingSlash(it) + generateDirName(layout, opt).toStdString();
if (isImageValid(path, layout))
return path;
}
} else {
string path = getDefaultImageDir(layout, opt, false);
if (isImageValid(path, layout))
return path;
}
return "";
}
string
getWriteImageDir(const string& layout, const abLoadOption& opt) {
for (const auto& it : s_cachePaths) {
string path = ensureTrailingSlash(it) + generateDirName(layout, opt).toStdString();
if (createWritableDir(path))
return path;
}
string path = getDefaultImageDir(layout, opt, false);
if (createWritableDir(path))
return path;
return "";
}
bool
createWritableDir(const string& path) {
QDir dir(path.c_str());
if (!dir.mkpath(dir.absolutePath()))
return false;
QFileInfo info(path.c_str());
return info.exists() && info.isDir() && info.isWritable();
}
string
getDefaultImageDir(const string& layout, const abLoadOption& opt, bool isCreate) {
auto tryCreateWritableDir = [&](const QString& path) -> bool {
QDir dir(path);
if (isCreate) {
if (!dir.mkpath(dir.absolutePath()))
return false;
}
QFileInfo info(path);
return info.exists() && info.isDir() && info.isWritable();
};
QString dirName = generateDirName(layout, opt);
QStringList candidates = {
"./." + QString(abApp::getBinName()) + "/caches/" + dirName, // current dir
QDir::homePath() + "/." + abApp::getBinName() + "/caches/" + dirName // home dir
};
for (const QString& candidate : candidates) {
if (tryCreateWritableDir(candidate)) {
return candidate.toStdString();
}
}
return "";
}
QString
generateDirName(const string& layout, const abLoadOption& opt) {
struct stat fileStat;
if (::stat(layout.c_str(), &fileStat) != 0)
return "";
QFileInfo fi(layout.c_str());
QString fileName = fi.fileName();
// 文件名 + inode + size + mtime + optkey + version
QString res = QString("%1_%2_%3_%4_%5_%6")
.arg(fileName)
.arg(fileStat.st_ino)
.arg(fileStat.st_size)
.arg(fileStat.st_mtime)
.arg(getOpenOptionKey(opt))
.arg(s_imageVersion.c_str());
return res;
}
quint64
calculateDirectorySnapshot(const QString& imageDir) {
quint64 snapshot = 0;
QDirIterator it(imageDir, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot,
QDirIterator::Subdirectories);
while (it.hasNext()) {
QString path = it.next();
QFileInfo fi(path);
snapshot += fi.size();
snapshot += fi.lastModified().toTime_t();
}
return snapshot;
}
bool
isImageValid(const string& imageDir, const string& layout) {
struct stat fileStat;
if (::stat(layout.c_str(), &fileStat) != 0) return false;
string metaPath = imageDir + s_metaData;
QFile metaFile(metaPath.c_str());
if (!metaFile.exists() ||
!metaFile.open(QIODevice::ReadOnly | QIODevice::Text))
return false;
QTextStream in(&metaFile);
QString inodeLine = in.readLine();
QString sizeLine = in.readLine();
QString mtimeLine = in.readLine();
QString cacheDirSnapshotLine = in.readLine();
metaFile.close();
if (inodeLine != QString::number(fileStat.st_ino)) return false;
if (sizeLine != QString::number(fileStat.st_size)) return false;
if (mtimeLine != QString::number(fileStat.st_mtime)) return false;
quint64 currentSnapshot = calculateDirectorySnapshot(imageDir.c_str());
quint64 savedSnapshot = cacheDirSnapshotLine.toULongLong();
if (currentSnapshot != savedSnapshot) return false;
return true;
}
bool
saveMetadata(const string& imageDir, const string& layout) {
QDir dir;
if (!dir.exists(imageDir.c_str())) {
if (!dir.mkpath(imageDir.c_str())) return false;
}
struct stat fileStat;
if (::stat(layout.c_str(), &fileStat) != 0) return false;
quint64 cacheSnapshot = calculateDirectorySnapshot(imageDir.c_str());
QFile metaFile(QString::fromStdString(imageDir + s_metaData));
if (!metaFile.open(QIODevice::WriteOnly | QIODevice::Text)) return false;
QTextStream out(&metaFile);
out << QString::number(fileStat.st_ino) << "\n";
out << QString::number(fileStat.st_size) << "\n";
out << QString::number(fileStat.st_mtime) << "\n";
out << QString::number(cacheSnapshot) << "\n";
out.flush();
if (metaFile.error() != QFile::NoError) {
fprintf(stderr, "File write failed: %s\n", qPrintable(metaFile.errorString()));
return false;
}
metaFile.close();
return true;
}
uint64_t
getOpenOptionKey(const abLoadOption& opt) {
auto fnv1a64 = [](const char* data, size_t len) -> uint64_t {
uint64_t hash = 0xcbf29ce484222325ULL;
for (size_t i = 0; i < len; ++i) {
hash ^= static_cast<uint8_t>(data[i]);
hash *= 0x100000001b3ULL;
}
return hash;
};
zaUInt32 flag = opt.getFlag();
if (s_splitUngridArray) flag |= 0x100;
uint64_t key = static_cast<uint64_t>(flag);
double hw = opt.zeroPathHalfWidth();
if (!zaIsFloatEqual(hw, 0)) {
uint64_t hwBits = *reinterpret_cast<uint64_t*>(&hw);
key ^= hwBits + 0x9e3779b97f4a7c15ULL + (key << 6) + (key >> 2);
}
if (opt.layerMap()) {
std::string layerStr = opt.layerMap()->toSortString();
uint64_t layerHash = fnv1a64(layerStr.c_str(), layerStr.size());
key ^= layerHash + 0x9e3779b97f4a7c15ULL + (key << 6) + (key >> 2);
}
return key;
}
bool
removeDir(const QString &dirPath) {
QDir dir(dirPath);
if (!dir.exists())
return false;
QFileInfoList entries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries);
for (const QFileInfo &entry : entries) {
QString path = entry.absoluteFilePath();
if (entry.isDir()) {
if (!removeDir(path))
return false;
} else {
if (!QFile::remove(path))
return false;
}
}
if (!dir.rmdir(dirPath))
return false;
return true;
}
string
replaceLastFolder(const string& path) {
string trimmed = path;
while (!trimmed.empty() && trimmed.back() == '/')
trimmed.pop_back();
size_t pos = trimmed.find_last_of('/');
if (pos == string::npos)
return "caches";
string parent = trimmed.substr(0, pos);
return parent + "/caches/";
}
浙公网安备 33010602011771号