2
#include <dispatch/dispatch.h>
3
#include "fsm-darwin-gcc.h"
5
#include <CoreFoundation/CoreFoundation.h>
6
#include <CoreServices/CoreServices.h>
8
#ifndef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER
10
* This enum value was added in 10.13 to:
12
* /Applications/Xcode.app/Contents/Developer/Platforms/ \
13
* MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/ \
14
* Library/Frameworks/CoreServices.framework/Frameworks/ \
15
* FSEvents.framework/Versions/Current/Headers/FSEvents.h
17
* If we're compiling against an older SDK, this symbol won't be
18
* present. Silently define it here so that we don't have to ifdef
19
* the logging or masking below. This should be harmless since older
20
* versions of macOS won't ever emit this FS event anyway.
22
#define kFSEventStreamEventFlagItemCloned 0x00400000
26
#include "git-compat-util.h"
27
#include "fsmonitor-ll.h"
28
#include "fsm-listen.h"
29
#include "fsmonitor--daemon.h"
30
#include "fsmonitor-path-utils.h"
32
#include "simple-ipc.h"
33
#include "string-list.h"
38
CFStringRef cfsr_worktree_path;
39
CFStringRef cfsr_gitdir_path;
41
CFArrayRef cfar_paths_to_watch;
42
int nr_paths_watching;
44
FSEventStreamRef stream;
47
pthread_cond_t dq_finished;
48
pthread_mutex_t dq_lock;
56
unsigned int stream_scheduled:1;
57
unsigned int stream_started:1;
60
static void log_flags_set(const char *path, const FSEventStreamEventFlags flag)
62
struct strbuf msg = STRBUF_INIT;
64
if (flag & kFSEventStreamEventFlagMustScanSubDirs)
65
strbuf_addstr(&msg, "MustScanSubDirs|");
66
if (flag & kFSEventStreamEventFlagUserDropped)
67
strbuf_addstr(&msg, "UserDropped|");
68
if (flag & kFSEventStreamEventFlagKernelDropped)
69
strbuf_addstr(&msg, "KernelDropped|");
70
if (flag & kFSEventStreamEventFlagEventIdsWrapped)
71
strbuf_addstr(&msg, "EventIdsWrapped|");
72
if (flag & kFSEventStreamEventFlagHistoryDone)
73
strbuf_addstr(&msg, "HistoryDone|");
74
if (flag & kFSEventStreamEventFlagRootChanged)
75
strbuf_addstr(&msg, "RootChanged|");
76
if (flag & kFSEventStreamEventFlagMount)
77
strbuf_addstr(&msg, "Mount|");
78
if (flag & kFSEventStreamEventFlagUnmount)
79
strbuf_addstr(&msg, "Unmount|");
80
if (flag & kFSEventStreamEventFlagItemChangeOwner)
81
strbuf_addstr(&msg, "ItemChangeOwner|");
82
if (flag & kFSEventStreamEventFlagItemCreated)
83
strbuf_addstr(&msg, "ItemCreated|");
84
if (flag & kFSEventStreamEventFlagItemFinderInfoMod)
85
strbuf_addstr(&msg, "ItemFinderInfoMod|");
86
if (flag & kFSEventStreamEventFlagItemInodeMetaMod)
87
strbuf_addstr(&msg, "ItemInodeMetaMod|");
88
if (flag & kFSEventStreamEventFlagItemIsDir)
89
strbuf_addstr(&msg, "ItemIsDir|");
90
if (flag & kFSEventStreamEventFlagItemIsFile)
91
strbuf_addstr(&msg, "ItemIsFile|");
92
if (flag & kFSEventStreamEventFlagItemIsHardlink)
93
strbuf_addstr(&msg, "ItemIsHardlink|");
94
if (flag & kFSEventStreamEventFlagItemIsLastHardlink)
95
strbuf_addstr(&msg, "ItemIsLastHardlink|");
96
if (flag & kFSEventStreamEventFlagItemIsSymlink)
97
strbuf_addstr(&msg, "ItemIsSymlink|");
98
if (flag & kFSEventStreamEventFlagItemModified)
99
strbuf_addstr(&msg, "ItemModified|");
100
if (flag & kFSEventStreamEventFlagItemRemoved)
101
strbuf_addstr(&msg, "ItemRemoved|");
102
if (flag & kFSEventStreamEventFlagItemRenamed)
103
strbuf_addstr(&msg, "ItemRenamed|");
104
if (flag & kFSEventStreamEventFlagItemXattrMod)
105
strbuf_addstr(&msg, "ItemXattrMod|");
106
if (flag & kFSEventStreamEventFlagOwnEvent)
107
strbuf_addstr(&msg, "OwnEvent|");
108
if (flag & kFSEventStreamEventFlagItemCloned)
109
strbuf_addstr(&msg, "ItemCloned|");
111
trace_printf_key(&trace_fsmonitor, "fsevent: '%s', flags=0x%x %s",
112
path, flag, msg.buf);
114
strbuf_release(&msg);
117
static int ef_is_root_changed(const FSEventStreamEventFlags ef)
119
return (ef & kFSEventStreamEventFlagRootChanged);
122
static int ef_is_root_delete(const FSEventStreamEventFlags ef)
124
return (ef & kFSEventStreamEventFlagItemIsDir &&
125
ef & kFSEventStreamEventFlagItemRemoved);
128
static int ef_is_root_renamed(const FSEventStreamEventFlags ef)
130
return (ef & kFSEventStreamEventFlagItemIsDir &&
131
ef & kFSEventStreamEventFlagItemRenamed);
134
static int ef_is_dropped(const FSEventStreamEventFlags ef)
136
return (ef & kFSEventStreamEventFlagMustScanSubDirs ||
137
ef & kFSEventStreamEventFlagKernelDropped ||
138
ef & kFSEventStreamEventFlagUserDropped);
142
* If an `xattr` change is the only reason we received this event,
143
* then silently ignore it. Git doesn't care about xattr's. We
144
* have to be careful here because the kernel can combine multiple
145
* events for a single path. And because events always have certain
146
* bits set, such as `ItemIsFile` or `ItemIsDir`.
148
* Return 1 if we should ignore it.
150
static int ef_ignore_xattr(const FSEventStreamEventFlags ef)
152
static const FSEventStreamEventFlags mask =
153
kFSEventStreamEventFlagItemChangeOwner |
154
kFSEventStreamEventFlagItemCreated |
155
kFSEventStreamEventFlagItemFinderInfoMod |
156
kFSEventStreamEventFlagItemInodeMetaMod |
157
kFSEventStreamEventFlagItemModified |
158
kFSEventStreamEventFlagItemRemoved |
159
kFSEventStreamEventFlagItemRenamed |
160
kFSEventStreamEventFlagItemXattrMod |
161
kFSEventStreamEventFlagItemCloned;
163
return ((ef & mask) == kFSEventStreamEventFlagItemXattrMod);
167
* On MacOS we have to adjust for Unicode composition insensitivity
168
* (where NFC and NFD spellings are not respected). The different
169
* spellings are essentially aliases regardless of how the path is
170
* actually stored on the disk.
172
* This is related to "core.precomposeUnicode" (which wants to try
173
* to hide NFD completely and treat everything as NFC). Here, we
174
* don't know what the value the client has (or will have) for this
175
* config setting when they make a query, so assume the worst and
176
* emit both when the OS gives us an NFD path.
178
static void my_add_path(struct fsmonitor_batch *batch, const char *path)
182
/* add the NFC or NFD path as received from the OS */
183
fsmonitor_batch__add_path(batch, path);
185
/* if NFD, also add the corresponding NFC spelling */
186
composed = (char *)precompose_string_if_needed(path);
187
if (!composed || composed == path)
190
fsmonitor_batch__add_path(batch, composed);
195
static void fsevent_callback(ConstFSEventStreamRef streamRef UNUSED,
197
size_t num_of_events,
199
const FSEventStreamEventFlags event_flags[],
200
const FSEventStreamEventId event_ids[] UNUSED)
202
struct fsmonitor_daemon_state *state = ctx;
203
struct fsm_listen_data *data = state->listen_data;
204
char **paths = (char **)event_paths;
205
struct fsmonitor_batch *batch = NULL;
206
struct string_list cookie_list = STRING_LIST_INIT_DUP;
209
char *resolved = NULL;
210
struct strbuf tmp = STRBUF_INIT;
214
* Build a list of all filesystem changes into a private/local
215
* list and without holding any locks.
217
for (k = 0; k < num_of_events; k++) {
219
* On Mac, we receive an array of absolute paths.
222
resolved = fsmonitor__resolve_alias(paths[k], &state->alias);
229
* If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.
230
* Please don't log them to Trace2.
232
* trace_printf_key(&trace_fsmonitor, "Path: '%s'", path_k);
236
* If event[k] is marked as dropped, we assume that we have
237
* lost sync with the filesystem and should flush our cached
240
* [1] Abort/wake any client threads waiting for a cookie and
241
* flush the cached state data (the current token), and
242
* create a new token.
244
* [2] Discard the batch that we were locally building (since
245
* they are conceptually relative to the just flushed
248
if (ef_is_dropped(event_flags[k])) {
249
if (trace_pass_fl(&trace_fsmonitor))
250
log_flags_set(path_k, event_flags[k]);
252
fsmonitor_force_resync(state);
253
fsmonitor_batch__free_list(batch);
254
string_list_clear(&cookie_list, 0);
258
* We assume that any events that we received
259
* in this callback after this dropped event
260
* may still be valid, so we continue rather
261
* than break. (And just in case there is a
262
* delete of ".git" hiding in there.)
267
if (ef_is_root_changed(event_flags[k])) {
269
* The spelling of the pathname of the root directory
270
* has changed. This includes the name of the root
271
* directory itself or of any parent directory in the
274
* (There may be other conditions that throw this,
275
* but I couldn't find any information on it.)
277
* Force a shutdown now and avoid things getting
278
* out of sync. The Unix domain socket is inside
279
* the .git directory and a spelling change will make
280
* it hard for clients to rendezvous with us.
282
trace_printf_key(&trace_fsmonitor,
283
"event: root changed");
287
if (ef_ignore_xattr(event_flags[k])) {
288
trace_printf_key(&trace_fsmonitor,
289
"ignore-xattr: '%s', flags=0x%x",
290
path_k, event_flags[k]);
294
switch (fsmonitor_classify_path_absolute(state, path_k)) {
296
case IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX:
297
case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX:
298
/* special case cookie files within .git or gitdir */
300
/* Use just the filename of the cookie file. */
301
slash = find_last_dir_sep(path_k);
302
string_list_append(&cookie_list,
303
slash ? slash + 1 : path_k);
306
case IS_INSIDE_DOT_GIT:
307
case IS_INSIDE_GITDIR:
308
/* ignore all other paths inside of .git or gitdir */
314
* If .git directory is deleted or renamed away,
317
if (ef_is_root_delete(event_flags[k])) {
318
trace_printf_key(&trace_fsmonitor,
319
"event: gitdir removed");
322
if (ef_is_root_renamed(event_flags[k])) {
323
trace_printf_key(&trace_fsmonitor,
324
"event: gitdir renamed");
329
case IS_WORKDIR_PATH:
330
/* try to queue normal pathnames */
332
if (trace_pass_fl(&trace_fsmonitor))
333
log_flags_set(path_k, event_flags[k]);
336
* Because of the implicit "binning" (the
337
* kernel calls us at a given frequency) and
338
* de-duping (the kernel is free to combine
339
* multiple events for a given pathname), an
340
* individual fsevent could be marked as both
341
* a file and directory. Add it to the queue
342
* with both spellings so that the client will
343
* know how much to invalidate/refresh.
346
if (event_flags[k] & (kFSEventStreamEventFlagItemIsFile | kFSEventStreamEventFlagItemIsSymlink)) {
347
const char *rel = path_k +
348
state->path_worktree_watch.len + 1;
351
batch = fsmonitor_batch__new();
352
my_add_path(batch, rel);
355
if (event_flags[k] & kFSEventStreamEventFlagItemIsDir) {
356
const char *rel = path_k +
357
state->path_worktree_watch.len + 1;
360
strbuf_addstr(&tmp, rel);
361
strbuf_addch(&tmp, '/');
364
batch = fsmonitor_batch__new();
365
my_add_path(batch, tmp.buf);
370
case IS_OUTSIDE_CONE:
372
trace_printf_key(&trace_fsmonitor,
373
"ignoring '%s'", path_k);
379
fsmonitor_publish(state, batch, &cookie_list);
380
string_list_clear(&cookie_list, 0);
381
strbuf_release(&tmp);
386
fsmonitor_batch__free_list(batch);
387
string_list_clear(&cookie_list, 0);
389
pthread_mutex_lock(&data->dq_lock);
390
data->shutdown_style = FORCE_SHUTDOWN;
391
pthread_cond_broadcast(&data->dq_finished);
392
pthread_mutex_unlock(&data->dq_lock);
394
strbuf_release(&tmp);
399
* In the call to `FSEventStreamCreate()` to setup our watch, the
400
* `latency` argument determines the frequency of calls to our callback
401
* with new FS events. Too slow and events get dropped; too fast and
402
* we burn CPU unnecessarily. Since it is rather obscure, I don't
403
* think this needs to be a config setting. I've done extensive
404
* testing on my systems and chosen the value below. It gives good
405
* results and I've not seen any dropped events.
407
* With a latency of 0.1, I was seeing lots of dropped events during
408
* the "touch 100000" files test within t/perf/p7519, but with a
409
* latency of 0.001 I did not see any dropped events. So I'm going
410
* to assume that this is the "correct" value.
412
* https://developer.apple.com/documentation/coreservices/1443980-fseventstreamcreate
415
int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
417
FSEventStreamCreateFlags flags = kFSEventStreamCreateFlagNoDefer |
418
kFSEventStreamCreateFlagWatchRoot |
419
kFSEventStreamCreateFlagFileEvents;
420
FSEventStreamContext ctx = {
427
struct fsm_listen_data *data;
428
const void *dir_array[2];
430
CALLOC_ARRAY(data, 1);
431
state->listen_data = data;
433
data->cfsr_worktree_path = CFStringCreateWithCString(
434
NULL, state->path_worktree_watch.buf, kCFStringEncodingUTF8);
435
dir_array[data->nr_paths_watching++] = data->cfsr_worktree_path;
437
if (state->nr_paths_watching > 1) {
438
data->cfsr_gitdir_path = CFStringCreateWithCString(
439
NULL, state->path_gitdir_watch.buf,
440
kCFStringEncodingUTF8);
441
dir_array[data->nr_paths_watching++] = data->cfsr_gitdir_path;
444
data->cfar_paths_to_watch = CFArrayCreate(NULL, dir_array,
445
data->nr_paths_watching,
447
data->stream = FSEventStreamCreate(NULL, fsevent_callback, &ctx,
448
data->cfar_paths_to_watch,
449
kFSEventStreamEventIdSinceNow,
457
error(_("Unable to create FSEventStream."));
459
FREE_AND_NULL(state->listen_data);
463
void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
465
struct fsm_listen_data *data;
467
if (!state || !state->listen_data)
470
data = state->listen_data;
473
if (data->stream_started)
474
FSEventStreamStop(data->stream);
475
if (data->stream_scheduled)
476
FSEventStreamInvalidate(data->stream);
477
FSEventStreamRelease(data->stream);
481
dispatch_release(data->dq);
482
pthread_cond_destroy(&data->dq_finished);
483
pthread_mutex_destroy(&data->dq_lock);
485
FREE_AND_NULL(state->listen_data);
488
void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
490
struct fsm_listen_data *data;
492
data = state->listen_data;
494
pthread_mutex_lock(&data->dq_lock);
495
data->shutdown_style = SHUTDOWN_EVENT;
496
pthread_cond_broadcast(&data->dq_finished);
497
pthread_mutex_unlock(&data->dq_lock);
500
void fsm_listen__loop(struct fsmonitor_daemon_state *state)
502
struct fsm_listen_data *data;
504
data = state->listen_data;
506
pthread_mutex_init(&data->dq_lock, NULL);
507
pthread_cond_init(&data->dq_finished, NULL);
508
data->dq = dispatch_queue_create("FSMonitor", NULL);
510
FSEventStreamSetDispatchQueue(data->stream, data->dq);
511
data->stream_scheduled = 1;
513
if (!FSEventStreamStart(data->stream)) {
514
error(_("Failed to start the FSEventStream"));
515
goto force_error_stop_without_loop;
517
data->stream_started = 1;
519
pthread_mutex_lock(&data->dq_lock);
520
pthread_cond_wait(&data->dq_finished, &data->dq_lock);
521
pthread_mutex_unlock(&data->dq_lock);
523
switch (data->shutdown_style) {
524
case FORCE_ERROR_STOP:
525
state->listen_error_code = -1;
528
ipc_server_stop_async(state->ipc_server_data);
536
force_error_stop_without_loop:
537
state->listen_error_code = -1;
538
ipc_server_stop_async(state->ipc_server_data);