Skip to content

Commit aa755db

Browse files
jeffhostetlerdscho
authored andcommitted
Merge branch 'try-v4-fsmonitor-part4' into try-v4-fsmonitor
2 parents 58375ee + 596ea81 commit aa755db

12 files changed

+755
-121
lines changed

builtin/fsmonitor--daemon.c

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,18 +1168,20 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
11681168
*/
11691169
.uds_disallow_chdir = 0
11701170
};
1171+
int listener_started = 0;
1172+
int err = 0;
11711173

11721174
/*
11731175
* Start the IPC thread pool before the we've started the file
11741176
* system event listener thread so that we have the IPC handle
11751177
* before we need it.
11761178
*/
11771179
if (ipc_server_run_async(&state->ipc_server_data,
1178-
fsmonitor_ipc__get_path(), &ipc_opts,
1180+
state->path_ipc.buf, &ipc_opts,
11791181
handle_client, state))
11801182
return error_errno(
11811183
_("could not start IPC thread pool on '%s'"),
1182-
fsmonitor_ipc__get_path());
1184+
state->path_ipc.buf);
11831185

11841186
/*
11851187
* Start the fsmonitor listener thread to collect filesystem
@@ -1188,15 +1190,20 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
11881190
if (pthread_create(&state->listener_thread, NULL,
11891191
fsm_listen__thread_proc, state) < 0) {
11901192
ipc_server_stop_async(state->ipc_server_data);
1191-
ipc_server_await(state->ipc_server_data);
1192-
1193-
return error(_("could not start fsmonitor listener thread"));
1193+
err = error(_("could not start fsmonitor listener thread"));
1194+
goto cleanup;
11941195
}
1196+
listener_started = 1;
11951197

11961198
/*
11971199
* The daemon is now fully functional in background threads.
1200+
* Our primary thread should now just wait while the threads
1201+
* do all the work.
1202+
*/
1203+
cleanup:
1204+
/*
11981205
* Wait for the IPC thread pool to shutdown (whether by client
1199-
* request or from filesystem activity).
1206+
* request, from filesystem activity, or an error).
12001207
*/
12011208
ipc_server_await(state->ipc_server_data);
12021209

@@ -1205,23 +1212,30 @@ static int fsmonitor_run_daemon_1(struct fsmonitor_daemon_state *state)
12051212
* event from the IPC thread pool, but it doesn't hurt to tell
12061213
* it again. And wait for it to shutdown.
12071214
*/
1208-
fsm_listen__stop_async(state);
1209-
pthread_join(state->listener_thread, NULL);
1215+
if (listener_started) {
1216+
fsm_listen__stop_async(state);
1217+
pthread_join(state->listener_thread, NULL);
1218+
}
12101219

1211-
return state->error_code;
1220+
if (err)
1221+
return err;
1222+
if (state->listen_error_code)
1223+
return state->listen_error_code;
1224+
return 0;
12121225
}
12131226

12141227
static int fsmonitor_run_daemon(void)
12151228
{
12161229
struct fsmonitor_daemon_state state;
1230+
const char *home;
12171231
int err;
12181232

12191233
memset(&state, 0, sizeof(state));
12201234

12211235
hashmap_init(&state.cookies, cookies_cmp, NULL, 0);
12221236
pthread_mutex_init(&state.main_lock, NULL);
12231237
pthread_cond_init(&state.cookies_cond, NULL);
1224-
state.error_code = 0;
1238+
state.listen_error_code = 0;
12251239
state.current_token_data = fsmonitor_new_token_data();
12261240

12271241
/* Prepare to (recursively) watch the <worktree-root> directory. */
@@ -1283,6 +1297,15 @@ static int fsmonitor_run_daemon(void)
12831297

12841298
strbuf_addch(&state.path_cookie_prefix, '/');
12851299

1300+
/*
1301+
* We create a named-pipe or unix domain socket inside of the
1302+
* ".git" directory. (Well, on Windows, we base our named
1303+
* pipe in the NPFS on the absolute path of the git
1304+
* directory.)
1305+
*/
1306+
strbuf_init(&state.path_ipc, 0);
1307+
strbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));
1308+
12861309
/*
12871310
* Confirm that we can create platform-specific resources for the
12881311
* filesystem listener before we bother starting all the threads.
@@ -1292,6 +1315,23 @@ static int fsmonitor_run_daemon(void)
12921315
goto done;
12931316
}
12941317

1318+
/*
1319+
* CD out of the worktree root directory.
1320+
*
1321+
* The common Git startup mechanism causes our CWD to be the
1322+
* root of the worktree. On Windows, this causes our process
1323+
* to hold a locked handle on the CWD. This prevents the
1324+
* worktree from being moved or deleted while the daemon is
1325+
* running.
1326+
*
1327+
* We assume that our FS and IPC listener threads have either
1328+
* opened all of the handles that they need or will do
1329+
* everything using absolute paths.
1330+
*/
1331+
home = getenv("HOME");
1332+
if (home && *home && chdir(home))
1333+
die_errno("could not cd home '%s'", home);
1334+
12951335
err = fsmonitor_run_daemon_1(&state);
12961336

