summaryrefslogtreecommitdiff
path: root/compat/fsmonitor/fsm-listen-darwin.c
diff options
context:
space:
mode:
Diffstat (limited to 'compat/fsmonitor/fsm-listen-darwin.c')
-rw-r--r--compat/fsmonitor/fsm-listen-darwin.c124
1 files changed, 105 insertions, 19 deletions
diff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c
index 0741fe8..8e208e8 100644
--- a/compat/fsmonitor/fsm-listen-darwin.c
+++ b/compat/fsmonitor/fsm-listen-darwin.c
@@ -27,7 +27,7 @@
#include "fsm-listen.h"
#include "fsmonitor--daemon.h"
-struct fsmonitor_daemon_backend_data
+struct fsm_listen_data
{
CFStringRef cfsr_worktree_path;
CFStringRef cfsr_gitdir_path;
@@ -100,12 +100,17 @@ static void log_flags_set(const char *path, const FSEventStreamEventFlags flag)
if (flag & kFSEventStreamEventFlagItemCloned)
strbuf_addstr(&msg, "ItemCloned|");
- trace_printf_key(&trace_fsmonitor, "fsevent: '%s', flags=%u %s",
+ trace_printf_key(&trace_fsmonitor, "fsevent: '%s', flags=0x%x %s",
path, flag, msg.buf);
strbuf_release(&msg);
}
+static int ef_is_root_changed(const FSEventStreamEventFlags ef)
+{
+ return (ef & kFSEventStreamEventFlagRootChanged);
+}
+
static int ef_is_root_delete(const FSEventStreamEventFlags ef)
{
return (ef & kFSEventStreamEventFlagItemIsDir &&
@@ -125,6 +130,60 @@ static int ef_is_dropped(const FSEventStreamEventFlags ef)
ef & kFSEventStreamEventFlagUserDropped);
}
+/*
+ * If an `xattr` change is the only reason we received this event,
+ * then silently ignore it. Git doesn't care about xattr's. We
+ * have to be careful here because the kernel can combine multiple
+ * events for a single path. And because events always have certain
+ * bits set, such as `ItemIsFile` or `ItemIsDir`.
+ *
+ * Return 1 if we should ignore it.
+ */
+static int ef_ignore_xattr(const FSEventStreamEventFlags ef)
+{
+ static const FSEventStreamEventFlags mask =
+ kFSEventStreamEventFlagItemChangeOwner |
+ kFSEventStreamEventFlagItemCreated |
+ kFSEventStreamEventFlagItemFinderInfoMod |
+ kFSEventStreamEventFlagItemInodeMetaMod |
+ kFSEventStreamEventFlagItemModified |
+ kFSEventStreamEventFlagItemRemoved |
+ kFSEventStreamEventFlagItemRenamed |
+ kFSEventStreamEventFlagItemXattrMod |
+ kFSEventStreamEventFlagItemCloned;
+
+ return ((ef & mask) == kFSEventStreamEventFlagItemXattrMod);
+}
+
+/*
+ * On MacOS we have to adjust for Unicode composition insensitivity
+ * (where NFC and NFD spellings are not respected). The different
+ * spellings are essentially aliases regardless of how the path is
+ * actually stored on the disk.
+ *
+ * This is related to "core.precomposeUnicode" (which wants to try
+ * to hide NFD completely and treat everything as NFC). Here, we
+ * don't know what the value the client has (or will have) for this
+ * config setting when they make a query, so assume the worst and
+ * emit both when the OS gives us an NFD path.
+ */
+static void my_add_path(struct fsmonitor_batch *batch, const char *path)
+{
+ char *composed;
+
+ /* add the NFC or NFD path as received from the OS */
+ fsmonitor_batch__add_path(batch, path);
+
+ /* if NFD, also add the corresponding NFC spelling */
+ composed = (char *)precompose_string_if_needed(path);
+ if (!composed || composed == path)
+ return;
+
+ fsmonitor_batch__add_path(batch, composed);
+ free(composed);
+}
+
+
static void fsevent_callback(ConstFSEventStreamRef streamRef,
void *ctx,
size_t num_of_events,
@@ -133,7 +192,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
const FSEventStreamEventId event_ids[])
{
struct fsmonitor_daemon_state *state = ctx;
- struct fsmonitor_daemon_backend_data *data = state->backend_data;
+ struct fsm_listen_data *data = state->listen_data;
char **paths = (char **)event_paths;
struct fsmonitor_batch *batch = NULL;
struct string_list cookie_list = STRING_LIST_INIT_DUP;
@@ -190,6 +249,33 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
continue;
}
+ if (ef_is_root_changed(event_flags[k])) {
+ /*
+ * The spelling of the pathname of the root directory
+ * has changed. This includes the name of the root
+ * directory itself or of any parent directory in the
+ * path.
+ *
+ * (There may be other conditions that throw this,
+ * but I couldn't find any information on it.)
+ *
+ * Force a shutdown now and avoid things getting
+ * out of sync. The Unix domain socket is inside
+ * the .git directory and a spelling change will make
+ * it hard for clients to rendezvous with us.
+ */
+ trace_printf_key(&trace_fsmonitor,
+ "event: root changed");
+ goto force_shutdown;
+ }
+
+ if (ef_ignore_xattr(event_flags[k])) {
+ trace_printf_key(&trace_fsmonitor,
+ "ignore-xattr: '%s', flags=0x%x",
+ path_k, event_flags[k]);
+ continue;
+ }
+
switch (fsmonitor_classify_path_absolute(state, path_k)) {
case IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX:
@@ -248,7 +334,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
if (!batch)
batch = fsmonitor_batch__new();
- fsmonitor_batch__add_path(batch, rel);
+ my_add_path(batch, rel);
}
if (event_flags[k] & kFSEventStreamEventFlagItemIsDir) {
@@ -261,7 +347,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
if (!batch)
batch = fsmonitor_batch__new();
- fsmonitor_batch__add_path(batch, tmp.buf);
+ my_add_path(batch, tmp.buf);
}
break;
@@ -318,11 +404,11 @@ int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
NULL,
NULL
};
- struct fsmonitor_daemon_backend_data *data;
+ struct fsm_listen_data *data;
const void *dir_array[2];
CALLOC_ARRAY(data, 1);
- state->backend_data = data;
+ state->listen_data = data;
data->cfsr_worktree_path = CFStringCreateWithCString(
NULL, state->path_worktree_watch.buf, kCFStringEncodingUTF8);
@@ -342,7 +428,7 @@ int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
data->cfar_paths_to_watch,
kFSEventStreamEventIdSinceNow,
0.001, flags);
- if (data->stream == NULL)
+ if (!data->stream)
goto failed;
/*
@@ -354,18 +440,18 @@ int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
failed:
error(_("Unable to create FSEventStream."));
- FREE_AND_NULL(state->backend_data);
+ FREE_AND_NULL(state->listen_data);
return -1;
}
void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
{
- struct fsmonitor_daemon_backend_data *data;
+ struct fsm_listen_data *data;
- if (!state || !state->backend_data)
+ if (!state || !state->listen_data)
return;
- data = state->backend_data;
+ data = state->listen_data;
if (data->stream) {
if (data->stream_started)
@@ -375,14 +461,14 @@ void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
FSEventStreamRelease(data->stream);
}
- FREE_AND_NULL(state->backend_data);
+ FREE_AND_NULL(state->listen_data);
}
void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
{
- struct fsmonitor_daemon_backend_data *data;
+ struct fsm_listen_data *data;
- data = state->backend_data;
+ data = state->listen_data;
data->shutdown_style = SHUTDOWN_EVENT;
CFRunLoopStop(data->rl);
@@ -390,9 +476,9 @@ void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
void fsm_listen__loop(struct fsmonitor_daemon_state *state)
{
- struct fsmonitor_daemon_backend_data *data;
+ struct fsm_listen_data *data;
- data = state->backend_data;
+ data = state->listen_data;
data->rl = CFRunLoopGetCurrent();
@@ -409,7 +495,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
switch (data->shutdown_style) {
case FORCE_ERROR_STOP:
- state->error_code = -1;
+ state->listen_error_code = -1;
/* fall thru */
case FORCE_SHUTDOWN:
ipc_server_stop_async(state->ipc_server_data);
@@ -421,7 +507,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state)
return;
force_error_stop_without_loop:
- state->error_code = -1;
+ state->listen_error_code = -1;
ipc_server_stop_async(state->ipc_server_data);
return;
}