in watchman/watcher/fsevents.cpp [304:471]
std::unique_ptr<FSEventsStream> FSEventsWatcher::fse_stream_make(
const std::shared_ptr<Root>& root,
FSEventsWatcher* watcher,
FSEventStreamEventId since,
w_string& failure_reason) {
auto ctx = FSEventStreamContext();
unique_ref<CFMutableArrayRef> parray;
unique_ref<CFStringRef> cpath;
double latency;
FSEventStreamCreateFlags flags;
w_string path;
auto fse_stream = std::make_unique<FSEventsStream>(root, watcher, since);
// Each device has an optional journal maintained by fseventsd that keeps
// track of the change events. The journal may not be available if the
// filesystem was mounted read-only. The journal has an associated UUID
// to track the version of the data. In some cases the journal can become
// invalidated and it will have a new UUID generated. This can happen
// if the EventId rolls over.
// We need to lookup up the UUID for the associated path and use that to
// help decide whether we can use a value of `since` other than SinceNow.
struct stat st;
if (stat(root->root_path.c_str(), &st)) {
failure_reason = w_string::build(
"failed to stat(",
root->root_path,
"): ",
folly::errnoStr(errno),
"\n");
return nullptr;
}
// Obtain the UUID for the device associated with the root
fse_stream->uuid =
unique_ref<CFUUIDRef>{FSEventsCopyUUIDForDevice(st.st_dev)};
if (since != kFSEventStreamEventIdSinceNow) {
CFUUIDBytes a, b;
if (!fse_stream->uuid) {
// If there is no UUID available and we want to use an event offset,
// we fail: a nullptr UUID means that the journal is not available.
failure_reason = w_string::build(
"fsevents journal is not available for dev_t=", st.st_dev, "\n");
return nullptr;
}
// Compare the UUID with that of the current stream
if (!watcher->stream_->uuid) {
failure_reason = w_string(
"fsevents journal was not available for prior stream",
W_STRING_UNICODE);
return nullptr;
}
a = CFUUIDGetUUIDBytes(fse_stream->uuid.get());
b = CFUUIDGetUUIDBytes(watcher->stream_->uuid.get());
if (memcmp(&a, &b, sizeof(a)) != 0) {
failure_reason =
w_string("fsevents journal UUID is different", W_STRING_UNICODE);
return nullptr;
}
}
ctx.info = fse_stream.get();
parray.reset(CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks));
if (!parray) {
failure_reason = w_string("CFArrayCreateMutable failed", W_STRING_UNICODE);
return nullptr;
}
if (auto subdir = watcher->subdir) {
path = *subdir;
} else {
path = root->root_path;
}
cpath.reset(CFStringCreateWithBytes(
nullptr,
(const UInt8*)path.data(),
path.size(),
kCFStringEncodingUTF8,
false));
if (!cpath) {
failure_reason =
w_string("CFStringCreateWithBytes failed", W_STRING_UNICODE);
return nullptr;
}
CFArrayAppendValue(parray.get(), cpath.get());
latency = root->config.getDouble("fsevents_latency", 0.01),
logf(
DBG,
"FSEventStreamCreate for path {} with latency {} seconds\n",
path,
latency);
flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagWatchRoot;
if (watcher->hasFileWatching_) {
flags |= kFSEventStreamCreateFlagFileEvents;
}
fse_stream->stream = FSEventStreamCreate(
nullptr, fse_callback, &ctx, parray.get(), since, latency, flags);
if (!fse_stream->stream) {
failure_reason = w_string("FSEventStreamCreate failed", W_STRING_UNICODE);
return nullptr;
}
FSEventStreamScheduleWithRunLoop(
fse_stream->stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
if (root->config.getBool("_use_fsevents_exclusions", true)) {
auto& dirs_vec = root->ignore.getIgnoredDirs();
size_t nitems = std::min(dirs_vec.size(), kMaxExclusions);
size_t appended = 0;
unique_ref<CFMutableArrayRef> ignarray{
CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks)};
if (!ignarray) {
failure_reason =
w_string("CFArrayCreateMutable failed", W_STRING_UNICODE);
return nullptr;
}
for (const auto& path : dirs_vec) {
if (const auto& subdir = watcher->subdir) {
if (!w_string_startswith(path, *subdir)) {
continue;
}
logf(DBG, "Adding exclusion: {} for subdir: {}\n", path, *subdir);
}
unique_ref<CFStringRef> ignpath{CFStringCreateWithBytes(
nullptr,
(const UInt8*)path.data(),
path.size(),
kCFStringEncodingUTF8,
false)};
if (!ignpath) {
failure_reason =
w_string("CFStringCreateWithBytes failed", W_STRING_UNICODE);
return nullptr;
}
CFArrayAppendValue(ignarray.get(), ignpath.get());
appended++;
if (appended == nitems) {
break;
}
}
if (appended != 0) {
if (!FSEventStreamSetExclusionPaths(fse_stream->stream, ignarray.get())) {
failure_reason =
w_string("FSEventStreamSetExclusionPaths failed", W_STRING_UNICODE);
return nullptr;
}
}
}
return fse_stream;
}