12971337
done:
@@ -1304,6 +1344,7 @@ static int fsmonitor_run_daemon(void)
13041344
strbuf_release(&state.path_worktree_watch);
13051345
strbuf_release(&state.path_gitdir_watch);
13061346
strbuf_release(&state.path_cookie_prefix);
1347+
strbuf_release(&state.path_ipc);
13071348

13081349
return err;
13091350
}

compat/fsmonitor/fsm-listen-darwin.c

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ void FSEventStreamRelease(FSEventStreamRef stream);
9999
#include "fsm-listen.h"
100100
#include "fsmonitor--daemon.h"
101101

102-
struct fsmonitor_daemon_backend_data
102+
struct fsm_listen_data
103103
{
104104
CFStringRef cfsr_worktree_path;
105105
CFStringRef cfsr_gitdir_path;
@@ -172,12 +172,17 @@ static void log_flags_set(const char *path, const FSEventStreamEventFlags flag)
172172
if (flag & kFSEventStreamEventFlagItemCloned)
173173
strbuf_addstr(&msg, "ItemCloned|");
174174

175-
trace_printf_key(&trace_fsmonitor, "fsevent: '%s', flags=%u %s",
175+
trace_printf_key(&trace_fsmonitor, "fsevent: '%s', flags=0x%x %s",
176176
path, flag, msg.buf);
177177

178178
strbuf_release(&msg);
179179
}
180180

181+
static int ef_is_root_changed(const FSEventStreamEventFlags ef)
182+
{
183+
return (ef & kFSEventStreamEventFlagRootChanged);
184+
}
185+
181186
static int ef_is_root_delete(const FSEventStreamEventFlags ef)
182187
{
183188
return (ef & kFSEventStreamEventFlagItemIsDir &&
@@ -197,6 +202,31 @@ static int ef_is_dropped(const FSEventStreamEventFlags ef)
197202
ef & kFSEventStreamEventFlagUserDropped);
198203
}
199204

205+
/*
206+
* If an `xattr` change is the only reason we received this event,
207+
* then silently ignore it. Git doesn't care about xattr's. We
208+
* have to be careful here because the kernel can combine multiple
209+
* events for a single path. And because events always have certain
210+
* bits set, such as `ItemIsFile` or `ItemIsDir`.
211+
*
212+
* Return 1 if we should ignore it.
213+
*/
214+
static int ef_ignore_xattr(const FSEventStreamEventFlags ef)
215+
{
216+
static const FSEventStreamEventFlags mask =
217+
kFSEventStreamEventFlagItemChangeOwner |
218+
kFSEventStreamEventFlagItemCreated |
219+
kFSEventStreamEventFlagItemFinderInfoMod |
220+
kFSEventStreamEventFlagItemInodeMetaMod |
221+
kFSEventStreamEventFlagItemModified |
222+
kFSEventStreamEventFlagItemRemoved |
223+
kFSEventStreamEventFlagItemRenamed |
224+
kFSEventStreamEventFlagItemXattrMod |
225+
kFSEventStreamEventFlagItemCloned;
226+
227+
return ((ef & mask) == kFSEventStreamEventFlagItemXattrMod);
228+
}
229+
200230
static void fsevent_callback(ConstFSEventStreamRef streamRef,
201231
void *ctx,
202232
size_t num_of_events,
@@ -205,7 +235,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
205235
const FSEventStreamEventId event_ids[])
206236
{
207237
struct fsmonitor_daemon_state *state = ctx;
208-
struct fsmonitor_daemon_backend_data *data = state->backend_data;
238+
struct fsm_listen_data *data = state->listen_data;
209239
char **paths = (char **)event_paths;
210240
struct fsmonitor_batch *batch = NULL;
211241
struct string_list cookie_list = STRING_LIST_INIT_DUP;
@@ -262,6 +292,33 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
262292
continue;
263293
}
264294

295+
if (ef_is_root_changed(event_flags[k])) {
296+
/*
297+
* The spelling of the pathname of the root directory
298+
* has changed. This includes the name of the root
299+
* directory itself of of any parent directory in the
300+
* path.
301+
*
302+
* (There may be other conditions that throw this,
303+
* but I couldn't find any information on it.)
304+
*
305+
* Force a shutdown now and avoid things getting
306+
* out of sync. The Unix domain socket is inside
307+
* the .git directory and a spelling change will make
308+
* it hard for clients to rendezvous with us.
309+
*/
310+
trace_printf_key(&trace_fsmonitor,
311+
"event: root changed");
312+
goto force_shutdown;
313+
}
314+
315+
if (ef_ignore_xattr(event_flags[k])) {
316+
trace_printf_key(&trace_fsmonitor,
317+
"ignore-xattr: '%s', flags=0x%x",
318+
path_k, event_flags[k]);
319+
continue;
320+
}
321+
265322
switch (fsmonitor_classify_path_absolute(state, path_k)) {
266323

267324
case IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX:
@@ -390,11 +447,11 @@ int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
390447
NULL,
391448
NULL
392449
};
393-
struct fsmonitor_daemon_backend_data *data;
450+
struct fsm_listen_data *data;
394451
const void *dir_array[2];
395452

396453
CALLOC_ARRAY(data, 1);
397-
state->backend_data = data;
454+
state->listen_data = data;
398455

399456
data->cfsr_worktree_path = CFStringCreateWithCString(
400457
NULL, state->path_worktree_watch.buf, kCFStringEncodingUTF8);
@@ -426,18 +483,18 @@ int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
426483
failed:
427484
error("Unable to create FSEventStream.");
428485

429-
FREE_AND_NULL(state->backend_data);
486+
FREE_AND_NULL(state->listen_data);
430487
return -1;
431488
}
432489

