[英]How to detect (FSEvent) if a file is modified through a (hard) symlink?
尝试为列表中的每个文件的内容保留一个哈希值。
例子
file1.txt "This is a long story about dragons and..." -> Hash A28F30...
file2.txt "Larousse dictionary. Letter A..." -> Hash 98BC012...
为此,程序侦听器检测对这些文件的任何修改,以便在创建/修改/删除的情况下使散列无效(并重新计算)。
文件路径是为 FSEvent 注册的,对文件的任何修改都会使散列无效。
这种策略在大多数情况下都有效,但在使用链接时会出现复杂情况:
$ ln /Users/myself/test_file.txt /other_path/file.txt
$ echo "changed" >> /other_path/file.txt
这会修改/Users/myself/test_file.txt
的文件,但没有收到此修改的事件(破坏哈希 <-> 文件等效性)。
以下是一个简短的代码示例:
#include <iostream>
#include <unordered_map>
#include <vector>
#include <CoreServices/CoreServices.h>
#include <sys/stat.h>
using namespace std;
using FileKey = string;
using FileData = struct{};
using AppHashTable = std::unordered_map<FileKey, FileData>;
void invalidateCache( AppHashTable& appCache, const string& file)
{
(void) appCache; //no unused
cout << "Invalidate " << file << endl;
}
auto callback(
ConstFSEventStreamRef streamRef,
void *info,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags *eventFlags,
const FSEventStreamEventId * eventIds)-> void
{
(void) streamRef; //No unused
(void) eventIds; //No unused
static const char* flags[] = {
"MustScanSubDirs",
"UserDropped",
"KernelDropped",
"EventIdsWrapped",
"HistoryDone",
"RootChanged",
"Mount",
"Unmount",
"ItemCreated",
"ItemRemoved",
"ItemInodeMetaMod",
"ItemRenamed",
"ItemModified",
"ItemFinderInfoMod",
"ItemChangeOwner",
"ItemXattrMod",
"ItemIsFile",
"ItemIsDir",
"ItemIsSymlink",
"OwnEvent"
} ;
cout << endl;
char** pathsList = static_cast<char**>(eventPaths);
for(size_t i = 0; i<numEvents; i++)
{
cout << "Event " << i << " of " << numEvents << endl;
cout << " " << pathsList[i] << endl;
cout << " Flags: ";
long bit = 1 ;
for( int index=0, count = sizeof( flags ) / sizeof( flags[0]);
index < count; ++index )
{
if ( ( eventFlags[i] & bit ) != 0 )
{
cout << flags[index] << " ";
}
bit <<= 1 ;
}
cout << endl;
invalidateCache(*static_cast<AppHashTable*>(info), pathsList[i]);
}
}
vector<string> getPaths()
{
return {
"/Users/myself/test_folder",
"/Users/myself/test_file.txt"};
}
int main(int , const char * [])
{
AppHashTable appHashTable;
const auto vecPaths = getPaths();
// Create CF array of paths TODO to structure RAII
vector<CFStringRef> cfVecPaths;
for (const auto& p: vecPaths)
{
cfVecPaths.push_back( CFStringCreateWithCString( kCFAllocatorDefault, p.c_str(),
kCFStringEncodingUTF8 ));
}
CFArrayRef paths = CFArrayCreate(nullptr, reinterpret_cast<const void **>(cfVecPaths.data()),
static_cast<CFIndex>(cfVecPaths.size()), &kCFTypeArrayCallBacks );
FSEventStreamContext info{0, reinterpret_cast<void*>(&appHashTable), nullptr, nullptr, nullptr};
CFRunLoopRef loop = CFRunLoopGetCurrent() ;
FSEventStreamRef stream = FSEventStreamCreate(nullptr, callback, &info, paths,
kFSEventStreamEventIdSinceNow, 0.1, kFSEventStreamCreateFlagFileEvents );
FSEventStreamScheduleWithRunLoop(stream, loop, kCFRunLoopDefaultMode);
FSEventStreamStart(stream);
CFRunLoopRun() ;
FSEventStreamStop(stream);
FSEventStreamInvalidate(stream);
FSEventStreamRelease(stream);
// Release CF array of paths
if ( paths ) { CFRelease( paths ) ; }
for (auto& p: cfVecPaths)
{
CFRelease(p);
}
return 0;
}
如何通过链接检测对文件的修改?
为什么未检测到链接创建/修改?
显然,为整个文件系统注册事件不是一种选择。
(针对 macOS High-sierra 到 Big-Sur,使用 C++)
为了方便:
cmake_minimum_required(VERSION 3.0)
set(PROJECT_NAME "poc_events")
project(${PROJECT_NAME})
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework CoreServices")
set ( TESTCPP main.cpp )
add_executable( ${PROJECT_NAME} ${TESTCPP} )
到目前为止我的发现(我将停止研究这个主题):
可以在此处找到如何使用内核队列监视文件的示例:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.