433490
void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
434491
{
435-
struct fsmonitor_daemon_backend_data *data;
492+
struct fsm_listen_data *data;
436493

437-
if (!state || !state->backend_data)
494+
if (!state || !state->listen_data)
438495
return;
439496

440-
data = state->backend_data;
497+
data = state->listen_data;
441498

442499
if (data->stream) {
443500
if (data->stream_started)
@@ -447,24 +504,24 @@ void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
447504
FSEventStreamRelease(data->stream);
448505
}
449506

450-
FREE_AND_NULL(state->backend_data);
507+
FREE_AND_NULL(state->listen_data);
451508
}
452509

453510
void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
454511
{
455-
struct fsmonitor_daemon_backend_data *data;
512+
struct fsm_listen_data *data;
456513

457-
data = state->backend_data;
514+
data = state->listen_data;
458515
data->shutdown_style = SHUTDOWN_EVENT;
459516

460517
CFRunLoopStop(data->rl);
461518
}
462519

463520
void fsm_listen__loop(struct fsmonitor_daemon_state *state)
464521
{
465-
struct fsmonitor_daemon_backend_data *data;
522+
struct fsm_listen_data *data;
466523

467-
data = state->backend_data;
524+
data = state->listen_data;
468525

469526
data->rl = CFRunLoopGetCurrent();
470527

@@ -481,7 +538,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
481538

482539
switch (data->shutdown_style) {
483540
case FORCE_ERROR_STOP:
484-
state->error_code = -1;
541+
state->listen_error_code = -1;
485542
/* fall thru */
486543
case FORCE_SHUTDOWN:
487544
ipc_server_stop_async(state->ipc_server_data);
@@ -493,7 +550,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
493550
return;
494551

495552
force_error_stop_without_loop:
496-
state->error_code = -1;
553+
state->listen_error_code = -1;
497554
ipc_server_stop_async(state->ipc_server_data);
498555
return;
499556
}

0 commit comments

Comments
 (0)