{"thread":{"id":"58324","subject":"[PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","startedAt":"2022-08-18T20:48:34Z","lastAt":"2023-01-30T10:26:41Z","messageCount":170,"participants":["Eric DeCosta via GitGitGadget","Junio C Hamano","Johannes Schindelin","Jeff Hostetler","Eric Sunshine","Eric DeCosta","Torsten Bögershausen","edecosta via GitGitGadget","Ævar Arnfjörð Bjarmason","Ramsay Jones"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"461467","messageId":"pull.1326.git.1660855703816.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":null,"subject":"[PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-18T20:48:23Z","receivedAt":"2022-08-18T20:48:34Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nThough perhaps not common, there are uses cases where users have large,\nnetwork-mounted repos. Having the ability to run fsmonitor against\nnetwork paths would benefit those users.\n\nAs a first step towards enabling fsmonitor to work against\nnetwork-mounted repos, a configuration option, 'fsmonitor.allowRemote'\nwas introduced for Windows. Setting this option to true will override\nthe default behavior (erroring-out) when a network-mounted repo is\ndetected by fsmonitor. In order for macOS to have parity with Windows,\nthe same option is now introduced for macOS.\n\nThe the added wrinkle being that the Unix domain socket (UDS) file\nused for IPC cannot be created in a network location; instead the\ntemporary directory is used.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n    fsmonitor: option to allow fsmonitor to run against network-mounted\n    repos\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\n compat/fsmonitor/fsm-settings-darwin.c | 77 ++++++++++++++++++++++----\n fsmonitor-ipc.c                        | 47 +++++++++++++++-\n fsmonitor-ipc.h                        |  6 ++\n 3 files changed, 117 insertions(+), 13 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..9e2ea3b90cc 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -2,10 +2,28 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-ipc.h\"\n #include \"fsmonitor.h\"\n #include <sys/param.h>\n #include <sys/mount.h>\n \n+/*\n+ * Check if monitoring remote working directories is allowed.\n+ *\n+ * By default, monitoring remote working directories is\n+ * disabled.  Users may override this behavior in enviroments where\n+ * they have proper support.\n+ */\n+static int check_config_allowremote(struct repository *r)\n+{\n+\tint allow;\n+\n+\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n+\t\treturn allow;\n+\n+\treturn -1; /* fsmonitor.allowremote not set */\n+}\n+\n /*\n  * [1] Remote working directories are problematic for FSMonitor.\n  *\n@@ -27,24 +45,22 @@\n  * In theory, the above issues need to be addressed whether we are\n  * using the Hook or IPC API.\n  *\n+ * So (for now at least), mark remote working directories as\n+ * incompatible by default.\n+ *\n  * For the builtin FSMonitor, we create the Unix domain socket for the\n- * IPC in the .git directory.  If the working directory is remote,\n- * then the socket will be created on the remote file system.  This\n- * can fail if the remote file system does not support UDS file types\n- * (e.g. smbfs to a Windows server) or if the remote kernel does not\n- * allow a non-local process to bind() the socket.  (These problems\n- * could be fixed by moving the UDS out of the .git directory and to a\n- * well-known local directory on the client machine, but care should\n- * be taken to ensure that $HOME is actually local and not a managed\n- * file share.)\n+ * IPC in the temporary directory.  If the temporary directory is\n+ * remote, then the socket will be created on the remote file system.\n+ * This can fail if the remote file system does not support UDS file\n+ * types (e.g. smbfs to a Windows server) or if the remote kernel does\n+ * not allow a non-local process to bind() the socket.\n  *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n+ * Therefore remote UDS locations are marked as incompatible.\n  *\n  *\n  * [2] FAT32 and NTFS working directories are problematic too.\n  *\n- * The builtin FSMonitor uses a Unix domain socket in the .git\n+ * The builtin FSMonitor uses a Unix domain socket in the temporary\n  * directory for IPC.  These Windows drive formats do not support\n  * Unix domain sockets, so mark them as incompatible for the daemon.\n  *\n@@ -65,6 +81,39 @@ static enum fsmonitor_reason check_volume(struct repository *r)\n \t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n \t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n \n+\tif (!(fs.f_flags & MNT_LOCAL)) {\n+\t\tswitch (check_config_allowremote(r)) {\n+\t\tcase 0: /* config overrides and disables */\n+\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\tcase 1: /* config overrides and enables */\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\tbreak; /* config has no opinion */\n+\t\t}\n+\n+\t\treturn FSMONITOR_REASON_REMOTE;\n+\t}\n+\n+\treturn FSMONITOR_REASON_OK;\n+}\n+\n+static enum fsmonitor_reason check_uds_volume(void)\n+{\n+\tstruct statfs fs;\n+\tconst char *path = fsmonitor_ipc__get_path();\n+\n+\tif (statfs(path, &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\n \tif (!(fs.f_flags & MNT_LOCAL))\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n@@ -85,5 +134,9 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n+\treason = check_uds_volume();\n+\tif (reason != FSMONITOR_REASON_OK)\n+\t\treturn reason;\n+\n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..6e9b40a03d5 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -4,6 +4,7 @@\n #include \"fsmonitor-ipc.h\"\n #include \"run-command.h\"\n #include \"strbuf.h\"\n+#include \"tempfile.h\"\n #include \"trace2.h\"\n \n #ifndef HAVE_FSMONITOR_DAEMON_BACKEND\n@@ -47,7 +48,51 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n+GIT_PATH_FUNC(fsmonitor_ipc__get_pathfile, \"fsmonitor--daemon.ipc\")\n+\n+static char *gen_ipc_file(void)\n+{\n+\tchar *retval = NULL;\n+\tstruct tempfile *ipc;\n+\n+\tconst char *ipc_file = fsmonitor_ipc__get_pathfile();\n+\tFILE *fp = fopen(ipc_file, \"w\");\n+\n+\tif (!fp)\n+\t\tdie_errno(\"error opening '%s'\", ipc_file);\n+\tipc = mks_tempfile_t(\"fsmonitor_ipc_XXXXXX\");\n+\tstrbuf_write(&ipc->filename, fp);\n+\tfclose(fp);\n+\tretval = strbuf_detach(&ipc->filename, NULL);\n+\tstrbuf_release(&ipc->filename);\n+\treturn retval;\n+}\n+\n+const char *fsmonitor_ipc__get_path(void)\n+{\n+\tchar *retval = NULL;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tconst char *ipc_file = fsmonitor_ipc__get_pathfile();\n+\tFILE *fp = fopen(ipc_file, \"r\");\n+\n+\tif (!fp) {\n+\t\treturn gen_ipc_file();\n+\t} else {\n+\t\tstrbuf_read(&sb, fileno(fp), 0);\n+\t\tfclose(fp);\n+\t\tfp = fopen(sb.buf, \"r\");\n+\t\tif (!fp) { /* generate new file */\n+\t\t\tif (unlink(ipc_file) < 0)\n+\t\t\t\tdie_errno(\"could not remove '%s'\", ipc_file);\n+\t\t\treturn gen_ipc_file();\n+\t\t}\n+\t\tfclose(fp);\n+\t\tretval = strbuf_detach(&sb, NULL);\n+\t\tstrbuf_release(&sb);\n+\t\treturn retval;\n+\t}\n+}\n \n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..63277dea39e 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -18,6 +18,12 @@ int fsmonitor_ipc__is_supported(void);\n  */\n const char *fsmonitor_ipc__get_path(void);\n \n+/*\n+ * Returns the pathname to the file that contains the pathname to the\n+ * IPC named pipe or Unix domain socket.\n+ */\n+const char *fsmonitor_ipc__get_pathfile(void);\n+\n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n  * listening on the IPC pipe/socket.\n\nbase-commit: 9bf691b78cf906751e65d65ba0c6ffdcd9a5a12c\n-- \ngitgitgadget\n"},{"id":"461470","messageId":"xmqq5yipnsl6.fsf@gitster.g","threadId":"58324","inReplyTo":"pull.1326.git.1660855703816.gitgitgadget@gmail.com","subject":"Re: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-08-18T21:35:01Z","receivedAt":"2022-08-18T21:35:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n>\n> Though perhaps not common, there are uses cases where users have large,\n> network-mounted repos. Having the ability to run fsmonitor against\n> network paths would benefit those users.\n>\n> As a first step towards enabling fsmonitor to work ...\n\nDidn't we already see the first step and got it reviewed?  I think\nI've already merged the last round to 'next'.\n\nPuzzled...\n"},{"id":"461471","messageId":"xmqq1qtdnsfg.fsf@gitster.g","threadId":"58324","inReplyTo":"xmqq5yipnsl6.fsf@gitster.g","subject":"Re: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-08-18T21:38:27Z","receivedAt":"2022-08-18T21:38:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> \"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> From: Eric DeCosta <edecosta@mathworks.com>\n>>\n>> Though perhaps not common, there are uses cases where users have large,\n>> network-mounted repos. Having the ability to run fsmonitor against\n>> network paths would benefit those users.\n>>\n>> As a first step towards enabling fsmonitor to work ...\n>\n> Didn't we already see the first step and got it reviewed?  I think\n> I've already merged the last round to 'next'.\n>\n> Puzzled...\n\nAh, OK, so this is not exactly the \"first step\", but builds on top\nof the previous one?  At least the patch needs a better title to\nmake it distinguishable from the other one.\n\nWill queue to wait for others to review.\n\nThanks.\n"},{"id":"461525","messageId":"4q6248n0-nqr5-p5pp-64s3-qq7nr53q01op@tzk.qr","threadId":"58324","inReplyTo":"pull.1326.git.1660855703816.gitgitgadget@gmail.com","subject":"Re: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2022-08-19T10:05:17Z","receivedAt":"2022-08-19T10:05:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric,\n\nOn Thu, 18 Aug 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n>\n> Though perhaps not common, there are uses cases where users have large,\n> network-mounted repos. Having the ability to run fsmonitor against\n> network paths would benefit those users.\n>\n> As a first step towards enabling fsmonitor to work against\n> network-mounted repos, a configuration option, 'fsmonitor.allowRemote'\n> was introduced for Windows.\n\nIf you start the commit message along the following lines, it might be\neasier/quicker to grok the context for the keen reader:\n\n\tIn 85dc0da6dcf (fsmonitor: option to allow fsmonitor to run against\n\tnetwork-mounted repos, 2022-08-11), the Windows backend of the\n\tFSMonitor learned to allow running on network drives, via the\n\t`fsmonitor.allowRemote` config setting.\n\n> Setting this option to true will override the default behavior\n> (erroring-out) when a network-mounted repo is detected by fsmonitor. In\n> order for macOS to have parity with Windows, the same option is now\n> introduced for macOS.\n>\n> The the added wrinkle being that the Unix domain socket (UDS) file\n> used for IPC cannot be created in a network location; instead the\n> temporary directory is used.\n\nThank you very much for this note, after a cursory read I expected that\npart of the code to be a left-over from some \"We know better than the\nuser\" type of automatic default, and this paragraph definitely helped me\novercome that expectation.\n\n>\n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n>     fsmonitor: option to allow fsmonitor to run against network-mounted\n>     repos\n>\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v1\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v1\n> Pull-Request: https://github.com/gitgitgadget/git/pull/1326\n>\n>  compat/fsmonitor/fsm-settings-darwin.c | 77 ++++++++++++++++++++++----\n>  fsmonitor-ipc.c                        | 47 +++++++++++++++-\n>  fsmonitor-ipc.h                        |  6 ++\n>  3 files changed, 117 insertions(+), 13 deletions(-)\n\nI am somewhat puzzled that this has no corresponding change to\n`Documentation/`.\n\nAnd now I realize that this was the case also for the patch adding\n`fsmonitor.allowRemote` support for Windows.\n\nCould I ask you to add a patch to document this config setting?\n\n>\n> diff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\n> index efc732c0f31..9e2ea3b90cc 100644\n> --- a/compat/fsmonitor/fsm-settings-darwin.c\n> +++ b/compat/fsmonitor/fsm-settings-darwin.c\n> @@ -2,10 +2,28 @@\n>  #include \"config.h\"\n>  #include \"repository.h\"\n>  #include \"fsmonitor-settings.h\"\n> +#include \"fsmonitor-ipc.h\"\n>  #include \"fsmonitor.h\"\n>  #include <sys/param.h>\n>  #include <sys/mount.h>\n>\n> +/*\n> + * Check if monitoring remote working directories is allowed.\n> + *\n> + * By default, monitoring remote working directories is\n> + * disabled.  Users may override this behavior in enviroments where\n> + * they have proper support.\n> + */\n> +static int check_config_allowremote(struct repository *r)\n> +{\n> +\tint allow;\n> +\n> +\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n> +\t\treturn allow;\n> +\n> +\treturn -1; /* fsmonitor.allowremote not set */\n> +}\n> +\n>  /*\n>   * [1] Remote working directories are problematic for FSMonitor.\n>   *\n> @@ -27,24 +45,22 @@\n>   * In theory, the above issues need to be addressed whether we are\n>   * using the Hook or IPC API.\n>   *\n> + * So (for now at least), mark remote working directories as\n> + * incompatible by default.\n> + *\n\nThis was moved up, okay.\n\n>   * For the builtin FSMonitor, we create the Unix domain socket for the\n> - * IPC in the .git directory.  If the working directory is remote,\n> - * then the socket will be created on the remote file system.  This\n> - * can fail if the remote file system does not support UDS file types\n> - * (e.g. smbfs to a Windows server) or if the remote kernel does not\n> - * allow a non-local process to bind() the socket.  (These problems\n> - * could be fixed by moving the UDS out of the .git directory and to a\n> - * well-known local directory on the client machine, but care should\n> - * be taken to ensure that $HOME is actually local and not a managed\n> - * file share.)\n> + * IPC in the temporary directory.  If the temporary directory is\n\nThis is incorrect. It is still the `.git` directory in the common case,\nnot a temporary directory.\n\n> + * remote, then the socket will be created on the remote file system.\n> + * This can fail if the remote file system does not support UDS file\n> + * types (e.g. smbfs to a Windows server) or if the remote kernel does\n> + * not allow a non-local process to bind() the socket.\n>   *\n> - * So (for now at least), mark remote working directories as\n> - * incompatible.\n> + * Therefore remote UDS locations are marked as incompatible.\n>   *\n>   *\n>   * [2] FAT32 and NTFS working directories are problematic too.\n\nDoesn't this patch address this, too? See below for more on that.\n\n>   *\n> - * The builtin FSMonitor uses a Unix domain socket in the .git\n> + * The builtin FSMonitor uses a Unix domain socket in the temporary\n>   * directory for IPC.  These Windows drive formats do not support\n>   * Unix domain sockets, so mark them as incompatible for the daemon.\n>   *\n> @@ -65,6 +81,39 @@ static enum fsmonitor_reason check_volume(struct repository *r)\n>  \t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n>  \t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n>\n> +\tif (!(fs.f_flags & MNT_LOCAL)) {\n> +\t\tswitch (check_config_allowremote(r)) {\n> +\t\tcase 0: /* config overrides and disables */\n> +\t\t\treturn FSMONITOR_REASON_REMOTE;\n> +\t\tcase 1: /* config overrides and enables */\n> +\t\t\treturn FSMONITOR_REASON_OK;\n> +\t\tdefault:\n> +\t\t\tbreak; /* config has no opinion */\n> +\t\t}\n> +\n> +\t\treturn FSMONITOR_REASON_REMOTE;\n> +\t}\n\nThis `switch()` statement sounds like a verbose way to say the same as:\n\n\t\treturn check_config_allowremote(r) == 1 ?\n\t\t\tFSMONITOR_REASON_OK : FSMONITOR_REASON_REMOTE;\n\n> +\n> +\treturn FSMONITOR_REASON_OK;\n> +}\n> +\n> +static enum fsmonitor_reason check_uds_volume(void)\n\nWhat's an UDS volume? Do you mean to say \"Unix Domain Socket Volume\"?\n\nIf so, it would be better to turn this into a function called\n`filesystem_supports_unix_sockets()` and to return an `int`, 1 for \"yes\",\n0 for \"no\".\n\n> +{\n> +\tstruct statfs fs;\n> +\tconst char *path = fsmonitor_ipc__get_path();\n> +\n> +\tif (statfs(path, &fs) == -1) {\n> +\t\tint saved_errno = errno;\n> +\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n> +\t\t\t\t path, strerror(saved_errno));\n> +\t\terrno = saved_errno;\n> +\t\treturn FSMONITOR_REASON_ERROR;\n> +\t}\n> +\n> +\ttrace_printf_key(&trace_fsmonitor,\n> +\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n> +\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n> +\n>  \tif (!(fs.f_flags & MNT_LOCAL))\n>  \t\treturn FSMONITOR_REASON_REMOTE;\n>\n> @@ -85,5 +134,9 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n\nIt is unfortunate that the diff hunk stops here, and mails lack a button\nto increase the diff context. In this instance, the hidden part of the\n`check_volume()` function is quite interesting: it returns\n`FSMONITOR_REASON_NOSOCKETS` for `msdos` and `ntfs` file systems.\n\nWhich means that your patch changes behavior not only for remote file\nsystems, but also for local ones without support for Unix sockets.\n\nTo heed the principle of separation of concerns, please do split out that\npart. I would recommend to make it the first patch to support\n`msdos`/`ntfs` file systems (by registering the Unix sockets in a\ntemporary directory instead of the `.git/` directory). The second patch\ncan then introduce support for `fsmonitor.allowRemote` on macOS on top of\nthe first patch.\n\n>  \tif (reason != FSMONITOR_REASON_OK)\n>  \t\treturn reason;\n>\n> +\treason = check_uds_volume();\n> +\tif (reason != FSMONITOR_REASON_OK)\n> +\t\treturn reason;\n> +\n>  \treturn FSMONITOR_REASON_OK;\n>  }\n> diff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\n> index 789e7397baa..6e9b40a03d5 100644\n> --- a/fsmonitor-ipc.c\n> +++ b/fsmonitor-ipc.c\n> @@ -4,6 +4,7 @@\n>  #include \"fsmonitor-ipc.h\"\n>  #include \"run-command.h\"\n>  #include \"strbuf.h\"\n> +#include \"tempfile.h\"\n>  #include \"trace2.h\"\n>\n>  #ifndef HAVE_FSMONITOR_DAEMON_BACKEND\n> @@ -47,7 +48,51 @@ int fsmonitor_ipc__is_supported(void)\n>  \treturn 1;\n>  }\n>\n> -GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n> +GIT_PATH_FUNC(fsmonitor_ipc__get_pathfile, \"fsmonitor--daemon.ipc\")\n\nWhy rename this? That's unnecessary chatter in the patch. Let's avoid such\nthings in the future, it only costs reviewers time.\n\n> +\n> +static char *gen_ipc_file(void)\n> +{\n> +\tchar *retval = NULL;\n> +\tstruct tempfile *ipc;\n> +\n> +\tconst char *ipc_file = fsmonitor_ipc__get_pathfile();\n> +\tFILE *fp = fopen(ipc_file, \"w\");\n> +\n> +\tif (!fp)\n> +\t\tdie_errno(\"error opening '%s'\", ipc_file);\n> +\tipc = mks_tempfile_t(\"fsmonitor_ipc_XXXXXX\");\n> +\tstrbuf_write(&ipc->filename, fp);\n> +\tfclose(fp);\n> +\tretval = strbuf_detach(&ipc->filename, NULL);\n> +\tstrbuf_release(&ipc->filename);\n> +\treturn retval;\n> +}\n> +\n> +const char *fsmonitor_ipc__get_path(void)\n> +{\n> +\tchar *retval = NULL;\n> +\tstruct strbuf sb = STRBUF_INIT;\n> +\n> +\tconst char *ipc_file = fsmonitor_ipc__get_pathfile();\n> +\tFILE *fp = fopen(ipc_file, \"r\");\n> +\n> +\tif (!fp) {\n> +\t\treturn gen_ipc_file();\n> +\t} else {\n> +\t\tstrbuf_read(&sb, fileno(fp), 0);\n> +\t\tfclose(fp);\n> +\t\tfp = fopen(sb.buf, \"r\");\n> +\t\tif (!fp) { /* generate new file */\n> +\t\t\tif (unlink(ipc_file) < 0)\n> +\t\t\t\tdie_errno(\"could not remove '%s'\", ipc_file);\n> +\t\t\treturn gen_ipc_file();\n> +\t\t}\n> +\t\tfclose(fp);\n> +\t\tretval = strbuf_detach(&sb, NULL);\n> +\t\tstrbuf_release(&sb);\n> +\t\treturn retval;\n> +\t}\n> +}\n\nI am afraid I do not understand how this code can guarantee a fixed path\nfor the Unix domain socket.\n\nIt _needs_ to be fixed so that a singleton daemon can run and listen on\nit, and an arbitrary number of Git clients can connect to it.\n\nIf it is not fixed, you will cause Git to quite possibly start a new\nFSMonitor daemon for every invocation that wants to connect to an\nFSMonitor daemon.\n\nThis means that the path of the Unix socket needs to have a 1:1\nrelationship to the path of the `.git/` directory. If you install it in\nthat directory, that invariant is naturally fulfilled. If you want to\ninstall it elsewhere, you will have to come up with a reliable way to\nguarantee that connection.\n\nOne option would be to install the Unix sockets in the home directory,\nunder a name like `.git-fsmonitor-<hash>` where the <hash> is e.g. a\nSHA-1/SHA-256 of the canonicalized path of the `.git/` directory.\n\n>\n>  enum ipc_active_state fsmonitor_ipc__get_state(void)\n>  {\n> diff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\n> index b6a7067c3af..63277dea39e 100644\n> --- a/fsmonitor-ipc.h\n> +++ b/fsmonitor-ipc.h\n> @@ -18,6 +18,12 @@ int fsmonitor_ipc__is_supported(void);\n>   */\n>  const char *fsmonitor_ipc__get_path(void);\n>\n> +/*\n> + * Returns the pathname to the file that contains the pathname to the\n> + * IPC named pipe or Unix domain socket.\n> + */\n> +const char *fsmonitor_ipc__get_pathfile(void);\n> +\n>  /*\n>   * Try to determine whether there is a `git-fsmonitor--daemon` process\n>   * listening on the IPC pipe/socket.\n\nThank you for working on this, also on the Windows side. It definitely\nhelps!\n\nCiao,\nDscho\n"},{"id":"461580","messageId":"a63bda82-3dfc-44e0-8ecf-ca340d0a95b4@jeffhostetler.com","threadId":"58324","inReplyTo":"pull.1326.git.1660855703816.gitgitgadget@gmail.com","subject":"Re: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-08-19T16:50:19Z","receivedAt":"2022-08-19T17:32:57Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 8/18/22 4:48 PM, Eric DeCosta via GitGitGadget wrote:\n> From: Eric DeCosta <edecosta@mathworks.com>\n> \n> Though perhaps not common, there are uses cases where users have large,\n> network-mounted repos. Having the ability to run fsmonitor against\n> network paths would benefit those users.\n> \n> As a first step towards enabling fsmonitor to work against\n> network-mounted repos, a configuration option, 'fsmonitor.allowRemote'\n> was introduced for Windows. Setting this option to true will override\n> the default behavior (erroring-out) when a network-mounted repo is\n> detected by fsmonitor. In order for macOS to have parity with Windows,\n> the same option is now introduced for macOS.\n\nWe might also say that this config option only allows FSMonitor\nto TRY to consider using a network-mounted repo.  And that this\nability is considered experimental until sufficient testing can\nbe completed and we can determine the combinations of\n{ client os } x { server os } x { remote access } x { file system type }\nthat are known to work or not work and we can update the defaults\nand the documentation accordingly.\n\nFor example, on a MacOS client, we expect the local \"fseventsd\" service\nto send us recursive events on all files and sub directories under the\nrepo root.  If the server is a Linux machine (which doesn't really do\nrecursive events), does exporting the FS from the server over NFS or SMB\n(or whatever) cause the Linux host to send enough information to the\nclient machine for fseventsd to synthesize the recursive event stream\nlocally that FSMonitor expects.  It might.  It might not.  That\ncombination should be tested (along with a lot of other combinations).\n\nBut again, this patch is just about allowing the (informed?) user to\ntry it and begin testing various combinations.\n\n\n> \n> The the added wrinkle being that the Unix domain socket (UDS) file\n> used for IPC cannot be created in a network location; instead the\n> temporary directory is used.\n\nThis scares me a bit.  I put the socket in the .git directory\nso that we are guaranteed that only one daemon will run on the\nrepository and that all clients will know where to find that socket\n(if it exists).\n\nIt looks like you're creating the UDS using a tmp pathname and\nwriting the pathname to the actual .git/fsmonitor--daemon.ipc FILE.\nThis adds a layer of indirection and is prone to races.\n\n\nThe act of creating the actual socket is protected by code in\nunix-socket.c and unix-stream-server.c to try to safely create\nthe socket and avoid stepping on another active daemon (who\ncurrently has the server-side of the socket open).\n\nMy code also detects dead sockets (where a previous daemon died\nand failed to delete the socket).\n\n\nAdditionally, allowing remote access means that the repo could\nbe used by multiple client machines and/or by the server machine\nitself.  Consider the example of two MacOS clients mounting the\nremote repo and both wanting to start FSMonitor.  They would\nconstantly fight to recreate a new local-tmp-based socket and\nupdate your pathname FILE and end up invalidating each other on\neach command.\n\n\nAlso, if someone overwrites your new pathname FILE, but doesn't tell\nthe daemon, the daemon will be orphaned -- still running, but no one\nwill ever connect to it because the FILE no longer points to it.\n\n\nThere was a suggestion later in this thread about using a SHA-1\nor SHA-256 hash of the pathname to avoid the tmp XXXXXX pattern\nand just put the socket in $HOME (and omit the need for the new\nfsmonitor-daemon.ipc FILE completely).  This might work, but we\nneed to be careful because a user might have hardlinks or symlinks\nlocally so there may be more than one unique path to the repo\non the local system.  (It is OK to have more than one daemon\nlistening to a repo, just less efficient.)\n\n\nAs an interim step, you might try using my original socket code\nplus just the config.allowRemote=true change.  And test it on a\nmounted repo where you've converted the .git directory to a .git\nfile and moved contents of the .git directory to somewhere local.\nThen the UDS would be created in the local GITDIR instead of on\nthe remote system.  This won't help any of the sharing cases I\ndescribed above, but will let you experiment with getting remote\nevents.\n\nJeff\n\n\n\n> base-commit: 9bf691b78cf906751e65d65ba0c6ffdcd9a5a12c\n> \n"},{"id":"461586","messageId":"CAPig+cSwqNp5Jc7tcn7wLBHNCTHKYVzFs18KzVb+t9rRsrn1Fg@mail.gmail.com","threadId":"58324","inReplyTo":"pull.1326.git.1660855703816.gitgitgadget@gmail.com","subject":"Re: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-08-19T17:48:12Z","receivedAt":"2022-08-19T18:02:27Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Aug 18, 2022 at 5:00 PM Eric DeCosta via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> Though perhaps not common, there are uses cases where users have large,\n\ns/uses cases/use cases/\n\n> network-mounted repos. Having the ability to run fsmonitor against\n> network paths would benefit those users.\n>\n> As a first step towards enabling fsmonitor to work against\n> network-mounted repos, a configuration option, 'fsmonitor.allowRemote'\n> was introduced for Windows. Setting this option to true will override\n> the default behavior (erroring-out) when a network-mounted repo is\n> detected by fsmonitor. In order for macOS to have parity with Windows,\n> the same option is now introduced for macOS.\n>\n> The the added wrinkle being that the Unix domain socket (UDS) file\n\ns/The the/The/\n\n> used for IPC cannot be created in a network location; instead the\n> temporary directory is used.\n>\n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n"},{"id":"461601","messageId":"BL0PR05MB55718D2BE72492E74EF2A671D96C9@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"a63bda82-3dfc-44e0-8ecf-ca340d0a95b4@jeffhostetler.com","subject":"RE: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-08-19T18:38:33Z","receivedAt":"2022-08-19T18:39:50Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Jeff Hostetler <git@jeffhostetler.com>\n> Sent: Friday, August 19, 2022 12:50 PM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n> git@vger.kernel.org\n> Cc: Eric DeCosta <edecosta@mathworks.com>\n> Subject: Re: [PATCH] fsmonitor: option to allow fsmonitor to run against\n> network-mounted repos\n> \n> \n> \n> On 8/18/22 4:48 PM, Eric DeCosta via GitGitGadget wrote:\n> > From: Eric DeCosta <edecosta@mathworks.com>\n> >\n> > Though perhaps not common, there are uses cases where users have large,\n> > network-mounted repos. Having the ability to run fsmonitor against\n> > network paths would benefit those users.\n> >\n> > As a first step towards enabling fsmonitor to work against\n> > network-mounted repos, a configuration option, 'fsmonitor.allowRemote'\n> > was introduced for Windows. Setting this option to true will override\n> > the default behavior (erroring-out) when a network-mounted repo is\n> > detected by fsmonitor. In order for macOS to have parity with Windows,\n> > the same option is now introduced for macOS.\n> \n> We might also say that this config option only allows FSMonitor\n> to TRY to consider using a network-mounted repo.  And that this\n> ability is considered experimental until sufficient testing can\n> be completed and we can determine the combinations of\n> { client os } x { server os } x { remote access } x { file system type }\n> that are known to work or not work and we can update the defaults\n> and the documentation accordingly.\n> \nYes, very experimental. \n\n> For example, on a MacOS client, we expect the local \"fseventsd\" service\n> to send us recursive events on all files and sub directories under the\n> repo root.  If the server is a Linux machine (which doesn't really do\n> recursive events), does exporting the FS from the server over NFS or SMB\n> (or whatever) cause the Linux host to send enough information to the\n> client machine for fseventsd to synthesize the recursive event stream\n> locally that FSMonitor expects.  It might.  It might not.  That\n> combination should be tested (along with a lot of other combinations).\n> \n> But again, this patch is just about allowing the (informed?) user to\n> try it and begin testing various combinations.\n> \nYes, the point is to allow users to try it out.  Self-servingly, I have about\n3K users who make heavy use of network-mounted sandboxes on the\nthree major platforms; all connecting via NFS or SMB to Linux file\nservers.  Hardly exhaustive, but the file system change notification APIs\n(inotify, FSEvents, and ReadDirectoryCHangesW) all seem to work correctly.\nThus my motivation to work on this aspect of git :-)\n\n> \n> >\n> > The the added wrinkle being that the Unix domain socket (UDS) file\n> > used for IPC cannot be created in a network location; instead the\n> > temporary directory is used.\n> \n> This scares me a bit.  I put the socket in the .git directory\n> so that we are guaranteed that only one daemon will run on the\n> repository and that all clients will know where to find that socket\n> (if it exists).\n> \n> It looks like you're creating the UDS using a tmp pathname and\n> writing the pathname to the actual .git/fsmonitor--daemon.ipc FILE.\n> This adds a layer of indirection and is prone to races.\n> \nGood point.\n\n> \n> The act of creating the actual socket is protected by code in\n> unix-socket.c and unix-stream-server.c to try to safely create\n> the socket and avoid stepping on another active daemon (who\n> currently has the server-side of the socket open).\n> \n> My code also detects dead sockets (where a previous daemon died\n> and failed to delete the socket).\n> \n> \n> Additionally, allowing remote access means that the repo could\n> be used by multiple client machines and/or by the server machine\n> itself.  Consider the example of two MacOS clients mounting the\n> remote repo and both wanting to start FSMonitor.  They would\n> constantly fight to recreate a new local-tmp-based socket and\n> update your pathname FILE and end up invalidating each other on\n> each command.\n> \nI see your point - they'd stomp on each other. \n\nAs far as multiple client machines mounting the remote repo, I have\ndoubts that FSMonitor would even see changes made from another\nmachine. Worth trying out and documenting as needed - might even\nbe better off being considered as unsupported.\n\n> \n> Also, if someone overwrites your new pathname FILE, but doesn't tell\n> the daemon, the daemon will be orphaned -- still running, but no one\n> will ever connect to it because the FILE no longer points to it.\n> \nTrue, thanks for pointing that out.\n\n> \n> There was a suggestion later in this thread about using a SHA-1\n> or SHA-256 hash of the pathname to avoid the tmp XXXXXX pattern\n> and just put the socket in $HOME (and omit the need for the new\n> fsmonitor-daemon.ipc FILE completely).  This might work, but we\n> need to be careful because a user might have hardlinks or symlinks\n> locally so there may be more than one unique path to the repo\n> on the local system.  (It is OK to have more than one daemon\n> listening to a repo, just less efficient.)\n> \nAh, I see.\n\n> \n> As an interim step, you might try using my original socket code\n> plus just the config.allowRemote=true change.  And test it on a\n> mounted repo where you've converted the .git directory to a .git\n> file and moved contents of the .git directory to somewhere local.\n> Then the UDS would be created in the local GITDIR instead of on\n> the remote system.  This won't help any of the sharing cases I\n> described above, but will let you experiment with getting remote\n> events.\n> \nWithin the context of the environment that I have available to me\n(macOS over NFS to a Linux file server), FSEvents is working correctly.\nI can make changes at any arbitrary place inside of the repo and an\nevent is generated. \n\nIt's looking like that the Unix domain socket (UDS) file should remain\nwhere it is unless fsmonitor.allowRemote is true.\n\nIf fsmonitor.allowRemote is true then the UDS file can be located\nin $HOME with the caveat that if there is more than one path to\nthe repo (via hard or sym links) that things might not work as\nexpected.  I think that's OK given the experimental nature of the\nfeature.\n\n-Eric\n\n\n> Jeff\n> \n> \n> \n> > base-commit: 9bf691b78cf906751e65d65ba0c6ffdcd9a5a12c\n> >\n\n"},{"id":"461604","messageId":"20220819185847.ulr5yjcfsahydeff@tb-raspi4","threadId":"58324","inReplyTo":"pull.1326.git.1660855703816.gitgitgadget@gmail.com","subject":"Re: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Torsten Bögershausen","fromEmail":"tboegi@web.de","sentAt":"2022-08-19T18:58:47Z","receivedAt":"2022-08-19T18:58:58Z","isPatch":true,"sender":{"key":"tboegi@web.de","avatar":"https://avatars.githubusercontent.com/u/7138363?v=4"},"body":"On Thu, Aug 18, 2022 at 08:48:23PM +0000, Eric DeCosta via GitGitGadget wrote:\n> From: Eric DeCosta <edecosta@mathworks.com>\n\nJust some comments on the commit message, please see inline.\n\n>\n> Though perhaps not common, there are uses cases where users have large,\n> network-mounted repos. Having the ability to run fsmonitor against\n> network paths would benefit those users.\n\nI think that we can drop the \"Though perhaps not common\" - it doesn't\nadd any information about the technical part of the patch.\n(And that is, what is important)\n\nAnd I personally use network-mounted repos every day ;-)\n\nSone users have large, network-mounted repos. Having the ability\nto run fsmonitor against network paths would benefit those users.\n\n>\n> As a first step towards enabling fsmonitor to work against\n> network-mounted repos, a configuration option, 'fsmonitor.allowRemote'\n> was introduced for Windows. Setting this option to true will override\n> the default behavior (erroring-out) when a network-mounted repo is\n> detected by fsmonitor.\n[]\nThe same option is now introduced for macOS.\n\n>\n> The the added wrinkle being that the Unix domain socket (UDS) file\n  ^^^ ^^^\n\n> used for IPC cannot be created in a network location; instead the\n> temporary directory is used.\n"},{"id":"461611","messageId":"04e07c9a-6a74-b731-75e5-7d6cd9d8931c@jeffhostetler.com","threadId":"58324","inReplyTo":"BL0PR05MB55718D2BE72492E74EF2A671D96C9@BL0PR05MB5571.namprd05.prod.outlook.com","subject":"Re: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-08-19T20:15:31Z","receivedAt":"2022-08-19T20:15:42Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 8/19/22 2:38 PM, Eric DeCosta wrote:\n> \n> \n>> -----Original Message-----\n>> From: Jeff Hostetler <git@jeffhostetler.com>\n>> Sent: Friday, August 19, 2022 12:50 PM\n>> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n>> git@vger.kernel.org\n>> Cc: Eric DeCosta <edecosta@mathworks.com>\n>> Subject: Re: [PATCH] fsmonitor: option to allow fsmonitor to run against\n>> network-mounted repos\n>>\n>>\n>>\n>> On 8/18/22 4:48 PM, Eric DeCosta via GitGitGadget wrote:\n>>> From: Eric DeCosta <edecosta@mathworks.com>\n>>>\n\n[...]\n> Yes, the point is to allow users to try it out.  Self-servingly, I have about\n> 3K users who make heavy use of network-mounted sandboxes on the\n> three major platforms; all connecting via NFS or SMB to Linux file\n> servers.  Hardly exhaustive, but the file system change notification APIs\n> (inotify, FSEvents, and ReadDirectoryCHangesW) all seem to work correctly.\n> Thus my motivation to work on this aspect of git :-)\n\nPerfect.  I have to be careful here, I don't want to discourage\nexperimentation and testing -- and if you have access to a pool of\nusers who can beat on it, then great let's try it.\n\nAt the time, I didn't have such a \"captive audience\"... :-)\n\nAnd I just wanted to be sure that everyone understood that some of\nthese combinations have never been tried....\n\n[...]\n> As far as multiple client machines mounting the remote repo, I have\n> doubts that FSMonitor would even see changes made from another\n> machine. Worth trying out and documenting as needed - might even\n> be better off being considered as unsupported.\n\nYeah, that's another dimension.  (1) is whether we miss events\nthat we generated locally.  (2) is whether events get relayed to\nus from other clients.\n\n\n>> There was a suggestion later in this thread about using a SHA-1\n>> or SHA-256 hash of the pathname to avoid the tmp XXXXXX pattern\n>> and just put the socket in $HOME (and omit the need for the new\n>> fsmonitor-daemon.ipc FILE completely).  This might work, but we\n>> need to be careful because a user might have hardlinks or symlinks\n>> locally so there may be more than one unique path to the repo\n>> on the local system.  (It is OK to have more than one daemon\n>> listening to a repo, just less efficient.)\n>>\n> Ah, I see.\n> \n>>\n>> As an interim step, you might try using my original socket code\n>> plus just the config.allowRemote=true change.  And test it on a\n>> mounted repo where you've converted the .git directory to a .git\n>> file and moved contents of the .git directory to somewhere local.\n>> Then the UDS would be created in the local GITDIR instead of on\n>> the remote system.  This won't help any of the sharing cases I\n>> described above, but will let you experiment with getting remote\n>> events.\n>>\n> Within the context of the environment that I have available to me\n> (macOS over NFS to a Linux file server), FSEvents is working correctly.\n> I can make changes at any arbitrary place inside of the repo and an\n> event is generated.\n> \n> It's looking like that the Unix domain socket (UDS) file should remain\n> where it is unless fsmonitor.allowRemote is true.\n> \n> If fsmonitor.allowRemote is true then the UDS file can be located\n> in $HOME with the caveat that if there is more than one path to\n> the repo (via hard or sym links) that things might not work as\n> expected.  I think that's OK given the experimental nature of the\n> feature.\n\nYeah, I'd experiment with moving the UDS to $HOME (assuming you don't\nremote mount home directories too) and see how it works for you.\nLater, after we have more experience with it, we can talk about just\nhaving it always be in $HOME.\n\nJeff\n\n"},{"id":"461666","messageId":"xmqqsflqlfjq.fsf@gitster.g","threadId":"58324","inReplyTo":"pull.1326.git.1660855703816.gitgitgadget@gmail.com","subject":"Re: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-08-20T22:24:09Z","receivedAt":"2022-08-20T22:24:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"> As a first step towards enabling fsmonitor to work against\n> network-mounted repos, a configuration option, 'fsmonitor.allowRemote'\n> was introduced for Windows. Setting this option to true will override\n> the default behavior (erroring-out) when a network-mounted repo is\n> detected by fsmonitor. In order for macOS to have parity with Windows,\n> the same option is now introduced for macOS.\n\nWith this merged in, recent CI runs for 'seen'\n\ne.g. https://github.com/git/git/actions/runs/2892889122\n\nseems to break macOS jobs, letting them hog CPU forever and exceed\n6hr or whatever the limit is.\n\nAs an experiment I pushed out 'seen' but without this commit (not\nthe entire topic is excluded, the Windows one is still included).\nAs there is nothing specific to macOS between 'next' and 'seen',\nmacOS jobs seem to pass, which is not very surprising.\n\nhttps://github.com/git/git/actions/runs/2896207171\n\nAs the patch collected some review comments, I've already marked it\nin the \"What's cooking\" draft as expecting a reroll of that step;\nuntil that happens, let's keep it out of 'seen'.\n\nThanks.\n"},{"id":"461753","messageId":"092oq98r-39q3-4s66-0n0p-r77po7pq8275@tzk.qr","threadId":"58324","inReplyTo":"xmqqsflqlfjq.fsf@gitster.g","subject":"Re: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2022-08-22T13:22:36Z","receivedAt":"2022-08-22T13:22:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Sat, 20 Aug 2022, Junio C Hamano wrote:\n\n> > As a first step towards enabling fsmonitor to work against\n> > network-mounted repos, a configuration option, 'fsmonitor.allowRemote'\n> > was introduced for Windows. Setting this option to true will override\n> > the default behavior (erroring-out) when a network-mounted repo is\n> > detected by fsmonitor. In order for macOS to have parity with Windows,\n> > the same option is now introduced for macOS.\n>\n> With this merged in, recent CI runs for 'seen'\n>\n> e.g. https://github.com/git/git/actions/runs/2892889122\n>\n> seems to break macOS jobs, letting them hog CPU forever and exceed\n> 6hr or whatever the limit is.\n>\n> As an experiment I pushed out 'seen' but without this commit (not\n> the entire topic is excluded, the Windows one is still included).\n> As there is nothing specific to macOS between 'next' and 'seen',\n> macOS jobs seem to pass, which is not very surprising.\n>\n> https://github.com/git/git/actions/runs/2896207171\n>\n> As the patch collected some review comments, I've already marked it\n> in the \"What's cooking\" draft as expecting a reroll of that step;\n> until that happens, let's keep it out of 'seen'.\n\nIt makes sense to keep it out of `seen`, and at the same time I would like\nto encourage Eric to investigate what causes those time-outs.\n\nWhen toggling timestamps (click on the wheel on the upper right) at\nhttps://github.com/git/git/runs/7927812510?check_suite_focus=true#step:4:1774,\nit can be seen that at close to 1am, t9903 finished, but then nothing\nhappens until twenty past 6am.\n\nI've downloaded the raw logs (also available via the wheel on the upper\nright) to find out which test timed out:\n\n\t$ diff -u \\\n\t  <(sed -n 's/.*\\] \\(t[0-9][^ ]*\\).*/\\1/p' <~/Downloads/17 | sort) \\\n\t  <(git ls-tree upstream/seen:t | cut -c 54- | grep '^t[0-9].*-.*sh$')\n\n\t--- /dev/fd/63  2022-08-22 14:56:05.510269527 +0200\n\t+++ /dev/fd/62  2022-08-22 14:56:05.510269527 +0200\n\t@@ -794,6 +794,7 @@\n\t t7524-commit-summary.sh\n\t t7525-status-rename.sh\n\t t7526-commit-pathspec-file.sh\n\t+t7527-builtin-fsmonitor.sh\n\t t7528-signed-commit-ssh.sh\n\t t7600-merge.sh\n\t t7601-merge-pull-config.sh\n\t@@ -945,6 +946,7 @@\n\t t9812-git-p4-wildcards.sh\n\t t9813-git-p4-preserve-users.sh\n\t t9814-git-p4-rename.sh\n\t+t9815-git-p4-submit-fail.sh\n\t t9816-git-p4-locked.sh\n\t t9817-git-p4-exclude.sh\n\t t9818-git-p4-block.sh\n\t@@ -964,5 +966,8 @@\n\t t9832-unshelve.sh\n\t t9833-errors.sh\n\t t9834-git-p4-file-dir-bug.sh\n\t+t9835-git-p4-metadata-encoding-python2.sh\n\t+t9836-git-p4-metadata-encoding-python3.sh\n\t t9901-git-web--browse.sh\n\t+t9902-completion.sh\n\t t9903-bash-prompt.sh\n\nI have no idea what's up with t98* and t9902, but I would bet that they\nwere somehow \"swallowed\" by `prove` being terminated, and that the\nactual test script that times out is t7527.\n\nEric: To investigate, you will want to reproduce the problem on a macOS\nmachine. If you have none available, you could create a temporary branch,\nheavily edit the CI definition, and push it to GitHub. And by heavy edits\nI mean something like this:\n\n- Remove all non-macOS jobs from `.github/workflows/main.yml` (that means\n  removing all but the `regular` job, removing all but at least one\n  `macos` matrix entry, and removing the the `needs: ci-config` and\n  corresponding `if:` line.\n\n- Edit `t/Makefile` to define `T = t7527-builtin-fsmonitor.sh` instead of\n  running all the tests.\n\n- Edit `.github/workflows/main.yml` so that the step that causes the\n  time-out has a chance of timing out much sooner (and the subsequent\n  steps then have a chance to upload the relevant logs):\n  https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepstimeout-minutes\n\nIf this does not shed any light into the issue, please let me know, I have\na couple more aces up my sleeve.\n\nCiao,\nDscho\n"},{"id":"461772","messageId":"xmqq35dofeik.fsf@gitster.g","threadId":"58324","inReplyTo":"092oq98r-39q3-4s66-0n0p-r77po7pq8275@tzk.qr","subject":"Re: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-08-22T16:07:31Z","receivedAt":"2022-08-22T16:07:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Eric: To investigate, you will want to reproduce the problem on a macOS\n> machine. If you have none available, you could create a temporary branch,\n> heavily edit the CI definition, and push it to GitHub. And by heavy edits\n> I mean something like this:\n>\n> - Remove all non-macOS jobs from `.github/workflows/main.yml` (that means\n>   removing all but the `regular` job, removing all but at least one\n>   `macos` matrix entry, and removing the the `needs: ci-config` and\n>   corresponding `if:` line.\n>\n> - Edit `t/Makefile` to define `T = t7527-builtin-fsmonitor.sh` instead of\n>   running all the tests.\n>\n> - Edit `.github/workflows/main.yml` so that the step that causes the\n>   time-out has a chance of timing out much sooner (and the subsequent\n>   steps then have a chance to upload the relevant logs):\n>   https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepstimeout-minutes\n>\n> If this does not shed any light into the issue, please let me know, I have\n> a couple more aces up my sleeve.\n\nThanks, both.\n"},{"id":"461850","messageId":"pull.1326.v2.git.1661259820.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.git.1660855703816.gitgitgadget@gmail.com","subject":"[PATCH v2 0/4] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-23T13:03:36Z","receivedAt":"2022-08-23T16:32:14Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"cc: Johannes Schindelin Johannes.Schindelin@gmx.de cc: Jeff Hostetler\ngit@jeffhostetler.com cc: Eric Sunshine sunshine@sunshineco.com cc: Torsten\nBögershausen tboegi@web.de\n\nEric DeCosta (2):\n  fsmonitor: option to allow fsmonitor to run against network-mounted\n    repos\n  fsmonitor: macOS: allow fsmonitor to run against network-mounted repos\n\nedecosta (2):\n  Check working directory and Unix domain socket file for compatability\n  Minor refactoring and simplification of Windows settings checks\n\n compat/fsmonitor/fsm-settings-darwin.c | 72 +++++++++++++++++++-------\n compat/fsmonitor/fsm-settings-win32.c  | 47 ++++++++++++++++-\n fsmonitor-ipc.c                        | 40 ++++++++++++--\n fsmonitor-ipc.h                        |  6 +++\n fsmonitor-settings.c                   | 67 +++++++++++++++++++++++-\n fsmonitor-settings.h                   |  4 ++\n 6 files changed, 210 insertions(+), 26 deletions(-)\n\n\nbase-commit: 9bf691b78cf906751e65d65ba0c6ffdcd9a5a12c\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v1:\n\n -:  ----------- > 1:  40ce21e85c9 fsmonitor: option to allow fsmonitor to run against network-mounted repos\n -:  ----------- > 2:  46b4efd96cc fsmonitor: macOS: allow fsmonitor to run against network-mounted repos\n 1:  425df16ac0b ! 3:  9b128a98149 fsmonitor: option to allow fsmonitor to run against network-mounted repos\n     @@\n       ## Metadata ##\n     -Author: Eric DeCosta <edecosta@mathworks.com>\n     +Author: edecosta <edecosta@mathworks.com>\n      \n       ## Commit message ##\n     -    fsmonitor: option to allow fsmonitor to run against network-mounted repos\n     +    Check working directory and Unix domain socket file for compatability\n      \n     -    Though perhaps not common, there are uses cases where users have large,\n     -    network-mounted repos. Having the ability to run fsmonitor against\n     -    network paths would benefit those users.\n     +    Perform separate checks for the working directory and Unix domain socket\n     +    (UDS) file location. The working directory may be located on a\n     +    network-mounted file system if 'fsmonitor.allowRemote' is true. The UDS\n     +    file may never be located on a network-mounted file system; additionally\n     +    it may not be located on FAT32 or NTFS file systems.\n      \n     -    As a first step towards enabling fsmonitor to work against\n     -    network-mounted repos, a configuration option, 'fsmonitor.allowRemote'\n     -    was introduced for Windows. Setting this option to true will override\n     -    the default behavior (erroring-out) when a network-mounted repo is\n     -    detected by fsmonitor. In order for macOS to have parity with Windows,\n     -    the same option is now introduced for macOS.\n     -\n     -    The the added wrinkle being that the Unix domain socket (UDS) file\n     -    used for IPC cannot be created in a network location; instead the\n     -    temporary directory is used.\n     -\n     -    Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n     +    Signed-off-by: edecosta <edecosta@mathworks.com>\n      \n       ## compat/fsmonitor/fsm-settings-darwin.c ##\n      @@\n     - #include \"config.h\"\n       #include \"repository.h\"\n       #include \"fsmonitor-settings.h\"\n     -+#include \"fsmonitor-ipc.h\"\n       #include \"fsmonitor.h\"\n     ++#include \"fsmonitor-ipc.h\"\n       #include <sys/param.h>\n       #include <sys/mount.h>\n       \n     -+/*\n     -+ * Check if monitoring remote working directories is allowed.\n     -+ *\n     -+ * By default, monitoring remote working directories is\n     -+ * disabled.  Users may override this behavior in enviroments where\n     -+ * they have proper support.\n     -+ */\n     -+static int check_config_allowremote(struct repository *r)\n     -+{\n     -+\tint allow;\n     -+\n     -+\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n     -+\t\treturn allow;\n     -+\n     -+\treturn -1; /* fsmonitor.allowremote not set */\n     -+}\n     -+\n       /*\n     -  * [1] Remote working directories are problematic for FSMonitor.\n     +- * [1] Remote working directories are problematic for FSMonitor.\n     ++ * Remote working directories are problematic for FSMonitor.\n        *\n     +  * The underlying file system on the server machine and/or the remote\n     +  * mount type (NFS, SAMBA, etc.) dictates whether notification events\n      @@\n        * In theory, the above issues need to be addressed whether we are\n        * using the Hook or IPC API.\n        *\n     -+ * So (for now at least), mark remote working directories as\n     -+ * incompatible by default.\n     -+ *\n     -  * For the builtin FSMonitor, we create the Unix domain socket for the\n     +- * For the builtin FSMonitor, we create the Unix domain socket for the\n      - * IPC in the .git directory.  If the working directory is remote,\n      - * then the socket will be created on the remote file system.  This\n      - * can fail if the remote file system does not support UDS file types\n     @@ compat/fsmonitor/fsm-settings-darwin.c\n      - * well-known local directory on the client machine, but care should\n      - * be taken to ensure that $HOME is actually local and not a managed\n      - * file share.)\n     -+ * IPC in the temporary directory.  If the temporary directory is\n     -+ * remote, then the socket will be created on the remote file system.\n     -+ * This can fail if the remote file system does not support UDS file\n     -+ * types (e.g. smbfs to a Windows server) or if the remote kernel does\n     -+ * not allow a non-local process to bind() the socket.\n     -  *\n     -- * So (for now at least), mark remote working directories as\n     +- *\n     +  * So (for now at least), mark remote working directories as\n      - * incompatible.\n     -+ * Therefore remote UDS locations are marked as incompatible.\n     -  *\n     -  *\n     -  * [2] FAT32 and NTFS working directories are problematic too.\n     -  *\n     +- *\n     +- *\n     +- * [2] FAT32 and NTFS working directories are problematic too.\n     +- *\n      - * The builtin FSMonitor uses a Unix domain socket in the .git\n     -+ * The builtin FSMonitor uses a Unix domain socket in the temporary\n     -  * directory for IPC.  These Windows drive formats do not support\n     -  * Unix domain sockets, so mark them as incompatible for the daemon.\n     +- * directory for IPC.  These Windows drive formats do not support\n     +- * Unix domain sockets, so mark them as incompatible for the daemon.\n     ++ * incompatible unless fsmonitor.allowRemote is true.\n        *\n     +  */\n     + static enum fsmonitor_reason check_volume(struct repository *r)\n      @@ compat/fsmonitor/fsm-settings-darwin.c: static enum fsmonitor_reason check_volume(struct repository *r)\n       \t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n       \t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n       \n     -+\tif (!(fs.f_flags & MNT_LOCAL)) {\n     -+\t\tswitch (check_config_allowremote(r)) {\n     -+\t\tcase 0: /* config overrides and disables */\n     ++\tif (!(fs.f_flags & MNT_LOCAL)\n     ++\t\t&& (fsm_settings__get_allow_remote(r) < 1))\n      +\t\t\treturn FSMONITOR_REASON_REMOTE;\n     -+\t\tcase 1: /* config overrides and enables */\n     -+\t\t\treturn FSMONITOR_REASON_OK;\n     -+\t\tdefault:\n     -+\t\t\tbreak; /* config has no opinion */\n     -+\t\t}\n     -+\n     -+\t\treturn FSMONITOR_REASON_REMOTE;\n     -+\t}\n      +\n      +\treturn FSMONITOR_REASON_OK;\n      +}\n      +\n     ++/*\n     ++ * For the builtin FSMonitor, we create the Unix domain socket (UDS)\n     ++ * for the IPC in the .git directory by default or $HOME if\n     ++ * fsmonitor.allowRemote is true.  If the directory is remote,\n     ++ * then the socket will be created on the remote file system. This\n     ++ * can fail if the remote file system does not support UDS file types\n     ++ * (e.g. smbfs to a Windows server) or if the remote kernel does not\n     ++ * allow a non-local process to bind() the socket.\n     ++ *\n     ++ * Therefore remote UDS locations are marked as incompatible.\n     ++ *\n     ++ * FAT32 and NTFS working directories are problematic too.\n     ++ *\n     ++ * These Windows drive formats do not support Unix domain sockets, so\n     ++ * mark them as incompatible for the location of the UDS file.\n     ++ *\n     ++ */\n      +static enum fsmonitor_reason check_uds_volume(void)\n      +{\n      +\tstruct statfs fs;\n     -+\tconst char *path = fsmonitor_ipc__get_path();\n     ++\tstruct strbuf path = STRBUF_INIT;\n     ++\tconst char *ipc_path = fsmonitor_ipc__get_path();\n     ++\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n      +\n     -+\tif (statfs(path, &fs) == -1) {\n     ++\tif (statfs(dirname(path.buf), &fs) == -1) {\n      +\t\tint saved_errno = errno;\n      +\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n     -+\t\t\t\t path, strerror(saved_errno));\n     ++\t\t\t\t path.buf, strerror(saved_errno));\n      +\t\terrno = saved_errno;\n     ++\t\tstrbuf_release(&path);\n      +\t\treturn FSMONITOR_REASON_ERROR;\n      +\t}\n      +\n      +\ttrace_printf_key(&trace_fsmonitor,\n      +\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n     -+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n     ++\t\t\t path.buf, fs.f_type, fs.f_flags, fs.f_fstypename);\n     ++\tstrbuf_release(&path);\n      +\n       \tif (!(fs.f_flags & MNT_LOCAL))\n       \t\treturn FSMONITOR_REASON_REMOTE;\n     @@ compat/fsmonitor/fsm-settings-darwin.c: enum fsmonitor_reason fsm_os__incompatib\n      +\n       \treturn FSMONITOR_REASON_OK;\n       }\n     -\n     - ## fsmonitor-ipc.c ##\n     -@@\n     - #include \"fsmonitor-ipc.h\"\n     - #include \"run-command.h\"\n     - #include \"strbuf.h\"\n     -+#include \"tempfile.h\"\n     - #include \"trace2.h\"\n     - \n     - #ifndef HAVE_FSMONITOR_DAEMON_BACKEND\n     -@@ fsmonitor-ipc.c: int fsmonitor_ipc__is_supported(void)\n     - \treturn 1;\n     - }\n     - \n     --GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n     -+GIT_PATH_FUNC(fsmonitor_ipc__get_pathfile, \"fsmonitor--daemon.ipc\")\n     -+\n     -+static char *gen_ipc_file(void)\n     -+{\n     -+\tchar *retval = NULL;\n     -+\tstruct tempfile *ipc;\n     -+\n     -+\tconst char *ipc_file = fsmonitor_ipc__get_pathfile();\n     -+\tFILE *fp = fopen(ipc_file, \"w\");\n     -+\n     -+\tif (!fp)\n     -+\t\tdie_errno(\"error opening '%s'\", ipc_file);\n     -+\tipc = mks_tempfile_t(\"fsmonitor_ipc_XXXXXX\");\n     -+\tstrbuf_write(&ipc->filename, fp);\n     -+\tfclose(fp);\n     -+\tretval = strbuf_detach(&ipc->filename, NULL);\n     -+\tstrbuf_release(&ipc->filename);\n     -+\treturn retval;\n     -+}\n     -+\n     -+const char *fsmonitor_ipc__get_path(void)\n     -+{\n     -+\tchar *retval = NULL;\n     -+\tstruct strbuf sb = STRBUF_INIT;\n     -+\n     -+\tconst char *ipc_file = fsmonitor_ipc__get_pathfile();\n     -+\tFILE *fp = fopen(ipc_file, \"r\");\n     -+\n     -+\tif (!fp) {\n     -+\t\treturn gen_ipc_file();\n     -+\t} else {\n     -+\t\tstrbuf_read(&sb, fileno(fp), 0);\n     -+\t\tfclose(fp);\n     -+\t\tfp = fopen(sb.buf, \"r\");\n     -+\t\tif (!fp) { /* generate new file */\n     -+\t\t\tif (unlink(ipc_file) < 0)\n     -+\t\t\t\tdie_errno(\"could not remove '%s'\", ipc_file);\n     -+\t\t\treturn gen_ipc_file();\n     -+\t\t}\n     -+\t\tfclose(fp);\n     -+\t\tretval = strbuf_detach(&sb, NULL);\n     -+\t\tstrbuf_release(&sb);\n     -+\t\treturn retval;\n     -+\t}\n     -+}\n     - \n     - enum ipc_active_state fsmonitor_ipc__get_state(void)\n     - {\n     -\n     - ## fsmonitor-ipc.h ##\n     -@@ fsmonitor-ipc.h: int fsmonitor_ipc__is_supported(void);\n     -  */\n     - const char *fsmonitor_ipc__get_path(void);\n     - \n     -+/*\n     -+ * Returns the pathname to the file that contains the pathname to the\n     -+ * IPC named pipe or Unix domain socket.\n     -+ */\n     -+const char *fsmonitor_ipc__get_pathfile(void);\n     -+\n     - /*\n     -  * Try to determine whether there is a `git-fsmonitor--daemon` process\n     -  * listening on the IPC pipe/socket.\n -:  ----------- > 4:  15c965801f8 Minor refactoring and simplification of Windows settings checks\n\n-- \ngitgitgadget\n"},{"id":"461851","messageId":"40ce21e85c970953d57e16f70073fbc2d7aa918d.1661259820.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v2.git.1661259820.gitgitgadget@gmail.com","subject":"[PATCH v2 1/4] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-23T13:03:37Z","receivedAt":"2022-08-23T16:32:16Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nThough perhaps not common, there are use cases where users have large,\nnetwork-mounted repos. Having the ability to run fsmonitor against\nnetwork paths would benefit those users.\n\nMost modern Samba-based filers have the necessary support to enable\nfsmonitor on network-mounted repos. As a first step towards enabling\nfsmonitor to work against network-mounted repos, introduce a\nconfiguration option, 'fsmonitor.allowRemote'. Setting this option to\ntrue will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-win32.c | 68 +++++++++++++++++++++++++++\n 1 file changed, 68 insertions(+)\n\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex 907655720bb..e5ec5b0a9f7 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -24,6 +24,59 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n+/*\n+ * Check if monitoring remote working directories is allowed.\n+ *\n+ * By default, monitoring remote working directories is\n+ * disabled.  Users may override this behavior in enviroments where\n+ * they have proper support.\n+ */\n+static int check_config_allowremote(struct repository *r)\n+{\n+\tint allow;\n+\n+\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n+\t\treturn allow;\n+\n+\treturn -1; /* fsmonitor.allowremote not set */\n+}\n+\n+/*\n+ * Check remote working directory protocol.\n+ *\n+ * Error if client machine cannot get remote protocol information.\n+ */\n+static int check_remote_protocol(wchar_t *wpath)\n+{\n+\tHANDLE h;\n+\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n+\n+\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n+\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\n+\tif (h == INVALID_HANDLE_VALUE) {\n+\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n+\t\t&proto_info, sizeof(proto_info))) {\n+\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\tCloseHandle(h);\n+\t\treturn -1;\n+\t}\n+\n+\tCloseHandle(h);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n+\t\t\t\twpath, proto_info.Protocol);\n+\n+\treturn 0;\n+}\n+\n /*\n  * Remote working directories are problematic for FSMonitor.\n  *\n@@ -76,6 +129,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n  */\n static enum fsmonitor_reason check_remote(struct repository *r)\n {\n+\tint ret;\n \twchar_t wpath[MAX_PATH];\n \twchar_t wfullpath[MAX_PATH];\n \tsize_t wlen;\n@@ -115,6 +169,20 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n \t\ttrace_printf_key(&trace_fsmonitor,\n \t\t\t\t \"check_remote('%s') true\",\n \t\t\t\t r->worktree);\n+\n+\t\tret = check_remote_protocol(wfullpath);\n+\t\tif (ret < 0)\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\n+\t\tswitch (check_config_allowremote(r)) {\n+\t\tcase 0: /* config overrides and disables */\n+\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\tcase 1: /* config overrides and enables */\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\tbreak; /* config has no opinion */\n+\t\t}\n+\n \t\treturn FSMONITOR_REASON_REMOTE;\n \t}\n \n-- \ngitgitgadget\n\n"},{"id":"461852","messageId":"46b4efd96ccf69db1525130efc1d34f13b649159.1661259820.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v2.git.1661259820.gitgitgadget@gmail.com","subject":"[PATCH v2 2/4] fsmonitor: macOS: allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-23T13:03:38Z","receivedAt":"2022-08-23T16:32:20Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nFollow-on to the work done to allow Windows to work against\nnetwork-mounted repos. Have macOS take advantage of the same\nconfiguration option, 'fsmonitor.allowRemote' that was introduced for\nWindows. Setting this option to true will override the default behavior\n(erroring-out) when a network-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for\nIPC cannot be created in a network location; instead $HOME is used if\n'fsmonitor.allowRemote' is true.\n\nIf $HOME is in a network location, allow the user to override this via\nthe 'fsmonitor.socketDir' configuration option. There the user can\nspecify any local directory for the location of the UDS file.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n fsmonitor-ipc.c      | 40 ++++++++++++++++++++++++--\n fsmonitor-ipc.h      |  6 ++++\n fsmonitor-settings.c | 67 ++++++++++++++++++++++++++++++++++++++++++--\n fsmonitor-settings.h |  4 +++\n 4 files changed, 112 insertions(+), 5 deletions(-)\n\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..1e3f0a6cf48 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -1,7 +1,6 @@\n #include \"cache.h\"\n-#include \"fsmonitor.h\"\n-#include \"simple-ipc.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n #include \"run-command.h\"\n #include \"strbuf.h\"\n #include \"trace2.h\"\n@@ -47,7 +46,42 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n+GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(void)\n+{\n+#ifdef WIN32\n+\treturn fsmonitor_ipc__get_default_path();\n+#else\n+\tchar *retval;\n+\tSHA_CTX sha1ctx;\n+\tconst char *git_dir;\n+\tconst char *sock_dir;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (fsm_settings__get_allow_remote(the_repository) < 1)\n+\t\treturn fsmonitor_ipc__get_default_path();\n+\n+\tgit_dir = get_git_dir();\n+\tsock_dir = fsm_settings__get_socket_dir(the_repository);\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, git_dir, strlen(git_dir));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\tif (sock_dir && *sock_dir)\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\telse\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\tretval = interpolate_path(ipc_file.buf, 1);\n+\tif (!retval)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\tstrbuf_release(&ipc_file);\n+\treturn retval;\n+#endif\n+}\n \n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..4d27223c2a6 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -18,6 +18,12 @@ int fsmonitor_ipc__is_supported(void);\n  */\n const char *fsmonitor_ipc__get_path(void);\n \n+/*\n+ * Returns the pathname to the default IPC named pipe or Unix domain\n+ * socket.\n+ */\n+const char *fsmonitor_ipc__get_default_path(void);\n+\n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n  * listening on the IPC pipe/socket.\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..a15eeecebf4 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -10,7 +10,9 @@\n struct fsmonitor_settings {\n \tenum fsmonitor_mode mode;\n \tenum fsmonitor_reason reason;\n+\tint allow_remote;\n \tchar *hook_path;\n+\tchar *sock_dir;\n };\n \n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n@@ -43,6 +45,7 @@ static struct fsmonitor_settings *alloc_settings(void)\n \tCALLOC_ARRAY(s, 1);\n \ts->mode = FSMONITOR_MODE_DISABLED;\n \ts->reason = FSMONITOR_REASON_UNTESTED;\n+\ts->allow_remote = -1;\n \n \treturn s;\n }\n@@ -90,6 +93,26 @@ static void lookup_fsmonitor_settings(struct repository *r)\n \t\tfsm_settings__set_disabled(r);\n }\n \n+int fsm_settings__get_allow_remote(struct repository *r)\n+{\n+\tif (!r)\n+\t\tr = the_repository;\n+\tif (!r->settings.fsmonitor)\n+\t\tlookup_fsmonitor_settings(r);\n+\n+\treturn r->settings.fsmonitor->allow_remote;\n+}\n+\n+const char *fsm_settings__get_socket_dir(struct repository *r)\n+{\n+\tif (!r)\n+\t\tr = the_repository;\n+\tif (!r->settings.fsmonitor)\n+\t\tlookup_fsmonitor_settings(r);\n+\n+\treturn r->settings.fsmonitor->sock_dir;\n+}\n+\n enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n {\n \tif (!r)\n@@ -100,6 +123,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n \treturn r->settings.fsmonitor->mode;\n }\n \n+\n const char *fsm_settings__get_hook_path(struct repository *r)\n {\n \tif (!r)\n@@ -110,9 +134,44 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \treturn r->settings.fsmonitor->hook_path;\n }\n \n+void fsm_settings__set_allow_remote(struct repository *r)\n+{\n+\tint allow;\n+\n+\tif (!r)\n+\t\tr = the_repository;\n+\tif (!r->settings.fsmonitor)\n+\t\tr->settings.fsmonitor = alloc_settings();\n+\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n+\t\tr->settings.fsmonitor->allow_remote = allow;\n+\n+\treturn;\n+}\n+\n+void fsm_settings__set_socket_dir(struct repository *r)\n+{\n+\tconst char *path;\n+\n+\tif (!r)\n+\t\tr = the_repository;\n+\tif (!r->settings.fsmonitor)\n+\t\tr->settings.fsmonitor = alloc_settings();\n+\n+\tif (!repo_config_get_pathname(r, \"fsmonitor.socketdir\", &path)) {\n+\t\tFREE_AND_NULL(r->settings.fsmonitor->sock_dir);\n+\t\tr->settings.fsmonitor->sock_dir = strdup(path);\n+\t}\n+\n+\treturn;\n+}\n+\n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason;\n+\n+\tfsm_settings__set_allow_remote(r);\n+\tfsm_settings__set_socket_dir(r);\n+\treason = check_for_incompatible(r);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -135,7 +194,11 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason;\n+\n+\tfsm_settings__set_allow_remote(r);\n+\tfsm_settings__set_socket_dir(r);\n+\treason = check_for_incompatible(r);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..2de54c85e94 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -23,12 +23,16 @@ enum fsmonitor_reason {\n \tFSMONITOR_REASON_NOSOCKETS, /* NTFS,FAT32 do not support Unix sockets */\n };\n \n+void fsm_settings__set_allow_remote(struct repository *r);\n+void fsm_settings__set_socket_dir(struct repository *r);\n void fsm_settings__set_ipc(struct repository *r);\n void fsm_settings__set_hook(struct repository *r, const char *path);\n void fsm_settings__set_disabled(struct repository *r);\n void fsm_settings__set_incompatible(struct repository *r,\n \t\t\t\t    enum fsmonitor_reason reason);\n \n+int fsm_settings__get_allow_remote(struct repository *r);\n+const char *fsm_settings__get_socket_dir(struct repository *r);\n enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n const char *fsm_settings__get_hook_path(struct repository *r);\n \n-- \ngitgitgadget\n\n"},{"id":"461853","messageId":"9b128a981494f5aa7f60062caecc24ee6ea802ec.1661259820.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v2.git.1661259820.gitgitgadget@gmail.com","subject":"[PATCH v2 3/4] Check working directory and Unix domain socket file for compatability","fromName":"edecosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-23T13:03:39Z","receivedAt":"2022-08-23T16:32:24Z","isPatch":true,"sender":{"key":"name:edecosta","avatar":null},"body":"From: edecosta <edecosta@mathworks.com>\n\nPerform separate checks for the working directory and Unix domain socket\n(UDS) file location. The working directory may be located on a\nnetwork-mounted file system if 'fsmonitor.allowRemote' is true. The UDS\nfile may never be located on a network-mounted file system; additionally\nit may not be located on FAT32 or NTFS file systems.\n\nSigned-off-by: edecosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 72 +++++++++++++++++++-------\n 1 file changed, 52 insertions(+), 20 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dc79538607f 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -3,11 +3,12 @@\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n #include <sys/param.h>\n #include <sys/mount.h>\n \n /*\n- * [1] Remote working directories are problematic for FSMonitor.\n+ * Remote working directories are problematic for FSMonitor.\n  *\n  * The underlying file system on the server machine and/or the remote\n  * mount type (NFS, SAMBA, etc.) dictates whether notification events\n@@ -27,26 +28,8 @@\n  * In theory, the above issues need to be addressed whether we are\n  * using the Hook or IPC API.\n  *\n- * For the builtin FSMonitor, we create the Unix domain socket for the\n- * IPC in the .git directory.  If the working directory is remote,\n- * then the socket will be created on the remote file system.  This\n- * can fail if the remote file system does not support UDS file types\n- * (e.g. smbfs to a Windows server) or if the remote kernel does not\n- * allow a non-local process to bind() the socket.  (These problems\n- * could be fixed by moving the UDS out of the .git directory and to a\n- * well-known local directory on the client machine, but care should\n- * be taken to ensure that $HOME is actually local and not a managed\n- * file share.)\n- *\n  * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n- *\n- * The builtin FSMonitor uses a Unix domain socket in the .git\n- * directory for IPC.  These Windows drive formats do not support\n- * Unix domain sockets, so mark them as incompatible for the daemon.\n+ * incompatible unless fsmonitor.allowRemote is true.\n  *\n  */\n static enum fsmonitor_reason check_volume(struct repository *r)\n@@ -65,6 +48,51 @@ static enum fsmonitor_reason check_volume(struct repository *r)\n \t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n \t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n \n+\tif (!(fs.f_flags & MNT_LOCAL)\n+\t\t&& (fsm_settings__get_allow_remote(r) < 1))\n+\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\n+\treturn FSMONITOR_REASON_OK;\n+}\n+\n+/*\n+ * For the builtin FSMonitor, we create the Unix domain socket (UDS)\n+ * for the IPC in the .git directory by default or $HOME if\n+ * fsmonitor.allowRemote is true.  If the directory is remote,\n+ * then the socket will be created on the remote file system. This\n+ * can fail if the remote file system does not support UDS file types\n+ * (e.g. smbfs to a Windows server) or if the remote kernel does not\n+ * allow a non-local process to bind() the socket.\n+ *\n+ * Therefore remote UDS locations are marked as incompatible.\n+ *\n+ * FAT32 and NTFS working directories are problematic too.\n+ *\n+ * These Windows drive formats do not support Unix domain sockets, so\n+ * mark them as incompatible for the location of the UDS file.\n+ *\n+ */\n+static enum fsmonitor_reason check_uds_volume(void)\n+{\n+\tstruct statfs fs;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n+\n+\tif (statfs(dirname(path.buf), &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path.buf, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\tstrbuf_release(&path);\n+\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path.buf, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n+\n \tif (!(fs.f_flags & MNT_LOCAL))\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n@@ -85,5 +113,9 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n+\treason = check_uds_volume();\n+\tif (reason != FSMONITOR_REASON_OK)\n+\t\treturn reason;\n+\n \treturn FSMONITOR_REASON_OK;\n }\n-- \ngitgitgadget\n\n"},{"id":"461854","messageId":"15c965801f81d8b5c42223aa422b23302cb9210b.1661259820.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v2.git.1661259820.gitgitgadget@gmail.com","subject":"[PATCH v2 4/4] Minor refactoring and simplification of Windows settings checks","fromName":"edecosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-23T13:03:40Z","receivedAt":"2022-08-23T16:32:36Z","isPatch":true,"sender":{"key":"name:edecosta","avatar":null},"body":"From: edecosta <edecosta@mathworks.com>\n\nRead the value of 'fsmonitor.allowRemote' from fsmonitor_settings\nvia fsm_settings__get_allow_remote getter. Remove check_config_allowremote.\nReplace switch statement with a simpler if statement. Move the check\nfor 'fsmonitor.allowRemote' above the remote protocol check.\n\nSigned-off-by: edecosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-win32.c | 31 ++++-----------------------\n 1 file changed, 4 insertions(+), 27 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex e5ec5b0a9f7..34635e6c849 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -24,23 +24,6 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-/*\n- * Check if monitoring remote working directories is allowed.\n- *\n- * By default, monitoring remote working directories is\n- * disabled.  Users may override this behavior in enviroments where\n- * they have proper support.\n- */\n-static int check_config_allowremote(struct repository *r)\n-{\n-\tint allow;\n-\n-\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n-\t\treturn allow;\n-\n-\treturn -1; /* fsmonitor.allowremote not set */\n-}\n-\n /*\n  * Check remote working directory protocol.\n  *\n@@ -170,20 +153,14 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n \t\t\t\t \"check_remote('%s') true\",\n \t\t\t\t r->worktree);\n \n+\t\tif (fsm_settings__get_allow_remote(r) < 1)\n+\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\n \t\tret = check_remote_protocol(wfullpath);\n \t\tif (ret < 0)\n \t\t\treturn FSMONITOR_REASON_ERROR;\n \n-\t\tswitch (check_config_allowremote(r)) {\n-\t\tcase 0: /* config overrides and disables */\n-\t\t\treturn FSMONITOR_REASON_REMOTE;\n-\t\tcase 1: /* config overrides and enables */\n-\t\t\treturn FSMONITOR_REASON_OK;\n-\t\tdefault:\n-\t\t\tbreak; /* config has no opinion */\n-\t\t}\n-\n-\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\treturn FSMONITOR_REASON_OK;\n \t}\n \n \treturn FSMONITOR_REASON_OK;\n-- \ngitgitgadget\n"},{"id":"461858","messageId":"055470ae-1483-bbd4-5b23-3762a4c131b8@jeffhostetler.com","threadId":"58324","inReplyTo":"092oq98r-39q3-4s66-0n0p-r77po7pq8275@tzk.qr","subject":"Re: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-08-23T13:51:01Z","receivedAt":"2022-08-23T17:16:44Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 8/22/22 9:22 AM, Johannes Schindelin wrote:\n> Hi Junio,\n> \n> On Sat, 20 Aug 2022, Junio C Hamano wrote:\n> \n>>> As a first step towards enabling fsmonitor to work against\n>>> network-mounted repos, a configuration option, 'fsmonitor.allowRemote'\n>>> was introduced for Windows. Setting this option to true will override\n>>> the default behavior (erroring-out) when a network-mounted repo is\n>>> detected by fsmonitor. In order for macOS to have parity with Windows,\n>>> the same option is now introduced for macOS.\n>>\n>> With this merged in, recent CI runs for 'seen'\n>>\n>> e.g. https://github.com/git/git/actions/runs/2892889122\n>>\n>> seems to break macOS jobs, letting them hog CPU forever and exceed\n>> 6hr or whatever the limit is.\n>>\n>> As an experiment I pushed out 'seen' but without this commit (not\n>> the entire topic is excluded, the Windows one is still included).\n>> As there is nothing specific to macOS between 'next' and 'seen',\n>> macOS jobs seem to pass, which is not very surprising.\n>>\n>> https://github.com/git/git/actions/runs/2896207171\n>>\n>> As the patch collected some review comments, I've already marked it\n>> in the \"What's cooking\" draft as expecting a reroll of that step;\n>> until that happens, let's keep it out of 'seen'.\n> \n> It makes sense to keep it out of `seen`, and at the same time I would like\n> to encourage Eric to investigate what causes those time-outs.\n> \n> When toggling timestamps (click on the wheel on the upper right) at\n> https://github.com/git/git/runs/7927812510?check_suite_focus=true#step:4:1774,\n> it can be seen that at close to 1am, t9903 finished, but then nothing\n> happens until twenty past 6am.\n> \n> I've downloaded the raw logs (also available via the wheel on the upper\n> right) to find out which test timed out:\n> \n> \t$ diff -u \\\n> \t  <(sed -n 's/.*\\] \\(t[0-9][^ ]*\\).*/\\1/p' <~/Downloads/17 | sort) \\\n> \t  <(git ls-tree upstream/seen:t | cut -c 54- | grep '^t[0-9].*-.*sh$')\n> \n> \t--- /dev/fd/63  2022-08-22 14:56:05.510269527 +0200\n> \t+++ /dev/fd/62  2022-08-22 14:56:05.510269527 +0200\n> \t@@ -794,6 +794,7 @@\n> \t t7524-commit-summary.sh\n> \t t7525-status-rename.sh\n> \t t7526-commit-pathspec-file.sh\n> \t+t7527-builtin-fsmonitor.sh\n> \t t7528-signed-commit-ssh.sh\n> \t t7600-merge.sh\n> \t t7601-merge-pull-config.sh\n> \t@@ -945,6 +946,7 @@\n> \t t9812-git-p4-wildcards.sh\n> \t t9813-git-p4-preserve-users.sh\n> \t t9814-git-p4-rename.sh\n> \t+t9815-git-p4-submit-fail.sh\n> \t t9816-git-p4-locked.sh\n> \t t9817-git-p4-exclude.sh\n> \t t9818-git-p4-block.sh\n> \t@@ -964,5 +966,8 @@\n> \t t9832-unshelve.sh\n> \t t9833-errors.sh\n> \t t9834-git-p4-file-dir-bug.sh\n> \t+t9835-git-p4-metadata-encoding-python2.sh\n> \t+t9836-git-p4-metadata-encoding-python3.sh\n> \t t9901-git-web--browse.sh\n> \t+t9902-completion.sh\n> \t t9903-bash-prompt.sh\n> \n> I have no idea what's up with t98* and t9902, but I would bet that they\n> were somehow \"swallowed\" by `prove` being terminated, and that the\n> actual test script that times out is t7527.\n> \n> Eric: To investigate, you will want to reproduce the problem on a macOS\n> machine. If you have none available, you could create a temporary branch,\n> heavily edit the CI definition, and push it to GitHub. And by heavy edits\n> I mean something like this:\n> \n> - Remove all non-macOS jobs from `.github/workflows/main.yml` (that means\n>    removing all but the `regular` job, removing all but at least one\n>    `macos` matrix entry, and removing the the `needs: ci-config` and\n>    corresponding `if:` line.\n> \n> - Edit `t/Makefile` to define `T = t7527-builtin-fsmonitor.sh` instead of\n>    running all the tests.\n> \n> - Edit `.github/workflows/main.yml` so that the step that causes the\n>    time-out has a chance of timing out much sooner (and the subsequent\n>    steps then have a chance to upload the relevant logs):\n>    https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepstimeout-minutes\n\nI would also set GIT_TRACE_FSMONITOR and GIT_TRACE2_PERF (on both daemon\nand client sides of the tests) and capture the logs and try to figure\nout what is happening.\n\nI suspect that this testing should wait until you redo the patch to\nremove the tmp file stuff and just move the socket into $HOME as we\ntalked about earlier.\n\nJeff\n\n"},{"id":"461873","messageId":"pull.1326.v3.git.1661280941.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v2.git.1661259820.gitgitgadget@gmail.com","subject":"[PATCH v3 0/2] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-23T18:55:39Z","receivedAt":"2022-08-23T19:51:56Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"cc: Johannes Schindelin Johannes.Schindelin@gmx.de cc: Jeff Hostetler\ngit@jeffhostetler.com cc: Eric Sunshine sunshine@sunshineco.com cc: Torsten\nBögershausen tboegi@web.de\n\nEric DeCosta (1):\n  fsmonitor: macOS: allow fsmonitor to run against network-mounted repos\n\nedecosta (1):\n  Check working directory and Unix domain socket file for compatability\n\n compat/fsmonitor/fsm-settings-darwin.c | 72 +++++++++++++++++++-------\n fsmonitor-ipc.c                        | 40 ++++++++++++--\n fsmonitor-ipc.h                        |  6 +++\n fsmonitor-settings.c                   | 67 +++++++++++++++++++++++-\n fsmonitor-settings.h                   |  4 ++\n 5 files changed, 164 insertions(+), 25 deletions(-)\n\n\nbase-commit: 795ea8776befc95ea2becd8020c7a284677b4161\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v2:\n\n 1:  40ce21e85c9 < -:  ----------- fsmonitor: option to allow fsmonitor to run against network-mounted repos\n 2:  46b4efd96cc = 1:  cd16d8bb3d6 fsmonitor: macOS: allow fsmonitor to run against network-mounted repos\n 3:  9b128a98149 = 2:  f977d140afa Check working directory and Unix domain socket file for compatability\n 4:  15c965801f8 < -:  ----------- Minor refactoring and simplification of Windows settings checks\n\n-- \ngitgitgadget\n"},{"id":"461874","messageId":"f977d140afac8b2d205a66ad4deb1ec1913588fb.1661280941.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v3.git.1661280941.gitgitgadget@gmail.com","subject":"[PATCH v3 2/2] Check working directory and Unix domain socket file for compatability","fromName":"edecosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-23T18:55:41Z","receivedAt":"2022-08-23T19:52:00Z","isPatch":true,"sender":{"key":"name:edecosta","avatar":null},"body":"From: edecosta <edecosta@mathworks.com>\n\nPerform separate checks for the working directory and Unix domain socket\n(UDS) file location. The working directory may be located on a\nnetwork-mounted file system if 'fsmonitor.allowRemote' is true. The UDS\nfile may never be located on a network-mounted file system; additionally\nit may not be located on FAT32 or NTFS file systems.\n\nSigned-off-by: edecosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 72 +++++++++++++++++++-------\n 1 file changed, 52 insertions(+), 20 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dc79538607f 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -3,11 +3,12 @@\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n #include <sys/param.h>\n #include <sys/mount.h>\n \n /*\n- * [1] Remote working directories are problematic for FSMonitor.\n+ * Remote working directories are problematic for FSMonitor.\n  *\n  * The underlying file system on the server machine and/or the remote\n  * mount type (NFS, SAMBA, etc.) dictates whether notification events\n@@ -27,26 +28,8 @@\n  * In theory, the above issues need to be addressed whether we are\n  * using the Hook or IPC API.\n  *\n- * For the builtin FSMonitor, we create the Unix domain socket for the\n- * IPC in the .git directory.  If the working directory is remote,\n- * then the socket will be created on the remote file system.  This\n- * can fail if the remote file system does not support UDS file types\n- * (e.g. smbfs to a Windows server) or if the remote kernel does not\n- * allow a non-local process to bind() the socket.  (These problems\n- * could be fixed by moving the UDS out of the .git directory and to a\n- * well-known local directory on the client machine, but care should\n- * be taken to ensure that $HOME is actually local and not a managed\n- * file share.)\n- *\n  * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n- *\n- * The builtin FSMonitor uses a Unix domain socket in the .git\n- * directory for IPC.  These Windows drive formats do not support\n- * Unix domain sockets, so mark them as incompatible for the daemon.\n+ * incompatible unless fsmonitor.allowRemote is true.\n  *\n  */\n static enum fsmonitor_reason check_volume(struct repository *r)\n@@ -65,6 +48,51 @@ static enum fsmonitor_reason check_volume(struct repository *r)\n \t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n \t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n \n+\tif (!(fs.f_flags & MNT_LOCAL)\n+\t\t&& (fsm_settings__get_allow_remote(r) < 1))\n+\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\n+\treturn FSMONITOR_REASON_OK;\n+}\n+\n+/*\n+ * For the builtin FSMonitor, we create the Unix domain socket (UDS)\n+ * for the IPC in the .git directory by default or $HOME if\n+ * fsmonitor.allowRemote is true.  If the directory is remote,\n+ * then the socket will be created on the remote file system. This\n+ * can fail if the remote file system does not support UDS file types\n+ * (e.g. smbfs to a Windows server) or if the remote kernel does not\n+ * allow a non-local process to bind() the socket.\n+ *\n+ * Therefore remote UDS locations are marked as incompatible.\n+ *\n+ * FAT32 and NTFS working directories are problematic too.\n+ *\n+ * These Windows drive formats do not support Unix domain sockets, so\n+ * mark them as incompatible for the location of the UDS file.\n+ *\n+ */\n+static enum fsmonitor_reason check_uds_volume(void)\n+{\n+\tstruct statfs fs;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n+\n+\tif (statfs(dirname(path.buf), &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path.buf, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\tstrbuf_release(&path);\n+\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path.buf, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n+\n \tif (!(fs.f_flags & MNT_LOCAL))\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n@@ -85,5 +113,9 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n+\treason = check_uds_volume();\n+\tif (reason != FSMONITOR_REASON_OK)\n+\t\treturn reason;\n+\n \treturn FSMONITOR_REASON_OK;\n }\n-- \ngitgitgadget\n"},{"id":"461875","messageId":"cd16d8bb3d6a653ebe771d10821dd789ad87f90d.1661280941.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v3.git.1661280941.gitgitgadget@gmail.com","subject":"[PATCH v3 1/2] fsmonitor: macOS: allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-23T18:55:40Z","receivedAt":"2022-08-23T19:52:01Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nFollow-on to the work done to allow Windows to work against\nnetwork-mounted repos. Have macOS take advantage of the same\nconfiguration option, 'fsmonitor.allowRemote' that was introduced for\nWindows. Setting this option to true will override the default behavior\n(erroring-out) when a network-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for\nIPC cannot be created in a network location; instead $HOME is used if\n'fsmonitor.allowRemote' is true.\n\nIf $HOME is in a network location, allow the user to override this via\nthe 'fsmonitor.socketDir' configuration option. There the user can\nspecify any local directory for the location of the UDS file.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n fsmonitor-ipc.c      | 40 ++++++++++++++++++++++++--\n fsmonitor-ipc.h      |  6 ++++\n fsmonitor-settings.c | 67 ++++++++++++++++++++++++++++++++++++++++++--\n fsmonitor-settings.h |  4 +++\n 4 files changed, 112 insertions(+), 5 deletions(-)\n\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..1e3f0a6cf48 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -1,7 +1,6 @@\n #include \"cache.h\"\n-#include \"fsmonitor.h\"\n-#include \"simple-ipc.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n #include \"run-command.h\"\n #include \"strbuf.h\"\n #include \"trace2.h\"\n@@ -47,7 +46,42 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n+GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(void)\n+{\n+#ifdef WIN32\n+\treturn fsmonitor_ipc__get_default_path();\n+#else\n+\tchar *retval;\n+\tSHA_CTX sha1ctx;\n+\tconst char *git_dir;\n+\tconst char *sock_dir;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (fsm_settings__get_allow_remote(the_repository) < 1)\n+\t\treturn fsmonitor_ipc__get_default_path();\n+\n+\tgit_dir = get_git_dir();\n+\tsock_dir = fsm_settings__get_socket_dir(the_repository);\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, git_dir, strlen(git_dir));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\tif (sock_dir && *sock_dir)\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\telse\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\tretval = interpolate_path(ipc_file.buf, 1);\n+\tif (!retval)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\tstrbuf_release(&ipc_file);\n+\treturn retval;\n+#endif\n+}\n \n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..4d27223c2a6 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -18,6 +18,12 @@ int fsmonitor_ipc__is_supported(void);\n  */\n const char *fsmonitor_ipc__get_path(void);\n \n+/*\n+ * Returns the pathname to the default IPC named pipe or Unix domain\n+ * socket.\n+ */\n+const char *fsmonitor_ipc__get_default_path(void);\n+\n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n  * listening on the IPC pipe/socket.\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..a15eeecebf4 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -10,7 +10,9 @@\n struct fsmonitor_settings {\n \tenum fsmonitor_mode mode;\n \tenum fsmonitor_reason reason;\n+\tint allow_remote;\n \tchar *hook_path;\n+\tchar *sock_dir;\n };\n \n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n@@ -43,6 +45,7 @@ static struct fsmonitor_settings *alloc_settings(void)\n \tCALLOC_ARRAY(s, 1);\n \ts->mode = FSMONITOR_MODE_DISABLED;\n \ts->reason = FSMONITOR_REASON_UNTESTED;\n+\ts->allow_remote = -1;\n \n \treturn s;\n }\n@@ -90,6 +93,26 @@ static void lookup_fsmonitor_settings(struct repository *r)\n \t\tfsm_settings__set_disabled(r);\n }\n \n+int fsm_settings__get_allow_remote(struct repository *r)\n+{\n+\tif (!r)\n+\t\tr = the_repository;\n+\tif (!r->settings.fsmonitor)\n+\t\tlookup_fsmonitor_settings(r);\n+\n+\treturn r->settings.fsmonitor->allow_remote;\n+}\n+\n+const char *fsm_settings__get_socket_dir(struct repository *r)\n+{\n+\tif (!r)\n+\t\tr = the_repository;\n+\tif (!r->settings.fsmonitor)\n+\t\tlookup_fsmonitor_settings(r);\n+\n+\treturn r->settings.fsmonitor->sock_dir;\n+}\n+\n enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n {\n \tif (!r)\n@@ -100,6 +123,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n \treturn r->settings.fsmonitor->mode;\n }\n \n+\n const char *fsm_settings__get_hook_path(struct repository *r)\n {\n \tif (!r)\n@@ -110,9 +134,44 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \treturn r->settings.fsmonitor->hook_path;\n }\n \n+void fsm_settings__set_allow_remote(struct repository *r)\n+{\n+\tint allow;\n+\n+\tif (!r)\n+\t\tr = the_repository;\n+\tif (!r->settings.fsmonitor)\n+\t\tr->settings.fsmonitor = alloc_settings();\n+\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n+\t\tr->settings.fsmonitor->allow_remote = allow;\n+\n+\treturn;\n+}\n+\n+void fsm_settings__set_socket_dir(struct repository *r)\n+{\n+\tconst char *path;\n+\n+\tif (!r)\n+\t\tr = the_repository;\n+\tif (!r->settings.fsmonitor)\n+\t\tr->settings.fsmonitor = alloc_settings();\n+\n+\tif (!repo_config_get_pathname(r, \"fsmonitor.socketdir\", &path)) {\n+\t\tFREE_AND_NULL(r->settings.fsmonitor->sock_dir);\n+\t\tr->settings.fsmonitor->sock_dir = strdup(path);\n+\t}\n+\n+\treturn;\n+}\n+\n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason;\n+\n+\tfsm_settings__set_allow_remote(r);\n+\tfsm_settings__set_socket_dir(r);\n+\treason = check_for_incompatible(r);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -135,7 +194,11 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason;\n+\n+\tfsm_settings__set_allow_remote(r);\n+\tfsm_settings__set_socket_dir(r);\n+\treason = check_for_incompatible(r);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..2de54c85e94 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -23,12 +23,16 @@ enum fsmonitor_reason {\n \tFSMONITOR_REASON_NOSOCKETS, /* NTFS,FAT32 do not support Unix sockets */\n };\n \n+void fsm_settings__set_allow_remote(struct repository *r);\n+void fsm_settings__set_socket_dir(struct repository *r);\n void fsm_settings__set_ipc(struct repository *r);\n void fsm_settings__set_hook(struct repository *r, const char *path);\n void fsm_settings__set_disabled(struct repository *r);\n void fsm_settings__set_incompatible(struct repository *r,\n \t\t\t\t    enum fsmonitor_reason reason);\n \n+int fsm_settings__get_allow_remote(struct repository *r);\n+const char *fsm_settings__get_socket_dir(struct repository *r);\n enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n const char *fsm_settings__get_hook_path(struct repository *r);\n \n-- \ngitgitgadget\n\n"},{"id":"461897","messageId":"BL0PR05MB55716C6CC59D3258E55B25B9D9739@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"055470ae-1483-bbd4-5b23-3762a4c131b8@jeffhostetler.com","subject":"RE: [PATCH] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-08-24T15:45:23Z","receivedAt":"2022-08-24T15:47:33Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"> I would also set GIT_TRACE_FSMONITOR and GIT_TRACE2_PERF (on both daemon\n> and client sides of the tests) and capture the logs and try to figure\n> out what is happening.\n>\n> I suspect that this testing should wait until you redo the patch to\n> remove the tmp file stuff and just move the socket into $HOME as we\n> talked about earlier.\n>\n> Jeff\n\nAll tests are passing now with the new patch. By default the socket is written into\nthe original location (.git directory) unless 'fsmonitor.allowRemote' is true. Only\nthen is $HOME used and if $HOME proves unsuitable the user can override that\nby setting 'fsmonitor.sockerDir' to some valid, local directory.\n\n-Eric\n"},{"id":"461901","messageId":"xmqqfshl8u9h.fsf@gitster.g","threadId":"58324","inReplyTo":"pull.1326.v3.git.1661280941.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 0/2] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-08-24T16:46:02Z","receivedAt":"2022-08-24T16:47:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> cc: Johannes Schindelin Johannes.Schindelin@gmx.de cc: Jeff Hostetler\n> git@jeffhostetler.com cc: Eric Sunshine sunshine@sunshineco.com cc: Torsten\n> Bögershausen tboegi@web.de\n>\n> Eric DeCosta (1):\n>   fsmonitor: macOS: allow fsmonitor to run against network-mounted repos\n>\n> edecosta (1):\n>   Check working directory and Unix domain socket file for compatability\n\nPlease describe why v3 exists (iow, what was wrong in v2 that v3\nimproves) in the cover letter.  Especially when you send out two\nrevisions in less than 12 hours, it gets disorienting at the\nreceiving end.\n\nThanks.\n\n>  compat/fsmonitor/fsm-settings-darwin.c | 72 +++++++++++++++++++-------\n>  fsmonitor-ipc.c                        | 40 ++++++++++++--\n>  fsmonitor-ipc.h                        |  6 +++\n>  fsmonitor-settings.c                   | 67 +++++++++++++++++++++++-\n>  fsmonitor-settings.h                   |  4 ++\n>  5 files changed, 164 insertions(+), 25 deletions(-)\n>\n>\n> base-commit: 795ea8776befc95ea2becd8020c7a284677b4161\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v3\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v3\n> Pull-Request: https://github.com/gitgitgadget/git/pull/1326\n>\n> Range-diff vs v2:\n>\n>  1:  40ce21e85c9 < -:  ----------- fsmonitor: option to allow fsmonitor to run against network-mounted repos\n>  2:  46b4efd96cc = 1:  cd16d8bb3d6 fsmonitor: macOS: allow fsmonitor to run against network-mounted repos\n>  3:  9b128a98149 = 2:  f977d140afa Check working directory and Unix domain socket file for compatability\n>  4:  15c965801f8 < -:  ----------- Minor refactoring and simplification of Windows settings checks\n"},{"id":"462113","messageId":"xmqqpmgp5qp8.fsf@gitster.g","threadId":"58324","inReplyTo":"f977d140afac8b2d205a66ad4deb1ec1913588fb.1661280941.git.gitgitgadget@gmail.com","subject":"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-08-24T20:31:15Z","receivedAt":"2022-08-24T20:31:24Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"edecosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: edecosta <edecosta@mathworks.com>\n\nDo you have some specific reason to avoid using\n\n    Eric DeCosta <edecosta@mathworks.com>\n\nto identify yourself?  Otherwise, we'd prefer to see it that way.\n\n> Subject: Re: [PATCH v3 2/2] Check working directory and Unix domain socket file for compatability\n\nThis project is wider than just its fsmonitor part, so please make\nsure to give your change a title that will not be lost in the noise\nwhen it is shown together with other patfches in the output from\n\"git shortlog --no-merges\".  I haven't spent more than 30 seconds so\nyou may be able to summarize it better, but perhaps\n\nfsmonitor: ensure filesystem and unix socket are usable by fsmonitor\n\nor something?  If I am reading the early part of the patch\ncorrectly, the value of this change primarily comes from the fact\nthat it separates (1) filesystems that are inherently incompatible\n(e.g. cannot create sockets) and (2) filesystems that pretend to\nsuccessfully create sockets but the sockets are unusable, so a title\nwith \"separately\" somewhere might convey its essence more clearly.\nI dunno.\n\nThe patch itself looked sensible (I didn't look for typos in\nconstants etc., though).\n\nThanks.\n"},{"id":"462290","messageId":"pull.1326.v4.git.1661962145.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v3.git.1661280941.gitgitgadget@gmail.com","subject":"[PATCH v4 0/4] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-31T16:09:00Z","receivedAt":"2022-08-31T16:09:14Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"cc: Johannes Schindelin Johannes.Schindelin@gmx.de cc: Jeff Hostetler\ngit@jeffhostetler.com cc: Eric Sunshine sunshine@sunshineco.com cc: Torsten\nBögershausen tboegi@web.de\n\nAllow fsmonitor to run against network-mounted repos on macOS.\n\nThere are four parts to this effort:\n\n 1. Introduce two new configuration options\n    \n    fsmonitor.allowRemote - setting this to true overrides fsmonitor's\n    default behavior of erroring out when enountering network file systems.\n    Additionly, when true, the Unix domain socket (UDS) file used for IPC is\n    located in $HOME rather than in the .git directory.\n    \n    fsmonitor.socketDir - allows for the UDS file to be located anywhere the\n    user chooses rather $HOME.\n\n 2. Using the values of above configuration options, locate the UDS file in\n    the desired location with a unique name based on the SHA1 of the path of\n    the .git folder.\n\n 3. Ensure that both the working directory (.git directory) and the UDS file\n    location are compatible with fsmonitor\n\n 4. Normalize the paths returned by FSEvents to the real path for each\n    affected file or directory\n\nEric DeCosta (4):\n  fsmonitor: add two new config options, allowRemote and socketDir\n  fsmonitor: generate unique Unix socket file name in the desired\n    location\n  fsmonitor: ensure filesystem and unix socket filesystem are compatible\n  fsmonitor: normalize FSEvents event paths to the real path\n\n compat/fsmonitor/fsm-listen-darwin.c   | 11 ++--\n compat/fsmonitor/fsm-settings-darwin.c | 72 +++++++++++++++++++-------\n fsmonitor-ipc.c                        | 40 ++++++++++++--\n fsmonitor-ipc.h                        |  6 +++\n fsmonitor-settings.c                   | 67 +++++++++++++++++++++++-\n fsmonitor-settings.h                   |  4 ++\n 6 files changed, 172 insertions(+), 28 deletions(-)\n\n\nbase-commit: 795ea8776befc95ea2becd8020c7a284677b4161\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v4\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v3:\n\n 1:  cd16d8bb3d6 ! 1:  836a791e6b7 fsmonitor: macOS: allow fsmonitor to run against network-mounted repos\n     @@ Metadata\n      Author: Eric DeCosta <edecosta@mathworks.com>\n      \n       ## Commit message ##\n     -    fsmonitor: macOS: allow fsmonitor to run against network-mounted repos\n     +    fsmonitor: add two new config options, allowRemote and socketDir\n      \n     -    Follow-on to the work done to allow Windows to work against\n     -    network-mounted repos. Have macOS take advantage of the same\n     -    configuration option, 'fsmonitor.allowRemote' that was introduced for\n     -    Windows. Setting this option to true will override the default behavior\n     -    (erroring-out) when a network-mounted repo is detected by fsmonitor.\n     +    Introduce two new configuration options\n      \n     -    The added wrinkle being that the Unix domain socket (UDS) file used for\n     -    IPC cannot be created in a network location; instead $HOME is used if\n     -    'fsmonitor.allowRemote' is true.\n     +       fsmonitor.allowRemote - setting this to true overrides fsmonitor's\n     +       default behavior of erroring out when enountering network file\n     +       systems. Additionly, when true, the Unix domain socket (UDS) file\n     +       used for IPC is located in $HOME rather than in the .git directory.\n      \n     -    If $HOME is in a network location, allow the user to override this via\n     -    the 'fsmonitor.socketDir' configuration option. There the user can\n     -    specify any local directory for the location of the UDS file.\n     +       fsmonitor.socketDir - allows for the UDS file to be located\n     +       anywhere the user chooses rather $HOME.\n      \n          Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n      \n     - ## fsmonitor-ipc.c ##\n     -@@\n     - #include \"cache.h\"\n     --#include \"fsmonitor.h\"\n     --#include \"simple-ipc.h\"\n     - #include \"fsmonitor-ipc.h\"\n     -+#include \"fsmonitor-settings.h\"\n     - #include \"run-command.h\"\n     - #include \"strbuf.h\"\n     - #include \"trace2.h\"\n     -@@ fsmonitor-ipc.c: int fsmonitor_ipc__is_supported(void)\n     - \treturn 1;\n     - }\n     - \n     --GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n     -+GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n     -+\n     -+const char *fsmonitor_ipc__get_path(void)\n     -+{\n     -+#ifdef WIN32\n     -+\treturn fsmonitor_ipc__get_default_path();\n     -+#else\n     -+\tchar *retval;\n     -+\tSHA_CTX sha1ctx;\n     -+\tconst char *git_dir;\n     -+\tconst char *sock_dir;\n     -+\tstruct strbuf ipc_file = STRBUF_INIT;\n     -+\tunsigned char hash[SHA_DIGEST_LENGTH];\n     -+\n     -+\tif (fsm_settings__get_allow_remote(the_repository) < 1)\n     -+\t\treturn fsmonitor_ipc__get_default_path();\n     -+\n     -+\tgit_dir = get_git_dir();\n     -+\tsock_dir = fsm_settings__get_socket_dir(the_repository);\n     -+\n     -+\tSHA1_Init(&sha1ctx);\n     -+\tSHA1_Update(&sha1ctx, git_dir, strlen(git_dir));\n     -+\tSHA1_Final(hash, &sha1ctx);\n     -+\n     -+\tif (sock_dir && *sock_dir)\n     -+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n     -+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n     -+\telse\n     -+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n     -+\tretval = interpolate_path(ipc_file.buf, 1);\n     -+\tif (!retval)\n     -+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n     -+\tstrbuf_release(&ipc_file);\n     -+\treturn retval;\n     -+#endif\n     -+}\n     - \n     - enum ipc_active_state fsmonitor_ipc__get_state(void)\n     - {\n     -\n     - ## fsmonitor-ipc.h ##\n     -@@ fsmonitor-ipc.h: int fsmonitor_ipc__is_supported(void);\n     -  */\n     - const char *fsmonitor_ipc__get_path(void);\n     - \n     -+/*\n     -+ * Returns the pathname to the default IPC named pipe or Unix domain\n     -+ * socket.\n     -+ */\n     -+const char *fsmonitor_ipc__get_default_path(void);\n     -+\n     - /*\n     -  * Try to determine whether there is a `git-fsmonitor--daemon` process\n     -  * listening on the IPC pipe/socket.\n     -\n       ## fsmonitor-settings.c ##\n      @@\n       struct fsmonitor_settings {\n -:  ----------- > 2:  2cb026a6317 fsmonitor: generate unique Unix socket file name in the desired location\n 2:  f977d140afa ! 3:  a3110f1e25a Check working directory and Unix domain socket file for compatability\n     @@\n       ## Metadata ##\n     -Author: edecosta <edecosta@mathworks.com>\n     +Author: Eric DeCosta <edecosta@mathworks.com>\n      \n       ## Commit message ##\n     -    Check working directory and Unix domain socket file for compatability\n     +    fsmonitor: ensure filesystem and unix socket filesystem are compatible\n      \n          Perform separate checks for the working directory and Unix domain socket\n          (UDS) file location. The working directory may be located on a\n     @@ Commit message\n          file may never be located on a network-mounted file system; additionally\n          it may not be located on FAT32 or NTFS file systems.\n      \n     -    Signed-off-by: edecosta <edecosta@mathworks.com>\n     +    Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n      \n       ## compat/fsmonitor/fsm-settings-darwin.c ##\n      @@\n -:  ----------- > 4:  56cabf3be3b fsmonitor: normalize FSEvents event paths to the real path\n\n-- \ngitgitgadget\n"},{"id":"462291","messageId":"836a791e6b7fd4490674254ce03105a8ca2175cb.1661962145.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v4.git.1661962145.gitgitgadget@gmail.com","subject":"[PATCH v4 1/4] fsmonitor: add two new config options, allowRemote and socketDir","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-31T16:09:01Z","receivedAt":"2022-08-31T16:09:16Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIntroduce two new configuration options\n\n   fsmonitor.allowRemote - setting this to true overrides fsmonitor's\n   default behavior of erroring out when enountering network file\n   systems. Additionly, when true, the Unix domain socket (UDS) file\n   used for IPC is located in $HOME rather than in the .git directory.\n\n   fsmonitor.socketDir - allows for the UDS file to be located\n   anywhere the user chooses rather $HOME.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n fsmonitor-settings.c | 67 ++++++++++++++++++++++++++++++++++++++++++--\n fsmonitor-settings.h |  4 +++\n 2 files changed, 69 insertions(+), 2 deletions(-)\n\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..a15eeecebf4 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -10,7 +10,9 @@\n struct fsmonitor_settings {\n \tenum fsmonitor_mode mode;\n \tenum fsmonitor_reason reason;\n+\tint allow_remote;\n \tchar *hook_path;\n+\tchar *sock_dir;\n };\n \n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n@@ -43,6 +45,7 @@ static struct fsmonitor_settings *alloc_settings(void)\n \tCALLOC_ARRAY(s, 1);\n \ts->mode = FSMONITOR_MODE_DISABLED;\n \ts->reason = FSMONITOR_REASON_UNTESTED;\n+\ts->allow_remote = -1;\n \n \treturn s;\n }\n@@ -90,6 +93,26 @@ static void lookup_fsmonitor_settings(struct repository *r)\n \t\tfsm_settings__set_disabled(r);\n }\n \n+int fsm_settings__get_allow_remote(struct repository *r)\n+{\n+\tif (!r)\n+\t\tr = the_repository;\n+\tif (!r->settings.fsmonitor)\n+\t\tlookup_fsmonitor_settings(r);\n+\n+\treturn r->settings.fsmonitor->allow_remote;\n+}\n+\n+const char *fsm_settings__get_socket_dir(struct repository *r)\n+{\n+\tif (!r)\n+\t\tr = the_repository;\n+\tif (!r->settings.fsmonitor)\n+\t\tlookup_fsmonitor_settings(r);\n+\n+\treturn r->settings.fsmonitor->sock_dir;\n+}\n+\n enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n {\n \tif (!r)\n@@ -100,6 +123,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n \treturn r->settings.fsmonitor->mode;\n }\n \n+\n const char *fsm_settings__get_hook_path(struct repository *r)\n {\n \tif (!r)\n@@ -110,9 +134,44 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \treturn r->settings.fsmonitor->hook_path;\n }\n \n+void fsm_settings__set_allow_remote(struct repository *r)\n+{\n+\tint allow;\n+\n+\tif (!r)\n+\t\tr = the_repository;\n+\tif (!r->settings.fsmonitor)\n+\t\tr->settings.fsmonitor = alloc_settings();\n+\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n+\t\tr->settings.fsmonitor->allow_remote = allow;\n+\n+\treturn;\n+}\n+\n+void fsm_settings__set_socket_dir(struct repository *r)\n+{\n+\tconst char *path;\n+\n+\tif (!r)\n+\t\tr = the_repository;\n+\tif (!r->settings.fsmonitor)\n+\t\tr->settings.fsmonitor = alloc_settings();\n+\n+\tif (!repo_config_get_pathname(r, \"fsmonitor.socketdir\", &path)) {\n+\t\tFREE_AND_NULL(r->settings.fsmonitor->sock_dir);\n+\t\tr->settings.fsmonitor->sock_dir = strdup(path);\n+\t}\n+\n+\treturn;\n+}\n+\n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason;\n+\n+\tfsm_settings__set_allow_remote(r);\n+\tfsm_settings__set_socket_dir(r);\n+\treason = check_for_incompatible(r);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -135,7 +194,11 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason;\n+\n+\tfsm_settings__set_allow_remote(r);\n+\tfsm_settings__set_socket_dir(r);\n+\treason = check_for_incompatible(r);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..2de54c85e94 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -23,12 +23,16 @@ enum fsmonitor_reason {\n \tFSMONITOR_REASON_NOSOCKETS, /* NTFS,FAT32 do not support Unix sockets */\n };\n \n+void fsm_settings__set_allow_remote(struct repository *r);\n+void fsm_settings__set_socket_dir(struct repository *r);\n void fsm_settings__set_ipc(struct repository *r);\n void fsm_settings__set_hook(struct repository *r, const char *path);\n void fsm_settings__set_disabled(struct repository *r);\n void fsm_settings__set_incompatible(struct repository *r,\n \t\t\t\t    enum fsmonitor_reason reason);\n \n+int fsm_settings__get_allow_remote(struct repository *r);\n+const char *fsm_settings__get_socket_dir(struct repository *r);\n enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n const char *fsm_settings__get_hook_path(struct repository *r);\n \n-- \ngitgitgadget\n\n"},{"id":"462292","messageId":"2cb026a631704b004b06e4a944c79a434df08440.1661962145.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v4.git.1661962145.gitgitgadget@gmail.com","subject":"[PATCH v4 2/4] fsmonitor: generate unique Unix socket file name in the desired location","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-31T16:09:02Z","receivedAt":"2022-08-31T16:09:30Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nBased on the values of fsmonitor.allowRemote and fsmonitor.socketDir\nlocate the Unix domain socket file in the desired location (either\nthe .git directory, $HOME, or fsmonitor.socketDir). If the location\nis other than the .git directory, generate a unique file name based\non the SHA1 has of the path to the .git directory.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n fsmonitor-ipc.c | 40 +++++++++++++++++++++++++++++++++++++---\n fsmonitor-ipc.h |  6 ++++++\n 2 files changed, 43 insertions(+), 3 deletions(-)\n\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..1e3f0a6cf48 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -1,7 +1,6 @@\n #include \"cache.h\"\n-#include \"fsmonitor.h\"\n-#include \"simple-ipc.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n #include \"run-command.h\"\n #include \"strbuf.h\"\n #include \"trace2.h\"\n@@ -47,7 +46,42 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n+GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(void)\n+{\n+#ifdef WIN32\n+\treturn fsmonitor_ipc__get_default_path();\n+#else\n+\tchar *retval;\n+\tSHA_CTX sha1ctx;\n+\tconst char *git_dir;\n+\tconst char *sock_dir;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (fsm_settings__get_allow_remote(the_repository) < 1)\n+\t\treturn fsmonitor_ipc__get_default_path();\n+\n+\tgit_dir = get_git_dir();\n+\tsock_dir = fsm_settings__get_socket_dir(the_repository);\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, git_dir, strlen(git_dir));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\tif (sock_dir && *sock_dir)\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\telse\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\tretval = interpolate_path(ipc_file.buf, 1);\n+\tif (!retval)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\tstrbuf_release(&ipc_file);\n+\treturn retval;\n+#endif\n+}\n \n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..4d27223c2a6 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -18,6 +18,12 @@ int fsmonitor_ipc__is_supported(void);\n  */\n const char *fsmonitor_ipc__get_path(void);\n \n+/*\n+ * Returns the pathname to the default IPC named pipe or Unix domain\n+ * socket.\n+ */\n+const char *fsmonitor_ipc__get_default_path(void);\n+\n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n  * listening on the IPC pipe/socket.\n-- \ngitgitgadget\n\n"},{"id":"462293","messageId":"a3110f1e25a19da5791aa1675a82556d95469248.1661962145.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v4.git.1661962145.gitgitgadget@gmail.com","subject":"[PATCH v4 3/4] fsmonitor: ensure filesystem and unix socket filesystem are compatible","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-31T16:09:03Z","receivedAt":"2022-08-31T16:09:33Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nPerform separate checks for the working directory and Unix domain socket\n(UDS) file location. The working directory may be located on a\nnetwork-mounted file system if 'fsmonitor.allowRemote' is true. The UDS\nfile may never be located on a network-mounted file system; additionally\nit may not be located on FAT32 or NTFS file systems.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 72 +++++++++++++++++++-------\n 1 file changed, 52 insertions(+), 20 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dc79538607f 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -3,11 +3,12 @@\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n #include <sys/param.h>\n #include <sys/mount.h>\n \n /*\n- * [1] Remote working directories are problematic for FSMonitor.\n+ * Remote working directories are problematic for FSMonitor.\n  *\n  * The underlying file system on the server machine and/or the remote\n  * mount type (NFS, SAMBA, etc.) dictates whether notification events\n@@ -27,26 +28,8 @@\n  * In theory, the above issues need to be addressed whether we are\n  * using the Hook or IPC API.\n  *\n- * For the builtin FSMonitor, we create the Unix domain socket for the\n- * IPC in the .git directory.  If the working directory is remote,\n- * then the socket will be created on the remote file system.  This\n- * can fail if the remote file system does not support UDS file types\n- * (e.g. smbfs to a Windows server) or if the remote kernel does not\n- * allow a non-local process to bind() the socket.  (These problems\n- * could be fixed by moving the UDS out of the .git directory and to a\n- * well-known local directory on the client machine, but care should\n- * be taken to ensure that $HOME is actually local and not a managed\n- * file share.)\n- *\n  * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n- *\n- * The builtin FSMonitor uses a Unix domain socket in the .git\n- * directory for IPC.  These Windows drive formats do not support\n- * Unix domain sockets, so mark them as incompatible for the daemon.\n+ * incompatible unless fsmonitor.allowRemote is true.\n  *\n  */\n static enum fsmonitor_reason check_volume(struct repository *r)\n@@ -65,6 +48,51 @@ static enum fsmonitor_reason check_volume(struct repository *r)\n \t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n \t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n \n+\tif (!(fs.f_flags & MNT_LOCAL)\n+\t\t&& (fsm_settings__get_allow_remote(r) < 1))\n+\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\n+\treturn FSMONITOR_REASON_OK;\n+}\n+\n+/*\n+ * For the builtin FSMonitor, we create the Unix domain socket (UDS)\n+ * for the IPC in the .git directory by default or $HOME if\n+ * fsmonitor.allowRemote is true.  If the directory is remote,\n+ * then the socket will be created on the remote file system. This\n+ * can fail if the remote file system does not support UDS file types\n+ * (e.g. smbfs to a Windows server) or if the remote kernel does not\n+ * allow a non-local process to bind() the socket.\n+ *\n+ * Therefore remote UDS locations are marked as incompatible.\n+ *\n+ * FAT32 and NTFS working directories are problematic too.\n+ *\n+ * These Windows drive formats do not support Unix domain sockets, so\n+ * mark them as incompatible for the location of the UDS file.\n+ *\n+ */\n+static enum fsmonitor_reason check_uds_volume(void)\n+{\n+\tstruct statfs fs;\n+\tstruct strbuf path = STRBUF_INIT;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n+\n+\tif (statfs(dirname(path.buf), &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path.buf, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\tstrbuf_release(&path);\n+\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path.buf, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n+\n \tif (!(fs.f_flags & MNT_LOCAL))\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n@@ -85,5 +113,9 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n+\treason = check_uds_volume();\n+\tif (reason != FSMONITOR_REASON_OK)\n+\t\treturn reason;\n+\n \treturn FSMONITOR_REASON_OK;\n }\n-- \ngitgitgadget\n\n"},{"id":"462294","messageId":"56cabf3be3b9e228bc948da372db4c9d11fd3926.1661962145.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v4.git.1661962145.gitgitgadget@gmail.com","subject":"[PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the real path","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-08-31T16:09:04Z","receivedAt":"2022-08-31T16:09:35Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nConsider the following network working directory that is mounted under\n/System/Volumes/Data:\n\n/network/working/directory\n\nThe git working directory path is:\n\n/System/Volumes/Data/network/working/directory\n\nThe paths reported by FSEvents always start with /network. fsmonitor\nexpects paths to be under the working directory; therefore it\nfails to match /network/... and ignores the change.\n\nChange things such that if fsmonitor.allowRemote is true that the\npaths reported via FSEevents are normalized to the real path.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-listen-darwin.c | 11 ++++++++---\n 1 file changed, 8 insertions(+), 3 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\nindex 8e208e8289e..2ed828649ff 100644\n--- a/compat/fsmonitor/fsm-listen-darwin.c\n+++ b/compat/fsmonitor/fsm-listen-darwin.c\n@@ -26,6 +26,7 @@\n #include \"fsmonitor.h\"\n #include \"fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n+#include \"fsmonitor-settings.h\"\n \n struct fsm_listen_data\n {\n@@ -183,7 +184,6 @@ static void my_add_path(struct fsmonitor_batch *batch, const char *path)\n \tfree(composed);\n }\n \n-\n static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t\t     void *ctx,\n \t\t\t     size_t num_of_events,\n@@ -209,7 +209,12 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t/*\n \t\t * On Mac, we receive an array of absolute paths.\n \t\t */\n-\t\tpath_k = paths[k];\n+\t\tif (fsm_settings__get_allow_remote(the_repository) > 0) {\n+\t\t\tstrbuf_reset(&tmp);\n+\t\t\tstrbuf_realpath_forgiving(&tmp, paths[k], 0);\n+\t\t\tpath_k = tmp.buf;\n+\t\t} else\n+\t\t\tpath_k = paths[k];\n \n \t\t/*\n \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n@@ -237,6 +242,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \n \t\t\tfsmonitor_force_resync(state);\n \t\t\tfsmonitor_batch__free_list(batch);\n+\t\t\tbatch = NULL;\n \t\t\tstring_list_clear(&cookie_list, 0);\n \n \t\t\t/*\n@@ -313,7 +319,6 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \n \t\tcase IS_WORKDIR_PATH:\n \t\t\t/* try to queue normal pathnames */\n-\n \t\t\tif (trace_pass_fl(&trace_fsmonitor))\n \t\t\t\tlog_flags_set(path_k, event_flags[k]);\n \n-- \ngitgitgadget\n"},{"id":"462304","messageId":"220831.86ler48apq.gmgdl@evledraar.gmail.com","threadId":"58324","inReplyTo":"56cabf3be3b9e228bc948da372db4c9d11fd3926.1661962145.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the real path","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T19:37:53Z","receivedAt":"2022-08-31T19:38:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Aug 31 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n>\n> Consider the following network working directory that is mounted under\n> /System/Volumes/Data:\n>\n> /network/working/directory\n>\n> The git working directory path is:\n>\n> /System/Volumes/Data/network/working/directory\n>\n> The paths reported by FSEvents always start with /network. fsmonitor\n> expects paths to be under the working directory; therefore it\n> fails to match /network/... and ignores the change.\n>\n> Change things such that if fsmonitor.allowRemote is true that the\n> paths reported via FSEevents are normalized to the real path.\n>\n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n>  compat/fsmonitor/fsm-listen-darwin.c | 11 ++++++++---\n>  1 file changed, 8 insertions(+), 3 deletions(-)\n>\n> diff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\n> index 8e208e8289e..2ed828649ff 100644\n> --- a/compat/fsmonitor/fsm-listen-darwin.c\n> +++ b/compat/fsmonitor/fsm-listen-darwin.c\n> @@ -26,6 +26,7 @@\n>  #include \"fsmonitor.h\"\n>  #include \"fsm-listen.h\"\n>  #include \"fsmonitor--daemon.h\"\n> +#include \"fsmonitor-settings.h\"\n>  \n>  struct fsm_listen_data\n>  {\n> @@ -183,7 +184,6 @@ static void my_add_path(struct fsmonitor_batch *batch, const char *path)\n>  \tfree(composed);\n>  }\n>  \n> -\n\nStray whitespace change, any one isn't much, but they add up (I saw\nanother one earlier in this topic).\n\n>  static void fsevent_callback(ConstFSEventStreamRef streamRef,\n>  \t\t\t     void *ctx,\n>  \t\t\t     size_t num_of_events,\n> @@ -209,7 +209,12 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n>  \t\t/*\n>  \t\t * On Mac, we receive an array of absolute paths.\n>  \t\t */\n> -\t\tpath_k = paths[k];\n> +\t\tif (fsm_settings__get_allow_remote(the_repository) > 0) {\n> +\t\t\tstrbuf_reset(&tmp);\n> +\t\t\tstrbuf_realpath_forgiving(&tmp, paths[k], 0);\n> +\t\t\tpath_k = tmp.buf;\n> +\t\t} else\n> +\t\t\tpath_k = paths[k];\n\nStyle: This else should have braces if any if/else arm does, see\nCodingGuidelines.\n\n> [...]\n> @@ -313,7 +319,6 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n>  \n>  \t\tcase IS_WORKDIR_PATH:\n>  \t\t\t/* try to queue normal pathnames */\n> -\n\nMore stray whitespace\n"},{"id":"462305","messageId":"220831.86h71s8ad7.gmgdl@evledraar.gmail.com","threadId":"58324","inReplyTo":"836a791e6b7fd4490674254ce03105a8ca2175cb.1661962145.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 1/4] fsmonitor: add two new config options, allowRemote and socketDir","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T19:41:28Z","receivedAt":"2022-08-31T19:46:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Aug 31 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n> [...]\n>  \tenum fsmonitor_reason reason;\n> +\tint allow_remote;\n>  \tchar *hook_path;\n> +\tchar *sock_dir;\n>  };\n\nAny reason we couldn't just add this to \"struct repo_settings\" and ...\n\n> +int fsm_settings__get_allow_remote(struct repository *r)\n> +{\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tlookup_fsmonitor_settings(r);\n> +\n> +\treturn r->settings.fsmonitor->allow_remote;\n> +}\n> +\n> +const char *fsm_settings__get_socket_dir(struct repository *r)\n> +{\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tlookup_fsmonitor_settings(r);\n> +\n> +\treturn r->settings.fsmonitor->sock_dir;\n> +}\n> +\n\n...instead of this whole ceremony...\n\n> +void fsm_settings__set_allow_remote(struct repository *r)\n> +{\n> +\tint allow;\n> +\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tr->settings.fsmonitor = alloc_settings();\n> +\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n> +\t\tr->settings.fsmonitor->allow_remote = allow;\n> +\n> +\treturn;\n> +}\n\nJust have a single repo_cfg_bool() line in prepare_repo_settings()\ninstead?\n\n(There are some reasons for the \"lazy\" behavior of fsmonitor-settings.c,\nbut surely a simple boolean variable we can read on startup isn't it,\nand we already paid the cost to do so with the configset...)\n\n\n> +void fsm_settings__set_socket_dir(struct repository *r)\n> +{\n> +\tconst char *path;\n> +\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tr->settings.fsmonitor = alloc_settings();\n> +\n> +\tif (!repo_config_get_pathname(r, \"fsmonitor.socketdir\", &path)) {\n> +\t\tFREE_AND_NULL(r->settings.fsmonitor->sock_dir);\n\n...maybe this socket dir stuff is the exception though.\n\n> +\t\tr->settings.fsmonitor->sock_dir = strdup(path);\n\nAren't you strdup()-ing an already strdup()'d string, and therefore\nleaking memory? Also this should be xstrdup(), surely?\n\n> +\t}\n> +\n> +\treturn;\n> +}\n> +\n>  void fsm_settings__set_ipc(struct repository *r)\n>  {\n> -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n> +\tenum fsmonitor_reason reason;\n> +\n> +\tfsm_settings__set_allow_remote(r);\n> +\tfsm_settings__set_socket_dir(r);\n> +\treason = check_for_incompatible(r);\n\nThis seems rather backwards, as odd as this API itself is already, isn't\nthe whole idea that after we call lookup_fsmonitor_settings() it will\nhave set() anything that's mandatory?\n\nBut maybe I haven't grokked it ... :)\n"},{"id":"462307","messageId":"220831.86czcg89wx.gmgdl@evledraar.gmail.com","threadId":"58324","inReplyTo":"2cb026a631704b004b06e4a944c79a434df08440.1661962145.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 2/4] fsmonitor: generate unique Unix socket file name in the desired location","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-08-31T19:49:24Z","receivedAt":"2022-08-31T19:56:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Aug 31 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n>\n> Based on the values of fsmonitor.allowRemote and fsmonitor.socketDir\n> locate the Unix domain socket file in the desired location (either\n> the .git directory, $HOME, or fsmonitor.socketDir). If the location\n> is other than the .git directory, generate a unique file name based\n> on the SHA1 has of the path to the .git directory.\n\nPer:\n\n\tfsmonitor-ipc.h- * Returns the pathname to the IPC named pipe or Unix domain socket\n\tfsmonitor-ipc.h- * where a `git-fsmonitor--daemon` process will listen.  This is a\n\tfsmonitor-ipc.h- * per-worktree value.\n\n> +\tgit_dir = get_git_dir();\n> +\tsock_dir = fsm_settings__get_socket_dir(the_repository);\n> +\n> +\tSHA1_Init(&sha1ctx);\n> +\tSHA1_Update(&sha1ctx, git_dir, strlen(git_dir));\n> +\tSHA1_Final(hash, &sha1ctx);\n> +\n> +\tif (sock_dir && *sock_dir)\n> +\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n> +\t\t\t\t\tsock_dir, hash_to_hex(hash));\n> +\telse\n\nBut here we (from eyeballing this, maybe I've missed something):\n\n 1. Get the path to the git dir\n 2. SHA-1 hash that path, presumably to make it fixed size & get rid of\n    slashes etc.\n 3. Make that the IPC filename\n\nPer the \"per worktree\" can't we just check if:\n\n * We have a .git/worktree/? If so derive the name from that.\n * We don't? Then we just have one? Stick it in in there? I.e. isn't the\n   hash_to_hex() here redundant?\n\n...\n\n> +\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n\n... but not here, so is it just for this \"else\", but got carried over\nabove?\n\nI think if we're creating a new global cookie file, and presumably\npotentially a *lot* of them in the user's ~ we should at least\nprominently document that somewhere.\n\nBut more generally couldn't this?:\n\n * Play nice with $HOME/.config/git/ etc, as is the usual convention these days on *nix\n * We would have to the equivalent of a \"mkdir -p\", but if we stick it\n   in .config/git/fsmonitor-sockets or something we could have a nested\n   path there that mirrors the path to the repo.\n\nThe latter can really help with debugging, you have this random\n.git-fsmonitor-XXXXX file, where the XXXX is totally mysterious, until\nyou eventually find this code & discover it's a SHA-1 hash of some other\npath...\n"},{"id":"462309","messageId":"xmqq7d2ofact.fsf@gitster.g","threadId":"58324","inReplyTo":"836a791e6b7fd4490674254ce03105a8ca2175cb.1661962145.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 1/4] fsmonitor: add two new config options, allowRemote and socketDir","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-08-31T20:04:34Z","receivedAt":"2022-08-31T20:04:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n>\n> Introduce two new configuration options\n>\n>    fsmonitor.allowRemote - setting this to true overrides fsmonitor's\n>    default behavior of erroring out when enountering network file\n>    systems. Additionly, when true, the Unix domain socket (UDS) file\n>    used for IPC is located in $HOME rather than in the .git directory.\n>\n>    fsmonitor.socketDir - allows for the UDS file to be located\n>    anywhere the user chooses rather $HOME.\n\nBefore describing \"what they do\", please justify why we need to add\nthem.  The usual way to do so is to start with some observation of\nthe status quo, and describe the \"gap\" between what can be done with\nthe current system and what the users would want to do.  It might\nfurther be necessary to justify why \"would want to do\" is worthwhile\nto support.  I suspect that you can do all of the above in a couple\nof paragraphs, and you'd succeed if the solution you chose would\nfall out as a natural consequence in the mind of readers who follow\nyour line of thought by reading these introductory paragraphs.  And\nthen after the stage is set like so, the above description of what\nyou chose to implement as a solution should come.\n\n>  struct fsmonitor_settings {\n>  \tenum fsmonitor_mode mode;\n>  \tenum fsmonitor_reason reason;\n> +\tint allow_remote;\n\nI am debating myuself if a comment like\n\n\tint allow_remote; /* -1: undecided, 0: unallowed, 1: allowed */\n\nis necessary (I know it would help if we had one; I am just\nwondering if it is too obvious).\n\n>  \tchar *hook_path;\n> +\tchar *sock_dir;\n>  };\n>  \n>  static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n> @@ -43,6 +45,7 @@ static struct fsmonitor_settings *alloc_settings(void)\n>  \tCALLOC_ARRAY(s, 1);\n>  \ts->mode = FSMONITOR_MODE_DISABLED;\n>  \ts->reason = FSMONITOR_REASON_UNTESTED;\n> +\ts->allow_remote = -1;\n>  \n>  \treturn s;\n>  }\n\nOK.  socket_dir is naturally NULL at the start.\n\n> @@ -100,6 +123,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n>  \treturn r->settings.fsmonitor->mode;\n>  }\n>  \n> +\n>  const char *fsm_settings__get_hook_path(struct repository *r)\n>  {\n>  \tif (!r)\n\nA noise hunk?\n\n> @@ -110,9 +134,44 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n> ...\n> +void fsm_settings__set_socket_dir(struct repository *r)\n> +{\n> +\tconst char *path;\n> +\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tr->settings.fsmonitor = alloc_settings();\n> +\n> +\tif (!repo_config_get_pathname(r, \"fsmonitor.socketdir\", &path)) {\n> +\t\tFREE_AND_NULL(r->settings.fsmonitor->sock_dir);\n> +\t\tr->settings.fsmonitor->sock_dir = strdup(path);\n\nAs we are overwriting it immediately, just calling free(), not\nFREE_AND_NULL(), is more appropriate, isn't it?\n\n> @@ -135,7 +194,11 @@ void fsm_settings__set_ipc(struct repository *r)\n>  \n>  void fsm_settings__set_hook(struct repository *r, const char *path)\n>  {\n> -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n> +\tenum fsmonitor_reason reason;\n> +\n> +\tfsm_settings__set_allow_remote(r);\n> +\tfsm_settings__set_socket_dir(r);\n> +\treason = check_for_incompatible(r);\n>  \n>  \tif (reason != FSMONITOR_REASON_OK) {\n>  \t\tfsm_settings__set_incompatible(r, reason);\n> diff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\n> index d9c2605197f..2de54c85e94 100644\n> --- a/fsmonitor-settings.h\n> +++ b/fsmonitor-settings.h\n> @@ -23,12 +23,16 @@ enum fsmonitor_reason {\n>  \tFSMONITOR_REASON_NOSOCKETS, /* NTFS,FAT32 do not support Unix sockets */\n>  };\n>  \n> +void fsm_settings__set_allow_remote(struct repository *r);\n> +void fsm_settings__set_socket_dir(struct repository *r);\n\nDo these two need to be extern?\n\nI would imagine that implementations in compat/fsmonitor/* would\njust call fsm_settings__set_hook() or __set_ipc() and that causes\nthese two to be called as part of the set-up sequence.  Do they need\nto call these two directly?\n\nIf not, we probably should make them static.  I suspect that some\nexisting declarations in this header file fall into the same\ncategory and may need to become static for the same reason, which\nyou can do as a preliminary clean-up patch, or post-clean-up after\nthe dust settles.  Regardless of which approach to take for existing\nones, we do not want to make it worse by adding new externally\nvisible names that do not have to be visible.\n\n>  void fsm_settings__set_ipc(struct repository *r);\n>  void fsm_settings__set_hook(struct repository *r, const char *path);\n>  void fsm_settings__set_disabled(struct repository *r);\n>  void fsm_settings__set_incompatible(struct repository *r,\n>  \t\t\t\t    enum fsmonitor_reason reason);\n>  \n> +int fsm_settings__get_allow_remote(struct repository *r);\n> +const char *fsm_settings__get_socket_dir(struct repository *r);\n\nOn the other hand, these may need to be query-able by the\nimplementations.\n\n>  enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n>  const char *fsm_settings__get_hook_path(struct repository *r);\n"},{"id":"462311","messageId":"xmqqzgfkdvga.fsf@gitster.g","threadId":"58324","inReplyTo":"2cb026a631704b004b06e4a944c79a434df08440.1661962145.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 2/4] fsmonitor: generate unique Unix socket file name in the desired location","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-08-31T20:11:49Z","receivedAt":"2022-08-31T20:12:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +const char *fsmonitor_ipc__get_path(void)\n> +{\n> +#ifdef WIN32\n> +\treturn fsmonitor_ipc__get_default_path();\n> +#else\n\nHmph.\n\nAs compat/fsmonitor/ directory already sets up a good way to have\nplatform-specific implementation of a common API function, I think\nthis patch goes in a wrong direction by fighting it.\n\nShouldn't the rest of the function we see here be made to another \nimplementation of fsmonitor_ipc__get_default_path() that is\nspecific to platforms, i.e. in compat/fsmonitor/$somenewfile.c,\nthat is used for !WIN32 case?\n"},{"id":"462408","messageId":"db2442fb-5b38-9556-39db-ba621bdf2476@ramsayjones.plus.com","threadId":"58324","inReplyTo":"xmqq7d2ofact.fsf@gitster.g","subject":"Re: [PATCH v4 1/4] fsmonitor: add two new config options, allowRemote and socketDir","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2022-09-01T02:25:22Z","receivedAt":"2022-09-01T02:28:49Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 31/08/2022 21:04, Junio C Hamano wrote:\n> \"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n[snip]\n>> diff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\n>> index d9c2605197f..2de54c85e94 100644\n>> --- a/fsmonitor-settings.h\n>> +++ b/fsmonitor-settings.h\n>> @@ -23,12 +23,16 @@ enum fsmonitor_reason {\n>>  \tFSMONITOR_REASON_NOSOCKETS, /* NTFS,FAT32 do not support Unix sockets */\n>>  };\n>>  \n>> +void fsm_settings__set_allow_remote(struct repository *r);\n>> +void fsm_settings__set_socket_dir(struct repository *r);\n> \n> Do these two need to be extern?\n> \n\nOn 'seen' after building:\n  ...\n  $ ./static-check.pl >ssc\n  $ diff nsc ssc\n  3a4\n  > bundle-uri.o\t- for_all_bundles_in_list\n  15d15\n  < config.o\t- git_config_from_file_with_options\n  40a41,43\n  > fsmonitor-settings.o\t- fsm_settings__get_allow_remote\n  > fsmonitor-settings.o\t- fsm_settings__get_socket_dir\n  > fsmonitor-settings.o\t- fsm_settings__set_allow_remote\n  44a48\n  > fsmonitor-settings.o\t- fsm_settings__set_socket_dir\n  $ \n\n.. so probably not. ;-)\n\nATB,\nRamsay Jones\n\n\n"},{"id":"462476","messageId":"8acf1e78-bac4-7f59-11a4-996aa160bbdc@jeffhostetler.com","threadId":"58324","inReplyTo":"836a791e6b7fd4490674254ce03105a8ca2175cb.1661962145.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 1/4] fsmonitor: add two new config options, allowRemote and socketDir","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-09-01T17:53:08Z","receivedAt":"2022-09-01T17:53:15Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 8/31/22 12:09 PM, Eric DeCosta via GitGitGadget wrote:\n> From: Eric DeCosta <edecosta@mathworks.com>\n> \n> Introduce two new configuration options\n> \n>     fsmonitor.allowRemote - setting this to true overrides fsmonitor's\n>     default behavior of erroring out when enountering network file\n>     systems. Additionly, when true, the Unix domain socket (UDS) file\n>     used for IPC is located in $HOME rather than in the .git directory.\n> \n>     fsmonitor.socketDir - allows for the UDS file to be located\n>     anywhere the user chooses rather $HOME.\n> \n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n>   fsmonitor-settings.c | 67 ++++++++++++++++++++++++++++++++++++++++++--\n>   fsmonitor-settings.h |  4 +++\n>   2 files changed, 69 insertions(+), 2 deletions(-)\n> \n> diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\n> index 464424a1e92..a15eeecebf4 100644\n> --- a/fsmonitor-settings.c\n> +++ b/fsmonitor-settings.c\n> @@ -10,7 +10,9 @@\n>   struct fsmonitor_settings {\n>   \tenum fsmonitor_mode mode;\n>   \tenum fsmonitor_reason reason;\n> +\tint allow_remote;\n>   \tchar *hook_path;\n> +\tchar *sock_dir;\n>   };\n>   \n>   static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n> @@ -43,6 +45,7 @@ static struct fsmonitor_settings *alloc_settings(void)\n>   \tCALLOC_ARRAY(s, 1);\n>   \ts->mode = FSMONITOR_MODE_DISABLED;\n>   \ts->reason = FSMONITOR_REASON_UNTESTED;\n> +\ts->allow_remote = -1;\n>   \n>   \treturn s;\n>   }\n> @@ -90,6 +93,26 @@ static void lookup_fsmonitor_settings(struct repository *r)\n>   \t\tfsm_settings__set_disabled(r);\n>   }\n>   \n> +int fsm_settings__get_allow_remote(struct repository *r)\n> +{\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tlookup_fsmonitor_settings(r);\n> +\n> +\treturn r->settings.fsmonitor->allow_remote;\n> +}\n> +\n> +const char *fsm_settings__get_socket_dir(struct repository *r)\n> +{\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tlookup_fsmonitor_settings(r);\n> +\n> +\treturn r->settings.fsmonitor->sock_dir;\n> +}\n> +\n>   enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n>   {\n>   \tif (!r)\n> @@ -100,6 +123,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n>   \treturn r->settings.fsmonitor->mode;\n>   }\n>   \n> +\n>   const char *fsm_settings__get_hook_path(struct repository *r)\n>   {\n>   \tif (!r)\n> @@ -110,9 +134,44 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n>   \treturn r->settings.fsmonitor->hook_path;\n>   }\n>   \n> +void fsm_settings__set_allow_remote(struct repository *r)\n> +{\n> +\tint allow;\n> +\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tr->settings.fsmonitor = alloc_settings();\n> +\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n> +\t\tr->settings.fsmonitor->allow_remote = allow;\n> +\n> +\treturn;\n> +}\n> +\n> +void fsm_settings__set_socket_dir(struct repository *r)\n> +{\n> +\tconst char *path;\n> +\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tr->settings.fsmonitor = alloc_settings();\n> +\n> +\tif (!repo_config_get_pathname(r, \"fsmonitor.socketdir\", &path)) {\n> +\t\tFREE_AND_NULL(r->settings.fsmonitor->sock_dir);\n> +\t\tr->settings.fsmonitor->sock_dir = strdup(path);\n> +\t}\n> +\n> +\treturn;\n> +}\n> +\n>   void fsm_settings__set_ipc(struct repository *r)\n>   {\n> -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n> +\tenum fsmonitor_reason reason;\n> +\n> +\tfsm_settings__set_allow_remote(r);\n> +\tfsm_settings__set_socket_dir(r);\n> +\treason = check_for_incompatible(r);\n>   \n>   \tif (reason != FSMONITOR_REASON_OK) {\n>   \t\tfsm_settings__set_incompatible(r, reason);\n> @@ -135,7 +194,11 @@ void fsm_settings__set_ipc(struct repository *r)\n>   \n>   void fsm_settings__set_hook(struct repository *r, const char *path)\n>   {\n> -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n> +\tenum fsmonitor_reason reason;\n> +\n> +\tfsm_settings__set_allow_remote(r);\n> +\tfsm_settings__set_socket_dir(r);\n> +\treason = check_for_incompatible(r);\n\nWhen we use the hook interface (to talk to a third-party\napp like Watchman) we do not use the unix domain socket.\n(We talk to the hook child using a normal pipe.)\n\nFWIW, I added code in my series to limit the use of hook-based\nfsmonitors to local filesystems for the paranoia-based\nreasons I've described before.  It wasn't because of the UDS.\n\nSo we don't need to set the socket directory in this case.\n\n>   \n>   \tif (reason != FSMONITOR_REASON_OK) {\n>   \t\tfsm_settings__set_incompatible(r, reason);\n> diff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\n> index d9c2605197f..2de54c85e94 100644\n> --- a/fsmonitor-settings.h\n> +++ b/fsmonitor-settings.h\n> @@ -23,12 +23,16 @@ enum fsmonitor_reason {\n>   \tFSMONITOR_REASON_NOSOCKETS, /* NTFS,FAT32 do not support Unix sockets */\n>   };\n>   \n> +void fsm_settings__set_allow_remote(struct repository *r);\n> +void fsm_settings__set_socket_dir(struct repository *r);\n>   void fsm_settings__set_ipc(struct repository *r);\n>   void fsm_settings__set_hook(struct repository *r, const char *path);\n>   void fsm_settings__set_disabled(struct repository *r);\n>   void fsm_settings__set_incompatible(struct repository *r,\n>   \t\t\t\t    enum fsmonitor_reason reason);\n>   \n> +int fsm_settings__get_allow_remote(struct repository *r);\n> +const char *fsm_settings__get_socket_dir(struct repository *r);\n>   enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n>   const char *fsm_settings__get_hook_path(struct repository *r);\n>   \n> \n"},{"id":"462478","messageId":"3a095822-8bf3-4bd0-a308-cdf4ac373428@jeffhostetler.com","threadId":"58324","inReplyTo":"836a791e6b7fd4490674254ce03105a8ca2175cb.1661962145.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 1/4] fsmonitor: add two new config options, allowRemote and socketDir","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-09-01T18:04:22Z","receivedAt":"2022-09-01T18:04:29Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 8/31/22 12:09 PM, Eric DeCosta via GitGitGadget wrote:\n> From: Eric DeCosta <edecosta@mathworks.com>\n> \n> Introduce two new configuration options\n> \n>     fsmonitor.allowRemote - setting this to true overrides fsmonitor's\n>     default behavior of erroring out when enountering network file\n>     systems. Additionly, when true, the Unix domain socket (UDS) file\n>     used for IPC is located in $HOME rather than in the .git directory.\n> \n>     fsmonitor.socketDir - allows for the UDS file to be located\n>     anywhere the user chooses rather $HOME.\n\nIf we add these config settings, we should update the docs\nto describe them.\n\nJeff\n"},{"id":"462488","messageId":"bdf26d53-7068-87a6-484b-aeef99be35bd@jeffhostetler.com","threadId":"58324","inReplyTo":"56cabf3be3b9e228bc948da372db4c9d11fd3926.1661962145.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the real path","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-09-01T20:05:32Z","receivedAt":"2022-09-01T20:05:41Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 8/31/22 12:09 PM, Eric DeCosta via GitGitGadget wrote:\n> From: Eric DeCosta <edecosta@mathworks.com>\n> \n> Consider the following network working directory that is mounted under\n> /System/Volumes/Data:\n> \n> /network/working/directory\n> \n> The git working directory path is:\n> \n> /System/Volumes/Data/network/working/directory\n> \n> The paths reported by FSEvents always start with /network. fsmonitor\n> expects paths to be under the working directory; therefore it\n> fails to match /network/... and ignores the change.\n\nI'm not sure I understand what's going on here.\nAre you saying that FSEvents reports network mounted\ndirectories with a path relative to the mount-point\nrather than an absolute path?\n\nIn your example, is \"/network/working/directory\" a\nvalid path on your system (that also happens to be the\nsame directory as \"/System/Volumes/...\")\n\nThat is, do you have some aliasing going on where both paths\nare valid -- like a pair of hard-linked directories?\nOr, is there something special about a network mount point?\n\n\nDid you have to \"cd /System/Volumes/...\" to get Git to have\nthe absolute path be this?  Or were you doing a \"cd /network/...\"?\n(Sorry to ask so many questions but I don't have a pair of\nsystems to test any of this on right now.)\n\n> \n> Change things such that if fsmonitor.allowRemote is true that the\n> paths reported via FSEevents are normalized to the real path.\n> \n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n>   compat/fsmonitor/fsm-listen-darwin.c | 11 ++++++++---\n>   1 file changed, 8 insertions(+), 3 deletions(-)\n> \n> diff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\n> index 8e208e8289e..2ed828649ff 100644\n> --- a/compat/fsmonitor/fsm-listen-darwin.c\n> +++ b/compat/fsmonitor/fsm-listen-darwin.c\n> @@ -26,6 +26,7 @@\n>   #include \"fsmonitor.h\"\n>   #include \"fsm-listen.h\"\n>   #include \"fsmonitor--daemon.h\"\n> +#include \"fsmonitor-settings.h\"\n>   \n>   struct fsm_listen_data\n>   {\n> @@ -183,7 +184,6 @@ static void my_add_path(struct fsmonitor_batch *batch, const char *path)\n>   \tfree(composed);\n>   }\n>   \n> -\n>   static void fsevent_callback(ConstFSEventStreamRef streamRef,\n>   \t\t\t     void *ctx,\n>   \t\t\t     size_t num_of_events,\n> @@ -209,7 +209,12 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n>   \t\t/*\n>   \t\t * On Mac, we receive an array of absolute paths.\n>   \t\t */\n> -\t\tpath_k = paths[k];\n> +\t\tif (fsm_settings__get_allow_remote(the_repository) > 0) {\n> +\t\t\tstrbuf_reset(&tmp);\n> +\t\t\tstrbuf_realpath_forgiving(&tmp, paths[k], 0);\n> +\t\t\tpath_k = tmp.buf;\n> +\t\t} else\n> +\t\t\tpath_k = paths[k];\n\nThis feels wrong.\n\nIt is not that fsmonitor.allowRemote is true, but whether or not\nthis particular file system *IS* remote, right?\n\n>   \n>   \t\t/*\n>   \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n> @@ -237,6 +242,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n>   \n>   \t\t\tfsmonitor_force_resync(state);\n>   \t\t\tfsmonitor_batch__free_list(batch);\n> +\t\t\tbatch = NULL;\n>   \t\t\tstring_list_clear(&cookie_list, 0);\n>   \n>   \t\t\t/*\n> @@ -313,7 +319,6 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n>   \n>   \t\tcase IS_WORKDIR_PATH:\n>   \t\t\t/* try to queue normal pathnames */\n> -\n>   \t\t\tif (trace_pass_fl(&trace_fsmonitor))\n>   \t\t\t\tlog_flags_set(path_k, event_flags[k]);\n>   \n> \n"},{"id":"462495","messageId":"791b3386-501b-d69d-3f4e-1401aa0c4865@jeffhostetler.com","threadId":"58324","inReplyTo":"836a791e6b7fd4490674254ce03105a8ca2175cb.1661962145.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 1/4] fsmonitor: add two new config options, allowRemote and socketDir","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-09-01T21:21:54Z","receivedAt":"2022-09-01T21:22:03Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 8/31/22 12:09 PM, Eric DeCosta via GitGitGadget wrote:\n> From: Eric DeCosta <edecosta@mathworks.com>\n> \n> Introduce two new configuration options\n> \n>     fsmonitor.allowRemote - setting this to true overrides fsmonitor's\n>     default behavior of erroring out when enountering network file\n>     systems. Additionly, when true, the Unix domain socket (UDS) file\n>     used for IPC is located in $HOME rather than in the .git directory.\n> \n>     fsmonitor.socketDir - allows for the UDS file to be located\n>     anywhere the user chooses rather $HOME.\n> \n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n\nI hate to say it, but I think I'd like to start over on this\npatch.  I think it adds too much to the upper layers.\n\nCould we:\n\n1. Move the GIT_PATH_FUNC() down into compat/fsmonitor/fsm-settings-*.c\n    so that we don't need the ifdef-ing because we only really need to\n    change the path on MacOS.\n\n    Change it from this macro trick to be an actual function\n    that computes the path and sticks it in a static buffer\n    and returns it on future calls.  (We call it from several\n    different places.)\n\n    On MacOS have it always compute a $HOME or fsmonitor.socketDir\n    based path.\n\n    (This is called early, so we don't know anything about the\n     r->settings.fsmonitor data yet (like whether we'll have an\n     ipc or hook), so we don't know whether to worry about whether\n     the socket-directory is local/remote yet.)\n\n2. Get rid of the fsm_settings__get_allow_remote() and\n    __get_socket_dir() functions in fsmonitor-settings.c.\n\n    Besides, having it at this layer says that \"allow-remote\"\n    is an all-or-nothing flag (that the platform-independent\n    layer doesn't know anything about).  We may eventually find\n    that we want the platform code to be smarter about that,\n    such as allow remote from NFSv4 but not NFSv3.  We don't\n    want to litter the platform-independent code with that.\n\n    And it says that we always use a UDS.  We might change\n    that later on MacOS.  And we don't need them at all on Windows.\n\n3. Get rid of the fsm_settings__set_allow_remote() and\n    __set_socket_dir().  The FSMonitor code only needs to access\n    them once down in the fsm_os__incompatible() code, so we\n    could just inline the relevant parts down there.\n\n4. Leave the 'reason = check_for_incompatible()' as it was\n    in fsm_setttings__set_ipc() and __set_hook().\n\n    But you might pass a boolean \"is-ipc\" or \"is-hook\" to\n    check_for_incompatible() that it could pass down to\n    fsm_os__incompatible().  That might save the need to the\n    fsmonitor.socketDir stuff when they want to use Watchman.\n\n5. In fsm-settings-darwin.c:fsm_os__incompatible()\n    complain if the socket path is remote (when is-ipc is set).\n\n    This probably ought to be a new _REASON_ type for non-local\n    UDS.  And this is independent of whether the actual worktree\n    is remote or whether remote worktrees are allowed.\n\n6. In fsm-settings-darwin.c:check_volume()\n\nAll that should be necessary is to:\n\n     if (!(fs.f_flags & MNT_LOCAL))\n+   {\n+       // ... repo_config_get_bool(... \"fsmonitor.allowremote\" ...)\n         return FSMONITOR_REASON_REMOTE;\n+   }\n\n\nAlternatively, for 5 & 6, we could pass a pathname to check_volume()\nso that fsm_os__incompatible() calls it twice:  once for the worktree\nand once for the socketdir.\n\nThe ntfs and msdos restrictions were for UDS reasons, so you might\nwant pass some flags to check_volume() too.\n\nSorry for the redesign and I hope this all makes sense,\nJeff\n\n\n> ---\n>   fsmonitor-settings.c | 67 ++++++++++++++++++++++++++++++++++++++++++--\n>   fsmonitor-settings.h |  4 +++\n>   2 files changed, 69 insertions(+), 2 deletions(-)\n> \n> diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\n> index 464424a1e92..a15eeecebf4 100644\n> --- a/fsmonitor-settings.c\n> +++ b/fsmonitor-settings.c\n> @@ -10,7 +10,9 @@\n>   struct fsmonitor_settings {\n>   \tenum fsmonitor_mode mode;\n>   \tenum fsmonitor_reason reason;\n> +\tint allow_remote;\n>   \tchar *hook_path;\n> +\tchar *sock_dir;\n>   };\n>   \n>   static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n> @@ -43,6 +45,7 @@ static struct fsmonitor_settings *alloc_settings(void)\n>   \tCALLOC_ARRAY(s, 1);\n>   \ts->mode = FSMONITOR_MODE_DISABLED;\n>   \ts->reason = FSMONITOR_REASON_UNTESTED;\n> +\ts->allow_remote = -1;\n>   \n>   \treturn s;\n>   }\n> @@ -90,6 +93,26 @@ static void lookup_fsmonitor_settings(struct repository *r)\n>   \t\tfsm_settings__set_disabled(r);\n>   }\n>   \n> +int fsm_settings__get_allow_remote(struct repository *r)\n> +{\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tlookup_fsmonitor_settings(r);\n> +\n> +\treturn r->settings.fsmonitor->allow_remote;\n> +}\n> +\n> +const char *fsm_settings__get_socket_dir(struct repository *r)\n> +{\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tlookup_fsmonitor_settings(r);\n> +\n> +\treturn r->settings.fsmonitor->sock_dir;\n> +}\n> +\n>   enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n>   {\n>   \tif (!r)\n> @@ -100,6 +123,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n>   \treturn r->settings.fsmonitor->mode;\n>   }\n>   \n> +\n>   const char *fsm_settings__get_hook_path(struct repository *r)\n>   {\n>   \tif (!r)\n> @@ -110,9 +134,44 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n>   \treturn r->settings.fsmonitor->hook_path;\n>   }\n>   \n> +void fsm_settings__set_allow_remote(struct repository *r)\n> +{\n> +\tint allow;\n> +\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tr->settings.fsmonitor = alloc_settings();\n> +\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n> +\t\tr->settings.fsmonitor->allow_remote = allow;\n> +\n> +\treturn;\n> +}\n> +\n> +void fsm_settings__set_socket_dir(struct repository *r)\n> +{\n> +\tconst char *path;\n> +\n> +\tif (!r)\n> +\t\tr = the_repository;\n> +\tif (!r->settings.fsmonitor)\n> +\t\tr->settings.fsmonitor = alloc_settings();\n> +\n> +\tif (!repo_config_get_pathname(r, \"fsmonitor.socketdir\", &path)) {\n> +\t\tFREE_AND_NULL(r->settings.fsmonitor->sock_dir);\n> +\t\tr->settings.fsmonitor->sock_dir = strdup(path);\n> +\t}\n> +\n> +\treturn;\n> +}\n> +\n>   void fsm_settings__set_ipc(struct repository *r)\n>   {\n> -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n> +\tenum fsmonitor_reason reason;\n> +\n> +\tfsm_settings__set_allow_remote(r);\n> +\tfsm_settings__set_socket_dir(r);\n> +\treason = check_for_incompatible(r);\n>   \n>   \tif (reason != FSMONITOR_REASON_OK) {\n>   \t\tfsm_settings__set_incompatible(r, reason);\n> @@ -135,7 +194,11 @@ void fsm_settings__set_ipc(struct repository *r)\n>   \n>   void fsm_settings__set_hook(struct repository *r, const char *path)\n>   {\n> -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n> +\tenum fsmonitor_reason reason;\n> +\n> +\tfsm_settings__set_allow_remote(r);\n> +\tfsm_settings__set_socket_dir(r);\n> +\treason = check_for_incompatible(r);\n>   \n>   \tif (reason != FSMONITOR_REASON_OK) {\n>   \t\tfsm_settings__set_incompatible(r, reason);\n> diff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\n> index d9c2605197f..2de54c85e94 100644\n> --- a/fsmonitor-settings.h\n> +++ b/fsmonitor-settings.h\n> @@ -23,12 +23,16 @@ enum fsmonitor_reason {\n>   \tFSMONITOR_REASON_NOSOCKETS, /* NTFS,FAT32 do not support Unix sockets */\n>   };\n>   \n> +void fsm_settings__set_allow_remote(struct repository *r);\n> +void fsm_settings__set_socket_dir(struct repository *r);\n>   void fsm_settings__set_ipc(struct repository *r);\n>   void fsm_settings__set_hook(struct repository *r, const char *path);\n>   void fsm_settings__set_disabled(struct repository *r);\n>   void fsm_settings__set_incompatible(struct repository *r,\n>   \t\t\t\t    enum fsmonitor_reason reason);\n>   \n> +int fsm_settings__get_allow_remote(struct repository *r);\n> +const char *fsm_settings__get_socket_dir(struct repository *r);\n>   enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n>   const char *fsm_settings__get_hook_path(struct repository *r);\n>   \n> \n"},{"id":"462540","messageId":"BYAPR05MB5573B477ED53CBC4C996729DD97A9@BYAPR05MB5573.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"bdf26d53-7068-87a6-484b-aeef99be35bd@jeffhostetler.com","subject":"RE: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the real path","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-02T16:35:42Z","receivedAt":"2022-09-02T16:35:58Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Jeff Hostetler <git@jeffhostetler.com>\n> Sent: Thursday, September 1, 2022 4:06 PM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n> git@vger.kernel.org\n> Cc: Eric DeCosta <edecosta@mathworks.com>\n> Subject: Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the\n> real path\n> \n> \n> \n> On 8/31/22 12:09 PM, Eric DeCosta via GitGitGadget wrote:\n> > From: Eric DeCosta <edecosta@mathworks.com>\n> >\n> > Consider the following network working directory that is mounted under\n> > /System/Volumes/Data:\n> >\n> > /network/working/directory\n> >\n> > The git working directory path is:\n> >\n> > /System/Volumes/Data/network/working/directory\n> >\n> > The paths reported by FSEvents always start with /network. fsmonitor\n> > expects paths to be under the working directory; therefore it fails to\n> > match /network/... and ignores the change.\n> \n> I'm not sure I understand what's going on here.\n> Are you saying that FSEvents reports network mounted directories with a\n> path relative to the mount-point rather than an absolute path?\n>\n\nYes\n\n> In your example, is \"/network/working/directory\" a valid path on your\n> system (that also happens to be the same directory as\n> \"/System/Volumes/...\")\n> \n> That is, do you have some aliasing going on where both paths are valid -- like\n> a pair of hard-linked directories?\n> Or, is there something special about a network mount point?\n> \n> \n> Did you have to \"cd /System/Volumes/...\" to get Git to have the absolute\n> path be this?  Or were you doing a \"cd /network/...\"?\n> (Sorry to ask so many questions but I don't have a pair of systems to test any\n> of this on right now.)\n> \n\n \"/network/working/directory\" is mounted under \n\"/System/Volumes/Data/network/working/directory\"\n\nThere is also a symlink:\n\n\"/network\" -> \"/System/Volumes/Data/network\"\n\nNo matter if I \"cd /System/Volumes/Data/network/working/directory\"\nor \"cd /network/working/directory\" the paths reported by FSEvents\nalways start with \"/network/working/directory\"\n\nIf I do a similar symlink with local directories, I do not get this\nunexpected behavior.\n\nI need to remove the symlink and try it that way, but I need to\ncoordinate with the machine's owner.\n\n> >\n> > Change things such that if fsmonitor.allowRemote is true that the\n> > paths reported via FSEevents are normalized to the real path.\n> >\n> > Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> > ---\n> >   compat/fsmonitor/fsm-listen-darwin.c | 11 ++++++++---\n> >   1 file changed, 8 insertions(+), 3 deletions(-)\n> >\n> > diff --git a/compat/fsmonitor/fsm-listen-darwin.c\n> > b/compat/fsmonitor/fsm-listen-darwin.c\n> > index 8e208e8289e..2ed828649ff 100644\n> > --- a/compat/fsmonitor/fsm-listen-darwin.c\n> > +++ b/compat/fsmonitor/fsm-listen-darwin.c\n> > @@ -26,6 +26,7 @@\n> >   #include \"fsmonitor.h\"\n> >   #include \"fsm-listen.h\"\n> >   #include \"fsmonitor--daemon.h\"\n> > +#include \"fsmonitor-settings.h\"\n> >\n> >   struct fsm_listen_data\n> >   {\n> > @@ -183,7 +184,6 @@ static void my_add_path(struct fsmonitor_batch\n> *batch, const char *path)\n> >   \tfree(composed);\n> >   }\n> >\n> > -\n> >   static void fsevent_callback(ConstFSEventStreamRef streamRef,\n> >   \t\t\t     void *ctx,\n> >   \t\t\t     size_t num_of_events,\n> > @@ -209,7 +209,12 @@ static void\n> fsevent_callback(ConstFSEventStreamRef streamRef,\n> >   \t\t/*\n> >   \t\t * On Mac, we receive an array of absolute paths.\n> >   \t\t */\n> > -\t\tpath_k = paths[k];\n> > +\t\tif (fsm_settings__get_allow_remote(the_repository) > 0) {\n> > +\t\t\tstrbuf_reset(&tmp);\n> > +\t\t\tstrbuf_realpath_forgiving(&tmp, paths[k], 0);\n> > +\t\t\tpath_k = tmp.buf;\n> > +\t\t} else\n> > +\t\t\tpath_k = paths[k];\n> \n> This feels wrong.\n> \n> It is not that fsmonitor.allowRemote is true, but whether or not this\n> particular file system *IS* remote, right?\n\nTrue. I suppose each path could be checked, or some bit could be set\nif the working directory is remote, perhaps along the lines of\nfsmonitor_ipc__get_path. Then the determination of the IPC path\nand whether it is remote would be in one place. fsm-settings-*\nwould then just get that bit and check it against allowRemote.\n\nThoughts?\n\n\n> >\n> >   \t\t/*\n> >   \t\t * If you want to debug FSEvents, log them to\n> GIT_TRACE_FSMONITOR.\n> > @@ -237,6 +242,7 @@ static void\n> fsevent_callback(ConstFSEventStreamRef\n> > streamRef,\n> >\n> >   \t\t\tfsmonitor_force_resync(state);\n> >   \t\t\tfsmonitor_batch__free_list(batch);\n> > +\t\t\tbatch = NULL;\n> >   \t\t\tstring_list_clear(&cookie_list, 0);\n> >\n> >   \t\t\t/*\n> > @@ -313,7 +319,6 @@ static void\n> fsevent_callback(ConstFSEventStreamRef\n> > streamRef,\n> >\n> >   \t\tcase IS_WORKDIR_PATH:\n> >   \t\t\t/* try to queue normal pathnames */\n> > -\n> >   \t\t\tif (trace_pass_fl(&trace_fsmonitor))\n> >   \t\t\t\tlog_flags_set(path_k, event_flags[k]);\n> >\n> >\n\n"},{"id":"462542","messageId":"BYAPR05MB5573AACF524A1319B8F1119CD97A9@BYAPR05MB5573.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"791b3386-501b-d69d-3f4e-1401aa0c4865@jeffhostetler.com","subject":"RE: [PATCH v4 1/4] fsmonitor: add two new config options, allowRemote and socketDir","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-02T16:54:27Z","receivedAt":"2022-09-02T16:54:38Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Jeff Hostetler <git@jeffhostetler.com>\n> Sent: Thursday, September 1, 2022 5:22 PM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n> git@vger.kernel.org\n> Cc: Eric DeCosta <edecosta@mathworks.com>\n> Subject: Re: [PATCH v4 1/4] fsmonitor: add two new config options,\n> allowRemote and socketDir\n> \n> \n> \n> On 8/31/22 12:09 PM, Eric DeCosta via GitGitGadget wrote:\n> > From: Eric DeCosta <edecosta@mathworks.com>\n> >\n> > Introduce two new configuration options\n> >\n> >     fsmonitor.allowRemote - setting this to true overrides fsmonitor's\n> >     default behavior of erroring out when enountering network file\n> >     systems. Additionly, when true, the Unix domain socket (UDS) file\n> >     used for IPC is located in $HOME rather than in the .git directory.\n> >\n> >     fsmonitor.socketDir - allows for the UDS file to be located\n> >     anywhere the user chooses rather $HOME.\n> >\n> > Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> \n> I hate to say it, but I think I'd like to start over on this patch.  I think it adds\n> too much to the upper layers.\n> \n> Could we:\n> \n> 1. Move the GIT_PATH_FUNC() down into compat/fsmonitor/fsm-settings-*.c\n>     so that we don't need the ifdef-ing because we only really need to\n>     change the path on MacOS.\n> \n>     Change it from this macro trick to be an actual function\n>     that computes the path and sticks it in a static buffer\n>     and returns it on future calls.  (We call it from several\n>     different places.)\n> \n>     On MacOS have it always compute a $HOME or fsmonitor.socketDir\n>     based path.\n> \n>     (This is called early, so we don't know anything about the\n>      r->settings.fsmonitor data yet (like whether we'll have an\n>      ipc or hook), so we don't know whether to worry about whether\n>      the socket-directory is local/remote yet.)\n> \n> 2. Get rid of the fsm_settings__get_allow_remote() and\n>     __get_socket_dir() functions in fsmonitor-settings.c.\n> \n>     Besides, having it at this layer says that \"allow-remote\"\n>     is an all-or-nothing flag (that the platform-independent\n>     layer doesn't know anything about).  We may eventually find\n>     that we want the platform code to be smarter about that,\n>     such as allow remote from NFSv4 but not NFSv3.  We don't\n>     want to litter the platform-independent code with that.\n> \n>     And it says that we always use a UDS.  We might change\n>     that later on MacOS.  And we don't need them at all on Windows.\n> \n> 3. Get rid of the fsm_settings__set_allow_remote() and\n>     __set_socket_dir().  The FSMonitor code only needs to access\n>     them once down in the fsm_os__incompatible() code, so we\n>     could just inline the relevant parts down there.\n \nGot it.\n\n> 4. Leave the 'reason = check_for_incompatible()' as it was\n>     in fsm_setttings__set_ipc() and __set_hook().\n> \n>     But you might pass a boolean \"is-ipc\" or \"is-hook\" to\n>     check_for_incompatible() that it could pass down to\n>     fsm_os__incompatible().  That might save the need to the\n>     fsmonitor.socketDir stuff when they want to use Watchman.\n\nI see, avoid the UDS check altogether if \"is-hook\"\n\n> 5. In fsm-settings-darwin.c:fsm_os__incompatible()\n>     complain if the socket path is remote (when is-ipc is set).\n> \n>     This probably ought to be a new _REASON_ type for non-local\n>     UDS.  And this is independent of whether the actual worktree\n>     is remote or whether remote worktrees are allowed.\n\nI thought that was what FSMONITOR_REASON_NOSOCKETS was for?\n\n> 6. In fsm-settings-darwin.c:check_volume()\n> \n> All that should be necessary is to:\n> \n>      if (!(fs.f_flags & MNT_LOCAL))\n> +   {\n> +       // ... repo_config_get_bool(... \"fsmonitor.allowremote\" ...)\n>          return FSMONITOR_REASON_REMOTE;\n> +   }\n> \n> \n> Alternatively, for 5 & 6, we could pass a pathname to check_volume() so that\n> fsm_os__incompatible() calls it twice:  once for the worktree and once for\n> the socketdir.\n> \n> The ntfs and msdos restrictions were for UDS reasons, so you might want\n> pass some flags to check_volume() too.\n> \n> Sorry for the redesign and I hope this all makes sense, Jeff\n> \n\nI've taken a first pass at all of this, will work on it some more.\n\n-Eric\n\n> \n> > ---\n> >   fsmonitor-settings.c | 67\n> ++++++++++++++++++++++++++++++++++++++++++--\n> >   fsmonitor-settings.h |  4 +++\n> >   2 files changed, 69 insertions(+), 2 deletions(-)\n> >\n> > diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c index\n> > 464424a1e92..a15eeecebf4 100644\n> > --- a/fsmonitor-settings.c\n> > +++ b/fsmonitor-settings.c\n> > @@ -10,7 +10,9 @@\n> >   struct fsmonitor_settings {\n> >   \tenum fsmonitor_mode mode;\n> >   \tenum fsmonitor_reason reason;\n> > +\tint allow_remote;\n> >   \tchar *hook_path;\n> > +\tchar *sock_dir;\n> >   };\n> >\n> >   static enum fsmonitor_reason check_for_incompatible(struct\n> > repository *r) @@ -43,6 +45,7 @@ static struct fsmonitor_settings\n> *alloc_settings(void)\n> >   \tCALLOC_ARRAY(s, 1);\n> >   \ts->mode = FSMONITOR_MODE_DISABLED;\n> >   \ts->reason = FSMONITOR_REASON_UNTESTED;\n> > +\ts->allow_remote = -1;\n> >\n> >   \treturn s;\n> >   }\n> > @@ -90,6 +93,26 @@ static void lookup_fsmonitor_settings(struct\n> repository *r)\n> >   \t\tfsm_settings__set_disabled(r);\n> >   }\n> >\n> > +int fsm_settings__get_allow_remote(struct repository *r) {\n> > +\tif (!r)\n> > +\t\tr = the_repository;\n> > +\tif (!r->settings.fsmonitor)\n> > +\t\tlookup_fsmonitor_settings(r);\n> > +\n> > +\treturn r->settings.fsmonitor->allow_remote;\n> > +}\n> > +\n> > +const char *fsm_settings__get_socket_dir(struct repository *r) {\n> > +\tif (!r)\n> > +\t\tr = the_repository;\n> > +\tif (!r->settings.fsmonitor)\n> > +\t\tlookup_fsmonitor_settings(r);\n> > +\n> > +\treturn r->settings.fsmonitor->sock_dir; }\n> > +\n> >   enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n> >   {\n> >   \tif (!r)\n> > @@ -100,6 +123,7 @@ enum fsmonitor_mode\n> fsm_settings__get_mode(struct repository *r)\n> >   \treturn r->settings.fsmonitor->mode;\n> >   }\n> >\n> > +\n> >   const char *fsm_settings__get_hook_path(struct repository *r)\n> >   {\n> >   \tif (!r)\n> > @@ -110,9 +134,44 @@ const char *fsm_settings__get_hook_path(struct\n> repository *r)\n> >   \treturn r->settings.fsmonitor->hook_path;\n> >   }\n> >\n> > +void fsm_settings__set_allow_remote(struct repository *r) {\n> > +\tint allow;\n> > +\n> > +\tif (!r)\n> > +\t\tr = the_repository;\n> > +\tif (!r->settings.fsmonitor)\n> > +\t\tr->settings.fsmonitor = alloc_settings();\n> > +\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n> > +\t\tr->settings.fsmonitor->allow_remote = allow;\n> > +\n> > +\treturn;\n> > +}\n> > +\n> > +void fsm_settings__set_socket_dir(struct repository *r) {\n> > +\tconst char *path;\n> > +\n> > +\tif (!r)\n> > +\t\tr = the_repository;\n> > +\tif (!r->settings.fsmonitor)\n> > +\t\tr->settings.fsmonitor = alloc_settings();\n> > +\n> > +\tif (!repo_config_get_pathname(r, \"fsmonitor.socketdir\", &path)) {\n> > +\t\tFREE_AND_NULL(r->settings.fsmonitor->sock_dir);\n> > +\t\tr->settings.fsmonitor->sock_dir = strdup(path);\n> > +\t}\n> > +\n> > +\treturn;\n> > +}\n> > +\n> >   void fsm_settings__set_ipc(struct repository *r)\n> >   {\n> > -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n> > +\tenum fsmonitor_reason reason;\n> > +\n> > +\tfsm_settings__set_allow_remote(r);\n> > +\tfsm_settings__set_socket_dir(r);\n> > +\treason = check_for_incompatible(r);\n> >\n> >   \tif (reason != FSMONITOR_REASON_OK) {\n> >   \t\tfsm_settings__set_incompatible(r, reason); @@ -135,7\n> +194,11 @@\n> > void fsm_settings__set_ipc(struct repository *r)\n> >\n> >   void fsm_settings__set_hook(struct repository *r, const char *path)\n> >   {\n> > -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n> > +\tenum fsmonitor_reason reason;\n> > +\n> > +\tfsm_settings__set_allow_remote(r);\n> > +\tfsm_settings__set_socket_dir(r);\n> > +\treason = check_for_incompatible(r);\n> >\n> >   \tif (reason != FSMONITOR_REASON_OK) {\n> >   \t\tfsm_settings__set_incompatible(r, reason); diff --git\n> > a/fsmonitor-settings.h b/fsmonitor-settings.h index\n> > d9c2605197f..2de54c85e94 100644\n> > --- a/fsmonitor-settings.h\n> > +++ b/fsmonitor-settings.h\n> > @@ -23,12 +23,16 @@ enum fsmonitor_reason {\n> >   \tFSMONITOR_REASON_NOSOCKETS, /* NTFS,FAT32 do not support\n> Unix sockets */\n> >   };\n> >\n> > +void fsm_settings__set_allow_remote(struct repository *r); void\n> > +fsm_settings__set_socket_dir(struct repository *r);\n> >   void fsm_settings__set_ipc(struct repository *r);\n> >   void fsm_settings__set_hook(struct repository *r, const char *path);\n> >   void fsm_settings__set_disabled(struct repository *r);\n> >   void fsm_settings__set_incompatible(struct repository *r,\n> >   \t\t\t\t    enum fsmonitor_reason reason);\n> >\n> > +int fsm_settings__get_allow_remote(struct repository *r); const char\n> > +*fsm_settings__get_socket_dir(struct repository *r);\n> >   enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n> >   const char *fsm_settings__get_hook_path(struct repository *r);\n> >\n> >\n\n"},{"id":"462655","messageId":"664ece2e-b149-8058-5937-b726810a59da@jeffhostetler.com","threadId":"58324","inReplyTo":"BYAPR05MB5573AACF524A1319B8F1119CD97A9@BYAPR05MB5573.namprd05.prod.outlook.com","subject":"Re: [PATCH v4 1/4] fsmonitor: add two new config options, allowRemote and socketDir","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-09-06T14:27:23Z","receivedAt":"2022-09-06T15:25:12Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 9/2/22 12:54 PM, Eric DeCosta wrote:\n> \n> \n>> -----Original Message-----\n>> From: Jeff Hostetler <git@jeffhostetler.com>\n>> Sent: Thursday, September 1, 2022 5:22 PM\n>> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n>> git@vger.kernel.org\n>> Cc: Eric DeCosta <edecosta@mathworks.com>\n>> Subject: Re: [PATCH v4 1/4] fsmonitor: add two new config options,\n>> allowRemote and socketDir\n>>\n>>\n>>\n>> On 8/31/22 12:09 PM, Eric DeCosta via GitGitGadget wrote:\n>>> From: Eric DeCosta <edecosta@mathworks.com>\n>>>\n>>> Introduce two new configuration options\n[...]\n>> 5. In fsm-settings-darwin.c:fsm_os__incompatible()\n>>      complain if the socket path is remote (when is-ipc is set).\n>>\n>>      This probably ought to be a new _REASON_ type for non-local\n>>      UDS.  And this is independent of whether the actual worktree\n>>      is remote or whether remote worktrees are allowed.\n> \n> I thought that was what FSMONITOR_REASON_NOSOCKETS was for?\n\nYeah, I was probably juggling too many things in my\nhead when I was making that list.  And I'm sure it'll\nfall our when you see what the code looks like.  The\n_NOSOCKETS case should basically be just the places where\nyou test the path of the socket-dir (if it is always outside\nof the .git dir).  None of the tests on the actual workdir\ninspection should hit it anymore, right?\n\nAnd we can save for a later day whether or not to test\nfor NTFS and FAT32 (local or remote) in the Darwin code\nbased on whether or not the kernel sends FSEvents for\nthem.\n\nJeff\n\n"},{"id":"462658","messageId":"a583a787-1f54-74ba-add1-61f9e30d5c2a@jeffhostetler.com","threadId":"58324","inReplyTo":"BYAPR05MB5573B477ED53CBC4C996729DD97A9@BYAPR05MB5573.namprd05.prod.outlook.com","subject":"Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the real path","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-09-06T17:13:38Z","receivedAt":"2022-09-06T17:24:57Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 9/2/22 12:35 PM, Eric DeCosta wrote:\n> \n> \n>> -----Original Message-----\n>> From: Jeff Hostetler <git@jeffhostetler.com>\n>> Sent: Thursday, September 1, 2022 4:06 PM\n>> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n>> git@vger.kernel.org\n>> Cc: Eric DeCosta <edecosta@mathworks.com>\n>> Subject: Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the\n>> real path\n>>\n>>\n>>\n>> On 8/31/22 12:09 PM, Eric DeCosta via GitGitGadget wrote:\n>>> From: Eric DeCosta <edecosta@mathworks.com>\n>>>\n>>> Consider the following network working directory that is mounted under\n>>> /System/Volumes/Data:\n>>>\n>>> /network/working/directory\n>>>\n>>> The git working directory path is:\n>>>\n>>> /System/Volumes/Data/network/working/directory\n>>>\n>>> The paths reported by FSEvents always start with /network. fsmonitor\n>>> expects paths to be under the working directory; therefore it fails to\n>>> match /network/... and ignores the change.\n>>\n>> I'm not sure I understand what's going on here.\n>> Are you saying that FSEvents reports network mounted directories with a\n>> path relative to the mount-point rather than an absolute path?\n>>\n> \n> Yes\n> \n>> In your example, is \"/network/working/directory\" a valid path on your\n>> system (that also happens to be the same directory as\n>> \"/System/Volumes/...\")\n>>\n>> That is, do you have some aliasing going on where both paths are valid -- like\n>> a pair of hard-linked directories?\n>> Or, is there something special about a network mount point?\n>>\n>>\n>> Did you have to \"cd /System/Volumes/...\" to get Git to have the absolute\n>> path be this?  Or were you doing a \"cd /network/...\"?\n>> (Sorry to ask so many questions but I don't have a pair of systems to test any\n>> of this on right now.)\n>>\n> \n>   \"/network/working/directory\" is mounted under\n> \"/System/Volumes/Data/network/working/directory\"\n> \n> There is also a symlink:\n> \n> \"/network\" -> \"/System/Volumes/Data/network\"\n> \n> No matter if I \"cd /System/Volumes/Data/network/working/directory\"\n> or \"cd /network/working/directory\" the paths reported by FSEvents\n> always start with \"/network/working/directory\"\n> \n> If I do a similar symlink with local directories, I do not get this\n> unexpected behavior.\n> \n> I need to remove the symlink and try it that way, but I need to\n> coordinate with the machine's owner.\n\n\nI think you have stumbled upon a recent MacOS feature called\n\"firmlinks\".  I'm just reading up on it myself, so I shouldn't\nspeculate here (yet), but maybe you too could do some reading\non the subject.\n\nThis makes me wonder if the symlink is historical.  The real\nmagic is in the firmlinks.  For example, if I do:\n\n\t$ (cd / ; ls -l | grep Users)\n\tdrwxr-xr-x   6 root  admin   192 Apr  6  2020 Users\n\n\t$ (cd /Users ; df .)\n\tFilesystem   512-blocks      Used Available Capacity iused      ifree \n%iused  Mounted on\n\t/dev/disk1s1  976490576 608954344 338850488    65% 4181323 4878271557 \n  0%   /System/Volumes/Data\n\nwe can see that /Users is on /System/Volumes/Data (and there is a\n/System/Volumes/Data/Users directory with the same metadata), but\nit is not a symlink.\n\n\nSee [1] for more info.\n\n[1] \nhttp://www.swiftforensics.com/2019/10/macos-1015-volumes-firmlink-magic.html\n\n\n[...]\n>>> @@ -209,7 +209,12 @@ static void\n>> fsevent_callback(ConstFSEventStreamRef streamRef,\n>>>    \t\t/*\n>>>    \t\t * On Mac, we receive an array of absolute paths.\n>>>    \t\t */\n>>> -\t\tpath_k = paths[k];\n>>> +\t\tif (fsm_settings__get_allow_remote(the_repository) > 0) {\n>>> +\t\t\tstrbuf_reset(&tmp);\n>>> +\t\t\tstrbuf_realpath_forgiving(&tmp, paths[k], 0);\n>>> +\t\t\tpath_k = tmp.buf;\n>>> +\t\t} else\n>>> +\t\t\tpath_k = paths[k];\n>>\n>> This feels wrong.\n>>\n>> It is not that fsmonitor.allowRemote is true, but whether or not this\n>> particular file system *IS* remote, right?\n> \n> True. I suppose each path could be checked, or some bit could be set\n> if the working directory is remote, perhaps along the lines of\n> fsmonitor_ipc__get_path. Then the determination of the IPC path\n> and whether it is remote would be in one place. fsm-settings-*\n> would then just get that bit and check it against allowRemote.\n> \n> Thoughts?\n\nI'll do some digging here.  There ought to be a way to tell if a\ncomponent directory in a pathname has a firmlink peer.  (But I'm\ntraveling for GitMerge, so I might not have time to look at this\nuntil afterwards.)\n\nThis would indicate that a \"bi-directional wormhole\" (their words)\nalias is available (and hopefully, a way to computer the other peer....)\n\nI'm thinking that the \"/network/...\" path in the FSEvents is because\nFSEventD is using a particular peer spelling (that we weren't\nexpecting).\n\nIf we can compute the spellings of the peers once when the daemon\nstarts up (and maybe make a little dictionary), then we can do\nprefix tricks on the absolute path after the\n         path_k = paths[k];\nstep in fsevent_callback() to extract a relative path rather than\nan absolute path.\n\nThen call fsmonitor_classify_path_relative() instead of _absolute().\n\nWatch out though, there are several places that do\n         rel = path+k + ...state->path_worktree_watch.len...\nthat would need to be adjusted.\n\nHope this helps,\nJeff\n\n\n\n\n\n"},{"id":"462662","messageId":"BL0PR05MB5571765C903A82FDD990956AD97E9@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"a583a787-1f54-74ba-add1-61f9e30d5c2a@jeffhostetler.com","subject":"RE: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the real path","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-06T19:02:24Z","receivedAt":"2022-09-06T19:03:04Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Jeff Hostetler <git@jeffhostetler.com>\n> Sent: Tuesday, September 6, 2022 1:14 PM\n> To: Eric DeCosta <edecosta@mathworks.com>; Eric DeCosta via GitGitGadget\n> <gitgitgadget@gmail.com>; git@vger.kernel.org\n> Subject: Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to\n> the real path\n> \n> \n> \n> On 9/2/22 12:35 PM, Eric DeCosta wrote:\n> >\n> >\n> >> -----Original Message-----\n> >> From: Jeff Hostetler <git@jeffhostetler.com>\n> >> Sent: Thursday, September 1, 2022 4:06 PM\n> >> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n> >> git@vger.kernel.org\n> >> Cc: Eric DeCosta <edecosta@mathworks.com>\n> >> Subject: Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths\n> >> to the real path\n> >>\n> >>\n> >>\n> >> On 8/31/22 12:09 PM, Eric DeCosta via GitGitGadget wrote:\n> >>> From: Eric DeCosta <edecosta@mathworks.com>\n> >>>\n> >>> Consider the following network working directory that is mounted\n> >>> under\n> >>> /System/Volumes/Data:\n> >>>\n> >>> /network/working/directory\n> >>>\n> >>> The git working directory path is:\n> >>>\n> >>> /System/Volumes/Data/network/working/directory\n> >>>\n> >>> The paths reported by FSEvents always start with /network. fsmonitor\n> >>> expects paths to be under the working directory; therefore it fails\n> >>> to match /network/... and ignores the change.\n> >>\n> >> I'm not sure I understand what's going on here.\n> >> Are you saying that FSEvents reports network mounted directories with\n> >> a path relative to the mount-point rather than an absolute path?\n> >>\n> >\n> > Yes\n> >\n> >> In your example, is \"/network/working/directory\" a valid path on your\n> >> system (that also happens to be the same directory as\n> >> \"/System/Volumes/...\")\n> >>\n> >> That is, do you have some aliasing going on where both paths are\n> >> valid -- like a pair of hard-linked directories?\n> >> Or, is there something special about a network mount point?\n> >>\n> >>\n> >> Did you have to \"cd /System/Volumes/...\" to get Git to have the\n> >> absolute path be this? Or were you doing a \"cd /network/...\"?\n> >> (Sorry to ask so many questions but I don't have a pair of systems to\n> >> test any of this on right now.)\n> >>\n> >\n> > \"/network/working/directory\" is mounted under\n> > \"/System/Volumes/Data/network/working/directory\"\n> >\n> > There is also a symlink:\n> >\n> > \"/network\" -> \"/System/Volumes/Data/network\"\n> >\n> > No matter if I \"cd /System/Volumes/Data/network/working/directory\"\n> > or \"cd /network/working/directory\" the paths reported by FSEvents\n> > always start with \"/network/working/directory\"\n> >\n> > If I do a similar symlink with local directories, I do not get this\n> > unexpected behavior.\n> >\n> > I need to remove the symlink and try it that way, but I need to\n> > coordinate with the machine's owner.\n> \n> \n> I think you have stumbled upon a recent MacOS feature called \"firmlinks\".\n> I'm just reading up on it myself, so I shouldn't speculate here (yet), but\n> maybe you too could do some reading on the subject.\n> \n> This makes me wonder if the symlink is historical. The real magic is in the\n> firmlinks. For example, if I do:\n> \n> $ (cd / ; ls -l | grep Users)\n> drwxr-xr-x 6 root admin 192 Apr 6 2020 Users\n> \n> $ (cd /Users ; df .)\n> Filesystem 512-blocks Used Available Capacity iused ifree %iused Mounted\n> on\n> /dev/disk1s1 976490576 608954344 338850488 65% 4181323 4878271557 0%\n> /System/Volumes/Data\n> \n> we can see that /Users is on /System/Volumes/Data (and there is a\n> /System/Volumes/Data/Users directory with the same metadata), but it is\n> not a symlink.\n> \n> \n> See [1] for more info.\n> \n> [1]\n> http://www.swiftforensics.com/2019/10/macos-1015-volumes-firmlink-\n> magic.html <https://protect-\n> us.mimecast.com/s/QN4qCYEZB2h1p7jWcG1Cbw?domain=swiftforensics.co\n> m>\n> \n> \n\nHmm, I don't see anything that suggests a firmlink is involved but this is new to me so maybe I just don't see it. There is an automount in addition to the symlink though.\n\nSo, dispensing with the abstract \"/network\" path, and getting to the details of my environment:\n\n% mount | grep /mathworks\nmap auto.sfs.sol2.mathworks on /System/Volumes/Data/mathworks (autofs, automounted, nobrowse)\ntriggered map auto.sfs.sol2.devel on /System/Volumes/Data/mathworks/devel (autofs, automounted, nobrowse)\ntriggered map sfs.worldwide.US-Natick.sol2.devel.sbs on /System/Volumes/Data/mathworks/devel/sbs (autofs, automounted, nobrowse)\ntriggered map sfs.worldwide.US-Natick.devel.sbs.29 on /System/Volumes/Data/mathworks/devel/sbs/29 (autofs, automounted, nobrowse)\nbatfs-sb29-nfs:/vmgr/sbs29/edecosta.Bbashprod1.j1928560.2 on /System/Volumes/Data/mathworks/devel/sbs/29/edecosta.Bbashprod1.j1928560.2 (nfs, nodev, automounted, noatime, nobrowse)\n...\n\n% ls -l / | grep mathworks\nlrwxr-xr-x   1 root  wheel    35 Aug 29 10:25 home@ -> /System/Volumes/Data/mathworks/home\nlrwxr-xr-x   1 root  wheel    30 Aug 29 10:25 mathworks@ -> /System/Volumes/Data/mathworks\n\nMy worktree is: /System/Volumes/Data/mathworks/devel/sbs/29/edecosta.Bbashprod1.j1928560.2\nor, equivalently via the symlink just: /mathworks/devel/sbs/29/edecosta.Bbashprod1.j1928560.2\n\nI have sudo'ers access now; so I can try mounting things manually, mess about with symlinks and see what I get.\n\n> [...]\n> >>> @@ -209,7 +209,12 @@ static void\n> >> fsevent_callback(ConstFSEventStreamRef streamRef,\n> >>> /*\n> >>> * On Mac, we receive an array of absolute paths.\n> >>> */\n> >>> - path_k = paths[k];\n> >>> + if (fsm_settings__get_allow_remote(the_repository) > 0) {\n> >>> + strbuf_reset(&tmp);\n> >>> + strbuf_realpath_forgiving(&tmp, paths[k], 0);\n> >>> + path_k = tmp.buf;\n> >>> + } else\n> >>> + path_k = paths[k];\n> >>\n> >> This feels wrong.\n> >>\n> >> It is not that fsmonitor.allowRemote is true, but whether or not this\n> >> particular file system *IS* remote, right?\n> >\n> > True. I suppose each path could be checked, or some bit could be set\n> > if the working directory is remote, perhaps along the lines of\n> > fsmonitor_ipc__get_path. Then the determination of the IPC path\n> > and whether it is remote would be in one place. fsm-settings-*\n> > would then just get that bit and check it against allowRemote.\n> >\n> > Thoughts?\n> \n> I'll do some digging here. There ought to be a way to tell if a\n> component directory in a pathname has a firmlink peer. (But I'm\n> traveling for GitMerge, so I might not have time to look at this\n> until afterwards.)\n> \n> This would indicate that a \"bi-directional wormhole\" (their words)\n> alias is available (and hopefully, a way to computer the other peer....)\n> \n> I'm thinking that the \"/network/...\" path in the FSEvents is because\n> FSEventD is using a particular peer spelling (that we weren't\n> expecting).\n> \n> If we can compute the spellings of the peers once when the daemon\n> starts up (and maybe make a little dictionary), then we can do\n> prefix tricks on the absolute path after the\n> path_k = paths[k];\n> step in fsevent_callback() to extract a relative path rather than\n> an absolute path.\n> \n> Then call fsmonitor_classify_path_relative() instead of _absolute().\n> \n> Watch out though, there are several places that do\n> rel = path+k + ...state->path_worktree_watch.len...\n> that would need to be adjusted.\n> \n> Hope this helps,\n> Jeff\n> \n> \nThanks for the insights, I'll dig around and test things out some more.\n\n-Eric\n\n"},{"id":"462663","messageId":"BL0PR05MB55712AAD73E0B82B84AC9640D97E9@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"BL0PR05MB5571765C903A82FDD990956AD97E9@BL0PR05MB5571.namprd05.prod.outlook.com","subject":"RE: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the real path","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-06T19:33:37Z","receivedAt":"2022-09-06T19:33:49Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Eric DeCosta\n> Sent: Tuesday, September 6, 2022 3:02 PM\n> To: Jeff Hostetler <git@jeffhostetler.com>; Eric DeCosta via GitGitGadget\n> <gitgitgadget@gmail.com>; git@vger.kernel.org\n> Subject: RE: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the\n> real path\n> \n> \n> \n> > -----Original Message-----\n> > From: Jeff Hostetler <git@jeffhostetler.com>\n> > Sent: Tuesday, September 6, 2022 1:14 PM\n> > To: Eric DeCosta <edecosta@mathworks.com>; Eric DeCosta via\n> > GitGitGadget <gitgitgadget@gmail.com>; git@vger.kernel.org\n> > Subject: Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths\n> > to the real path\n> >\n> >\n> >\n> > On 9/2/22 12:35 PM, Eric DeCosta wrote:\n> > >\n> > >\n> > >> -----Original Message-----\n> > >> From: Jeff Hostetler <git@jeffhostetler.com>\n> > >> Sent: Thursday, September 1, 2022 4:06 PM\n> > >> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n> > >> git@vger.kernel.org\n> > >> Cc: Eric DeCosta <edecosta@mathworks.com>\n> > >> Subject: Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event\n> > >> paths to the real path\n> > >>\n> > >>\n> > >>\n> > >> On 8/31/22 12:09 PM, Eric DeCosta via GitGitGadget wrote:\n> > >>> From: Eric DeCosta <edecosta@mathworks.com>\n> > >>>\n> > >>> Consider the following network working directory that is mounted\n> > >>> under\n> > >>> /System/Volumes/Data:\n> > >>>\n> > >>> /network/working/directory\n> > >>>\n> > >>> The git working directory path is:\n> > >>>\n> > >>> /System/Volumes/Data/network/working/directory\n> > >>>\n> > >>> The paths reported by FSEvents always start with /network.\n> > >>> fsmonitor expects paths to be under the working directory;\n> > >>> therefore it fails to match /network/... and ignores the change.\n> > >>\n> > >> I'm not sure I understand what's going on here.\n> > >> Are you saying that FSEvents reports network mounted directories\n> > >> with a path relative to the mount-point rather than an absolute path?\n> > >>\n> > >\n> > > Yes\n> > >\n> > >> In your example, is \"/network/working/directory\" a valid path on\n> > >> your system (that also happens to be the same directory as\n> > >> \"/System/Volumes/...\")\n> > >>\n> > >> That is, do you have some aliasing going on where both paths are\n> > >> valid -- like a pair of hard-linked directories?\n> > >> Or, is there something special about a network mount point?\n> > >>\n> > >>\n> > >> Did you have to \"cd /System/Volumes/...\" to get Git to have the\n> > >> absolute path be this? Or were you doing a \"cd /network/...\"?\n> > >> (Sorry to ask so many questions but I don't have a pair of systems\n> > >> to test any of this on right now.)\n> > >>\n> > >\n> > > \"/network/working/directory\" is mounted under\n> > > \"/System/Volumes/Data/network/working/directory\"\n> > >\n> > > There is also a symlink:\n> > >\n> > > \"/network\" -> \"/System/Volumes/Data/network\"\n> > >\n> > > No matter if I \"cd /System/Volumes/Data/network/working/directory\"\n> > > or \"cd /network/working/directory\" the paths reported by FSEvents\n> > > always start with \"/network/working/directory\"\n> > >\n> > > If I do a similar symlink with local directories, I do not get this\n> > > unexpected behavior.\n> > >\n> > > I need to remove the symlink and try it that way, but I need to\n> > > coordinate with the machine's owner.\n> >\n> >\n> > I think you have stumbled upon a recent MacOS feature called \"firmlinks\".\n> > I'm just reading up on it myself, so I shouldn't speculate here (yet),\n> > but maybe you too could do some reading on the subject.\n> >\n> > This makes me wonder if the symlink is historical. The real magic is\n> > in the firmlinks. For example, if I do:\n> >\n> > $ (cd / ; ls -l | grep Users)\n> > drwxr-xr-x 6 root admin 192 Apr 6 2020 Users\n> >\n> > $ (cd /Users ; df .)\n> > Filesystem 512-blocks Used Available Capacity iused ifree %iused\n> > Mounted on\n> > /dev/disk1s1 976490576 608954344 338850488 65% 4181323 4878271557 0%\n> > /System/Volumes/Data\n> >\n> > we can see that /Users is on /System/Volumes/Data (and there is a\n> > /System/Volumes/Data/Users directory with the same metadata), but it\n> > is not a symlink.\n> >\n> >\n> > See [1] for more info.\n> >\n> > [1]\n> > http://www.swiftforensics.com/2019/10/macos-1015-volumes-firmlink-\n> > magic.html <https://protect-\n> >\n> us.mimecast.com/s/QN4qCYEZB2h1p7jWcG1Cbw?domain=swiftforensics.co\n> > m>\n> >\n> >\n> \n> Hmm, I don't see anything that suggests a firmlink is involved but this is new\n> to me so maybe I just don't see it. There is an automount in addition to the\n> symlink though.\n> \n> So, dispensing with the abstract \"/network\" path, and getting to the details\n> of my environment:\n> \n> % mount | grep /mathworks\n> map auto.sfs.sol2.mathworks on /System/Volumes/Data/mathworks\n> (autofs, automounted, nobrowse) triggered map auto.sfs.sol2.devel on\n> /System/Volumes/Data/mathworks/devel (autofs, automounted,\n> nobrowse) triggered map sfs.worldwide.US-Natick.sol2.devel.sbs on\n> /System/Volumes/Data/mathworks/devel/sbs (autofs, automounted,\n> nobrowse) triggered map sfs.worldwide.US-Natick.devel.sbs.29 on\n> /System/Volumes/Data/mathworks/devel/sbs/29 (autofs, automounted,\n> nobrowse)\n> batfs-sb29-nfs:/vmgr/sbs29/edecosta.Bbashprod1.j1928560.2 on\n> /System/Volumes/Data/mathworks/devel/sbs/29/edecosta.Bbashprod1.j19\n> 28560.2 (nfs, nodev, automounted, noatime, nobrowse) ...\n> \n> % ls -l / | grep mathworks\n> lrwxr-xr-x   1 root  wheel    35 Aug 29 10:25 home@ ->\n> /System/Volumes/Data/mathworks/home\n> lrwxr-xr-x   1 root  wheel    30 Aug 29 10:25 mathworks@ ->\n> /System/Volumes/Data/mathworks\n> \n> My worktree is:\n> /System/Volumes/Data/mathworks/devel/sbs/29/edecosta.Bbashprod1.j19\n> 28560.2\n> or, equivalently via the symlink just:\n> /mathworks/devel/sbs/29/edecosta.Bbashprod1.j1928560.2\n> \n> I have sudo'ers access now; so I can try mounting things manually, mess\n> about with symlinks and see what I get.\n> \n> > [...]\n> > >>> @@ -209,7 +209,12 @@ static void\n> > >> fsevent_callback(ConstFSEventStreamRef streamRef,\n> > >>> /*\n> > >>> * On Mac, we receive an array of absolute paths.\n> > >>> */\n> > >>> - path_k = paths[k];\n> > >>> + if (fsm_settings__get_allow_remote(the_repository) > 0) {\n> > >>> + strbuf_reset(&tmp); strbuf_realpath_forgiving(&tmp, paths[k],\n> > >>> + 0); path_k = tmp.buf; } else path_k = paths[k];\n> > >>\n> > >> This feels wrong.\n> > >>\n> > >> It is not that fsmonitor.allowRemote is true, but whether or not\n> > >> this particular file system *IS* remote, right?\n> > >\n> > > True. I suppose each path could be checked, or some bit could be set\n> > > if the working directory is remote, perhaps along the lines of\n> > > fsmonitor_ipc__get_path. Then the determination of the IPC path and\n> > > whether it is remote would be in one place. fsm-settings-* would\n> > > then just get that bit and check it against allowRemote.\n> > >\n> > > Thoughts?\n> >\n> > I'll do some digging here. There ought to be a way to tell if a\n> > component directory in a pathname has a firmlink peer. (But I'm\n> > traveling for GitMerge, so I might not have time to look at this until\n> > afterwards.)\n> >\n> > This would indicate that a \"bi-directional wormhole\" (their words)\n> > alias is available (and hopefully, a way to computer the other\n> > peer....)\n> >\n> > I'm thinking that the \"/network/...\" path in the FSEvents is because\n> > FSEventD is using a particular peer spelling (that we weren't\n> > expecting).\n> >\n> > If we can compute the spellings of the peers once when the daemon\n> > starts up (and maybe make a little dictionary), then we can do prefix\n> > tricks on the absolute path after the path_k = paths[k]; step in\n> > fsevent_callback() to extract a relative path rather than an absolute\n> > path.\n> >\n> > Then call fsmonitor_classify_path_relative() instead of _absolute().\n> >\n> > Watch out though, there are several places that do rel = path+k +\n> > ...state->path_worktree_watch.len...\n> > that would need to be adjusted.\n> >\n> > Hope this helps,\n> > Jeff\n> >\n> >\n> Thanks for the insights, I'll dig around and test things out some more.\n> \n> -Eric\n\nThis is informative:\n\nhttps://developer.apple.com/forums/thread/120665\n\n-Eric\n\n\n"},{"id":"462672","messageId":"BL0PR05MB5571A7EAE44FED23A924A4A4D97E9@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"BL0PR05MB55712AAD73E0B82B84AC9640D97E9@BL0PR05MB5571.namprd05.prod.outlook.com","subject":"RE: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the real path","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-06T22:11:04Z","receivedAt":"2022-09-06T22:11:17Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Eric DeCosta\n> Sent: Tuesday, September 6, 2022 3:34 PM\n> To: 'Jeff Hostetler' <git@jeffhostetler.com>; 'Eric DeCosta via GitGitGadget'\n> <gitgitgadget@gmail.com>; 'git@vger.kernel.org' <git@vger.kernel.org>\n> Subject: RE: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the\n> real path\n> \n> \n> \n> > -----Original Message-----\n> > From: Eric DeCosta\n> > Sent: Tuesday, September 6, 2022 3:02 PM\n> > To: Jeff Hostetler <git@jeffhostetler.com>; Eric DeCosta via\n> > GitGitGadget <gitgitgadget@gmail.com>; git@vger.kernel.org\n> > Subject: RE: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths\n> > to the real path\n> >\n> >\n> >\n> > > -----Original Message-----\n> > > From: Jeff Hostetler <git@jeffhostetler.com>\n> > > Sent: Tuesday, September 6, 2022 1:14 PM\n> > > To: Eric DeCosta <edecosta@mathworks.com>; Eric DeCosta via\n> > > GitGitGadget <gitgitgadget@gmail.com>; git@vger.kernel.org\n> > > Subject: Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event\n> > > paths to the real path\n> > >\n> > >\n> > >\n> > > On 9/2/22 12:35 PM, Eric DeCosta wrote:\n> > > >\n> > > >\n> > > >> -----Original Message-----\n> > > >> From: Jeff Hostetler <git@jeffhostetler.com>\n> > > >> Sent: Thursday, September 1, 2022 4:06 PM\n> > > >> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n> > > >> git@vger.kernel.org\n> > > >> Cc: Eric DeCosta <edecosta@mathworks.com>\n> > > >> Subject: Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event\n> > > >> paths to the real path\n> > > >>\n> > > >>\n> > > >>\n> > > >> On 8/31/22 12:09 PM, Eric DeCosta via GitGitGadget wrote:\n> > > >>> From: Eric DeCosta <edecosta@mathworks.com>\n> > > >>>\n> > > >>> Consider the following network working directory that is mounted\n> > > >>> under\n> > > >>> /System/Volumes/Data:\n> > > >>>\n> > > >>> /network/working/directory\n> > > >>>\n> > > >>> The git working directory path is:\n> > > >>>\n> > > >>> /System/Volumes/Data/network/working/directory\n> > > >>>\n> > > >>> The paths reported by FSEvents always start with /network.\n> > > >>> fsmonitor expects paths to be under the working directory;\n> > > >>> therefore it fails to match /network/... and ignores the change.\n> > > >>\n> > > >> I'm not sure I understand what's going on here.\n> > > >> Are you saying that FSEvents reports network mounted directories\n> > > >> with a path relative to the mount-point rather than an absolute path?\n> > > >>\n> > > >\n> > > > Yes\n> > > >\n> > > >> In your example, is \"/network/working/directory\" a valid path on\n> > > >> your system (that also happens to be the same directory as\n> > > >> \"/System/Volumes/...\")\n> > > >>\n> > > >> That is, do you have some aliasing going on where both paths are\n> > > >> valid -- like a pair of hard-linked directories?\n> > > >> Or, is there something special about a network mount point?\n> > > >>\n> > > >>\n> > > >> Did you have to \"cd /System/Volumes/...\" to get Git to have the\n> > > >> absolute path be this? Or were you doing a \"cd /network/...\"?\n> > > >> (Sorry to ask so many questions but I don't have a pair of\n> > > >> systems to test any of this on right now.)\n> > > >>\n> > > >\n> > > > \"/network/working/directory\" is mounted under\n> > > > \"/System/Volumes/Data/network/working/directory\"\n> > > >\n> > > > There is also a symlink:\n> > > >\n> > > > \"/network\" -> \"/System/Volumes/Data/network\"\n> > > >\n> > > > No matter if I \"cd /System/Volumes/Data/network/working/directory\"\n> > > > or \"cd /network/working/directory\" the paths reported by FSEvents\n> > > > always start with \"/network/working/directory\"\n> > > >\n> > > > If I do a similar symlink with local directories, I do not get\n> > > > this unexpected behavior.\n> > > >\n> > > > I need to remove the symlink and try it that way, but I need to\n> > > > coordinate with the machine's owner.\n> > >\n> > >\n> > > I think you have stumbled upon a recent MacOS feature called\n> \"firmlinks\".\n> > > I'm just reading up on it myself, so I shouldn't speculate here\n> > > (yet), but maybe you too could do some reading on the subject.\n> > >\n> > > This makes me wonder if the symlink is historical. The real magic is\n> > > in the firmlinks. For example, if I do:\n> > >\n> > > $ (cd / ; ls -l | grep Users)\n> > > drwxr-xr-x 6 root admin 192 Apr 6 2020 Users\n> > >\n> > > $ (cd /Users ; df .)\n> > > Filesystem 512-blocks Used Available Capacity iused ifree %iused\n> > > Mounted on\n> > > /dev/disk1s1 976490576 608954344 338850488 65% 4181323 4878271557\n> 0%\n> > > /System/Volumes/Data\n> > >\n> > > we can see that /Users is on /System/Volumes/Data (and there is a\n> > > /System/Volumes/Data/Users directory with the same metadata), but it\n> > > is not a symlink.\n> > >\n> > >\n> > > See [1] for more info.\n> > >\n> > > [1]\n> > > http://www.swiftforensics.com/2019/10/macos-1015-volumes-firmlink-\n> > > magic.html <https://protect-\n> > >\n> >\n> us.mimecast.com/s/QN4qCYEZB2h1p7jWcG1Cbw?domain=swiftforensics.co\n> > > m>\n> > >\n> > >\n> >\n> > Hmm, I don't see anything that suggests a firmlink is involved but\n> > this is new to me so maybe I just don't see it. There is an automount\n> > in addition to the symlink though.\n> >\n> > So, dispensing with the abstract \"/network\" path, and getting to the\n> > details of my environment:\n> >\n> > % mount | grep /mathworks\n> > map auto.sfs.sol2.mathworks on /System/Volumes/Data/mathworks\n> (autofs,\n> > automounted, nobrowse) triggered map auto.sfs.sol2.devel on\n> > /System/Volumes/Data/mathworks/devel (autofs, automounted,\n> > nobrowse) triggered map sfs.worldwide.US-Natick.sol2.devel.sbs on\n> > /System/Volumes/Data/mathworks/devel/sbs (autofs, automounted,\n> > nobrowse) triggered map sfs.worldwide.US-Natick.devel.sbs.29 on\n> > /System/Volumes/Data/mathworks/devel/sbs/29 (autofs, automounted,\n> > nobrowse)\n> > batfs-sb29-nfs:/vmgr/sbs29/edecosta.Bbashprod1.j1928560.2 on\n> >\n> /System/Volumes/Data/mathworks/devel/sbs/29/edecosta.Bbashprod1.j19\n> > 28560.2 (nfs, nodev, automounted, noatime, nobrowse) ...\n> >\n> > % ls -l / | grep mathworks\n> > lrwxr-xr-x   1 root  wheel    35 Aug 29 10:25 home@ ->\n> > /System/Volumes/Data/mathworks/home\n> > lrwxr-xr-x   1 root  wheel    30 Aug 29 10:25 mathworks@ ->\n> > /System/Volumes/Data/mathworks\n> >\n> > My worktree is:\n> >\n> /System/Volumes/Data/mathworks/devel/sbs/29/edecosta.Bbashprod1.j19\n> > 28560.2\n> > or, equivalently via the symlink just:\n> > /mathworks/devel/sbs/29/edecosta.Bbashprod1.j1928560.2\n> >\n> > I have sudo'ers access now; so I can try mounting things manually,\n> > mess about with symlinks and see what I get.\n> >\n> > > [...]\n> > > >>> @@ -209,7 +209,12 @@ static void\n> > > >> fsevent_callback(ConstFSEventStreamRef streamRef,\n> > > >>> /*\n> > > >>> * On Mac, we receive an array of absolute paths.\n> > > >>> */\n> > > >>> - path_k = paths[k];\n> > > >>> + if (fsm_settings__get_allow_remote(the_repository) > 0) {\n> > > >>> + strbuf_reset(&tmp); strbuf_realpath_forgiving(&tmp, paths[k],\n> > > >>> + 0); path_k = tmp.buf; } else path_k = paths[k];\n> > > >>\n> > > >> This feels wrong.\n> > > >>\n> > > >> It is not that fsmonitor.allowRemote is true, but whether or not\n> > > >> this particular file system *IS* remote, right?\n> > > >\n> > > > True. I suppose each path could be checked, or some bit could be\n> > > > set if the working directory is remote, perhaps along the lines of\n> > > > fsmonitor_ipc__get_path. Then the determination of the IPC path\n> > > > and whether it is remote would be in one place. fsm-settings-*\n> > > > would then just get that bit and check it against allowRemote.\n> > > >\n> > > > Thoughts?\n> > >\n> > > I'll do some digging here. There ought to be a way to tell if a\n> > > component directory in a pathname has a firmlink peer. (But I'm\n> > > traveling for GitMerge, so I might not have time to look at this\n> > > until\n> > > afterwards.)\n> > >\n> > > This would indicate that a \"bi-directional wormhole\" (their words)\n> > > alias is available (and hopefully, a way to computer the other\n> > > peer....)\n> > >\n> > > I'm thinking that the \"/network/...\" path in the FSEvents is because\n> > > FSEventD is using a particular peer spelling (that we weren't\n> > > expecting).\n> > >\n> > > If we can compute the spellings of the peers once when the daemon\n> > > starts up (and maybe make a little dictionary), then we can do\n> > > prefix tricks on the absolute path after the path_k = paths[k]; step\n> > > in\n> > > fsevent_callback() to extract a relative path rather than an\n> > > absolute path.\n> > >\n> > > Then call fsmonitor_classify_path_relative() instead of _absolute().\n> > >\n> > > Watch out though, there are several places that do rel = path+k +\n> > > ...state->path_worktree_watch.len...\n> > > that would need to be adjusted.\n> > >\n> > > Hope this helps,\n> > > Jeff\n> > >\n> > >\n> > Thanks for the insights, I'll dig around and test things out some more.\n> >\n> > -Eric\n> \n> This is informative:\n> \n> https://developer.apple.com/forums/thread/120665\n> \n> -Eric\n> \nConversation with one of our macOS admins:\n\nQ: Is /mathworks a symbolic link or some sort of firmlink or synthetic firmlink?\n\nA: It’s a “synthetic” link, per apple. Config is managed in /etc/synthetic.conf\n\nThe auto mounter will also automatically create it based on the contents of /etc/auto_master even without the synthetic.conf file. We went back and forth with apple for a while on the “right” way to do it and now I’m not sure what we settled on, off the top of my head\n\nI don’t know if they’re the same under the hood, but it seemed to be able to create root level links even though you can’t with ln anymore\n\nI suspect apple wants users to use the synthetic.conf file in all cases...\n----\n\nIt appears that the automounter is creating \"synthetic firmlinks\", a la https://derflounder.wordpress.com/2020/01/18/creating-root-level-directories-and-symbolic-links-on-macos-catalina/\n\nWhen I look in /etc/auto_master I see several entries that appear as symlinks in the output of \"ls -l /\" but in reality they are synthetic firmlinks.\n\n-Eric\n\n"},{"id":"462749","messageId":"c9e9b38d-b2a8-7c01-9978-0cb2fc29880f@jeffhostetler.com","threadId":"58324","inReplyTo":"BL0PR05MB55712AAD73E0B82B84AC9640D97E9@BL0PR05MB5571.namprd05.prod.outlook.com","subject":"Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the real path","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-09-07T19:14:54Z","receivedAt":"2022-09-07T19:15:04Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 9/6/22 3:33 PM, Eric DeCosta wrote:\n> \n> \n[...]\n> \n> This is informative:\n> \n> https://developer.apple.com/forums/thread/120665\n> \n> -Eric\n> \n> \n\nHow strange........\n\nHave you tried the:\n    fcntl(F_GETPATH)\nvs fcntl(F_GETPATH_NOFIRMLINK)\nvs realpath()\n\ncomparison suggested in the comments and does it return\nanything sensical?  Such that we could record both spellings\nwhen the daemon starts up.  I think we'd have to do it on\nan open fd on the worktree root directory.\n\nJeff\n\n\nhttps://developer.apple.com/forums/thread/120665\n"},{"id":"462773","messageId":"BL0PR05MB5571C7A41CF397E7F7B05EC9D9419@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"c9e9b38d-b2a8-7c01-9978-0cb2fc29880f@jeffhostetler.com","subject":"RE: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the real path","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-07T23:04:34Z","receivedAt":"2022-09-07T23:04:44Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Jeff Hostetler <git@jeffhostetler.com>\n> Sent: Wednesday, September 7, 2022 3:15 PM\n> To: Eric DeCosta <edecosta@mathworks.com>; Eric DeCosta via GitGitGadget\n> <gitgitgadget@gmail.com>; git@vger.kernel.org\n> Subject: Re: [PATCH v4 4/4] fsmonitor: normalize FSEvents event paths to the\n> real path\n> \n> \n> \n> On 9/6/22 3:33 PM, Eric DeCosta wrote:\n> >\n> >\n> [...]\n> >\n> > This is informative:\n> >\n> > https://developer.apple.com/forums/thread/120665\n> > <https://protect-\n> us.mimecast.com/s/Vj1mCL9GlLUjmyJjfqLBqG?domain=devel\n> > oper.apple.com>\n> >\n> > -Eric\n> >\n> >\n> \n> How strange........\n> \n> Have you tried the:\n> fcntl(F_GETPATH)\n> vs fcntl(F_GETPATH_NOFIRMLINK)\n> vs realpath()\n> \n> comparison suggested in the comments and does it return anything sensical?\n> Such that we could record both spellings when the daemon starts up. I think\n> we'd have to do it on an open fd on the worktree root directory.\n> \n> Jeff\n> \n> \n> https://developer.apple.com/forums/thread/120665 <https://protect-\n> us.mimecast.com/s/Vj1mCL9GlLUjmyJjfqLBqG?domain=developer.apple.com\n> >\n\nI'm not getting the same result:\n\nWorktree is: '/System/Volumes/Data/mathworks/devel/sandbox/edecosta/git'\nF_GETPATH of worktree is '/System/Volumes/Data/mathworks/devel/sandbox/edecosta/git'\nF_GETPATH_NOFIRMLINK of worktree is '/System/Volumes/Data/mathworks/devel/sandbox/edecosta/git'\nrealpath() of worktree is '/System/Volumes/Data/mathworks/devel/sandbox/edecosta/git'\n\nTrying /mathworks/devel/sandbox/edecosta/git\nF_GETPATH of /mathworks/devel/sandbox/edecosta/git is '/System/Volumes/Data/mathworks/devel/sandbox/edecosta/git'\nF_GETPATH_NOFIRMLINK of /mathworks/devel/sandbox/edecosta/git is '/System/Volumes/Data/mathworks/devel/sandbox/edecosta/git'\nrealpath() of /mathworks/devel/sandbox/edecosta/git is '/System/Volumes/Data/mathworks/devel/sandbox/edecosta/git'\n\nEither something has changed or the fact that \"/mathworks\" is a \"synthetic firmlink\" has something to do with it.\n\nMaybe look for synthetic firmlinks in the root directory and use that information to figure out what aliases there might be to the worktree.\n\nlrwxr-xr-x   1 root  wheel    30 Sep  2 11:52 mathworks -> /System/Volumes/Data/mathworks\n\nIf what the link resolves to is a prefix of the worktree, then it is a firmlink to the worktree.\n\nSomething like that.\n\n-Eric\n"},{"id":"462896","messageId":"2f647b53e4dcedece0c7ac1474afe9943cfd6be7.1662840031.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v5.git.1662840031.gitgitgadget@gmail.com","subject":"[PATCH v5 1/4] fsmonitor: refactor filesystem checks to common interface","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-10T20:00:28Z","receivedAt":"2022-09-10T20:02:33Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nProvide a common interface for getting basic filesystem information\nincluding filesystem type and whether the filesystem is remote.\n\nRefactor existing code for getting basic filesystem info and detecting\nremote file systems to the new interface.\n\nRefactor filesystem checks to leverage new interface. For macOS,\nerror-out if the Unix Domain socket (UDS) file is on a remote\nfilesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                                 |   1 +\n compat/fsmonitor/fsm-path-utils-darwin.c |  40 +++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  |  89 +++++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  62 ++++----------\n compat/fsmonitor/fsm-settings-win32.c    | 104 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   2 +\n fsmonitor-path-utils.h                   |  23 +++++\n fsmonitor-settings.c                     |  50 +++++++++++\n 8 files changed, 224 insertions(+), 147 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\ndiff --git a/Makefile b/Makefile\nindex 924b864ae83..265fc585286 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2042,6 +2042,7 @@ endif\n ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n ifeq ($(TCLTK_PATH),)\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nnew file mode 100644\nindex 00000000000..067cbe6990a\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -0,0 +1,40 @@\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+#include <sys/param.h>\n+#include <sys/mount.h>\n+\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\tstruct statfs fs;\n+\tif (statfs(path, &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\treturn -1;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\n+\tif (!(fs.f_flags & MNT_LOCAL))\n+\t\tfs_info->is_remote = 1;\n+\telse\n+\t\tfs_info->is_remote = 0;\n+\n+\tfs_info->typename = fs.f_fstypename;\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nnew file mode 100644\nindex 00000000000..dc211de514f\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -0,0 +1,89 @@\n+#include \"cache.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+/*\n+ * Notes for testing:\n+ *\n+ * (a) Windows allows a network share to be mapped to a drive letter.\n+ *     (This is the normal method to access it.)\n+ *\n+ *     $ NET USE Z: \\\\server\\share\n+ *     $ git -C Z:/repo status\n+ *\n+ * (b) Windows allows a network share to be referenced WITHOUT mapping\n+ *     it to drive letter.\n+ *\n+ *     $ NET USE \\\\server\\share\\dir\n+ *     $ git -C //server/share/repo status\n+ *\n+ * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n+ *     arbitrary path (which may be remote)\n+ *\n+ *     $ SUBST Q: Z:\\repo\n+ *     $ git -C Q:/ status\n+ *\n+ * (d) Windows allows a directory symlink to be created on a local\n+ *     file system that points to a remote repo.\n+ *\n+ *     $ mklink /d ./link //server/share/repo\n+ *     $ git -C ./link status\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\twchar_t wpath[MAX_PATH];\n+\twchar_t wfullpath[MAX_PATH];\n+\tsize_t wlen;\n+\tUINT driveType;\n+\n+\t/*\n+\t * Do everything in wide chars because the drive letter might be\n+\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n+\t */\n+\tif (xutftowcs_path(wpath, path) < 0) {\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * GetDriveTypeW() requires a final slash.  We assume that the\n+\t * worktree pathname points to an actual directory.\n+\t */\n+\twlen = wcslen(wpath);\n+\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n+\t\twpath[wlen++] = L'\\\\';\n+\t\twpath[wlen] = 0;\n+\t}\n+\n+\t/*\n+\t * Normalize the path.  If nothing else, this converts forward\n+\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n+\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n+\t */\n+\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {\n+\t\treturn -1;\n+\t}\n+\n+\tdriveType = GetDriveTypeW(wfullpath);\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n+\t\t\t path, wfullpath, driveType);\n+\n+\tif (driveType == DRIVE_REMOTE)\n+\t\tfs_info->is_remote = 1;\n+\telse\n+\t\tfs_info->is_remote = 0;\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dba3ced6bb7 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -1,32 +1,10 @@\n-#include \"cache.h\"\n #include \"config.h\"\n-#include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n-#include <sys/param.h>\n-#include <sys/mount.h>\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n-/*\n- * [1] Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type (NFS, SAMBA, etc.) dictates whether notification events\n- * are available at all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n+ /*\n  * For the builtin FSMonitor, we create the Unix domain socket for the\n  * IPC in the .git directory.  If the working directory is remote,\n  * then the socket will be created on the remote file system.  This\n@@ -38,40 +16,34 @@\n  * be taken to ensure that $HOME is actually local and not a managed\n  * file share.)\n  *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n+ * FAT32 and NTFS working directories are problematic too.\n  *\n  * The builtin FSMonitor uses a Unix domain socket in the .git\n  * directory for IPC.  These Windows drive formats do not support\n  * Unix domain sockets, so mark them as incompatible for the daemon.\n  *\n  */\n-static enum fsmonitor_reason check_volume(struct repository *r)\n+static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n-\tstruct statfs fs;\n+\tstruct fs_info fs;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \n-\tif (statfs(r->worktree, &fs) == -1) {\n-\t\tint saved_errno = errno;\n-\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n-\t\t\t\t r->worktree, strerror(saved_errno));\n-\t\terrno = saved_errno;\n+\tif (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {\n+\t\tstrbuf_release(&path);\n \t\treturn FSMONITOR_REASON_ERROR;\n \t}\n \n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n-\t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n \n-\tif (!(fs.f_flags & MNT_LOCAL))\n+\tif (fs.is_remote)\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n-\tif (!strcmp(fs.f_fstypename, \"msdos\")) /* aka FAT32 */\n+\tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n-\tif (!strcmp(fs.f_fstypename, \"ntfs\"))\n+\tif (!strcmp(fs.typename, \"ntfs\"))\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \treturn FSMONITOR_REASON_OK;\n@@ -81,7 +53,7 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_volume(r);\n+\treason = check_uds_volume(r);\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex 907655720bb..d88b06ae610 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -1,8 +1,9 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * VFS for Git is incompatible with FSMonitor.\n@@ -24,103 +25,6 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-/*\n- * Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type dictates whether notification events are available at\n- * all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- * Notes for testing:\n- *\n- * (a) Windows allows a network share to be mapped to a drive letter.\n- *     (This is the normal method to access it.)\n- *\n- *     $ NET USE Z: \\\\server\\share\n- *     $ git -C Z:/repo status\n- *\n- * (b) Windows allows a network share to be referenced WITHOUT mapping\n- *     it to drive letter.\n- *\n- *     $ NET USE \\\\server\\share\\dir\n- *     $ git -C //server/share/repo status\n- *\n- * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n- *     arbitrary path (which may be remote)\n- *\n- *     $ SUBST Q: Z:\\repo\n- *     $ git -C Q:/ status\n- *\n- * (d) Windows allows a directory symlink to be created on a local\n- *     file system that points to a remote repo.\n- *\n- *     $ mklink /d ./link //server/share/repo\n- *     $ git -C ./link status\n- */\n-static enum fsmonitor_reason check_remote(struct repository *r)\n-{\n-\twchar_t wpath[MAX_PATH];\n-\twchar_t wfullpath[MAX_PATH];\n-\tsize_t wlen;\n-\tUINT driveType;\n-\n-\t/*\n-\t * Do everything in wide chars because the drive letter might be\n-\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n-\t */\n-\tif (xutftowcs_path(wpath, r->worktree) < 0)\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t/*\n-\t * GetDriveTypeW() requires a final slash.  We assume that the\n-\t * worktree pathname points to an actual directory.\n-\t */\n-\twlen = wcslen(wpath);\n-\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n-\t\twpath[wlen++] = L'\\\\';\n-\t\twpath[wlen] = 0;\n-\t}\n-\n-\t/*\n-\t * Normalize the path.  If nothing else, this converts forward\n-\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n-\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n-\t */\n-\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\tdriveType = GetDriveTypeW(wfullpath);\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n-\t\t\t r->worktree, wfullpath, driveType);\n-\n-\tif (driveType == DRIVE_REMOTE) {\n-\t\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t \"check_remote('%s') true\",\n-\t\t\t\t r->worktree);\n-\t\treturn FSMONITOR_REASON_REMOTE;\n-\t}\n-\n-\treturn FSMONITOR_REASON_OK;\n-}\n-\n enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n@@ -129,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n-\treason = check_remote(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n-\n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 2237109b57f..b88494bf59b 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)\n@@ -315,6 +316,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nnew file mode 100644\nindex 00000000000..e48592887e7\n--- /dev/null\n+++ b/fsmonitor-path-utils.h\n@@ -0,0 +1,23 @@\n+#ifndef FSM_PATH_UTILS_H\n+#define FSM_PATH_UTILS_H\n+\n+struct fs_info {\n+\tint is_remote;\n+\tchar *typename;\n+};\n+\n+/*\n+ * Get some basic filesystem informtion for the given path\n+ *\n+ * Returns -1 on error, zero otherwise.\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n+\n+/*\n+ * Determines if the filesystem that path resides on is remote.\n+ *\n+ * Returns -1 on error, 0 if not remote, 1 if remote.\n+ */\n+int fsmonitor__is_fs_remote(const char *path);\n+\n+#endif\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..d288cbad479 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * We keep this structure defintion private and have getters\n@@ -13,6 +14,52 @@ struct fsmonitor_settings {\n \tchar *hook_path;\n };\n \n+/*\n+ * Remote working directories are problematic for FSMonitor.\n+ *\n+ * The underlying file system on the server machine and/or the remote\n+ * mount type dictates whether notification events are available at\n+ * all to remote client machines.\n+ *\n+ * Kernel differences between the server and client machines also\n+ * dictate the how (buffering, frequency, de-dup) the events are\n+ * delivered to client machine processes.\n+ *\n+ * A client machine (such as a laptop) may choose to suspend/resume\n+ * and it is unclear (without lots of testing) whether the watcher can\n+ * resync after a resume.  We might be able to treat this as a normal\n+ * \"events were dropped by the kernel\" event and do our normal \"flush\n+ * and resync\" --or-- we might need to close the existing (zombie?)\n+ * notification fd and create a new one.\n+ *\n+ * In theory, the above issues need to be addressed whether we are\n+ * using the Hook or IPC API.\n+ *\n+ * So (for now at least), mark remote working directories as\n+ * incompatible unless 'fsmonitor.allowRemote' is true.\n+ *\n+ */\n+#ifdef HAVE_FSMONITOR_OS_SETTINGS\n+static enum fsmonitor_reason check_remote(struct repository *r)\n+{\n+\tint allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */\n+\tint is_remote = fsmonitor__is_fs_remote(r->worktree);\n+\n+\tswitch (is_remote) {\n+\t\tcase 0:\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tcase 1:\n+\t\t\trepo_config_get_bool(r, \"fsmonitor.allowremote\", &allow_remote);\n+\t\t\tif (allow_remote < 1)\n+\t\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\t\telse\n+\t\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+}\n+#endif\n+\n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n {\n \tif (!r->worktree) {\n@@ -27,6 +74,9 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t{\n \t\tenum fsmonitor_reason reason;\n \n+\t\treason = check_remote(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n \t\treason = fsm_os__incompatible(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-- \ngitgitgadget\n\n"},{"id":"462897","messageId":"pull.1326.v5.git.1662840031.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v4.git.1661962145.gitgitgadget@gmail.com","subject":"[PATCH v5 0/4] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-10T20:00:27Z","receivedAt":"2022-09-10T20:02:33Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"Follow-on to the work done to allow Windows to work against network-mounted\nrepos for macOS.\n\nHave macOS take advantage of the same configuration option,\n'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\nto true will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for IPC\ncannot be created in a network location; instead $HOME is used if the\ndefault location is on the network. The user may, optionally, set the\n'fsmonitor.socketDir' configuration option to a valid, local directory if\n$HOME itself is on the network or is simply not the desired location for the\nUDS file.\n\nAn additional issue is that for mount points in the root directory, FSEvents\ndoes not report a path that matches the worktree directory due to the\nintroduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\nto the worktree directory by interrogating the root filesystem for synthetic\nfirmlinks and using that information to translate the path.\n\nEric DeCosta (4):\n  fsmonitor: refactor filesystem checks to common interface\n  fsmonitor: relocate socket file if .git directory is remote\n  fsmonitor: avoid socket location check if using hook\n  fsmonitor: deal with synthetic firmlinks on macOS\n\n Makefile                                 |   2 +\n builtin/fsmonitor--daemon.c              |   8 ++\n compat/fsmonitor/fsm-ipc-darwin.c        |  46 ++++++++\n compat/fsmonitor/fsm-ipc-win32.c         |   4 +\n compat/fsmonitor/fsm-listen-darwin.c     |   6 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 131 +++++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 106 ++++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  70 ++++--------\n compat/fsmonitor/fsm-settings-win32.c    | 106 +-----------------\n contrib/buildsystems/CMakeLists.txt      |   4 +\n fsmonitor--daemon.h                      |   6 ++\n fsmonitor-ipc.c                          |   2 -\n fsmonitor-path-utils.h                   |  49 +++++++++\n fsmonitor-settings.c                     |  58 +++++++++-\n fsmonitor-settings.h                     |   2 +-\n 15 files changed, 441 insertions(+), 159 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\n\nbase-commit: be1a02a17ede4082a86dfbfee0f54f345e8b43ac\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v5\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v5\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v4:\n\n -:  ----------- > 1:  2f647b53e4d fsmonitor: refactor filesystem checks to common interface\n 2:  2cb026a6317 ! 2:  28d08bcf808 fsmonitor: generate unique Unix socket file name in the desired location\n     @@ Metadata\n      Author: Eric DeCosta <edecosta@mathworks.com>\n      \n       ## Commit message ##\n     -    fsmonitor: generate unique Unix socket file name in the desired location\n     +    fsmonitor: relocate socket file if .git directory is remote\n      \n     -    Based on the values of fsmonitor.allowRemote and fsmonitor.socketDir\n     -    locate the Unix domain socket file in the desired location (either\n     -    the .git directory, $HOME, or fsmonitor.socketDir). If the location\n     -    is other than the .git directory, generate a unique file name based\n     -    on the SHA1 has of the path to the .git directory.\n     +    If the .git directory is on a remote file system, create the socket\n     +    file in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n      \n          Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n      \n     - ## fsmonitor-ipc.c ##\n     -@@\n     - #include \"cache.h\"\n     --#include \"fsmonitor.h\"\n     --#include \"simple-ipc.h\"\n     - #include \"fsmonitor-ipc.h\"\n     -+#include \"fsmonitor-settings.h\"\n     - #include \"run-command.h\"\n     - #include \"strbuf.h\"\n     - #include \"trace2.h\"\n     -@@ fsmonitor-ipc.c: int fsmonitor_ipc__is_supported(void)\n     - \treturn 1;\n     - }\n     + ## Makefile ##\n     +@@ Makefile: ifdef FSMONITOR_DAEMON_BACKEND\n     + \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n     + \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n     + \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n     ++\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n     + endif\n       \n     --GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n     -+GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n     + ifdef FSMONITOR_OS_SETTINGS\n     +\n     + ## compat/fsmonitor/fsm-ipc-darwin.c (new) ##\n     +@@\n     ++#include \"cache.h\"\n     ++#include \"config.h\"\n     ++#include \"strbuf.h\"\n     ++#include \"fsmonitor.h\"\n     ++#include \"fsmonitor-ipc.h\"\n     ++#include \"fsmonitor-path-utils.h\"\n     ++\n     ++static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n      +\n      +const char *fsmonitor_ipc__get_path(void)\n      +{\n     -+#ifdef WIN32\n     -+\treturn fsmonitor_ipc__get_default_path();\n     -+#else\n     -+\tchar *retval;\n     ++\tstatic const char *ipc_path;\n      +\tSHA_CTX sha1ctx;\n     -+\tconst char *git_dir;\n     -+\tconst char *sock_dir;\n     ++\tchar *sock_dir;\n      +\tstruct strbuf ipc_file = STRBUF_INIT;\n      +\tunsigned char hash[SHA_DIGEST_LENGTH];\n      +\n     -+\tif (fsm_settings__get_allow_remote(the_repository) < 1)\n     -+\t\treturn fsmonitor_ipc__get_default_path();\n     ++\tif (ipc_path)\n     ++\t\treturn ipc_path;\n      +\n     -+\tgit_dir = get_git_dir();\n     -+\tsock_dir = fsm_settings__get_socket_dir(the_repository);\n     ++\tipc_path = fsmonitor_ipc__get_default_path();\n     ++\n     ++\t/* By default the socket file is created in the .git directory */\n     ++\tif (fsmonitor__is_fs_remote(ipc_path) < 1)\n     ++\t\treturn ipc_path;\n      +\n      +\tSHA1_Init(&sha1ctx);\n     -+\tSHA1_Update(&sha1ctx, git_dir, strlen(git_dir));\n     ++\tSHA1_Update(&sha1ctx, the_repository->worktree, strlen(the_repository->worktree));\n      +\tSHA1_Final(hash, &sha1ctx);\n      +\n     ++\trepo_config_get_string(the_repository, \"fsmonitor.socketdir\", &sock_dir);\n     ++\n     ++\t/* Create the socket file in either socketDir or $HOME */\n      +\tif (sock_dir && *sock_dir)\n      +\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n      +\t\t\t\t\tsock_dir, hash_to_hex(hash));\n      +\telse\n      +\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n     -+\tretval = interpolate_path(ipc_file.buf, 1);\n     -+\tif (!retval)\n     ++\n     ++\tipc_path = interpolate_path(ipc_file.buf, 1);\n     ++\tif (!ipc_path)\n      +\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n     ++\n      +\tstrbuf_release(&ipc_file);\n     -+\treturn retval;\n     -+#endif\n     ++\treturn ipc_path;\n      +}\n     +\n     + ## compat/fsmonitor/fsm-ipc-win32.c (new) ##\n     +@@\n     ++#include \"cache.h\"\n     ++#include \"fsmonitor-ipc.h\"\n     ++\n     ++GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n     +\n     + ## contrib/buildsystems/CMakeLists.txt ##\n     +@@ contrib/buildsystems/CMakeLists.txt: if(SUPPORTS_SIMPLE_IPC)\n     + \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n     + \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n     + \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n     ++\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n     + \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n       \n     - enum ipc_active_state fsmonitor_ipc__get_state(void)\n     - {\n     + \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n     +@@ contrib/buildsystems/CMakeLists.txt: if(SUPPORTS_SIMPLE_IPC)\n     + \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n     + \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n     + \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n     ++\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n     + \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n     + \n     + \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n      \n     - ## fsmonitor-ipc.h ##\n     -@@ fsmonitor-ipc.h: int fsmonitor_ipc__is_supported(void);\n     -  */\n     - const char *fsmonitor_ipc__get_path(void);\n     + ## fsmonitor-ipc.c ##\n     +@@ fsmonitor-ipc.c: int fsmonitor_ipc__is_supported(void)\n     + \treturn 1;\n     + }\n       \n     -+/*\n     -+ * Returns the pathname to the default IPC named pipe or Unix domain\n     -+ * socket.\n     -+ */\n     -+const char *fsmonitor_ipc__get_default_path(void);\n     -+\n     - /*\n     -  * Try to determine whether there is a `git-fsmonitor--daemon` process\n     -  * listening on the IPC pipe/socket.\n     +-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n     +-\n     + enum ipc_active_state fsmonitor_ipc__get_state(void)\n     + {\n     + \treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n 1:  836a791e6b7 ! 3:  ff54b6e0bb5 fsmonitor: add two new config options, allowRemote and socketDir\n     @@ Metadata\n      Author: Eric DeCosta <edecosta@mathworks.com>\n      \n       ## Commit message ##\n     -    fsmonitor: add two new config options, allowRemote and socketDir\n     +    fsmonitor: avoid socket location check if using hook\n      \n     -    Introduce two new configuration options\n     -\n     -       fsmonitor.allowRemote - setting this to true overrides fsmonitor's\n     -       default behavior of erroring out when enountering network file\n     -       systems. Additionly, when true, the Unix domain socket (UDS) file\n     -       used for IPC is located in $HOME rather than in the .git directory.\n     -\n     -       fsmonitor.socketDir - allows for the UDS file to be located\n     -       anywhere the user chooses rather $HOME.\n     +    If monitoring is done via fsmonitor hook rather than IPC there is no\n     +    need to check if the location of the Unix Domain socket (UDS) file is\n     +    on a remote filesystem.\n      \n          Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n      \n     - ## fsmonitor-settings.c ##\n     -@@\n     - struct fsmonitor_settings {\n     - \tenum fsmonitor_mode mode;\n     + ## compat/fsmonitor/fsm-settings-darwin.c ##\n     +@@ compat/fsmonitor/fsm-settings-darwin.c: static enum fsmonitor_reason check_uds_volume(struct repository *r)\n     + \treturn FSMONITOR_REASON_OK;\n     + }\n     + \n     +-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n     ++enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n     + {\n       \tenum fsmonitor_reason reason;\n     -+\tint allow_remote;\n     - \tchar *hook_path;\n     -+\tchar *sock_dir;\n     - };\n       \n     - static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n     -@@ fsmonitor-settings.c: static struct fsmonitor_settings *alloc_settings(void)\n     - \tCALLOC_ARRAY(s, 1);\n     - \ts->mode = FSMONITOR_MODE_DISABLED;\n     - \ts->reason = FSMONITOR_REASON_UNTESTED;\n     -+\ts->allow_remote = -1;\n     +-\treason = check_uds_volume(r);\n     +-\tif (reason != FSMONITOR_REASON_OK)\n     +-\t\treturn reason;\n     ++\tif (ipc) {\n     ++\t\treason = check_uds_volume(r);\n     ++\t\tif (reason != FSMONITOR_REASON_OK)\n     ++\t\t\treturn reason;\n     ++\t}\n       \n     - \treturn s;\n     + \treturn FSMONITOR_REASON_OK;\n       }\n     -@@ fsmonitor-settings.c: static void lookup_fsmonitor_settings(struct repository *r)\n     - \t\tfsm_settings__set_disabled(r);\n     +\n     + ## compat/fsmonitor/fsm-settings-win32.c ##\n     +@@ compat/fsmonitor/fsm-settings-win32.c: static enum fsmonitor_reason check_vfs4git(struct repository *r)\n     + \treturn FSMONITOR_REASON_OK;\n       }\n       \n     -+int fsm_settings__get_allow_remote(struct repository *r)\n     -+{\n     -+\tif (!r)\n     -+\t\tr = the_repository;\n     -+\tif (!r->settings.fsmonitor)\n     -+\t\tlookup_fsmonitor_settings(r);\n     -+\n     -+\treturn r->settings.fsmonitor->allow_remote;\n     -+}\n     -+\n     -+const char *fsm_settings__get_socket_dir(struct repository *r)\n     -+{\n     -+\tif (!r)\n     -+\t\tr = the_repository;\n     -+\tif (!r->settings.fsmonitor)\n     -+\t\tlookup_fsmonitor_settings(r);\n     -+\n     -+\treturn r->settings.fsmonitor->sock_dir;\n     -+}\n     -+\n     - enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n     +-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n     ++enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n       {\n     - \tif (!r)\n     -@@ fsmonitor-settings.c: enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n     - \treturn r->settings.fsmonitor->mode;\n     + \tenum fsmonitor_reason reason;\n     + \n     +\n     + ## fsmonitor-settings.c ##\n     +@@ fsmonitor-settings.c: static enum fsmonitor_reason check_remote(struct repository *r)\n       }\n     + #endif\n       \n     -+\n     - const char *fsm_settings__get_hook_path(struct repository *r)\n     +-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n     ++static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n       {\n     - \tif (!r)\n     + \tif (!r->worktree) {\n     + \t\t/*\n     +@@ fsmonitor-settings.c: static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n     + \t\treason = check_remote(r);\n     + \t\tif (reason != FSMONITOR_REASON_OK)\n     + \t\t\treturn reason;\n     +-\t\treason = fsm_os__incompatible(r);\n     ++\t\treason = fsm_os__incompatible(r, ipc);\n     + \t\tif (reason != FSMONITOR_REASON_OK)\n     + \t\t\treturn reason;\n     + \t}\n      @@ fsmonitor-settings.c: const char *fsm_settings__get_hook_path(struct repository *r)\n     - \treturn r->settings.fsmonitor->hook_path;\n     - }\n       \n     -+void fsm_settings__set_allow_remote(struct repository *r)\n     -+{\n     -+\tint allow;\n     -+\n     -+\tif (!r)\n     -+\t\tr = the_repository;\n     -+\tif (!r->settings.fsmonitor)\n     -+\t\tr->settings.fsmonitor = alloc_settings();\n     -+\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n     -+\t\tr->settings.fsmonitor->allow_remote = allow;\n     -+\n     -+\treturn;\n     -+}\n     -+\n     -+void fsm_settings__set_socket_dir(struct repository *r)\n     -+{\n     -+\tconst char *path;\n     -+\n     -+\tif (!r)\n     -+\t\tr = the_repository;\n     -+\tif (!r->settings.fsmonitor)\n     -+\t\tr->settings.fsmonitor = alloc_settings();\n     -+\n     -+\tif (!repo_config_get_pathname(r, \"fsmonitor.socketdir\", &path)) {\n     -+\t\tFREE_AND_NULL(r->settings.fsmonitor->sock_dir);\n     -+\t\tr->settings.fsmonitor->sock_dir = strdup(path);\n     -+\t}\n     -+\n     -+\treturn;\n     -+}\n     -+\n       void fsm_settings__set_ipc(struct repository *r)\n       {\n      -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n     -+\tenum fsmonitor_reason reason;\n     -+\n     -+\tfsm_settings__set_allow_remote(r);\n     -+\tfsm_settings__set_socket_dir(r);\n     -+\treason = check_for_incompatible(r);\n     ++\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n       \n       \tif (reason != FSMONITOR_REASON_OK) {\n       \t\tfsm_settings__set_incompatible(r, reason);\n     @@ fsmonitor-settings.c: void fsm_settings__set_ipc(struct repository *r)\n       void fsm_settings__set_hook(struct repository *r, const char *path)\n       {\n      -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n     -+\tenum fsmonitor_reason reason;\n     -+\n     -+\tfsm_settings__set_allow_remote(r);\n     -+\tfsm_settings__set_socket_dir(r);\n     -+\treason = check_for_incompatible(r);\n     ++\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n       \n       \tif (reason != FSMONITOR_REASON_OK) {\n       \t\tfsm_settings__set_incompatible(r, reason);\n      \n       ## fsmonitor-settings.h ##\n     -@@ fsmonitor-settings.h: enum fsmonitor_reason {\n     - \tFSMONITOR_REASON_NOSOCKETS, /* NTFS,FAT32 do not support Unix sockets */\n     - };\n     - \n     -+void fsm_settings__set_allow_remote(struct repository *r);\n     -+void fsm_settings__set_socket_dir(struct repository *r);\n     - void fsm_settings__set_ipc(struct repository *r);\n     - void fsm_settings__set_hook(struct repository *r, const char *path);\n     - void fsm_settings__set_disabled(struct repository *r);\n     - void fsm_settings__set_incompatible(struct repository *r,\n     - \t\t\t\t    enum fsmonitor_reason reason);\n     - \n     -+int fsm_settings__get_allow_remote(struct repository *r);\n     -+const char *fsm_settings__get_socket_dir(struct repository *r);\n     - enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n     - const char *fsm_settings__get_hook_path(struct repository *r);\n     +@@ fsmonitor-settings.h: struct fsmonitor_settings;\n     +  * fsm_os__* routines should considered private to fsm_settings__\n     +  * routines.\n     +  */\n     +-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n     ++enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n     + #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n       \n     + #endif /* FSMONITOR_SETTINGS_H */\n 3:  a3110f1e25a < -:  ----------- fsmonitor: ensure filesystem and unix socket filesystem are compatible\n 4:  56cabf3be3b ! 4:  b7d6cf44695 fsmonitor: normalize FSEvents event paths to the real path\n     @@ Metadata\n      Author: Eric DeCosta <edecosta@mathworks.com>\n      \n       ## Commit message ##\n     -    fsmonitor: normalize FSEvents event paths to the real path\n     +    fsmonitor: deal with synthetic firmlinks on macOS\n      \n     -    Consider the following network working directory that is mounted under\n     -    /System/Volumes/Data:\n     +    Starting with macOS 10.15 (Catalina), Apple introduced a new feature\n     +    called 'firmlinks' in order to separate the boot volume into two\n     +    volumes, one read-only and one writable but still present them to the\n     +    user as a single volume. Along with this change, Apple removed the\n     +    ability to create symlinks in the root directory and replaced them with\n     +    'synthetic firmlinks'. See 'man synthetic.conf'\n      \n     -    /network/working/directory\n     +    When FSEevents reports the path of changed files, if the path invloves\n     +    a synthetic firmlink, the path is reported from the point of the\n     +    synthetic firmlink and not the real path. For example:\n      \n     -    The git working directory path is:\n     +    Real path:\n     +    /System/Volumes/Data/network/working/directory/foo.txt\n      \n     -    /System/Volumes/Data/network/working/directory\n     +    Synthetic firmlink:\n     +    /network -> /System/Volumes/Data/network\n      \n     -    The paths reported by FSEvents always start with /network. fsmonitor\n     -    expects paths to be under the working directory; therefore it\n     -    fails to match /network/... and ignores the change.\n     +    FSEvents path:\n     +    /network/working/directory/foo.txt\n      \n     -    Change things such that if fsmonitor.allowRemote is true that the\n     -    paths reported via FSEevents are normalized to the real path.\n     +    This causes the FSEvents path to not match against the worktree\n     +    directory.\n     +\n     +    There are several ways in which synthetic firmlinks can be created:\n     +    they can be defined in /etc/synthetic.conf, the automounter can create\n     +    them, and there may be other means. Simply reading /etc/synthetic.conf\n     +    is insufficient. No matter what process creates synthetic firmlinks,\n     +    they all get created in the root directory.\n     +\n     +    Therefore, in order to deal with synthetic firmlinks, the root directory\n     +    is scanned and the first possible synthetic firmink that, when resolved,\n     +    is a prefix of the worktree is used to map FSEvents paths to worktree\n     +    paths.\n      \n          Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n      \n     + ## builtin/fsmonitor--daemon.c ##\n     +@@\n     + #include \"parse-options.h\"\n     + #include \"fsmonitor.h\"\n     + #include \"fsmonitor-ipc.h\"\n     ++#include \"fsmonitor-path-utils.h\"\n     + #include \"compat/fsmonitor/fsm-health.h\"\n     + #include \"compat/fsmonitor/fsm-listen.h\"\n     + #include \"fsmonitor--daemon.h\"\n     +@@ builtin/fsmonitor--daemon.c: static int fsmonitor_run_daemon(void)\n     + \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n     + \tstate.nr_paths_watching = 1;\n     + \n     ++\tstrbuf_init(&state.alias.alias, 0);\n     ++\tstrbuf_init(&state.alias.points_to, 0);\n     ++\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n     ++\t\terr = error(_(\"could not get worktree alias\"));\n     ++\t\tgoto done;\n     ++\t}\n     ++\n     + \t/*\n     + \t * We create and delete cookie files somewhere inside the .git\n     + \t * directory to help us keep sync with the file system.  If\n     +\n       ## compat/fsmonitor/fsm-listen-darwin.c ##\n      @@\n       #include \"fsmonitor.h\"\n       #include \"fsm-listen.h\"\n       #include \"fsmonitor--daemon.h\"\n     -+#include \"fsmonitor-settings.h\"\n     ++#include \"fsmonitor-path-utils.h\"\n       \n       struct fsm_listen_data\n       {\n     -@@ compat/fsmonitor/fsm-listen-darwin.c: static void my_add_path(struct fsmonitor_batch *batch, const char *path)\n     - \tfree(composed);\n     - }\n     - \n     --\n     - static void fsevent_callback(ConstFSEventStreamRef streamRef,\n     - \t\t\t     void *ctx,\n     - \t\t\t     size_t num_of_events,\n      @@ compat/fsmonitor/fsm-listen-darwin.c: static void fsevent_callback(ConstFSEventStreamRef streamRef,\n       \t\t/*\n       \t\t * On Mac, we receive an array of absolute paths.\n       \t\t */\n      -\t\tpath_k = paths[k];\n     -+\t\tif (fsm_settings__get_allow_remote(the_repository) > 0) {\n     -+\t\t\tstrbuf_reset(&tmp);\n     -+\t\t\tstrbuf_realpath_forgiving(&tmp, paths[k], 0);\n     -+\t\t\tpath_k = tmp.buf;\n     -+\t\t} else\n     ++\t\tpath_k = fsmonitor__resolve_alias(paths[k], &state->alias);\n     ++\t\tif (!path_k)\n      +\t\t\tpath_k = paths[k];\n       \n       \t\t/*\n       \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n      @@ compat/fsmonitor/fsm-listen-darwin.c: static void fsevent_callback(ConstFSEventStreamRef streamRef,\n     - \n       \t\t\tfsmonitor_force_resync(state);\n       \t\t\tfsmonitor_batch__free_list(batch);\n     -+\t\t\tbatch = NULL;\n       \t\t\tstring_list_clear(&cookie_list, 0);\n     ++\t\t\tbatch = NULL;\n       \n       \t\t\t/*\n     -@@ compat/fsmonitor/fsm-listen-darwin.c: static void fsevent_callback(ConstFSEventStreamRef streamRef,\n     + \t\t\t * We assume that any events that we received\n     +\n     + ## compat/fsmonitor/fsm-path-utils-darwin.c ##\n     +@@\n     + #include \"fsmonitor.h\"\n     + #include \"fsmonitor-path-utils.h\"\n     ++#include <dirent.h>\n     ++#include <errno.h>\n     ++#include <fcntl.h>\n     + #include <sys/param.h>\n     + #include <sys/mount.h>\n     + \n     +@@ compat/fsmonitor/fsm-path-utils-darwin.c: int fsmonitor__is_fs_remote(const char *path)\n     + \t\treturn -1;\n     + \treturn fs.is_remote;\n     + }\n     ++\n     ++/*\n     ++ * Scan the root directory for synthetic firmlinks that when resolved\n     ++ * are a prefix of the path, stopping at the first one found.\n     ++ *\n     ++ * Some information about firmlinks and synthetic firmlinks:\n     ++ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n     ++ *\n     ++ * macOS no longer allows symlinks in the root directory; any link found\n     ++ * there is therefore a synthetic firmlink.\n     ++ *\n     ++ * If this function gets called often, will want to cache all the firmlink\n     ++ * information, but for now there is only one caller of this function.\n     ++ *\n     ++ * If there is more than one alias for the path, that is another\n     ++ * matter altogteher.\n     ++ */\n     ++int fsmonitor__get_alias(const char *path, struct alias_info *info)\n     ++{\n     ++\tDIR * dir;\n     ++\tint read;\n     ++\tint retval;\n     ++\tstruct dirent *de;\n     ++\tstruct strbuf alias;\n     ++\tstruct strbuf points_to;\n     ++\n     ++\tretval = 0;\n     ++\tdir = opendir(\"/\");\n     ++\tif (!dir)\n     ++\t\treturn -1;\n     ++\n     ++\tstrbuf_init(&alias, 256);\n     ++\tstrbuf_init(&points_to, MAXPATHLEN);\n     ++\n     ++\twhile ((de = readdir(dir)) != NULL) {\n     ++\t\tstrbuf_reset(&alias);\n     ++\t\tstrbuf_addch(&alias, '/');\n     ++\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n     ++\n     ++\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n     ++\t\tif (read > 0) {\n     ++\t\t\tstrbuf_setlen(&points_to, read);\n     ++\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n     ++\t\t\t\t&& path[points_to.len] == '/') {\n     ++\t\t\t\tstrbuf_addbuf(&info->alias, &alias);\n     ++\t\t\t\tstrbuf_addbuf(&info->points_to, &points_to);\n     ++\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n     ++\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n     ++\t\t\t\t\tpath, info->alias.buf, info->points_to.buf);\n     ++\t\t\t\tretval = 0;\n     ++\t\t\t\tgoto done;\n     ++\t\t\t}\n     ++\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n     ++\t\t\ttrace_printf_key(&trace_fsmonitor, \"Error %s\", strerror(errno));\n     ++\t\t\tretval = -1;\n     ++\t\t\tgoto done;\n     ++\t\t}\n     ++\t}\n     ++\n     ++\tdone:\n     ++\tclosedir(dir);\n     ++\tstrbuf_release(&alias);\n     ++\tstrbuf_release(&points_to);\n     ++\treturn retval;\n     ++}\n     ++\n     ++char *fsmonitor__resolve_alias(const char *path,\n     ++\tconst struct alias_info *info)\n     ++{\n     ++\tint len = info->alias.len;\n     ++\n     ++\tif (!len)\n     ++\t\treturn NULL;\n     ++\n     ++\tif ((strncmp(info->alias.buf, path, len) == 0)\n     ++\t\t&& path[len] == '/') {\n     ++\t\tstruct strbuf tmp;\n     ++\t\tconst char *remainder = path + len;\n     ++\t\tint rem_len = strlen(remainder);\n     ++\n     ++\t\tstrbuf_init(&tmp, info->points_to.len + rem_len);\n     ++\t\tstrbuf_addbuf(&tmp, &info->points_to);\n     ++\t\tstrbuf_add(&tmp, remainder, rem_len);\n     ++\t\treturn strbuf_detach(&tmp, NULL);\n     ++\t}\n     ++\n     ++\treturn NULL;\n     ++}\n     +\n     + ## compat/fsmonitor/fsm-path-utils-win32.c ##\n     +@@ compat/fsmonitor/fsm-path-utils-win32.c: int fsmonitor__is_fs_remote(const char *path)\n     + \t\treturn -1;\n     + \treturn fs.is_remote;\n     + }\n     ++\n     ++/*\n     ++ * No-op for now.\n     ++ */\n     ++int fsmonitor__get_alias(const char *path, struct alias_info *info)\n     ++{\n     ++\treturn 0;\n     ++}\n     ++\n     ++/*\n     ++ * No-op for now.\n     ++ */\n     ++char *fsmonitor__resolve_alias(const char *path,\n     ++\tconst struct alias_info *info)\n     ++{\n     ++\treturn NULL;\n     ++}\n     +\n     + ## fsmonitor--daemon.h ##\n     +@@\n     + #include \"run-command.h\"\n     + #include \"simple-ipc.h\"\n     + #include \"thread-utils.h\"\n     ++#include \"fsmonitor-path-utils.h\"\n     + \n     + struct fsmonitor_batch;\n     + struct fsmonitor_token_data;\n     +@@ fsmonitor--daemon.h: struct fsmonitor_daemon_state {\n     + \n     + \tstruct strbuf path_worktree_watch;\n     + \tstruct strbuf path_gitdir_watch;\n     ++\tstruct alias_info alias;\n     + \tint nr_paths_watching;\n     + \n     + \tstruct fsmonitor_token_data *current_token_data;\n     +@@ fsmonitor--daemon.h: struct fsmonitor_daemon_state {\n     + \n     + \tstruct ipc_server_data *ipc_server_data;\n     + \tstruct strbuf path_ipc;\n     ++\n     + };\n     + \n     + /*\n     +@@ fsmonitor--daemon.h: void fsmonitor_publish(struct fsmonitor_daemon_state *state,\n     +  */\n     + void fsmonitor_force_resync(struct fsmonitor_daemon_state *state);\n     + \n     ++char *fsmonitor_resolve_alias(const char *path,\n     ++\tstruct alias_info *alias);\n     ++\n     + #endif /* HAVE_FSMONITOR_DAEMON_BACKEND */\n     + #endif /* FSMONITOR_DAEMON_H */\n     +\n     + ## fsmonitor-path-utils.h ##\n     +@@\n     + #ifndef FSM_PATH_UTILS_H\n     + #define FSM_PATH_UTILS_H\n       \n     - \t\tcase IS_WORKDIR_PATH:\n     - \t\t\t/* try to queue normal pathnames */\n     --\n     - \t\t\tif (trace_pass_fl(&trace_fsmonitor))\n     - \t\t\t\tlog_flags_set(path_k, event_flags[k]);\n     ++#include \"strbuf.h\"\n     ++\n     ++struct alias_info\n     ++{\n     ++\tstruct strbuf alias;\n     ++\tstruct strbuf points_to;\n     ++};\n     ++\n     + struct fs_info {\n     + \tint is_remote;\n     + \tchar *typename;\n     +@@ fsmonitor-path-utils.h: int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n     +  */\n     + int fsmonitor__is_fs_remote(const char *path);\n       \n     ++/*\n     ++ * Get the alias in given path, if any.\n     ++ *\n     ++ * Sets alias to the first alias that matches any part of the path.\n     ++ *\n     ++ * Returns -1 on error, 0 otherwise.\n     ++ */\n     ++int fsmonitor__get_alias(const char *path, struct alias_info *info);\n     ++\n     ++/*\n     ++ * Resolve the path against the given alias.\n     ++ *\n     ++ * Returns the resolved path if there is one, NULL otherwise.\n     ++ */\n     ++char *fsmonitor__resolve_alias(const char *path,\n     ++\tconst struct alias_info *info);\n     ++\n     ++\n     + #endif\n\n-- \ngitgitgadget\n"},{"id":"462898","messageId":"28d08bcf80810f0453c9c9ff03d45dd0e5795961.1662840031.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v5.git.1662840031.gitgitgadget@gmail.com","subject":"[PATCH v5 2/4] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-10T20:00:29Z","receivedAt":"2022-09-10T20:02:36Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf the .git directory is on a remote file system, create the socket\nfile in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                            |  1 +\n compat/fsmonitor/fsm-ipc-darwin.c   | 46 +++++++++++++++++++++++++++++\n compat/fsmonitor/fsm-ipc-win32.c    |  4 +++\n contrib/buildsystems/CMakeLists.txt |  2 ++\n fsmonitor-ipc.c                     |  2 --\n 5 files changed, 53 insertions(+), 2 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n\ndiff --git a/Makefile b/Makefile\nindex 265fc585286..cf9c51040a0 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2037,6 +2037,7 @@ ifdef FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n endif\n \n ifdef FSMONITOR_OS_SETTINGS\ndiff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\nnew file mode 100644\nindex 00000000000..afaca96dab9\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-darwin.c\n@@ -0,0 +1,46 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"strbuf.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(void)\n+{\n+\tstatic const char *ipc_path;\n+\tSHA_CTX sha1ctx;\n+\tchar *sock_dir;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (ipc_path)\n+\t\treturn ipc_path;\n+\n+\tipc_path = fsmonitor_ipc__get_default_path();\n+\n+\t/* By default the socket file is created in the .git directory */\n+\tif (fsmonitor__is_fs_remote(ipc_path) < 1)\n+\t\treturn ipc_path;\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, the_repository->worktree, strlen(the_repository->worktree));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\trepo_config_get_string(the_repository, \"fsmonitor.socketdir\", &sock_dir);\n+\n+\t/* Create the socket file in either socketDir or $HOME */\n+\tif (sock_dir && *sock_dir)\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\telse\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\n+\tipc_path = interpolate_path(ipc_file.buf, 1);\n+\tif (!ipc_path)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\n+\tstrbuf_release(&ipc_file);\n+\treturn ipc_path;\n+}\ndiff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\nnew file mode 100644\nindex 00000000000..769a88639f6\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-win32.c\n@@ -0,0 +1,4 @@\n+#include \"cache.h\"\n+#include \"fsmonitor-ipc.h\"\n+\n+GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex b88494bf59b..7e7b6b9a362 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n@@ -316,6 +317,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..caad2e246a0 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -47,8 +47,6 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n-\n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\n \treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n-- \ngitgitgadget\n\n"},{"id":"462899","messageId":"ff54b6e0bb5347a0401a184a93e25c2550291de7.1662840031.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v5.git.1662840031.gitgitgadget@gmail.com","subject":"[PATCH v5 3/4] fsmonitor: avoid socket location check if using hook","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-10T20:00:30Z","receivedAt":"2022-09-10T20:02:37Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf monitoring is done via fsmonitor hook rather than IPC there is no\nneed to check if the location of the Unix Domain socket (UDS) file is\non a remote filesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 10 ++++++----\n compat/fsmonitor/fsm-settings-win32.c  |  2 +-\n fsmonitor-settings.c                   |  8 ++++----\n fsmonitor-settings.h                   |  2 +-\n 4 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex dba3ced6bb7..3463c71763e 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -49,13 +49,15 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_uds_volume(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n+\tif (ipc) {\n+\t\treason = check_uds_volume(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n+\t}\n \n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex d88b06ae610..a8af31b71de 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex d288cbad479..531a1b6f956 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -60,7 +60,7 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n }\n #endif\n \n-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n+static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n {\n \tif (!r->worktree) {\n \t\t/*\n@@ -77,7 +77,7 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t\treason = check_remote(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-\t\treason = fsm_os__incompatible(r);\n+\t\treason = fsm_os__incompatible(r, ipc);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n \t}\n@@ -162,7 +162,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -185,7 +185,7 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..0721617b95a 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -48,7 +48,7 @@ struct fsmonitor_settings;\n  * fsm_os__* routines should considered private to fsm_settings__\n  * routines.\n  */\n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n \n #endif /* FSMONITOR_SETTINGS_H */\n-- \ngitgitgadget\n\n"},{"id":"462900","messageId":"b7d6cf44695c7b6285573f454ebfd3866fe49f38.1662840031.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v5.git.1662840031.gitgitgadget@gmail.com","subject":"[PATCH v5 4/4] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-10T20:00:31Z","receivedAt":"2022-09-10T20:02:50Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path invloves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n builtin/fsmonitor--daemon.c              |  8 +++\n compat/fsmonitor/fsm-listen-darwin.c     |  6 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 91 ++++++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 17 +++++\n fsmonitor--daemon.h                      |  6 ++\n fsmonitor-path-utils.h                   | 26 +++++++\n 6 files changed, 153 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 2c109cf8b37..2559150d9e7 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"fsmonitor.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n #include \"compat/fsmonitor/fsm-health.h\"\n #include \"compat/fsmonitor/fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n@@ -1282,6 +1283,13 @@ static int fsmonitor_run_daemon(void)\n \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n \tstate.nr_paths_watching = 1;\n \n+\tstrbuf_init(&state.alias.alias, 0);\n+\tstrbuf_init(&state.alias.points_to, 0);\n+\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n+\t\terr = error(_(\"could not get worktree alias\"));\n+\t\tgoto done;\n+\t}\n+\n \t/*\n \t * We create and delete cookie files somewhere inside the .git\n \t * directory to help us keep sync with the file system.  If\ndiff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\nindex 8e208e8289e..179886bc15b 100644\n--- a/compat/fsmonitor/fsm-listen-darwin.c\n+++ b/compat/fsmonitor/fsm-listen-darwin.c\n@@ -26,6 +26,7 @@\n #include \"fsmonitor.h\"\n #include \"fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsm_listen_data\n {\n@@ -209,7 +210,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t/*\n \t\t * On Mac, we receive an array of absolute paths.\n \t\t */\n-\t\tpath_k = paths[k];\n+\t\tpath_k = fsmonitor__resolve_alias(paths[k], &state->alias);\n+\t\tif (!path_k)\n+\t\t\tpath_k = paths[k];\n \n \t\t/*\n \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n@@ -238,6 +241,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t\tfsmonitor_force_resync(state);\n \t\t\tfsmonitor_batch__free_list(batch);\n \t\t\tstring_list_clear(&cookie_list, 0);\n+\t\t\tbatch = NULL;\n \n \t\t\t/*\n \t\t\t * We assume that any events that we received\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nindex 067cbe6990a..1f1550c485a 100644\n--- a/compat/fsmonitor/fsm-path-utils-darwin.c\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -1,5 +1,8 @@\n #include \"fsmonitor.h\"\n #include \"fsmonitor-path-utils.h\"\n+#include <dirent.h>\n+#include <errno.h>\n+#include <fcntl.h>\n #include <sys/param.h>\n #include <sys/mount.h>\n \n@@ -38,3 +41,91 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * Scan the root directory for synthetic firmlinks that when resolved\n+ * are a prefix of the path, stopping at the first one found.\n+ *\n+ * Some information about firmlinks and synthetic firmlinks:\n+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n+ *\n+ * macOS no longer allows symlinks in the root directory; any link found\n+ * there is therefore a synthetic firmlink.\n+ *\n+ * If this function gets called often, will want to cache all the firmlink\n+ * information, but for now there is only one caller of this function.\n+ *\n+ * If there is more than one alias for the path, that is another\n+ * matter altogteher.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\tDIR * dir;\n+\tint read;\n+\tint retval;\n+\tstruct dirent *de;\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+\n+\tretval = 0;\n+\tdir = opendir(\"/\");\n+\tif (!dir)\n+\t\treturn -1;\n+\n+\tstrbuf_init(&alias, 256);\n+\tstrbuf_init(&points_to, MAXPATHLEN);\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstrbuf_reset(&alias);\n+\t\tstrbuf_addch(&alias, '/');\n+\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n+\n+\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n+\t\tif (read > 0) {\n+\t\t\tstrbuf_setlen(&points_to, read);\n+\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n+\t\t\t\t&& path[points_to.len] == '/') {\n+\t\t\t\tstrbuf_addbuf(&info->alias, &alias);\n+\t\t\t\tstrbuf_addbuf(&info->points_to, &points_to);\n+\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n+\t\t\t\t\tpath, info->alias.buf, info->points_to.buf);\n+\t\t\t\tretval = 0;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n+\t\t\ttrace_printf_key(&trace_fsmonitor, \"Error %s\", strerror(errno));\n+\t\t\tretval = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n+\tdone:\n+\tclosedir(dir);\n+\tstrbuf_release(&alias);\n+\tstrbuf_release(&points_to);\n+\treturn retval;\n+}\n+\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\tint len = info->alias.len;\n+\n+\tif (!len)\n+\t\treturn NULL;\n+\n+\tif ((strncmp(info->alias.buf, path, len) == 0)\n+\t\t&& path[len] == '/') {\n+\t\tstruct strbuf tmp;\n+\t\tconst char *remainder = path + len;\n+\t\tint rem_len = strlen(remainder);\n+\n+\t\tstrbuf_init(&tmp, info->points_to.len + rem_len);\n+\t\tstrbuf_addbuf(&tmp, &info->points_to);\n+\t\tstrbuf_add(&tmp, remainder, rem_len);\n+\t\treturn strbuf_detach(&tmp, NULL);\n+\t}\n+\n+\treturn NULL;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nindex dc211de514f..0ca366bfe08 100644\n--- a/compat/fsmonitor/fsm-path-utils-win32.c\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -87,3 +87,20 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * No-op for now.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\treturn 0;\n+}\n+\n+/*\n+ * No-op for now.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\treturn NULL;\n+}\ndiff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\nindex 2102a5c9ff5..98cbb430083 100644\n--- a/fsmonitor--daemon.h\n+++ b/fsmonitor--daemon.h\n@@ -8,6 +8,7 @@\n #include \"run-command.h\"\n #include \"simple-ipc.h\"\n #include \"thread-utils.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsmonitor_batch;\n struct fsmonitor_token_data;\n@@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct strbuf path_worktree_watch;\n \tstruct strbuf path_gitdir_watch;\n+\tstruct alias_info alias;\n \tint nr_paths_watching;\n \n \tstruct fsmonitor_token_data *current_token_data;\n@@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct ipc_server_data *ipc_server_data;\n \tstruct strbuf path_ipc;\n+\n };\n \n /*\n@@ -167,5 +170,8 @@ void fsmonitor_publish(struct fsmonitor_daemon_state *state,\n  */\n void fsmonitor_force_resync(struct fsmonitor_daemon_state *state);\n \n+char *fsmonitor_resolve_alias(const char *path,\n+\tstruct alias_info *alias);\n+\n #endif /* HAVE_FSMONITOR_DAEMON_BACKEND */\n #endif /* FSMONITOR_DAEMON_H */\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nindex e48592887e7..7d9d71cb043 100644\n--- a/fsmonitor-path-utils.h\n+++ b/fsmonitor-path-utils.h\n@@ -1,6 +1,14 @@\n #ifndef FSM_PATH_UTILS_H\n #define FSM_PATH_UTILS_H\n \n+#include \"strbuf.h\"\n+\n+struct alias_info\n+{\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+};\n+\n struct fs_info {\n \tint is_remote;\n \tchar *typename;\n@@ -20,4 +28,22 @@ int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n  */\n int fsmonitor__is_fs_remote(const char *path);\n \n+/*\n+ * Get the alias in given path, if any.\n+ *\n+ * Sets alias to the first alias that matches any part of the path.\n+ *\n+ * Returns -1 on error, 0 otherwise.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info);\n+\n+/*\n+ * Resolve the path against the given alias.\n+ *\n+ * Returns the resolved path if there is one, NULL otherwise.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info);\n+\n+\n #endif\n-- \ngitgitgadget\n"},{"id":"462901","messageId":"CAPig+cT0mKAX86cKPaJ44tH1StH+GnOEHuJEu=G4w2fOKkEgpQ@mail.gmail.com","threadId":"58324","inReplyTo":"b7d6cf44695c7b6285573f454ebfd3866fe49f38.1662840031.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 4/4] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-09-11T01:01:41Z","receivedAt":"2022-09-11T01:02:01Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Sep 10, 2022 at 4:00 PM Eric DeCosta via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> Starting with macOS 10.15 (Catalina), Apple introduced a new feature\n> called 'firmlinks' in order to separate the boot volume into two\n> volumes, one read-only and one writable but still present them to the\n> user as a single volume. Along with this change, Apple removed the\n> ability to create symlinks in the root directory and replaced them with\n> 'synthetic firmlinks'. See 'man synthetic.conf'\n>\n> When FSEevents reports the path of changed files, if the path invloves\n> a synthetic firmlink, the path is reported from the point of the\n> synthetic firmlink and not the real path. For example:\n\ns/invloves/involves/\n\n> Real path:\n> /System/Volumes/Data/network/working/directory/foo.txt\n>\n> Synthetic firmlink:\n> /network -> /System/Volumes/Data/network\n>\n> FSEvents path:\n> /network/working/directory/foo.txt\n>\n> This causes the FSEvents path to not match against the worktree\n> directory.\n>\n> There are several ways in which synthetic firmlinks can be created:\n> they can be defined in /etc/synthetic.conf, the automounter can create\n> them, and there may be other means. Simply reading /etc/synthetic.conf\n> is insufficient. No matter what process creates synthetic firmlinks,\n> they all get created in the root directory.\n>\n> Therefore, in order to deal with synthetic firmlinks, the root directory\n> is scanned and the first possible synthetic firmink that, when resolved,\n> is a prefix of the worktree is used to map FSEvents paths to worktree\n> paths.\n>\n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n> diff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\n> @@ -38,3 +41,91 @@ int fsmonitor__is_fs_remote(const char *path)\n> +/*\n> + * Scan the root directory for synthetic firmlinks that when resolved\n> + * are a prefix of the path, stopping at the first one found.\n> + *\n> + * Some information about firmlinks and synthetic firmlinks:\n> + * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n> + *\n> + * macOS no longer allows symlinks in the root directory; any link found\n> + * there is therefore a synthetic firmlink.\n> + *\n> + * If this function gets called often, will want to cache all the firmlink\n> + * information, but for now there is only one caller of this function.\n> + *\n> + * If there is more than one alias for the path, that is another\n> + * matter altogteher.\n> + */\n\ns/altogteher/altogether/\n\n> diff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\n> @@ -1,6 +1,14 @@\n> +struct alias_info\n> +{\n> +       struct strbuf alias;\n> +       struct strbuf points_to;\n> +};\n> +/*\n> + * Get the alias in given path, if any.\n> + *\n> + * Sets alias to the first alias that matches any part of the path.\n> + *\n> + * Returns -1 on error, 0 otherwise.\n> + */\n> +int fsmonitor__get_alias(const char *path, struct alias_info *info);\n\nI suppose it's somewhat clear here that the caller is responsible for\nreleasing the strbufs in alias_info (though it's not clear why they\nneed to be strbufs in the first place)...\n\n> +/*\n> + * Resolve the path against the given alias.\n> + *\n> + * Returns the resolved path if there is one, NULL otherwise.\n> + */\n> +char *fsmonitor__resolve_alias(const char *path,\n> +       const struct alias_info *info);\n\nBut what about the return value from this function? It's `char*` which\nsuggests a possible ownership transfer(?). Is the caller responsible\nfor freeing it? If so, perhaps the documentation could state that.\n"},{"id":"462933","messageId":"xmqqedwghauv.fsf@gitster.g","threadId":"58324","inReplyTo":"pull.1326.v5.git.1662840031.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 0/4] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-12T15:27:04Z","receivedAt":"2022-09-12T15:27:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> Follow-on to the work done to allow Windows to work against network-mounted\n> repos for macOS.\n>\n> Have macOS take advantage of the same configuration option,\n> 'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\n> to true will override the default behavior (erroring-out) when a\n> network-mounted repo is detected by fsmonitor.\n>\n> The added wrinkle being that the Unix domain socket (UDS) file used for IPC\n> cannot be created in a network location; instead $HOME is used if the\n> default location is on the network. The user may, optionally, set the\n> 'fsmonitor.socketDir' configuration option to a valid, local directory if\n> $HOME itself is on the network or is simply not the desired location for the\n> UDS file.\n>\n> An additional issue is that for mount points in the root directory, FSEvents\n> does not report a path that matches the worktree directory due to the\n> introduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\n> to the worktree directory by interrogating the root filesystem for synthetic\n> firmlinks and using that information to translate the path.\n>\n> Eric DeCosta (4):\n>   fsmonitor: refactor filesystem checks to common interface\n>   fsmonitor: relocate socket file if .git directory is remote\n>   fsmonitor: avoid socket location check if using hook\n>   fsmonitor: deal with synthetic firmlinks on macOS\n\nIt looks like this one is organized quite differently from the last\niteration <pull.1326.v4.git.1661962145.gitgitgadget@gmail.com>\n\nUnless the changes since the last iteration is so obvious [*] that\nrange-diff will explain all of it, it would help the topic to make\nit easier for reviewers what has changed relative to the previous\none in your own words here in the cover letter.\n\n[*} Here, \"obvious\" does not mean \"diff is small and can be read in\n10 minutes\".  It is \"diff shows the reason why these changes are\nmade, so there is no need for additional explanation\", e.g. typofixes,\nrenaming of variables, making a helper function that was added as\nextern in the previous iteration to static, etc.\n\nThanks.\n\n>  Makefile                                 |   2 +\n>  builtin/fsmonitor--daemon.c              |   8 ++\n>  compat/fsmonitor/fsm-ipc-darwin.c        |  46 ++++++++\n>  compat/fsmonitor/fsm-ipc-win32.c         |   4 +\n>  compat/fsmonitor/fsm-listen-darwin.c     |   6 +-\n>  compat/fsmonitor/fsm-path-utils-darwin.c | 131 +++++++++++++++++++++++\n>  compat/fsmonitor/fsm-path-utils-win32.c  | 106 ++++++++++++++++++\n>  compat/fsmonitor/fsm-settings-darwin.c   |  70 ++++--------\n>  compat/fsmonitor/fsm-settings-win32.c    | 106 +-----------------\n>  contrib/buildsystems/CMakeLists.txt      |   4 +\n>  fsmonitor--daemon.h                      |   6 ++\n>  fsmonitor-ipc.c                          |   2 -\n>  fsmonitor-path-utils.h                   |  49 +++++++++\n>  fsmonitor-settings.c                     |  58 +++++++++-\n>  fsmonitor-settings.h                     |   2 +-\n>  15 files changed, 441 insertions(+), 159 deletions(-)\n>  create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n>  create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n>  create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n>  create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n>  create mode 100644 fsmonitor-path-utils.h\n>\n>\n> base-commit: be1a02a17ede4082a86dfbfee0f54f345e8b43ac\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v5\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v5\n> Pull-Request: https://github.com/gitgitgadget/git/pull/1326\n>\n> Range-diff vs v4:\n>\n>  -:  ----------- > 1:  2f647b53e4d fsmonitor: refactor filesystem checks to common interface\n>  2:  2cb026a6317 ! 2:  28d08bcf808 fsmonitor: generate unique Unix socket file name in the desired location\n>      @@ Metadata\n>       Author: Eric DeCosta <edecosta@mathworks.com>\n>       \n>        ## Commit message ##\n>      -    fsmonitor: generate unique Unix socket file name in the desired location\n>      +    fsmonitor: relocate socket file if .git directory is remote\n>       \n>      -    Based on the values of fsmonitor.allowRemote and fsmonitor.socketDir\n>      -    locate the Unix domain socket file in the desired location (either\n>      -    the .git directory, $HOME, or fsmonitor.socketDir). If the location\n>      -    is other than the .git directory, generate a unique file name based\n>      -    on the SHA1 has of the path to the .git directory.\n>      +    If the .git directory is on a remote file system, create the socket\n>      +    file in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n>       \n>           Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n>       \n>      - ## fsmonitor-ipc.c ##\n>      -@@\n>      - #include \"cache.h\"\n>      --#include \"fsmonitor.h\"\n>      --#include \"simple-ipc.h\"\n>      - #include \"fsmonitor-ipc.h\"\n>      -+#include \"fsmonitor-settings.h\"\n>      - #include \"run-command.h\"\n>      - #include \"strbuf.h\"\n>      - #include \"trace2.h\"\n>      -@@ fsmonitor-ipc.c: int fsmonitor_ipc__is_supported(void)\n>      - \treturn 1;\n>      - }\n>      + ## Makefile ##\n>      +@@ Makefile: ifdef FSMONITOR_DAEMON_BACKEND\n>      + \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n>      + \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n>      + \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n>      ++\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n>      + endif\n>        \n>      --GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n>      -+GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n>      + ifdef FSMONITOR_OS_SETTINGS\n>      +\n>      + ## compat/fsmonitor/fsm-ipc-darwin.c (new) ##\n>      +@@\n>      ++#include \"cache.h\"\n>      ++#include \"config.h\"\n>      ++#include \"strbuf.h\"\n>      ++#include \"fsmonitor.h\"\n>      ++#include \"fsmonitor-ipc.h\"\n>      ++#include \"fsmonitor-path-utils.h\"\n>      ++\n>      ++static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n>       +\n>       +const char *fsmonitor_ipc__get_path(void)\n>       +{\n>      -+#ifdef WIN32\n>      -+\treturn fsmonitor_ipc__get_default_path();\n>      -+#else\n>      -+\tchar *retval;\n>      ++\tstatic const char *ipc_path;\n>       +\tSHA_CTX sha1ctx;\n>      -+\tconst char *git_dir;\n>      -+\tconst char *sock_dir;\n>      ++\tchar *sock_dir;\n>       +\tstruct strbuf ipc_file = STRBUF_INIT;\n>       +\tunsigned char hash[SHA_DIGEST_LENGTH];\n>       +\n>      -+\tif (fsm_settings__get_allow_remote(the_repository) < 1)\n>      -+\t\treturn fsmonitor_ipc__get_default_path();\n>      ++\tif (ipc_path)\n>      ++\t\treturn ipc_path;\n>       +\n>      -+\tgit_dir = get_git_dir();\n>      -+\tsock_dir = fsm_settings__get_socket_dir(the_repository);\n>      ++\tipc_path = fsmonitor_ipc__get_default_path();\n>      ++\n>      ++\t/* By default the socket file is created in the .git directory */\n>      ++\tif (fsmonitor__is_fs_remote(ipc_path) < 1)\n>      ++\t\treturn ipc_path;\n>       +\n>       +\tSHA1_Init(&sha1ctx);\n>      -+\tSHA1_Update(&sha1ctx, git_dir, strlen(git_dir));\n>      ++\tSHA1_Update(&sha1ctx, the_repository->worktree, strlen(the_repository->worktree));\n>       +\tSHA1_Final(hash, &sha1ctx);\n>       +\n>      ++\trepo_config_get_string(the_repository, \"fsmonitor.socketdir\", &sock_dir);\n>      ++\n>      ++\t/* Create the socket file in either socketDir or $HOME */\n>       +\tif (sock_dir && *sock_dir)\n>       +\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n>       +\t\t\t\t\tsock_dir, hash_to_hex(hash));\n>       +\telse\n>       +\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n>      -+\tretval = interpolate_path(ipc_file.buf, 1);\n>      -+\tif (!retval)\n>      ++\n>      ++\tipc_path = interpolate_path(ipc_file.buf, 1);\n>      ++\tif (!ipc_path)\n>       +\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n>      ++\n>       +\tstrbuf_release(&ipc_file);\n>      -+\treturn retval;\n>      -+#endif\n>      ++\treturn ipc_path;\n>       +}\n>      +\n>      + ## compat/fsmonitor/fsm-ipc-win32.c (new) ##\n>      +@@\n>      ++#include \"cache.h\"\n>      ++#include \"fsmonitor-ipc.h\"\n>      ++\n>      ++GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n>      +\n>      + ## contrib/buildsystems/CMakeLists.txt ##\n>      +@@ contrib/buildsystems/CMakeLists.txt: if(SUPPORTS_SIMPLE_IPC)\n>      + \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n>      + \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n>      + \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n>      ++\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n>      + \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n>        \n>      - enum ipc_active_state fsmonitor_ipc__get_state(void)\n>      - {\n>      + \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n>      +@@ contrib/buildsystems/CMakeLists.txt: if(SUPPORTS_SIMPLE_IPC)\n>      + \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n>      + \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n>      + \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n>      ++\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n>      + \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n>      + \n>      + \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n>       \n>      - ## fsmonitor-ipc.h ##\n>      -@@ fsmonitor-ipc.h: int fsmonitor_ipc__is_supported(void);\n>      -  */\n>      - const char *fsmonitor_ipc__get_path(void);\n>      + ## fsmonitor-ipc.c ##\n>      +@@ fsmonitor-ipc.c: int fsmonitor_ipc__is_supported(void)\n>      + \treturn 1;\n>      + }\n>        \n>      -+/*\n>      -+ * Returns the pathname to the default IPC named pipe or Unix domain\n>      -+ * socket.\n>      -+ */\n>      -+const char *fsmonitor_ipc__get_default_path(void);\n>      -+\n>      - /*\n>      -  * Try to determine whether there is a `git-fsmonitor--daemon` process\n>      -  * listening on the IPC pipe/socket.\n>      +-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n>      +-\n>      + enum ipc_active_state fsmonitor_ipc__get_state(void)\n>      + {\n>      + \treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n>  1:  836a791e6b7 ! 3:  ff54b6e0bb5 fsmonitor: add two new config options, allowRemote and socketDir\n>      @@ Metadata\n>       Author: Eric DeCosta <edecosta@mathworks.com>\n>       \n>        ## Commit message ##\n>      -    fsmonitor: add two new config options, allowRemote and socketDir\n>      +    fsmonitor: avoid socket location check if using hook\n>       \n>      -    Introduce two new configuration options\n>      -\n>      -       fsmonitor.allowRemote - setting this to true overrides fsmonitor's\n>      -       default behavior of erroring out when enountering network file\n>      -       systems. Additionly, when true, the Unix domain socket (UDS) file\n>      -       used for IPC is located in $HOME rather than in the .git directory.\n>      -\n>      -       fsmonitor.socketDir - allows for the UDS file to be located\n>      -       anywhere the user chooses rather $HOME.\n>      +    If monitoring is done via fsmonitor hook rather than IPC there is no\n>      +    need to check if the location of the Unix Domain socket (UDS) file is\n>      +    on a remote filesystem.\n>       \n>           Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n>       \n>      - ## fsmonitor-settings.c ##\n>      -@@\n>      - struct fsmonitor_settings {\n>      - \tenum fsmonitor_mode mode;\n>      + ## compat/fsmonitor/fsm-settings-darwin.c ##\n>      +@@ compat/fsmonitor/fsm-settings-darwin.c: static enum fsmonitor_reason check_uds_volume(struct repository *r)\n>      + \treturn FSMONITOR_REASON_OK;\n>      + }\n>      + \n>      +-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n>      ++enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n>      + {\n>        \tenum fsmonitor_reason reason;\n>      -+\tint allow_remote;\n>      - \tchar *hook_path;\n>      -+\tchar *sock_dir;\n>      - };\n>        \n>      - static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n>      -@@ fsmonitor-settings.c: static struct fsmonitor_settings *alloc_settings(void)\n>      - \tCALLOC_ARRAY(s, 1);\n>      - \ts->mode = FSMONITOR_MODE_DISABLED;\n>      - \ts->reason = FSMONITOR_REASON_UNTESTED;\n>      -+\ts->allow_remote = -1;\n>      +-\treason = check_uds_volume(r);\n>      +-\tif (reason != FSMONITOR_REASON_OK)\n>      +-\t\treturn reason;\n>      ++\tif (ipc) {\n>      ++\t\treason = check_uds_volume(r);\n>      ++\t\tif (reason != FSMONITOR_REASON_OK)\n>      ++\t\t\treturn reason;\n>      ++\t}\n>        \n>      - \treturn s;\n>      + \treturn FSMONITOR_REASON_OK;\n>        }\n>      -@@ fsmonitor-settings.c: static void lookup_fsmonitor_settings(struct repository *r)\n>      - \t\tfsm_settings__set_disabled(r);\n>      +\n>      + ## compat/fsmonitor/fsm-settings-win32.c ##\n>      +@@ compat/fsmonitor/fsm-settings-win32.c: static enum fsmonitor_reason check_vfs4git(struct repository *r)\n>      + \treturn FSMONITOR_REASON_OK;\n>        }\n>        \n>      -+int fsm_settings__get_allow_remote(struct repository *r)\n>      -+{\n>      -+\tif (!r)\n>      -+\t\tr = the_repository;\n>      -+\tif (!r->settings.fsmonitor)\n>      -+\t\tlookup_fsmonitor_settings(r);\n>      -+\n>      -+\treturn r->settings.fsmonitor->allow_remote;\n>      -+}\n>      -+\n>      -+const char *fsm_settings__get_socket_dir(struct repository *r)\n>      -+{\n>      -+\tif (!r)\n>      -+\t\tr = the_repository;\n>      -+\tif (!r->settings.fsmonitor)\n>      -+\t\tlookup_fsmonitor_settings(r);\n>      -+\n>      -+\treturn r->settings.fsmonitor->sock_dir;\n>      -+}\n>      -+\n>      - enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n>      +-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n>      ++enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n>        {\n>      - \tif (!r)\n>      -@@ fsmonitor-settings.c: enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)\n>      - \treturn r->settings.fsmonitor->mode;\n>      + \tenum fsmonitor_reason reason;\n>      + \n>      +\n>      + ## fsmonitor-settings.c ##\n>      +@@ fsmonitor-settings.c: static enum fsmonitor_reason check_remote(struct repository *r)\n>        }\n>      + #endif\n>        \n>      -+\n>      - const char *fsm_settings__get_hook_path(struct repository *r)\n>      +-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n>      ++static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n>        {\n>      - \tif (!r)\n>      + \tif (!r->worktree) {\n>      + \t\t/*\n>      +@@ fsmonitor-settings.c: static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n>      + \t\treason = check_remote(r);\n>      + \t\tif (reason != FSMONITOR_REASON_OK)\n>      + \t\t\treturn reason;\n>      +-\t\treason = fsm_os__incompatible(r);\n>      ++\t\treason = fsm_os__incompatible(r, ipc);\n>      + \t\tif (reason != FSMONITOR_REASON_OK)\n>      + \t\t\treturn reason;\n>      + \t}\n>       @@ fsmonitor-settings.c: const char *fsm_settings__get_hook_path(struct repository *r)\n>      - \treturn r->settings.fsmonitor->hook_path;\n>      - }\n>        \n>      -+void fsm_settings__set_allow_remote(struct repository *r)\n>      -+{\n>      -+\tint allow;\n>      -+\n>      -+\tif (!r)\n>      -+\t\tr = the_repository;\n>      -+\tif (!r->settings.fsmonitor)\n>      -+\t\tr->settings.fsmonitor = alloc_settings();\n>      -+\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n>      -+\t\tr->settings.fsmonitor->allow_remote = allow;\n>      -+\n>      -+\treturn;\n>      -+}\n>      -+\n>      -+void fsm_settings__set_socket_dir(struct repository *r)\n>      -+{\n>      -+\tconst char *path;\n>      -+\n>      -+\tif (!r)\n>      -+\t\tr = the_repository;\n>      -+\tif (!r->settings.fsmonitor)\n>      -+\t\tr->settings.fsmonitor = alloc_settings();\n>      -+\n>      -+\tif (!repo_config_get_pathname(r, \"fsmonitor.socketdir\", &path)) {\n>      -+\t\tFREE_AND_NULL(r->settings.fsmonitor->sock_dir);\n>      -+\t\tr->settings.fsmonitor->sock_dir = strdup(path);\n>      -+\t}\n>      -+\n>      -+\treturn;\n>      -+}\n>      -+\n>        void fsm_settings__set_ipc(struct repository *r)\n>        {\n>       -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n>      -+\tenum fsmonitor_reason reason;\n>      -+\n>      -+\tfsm_settings__set_allow_remote(r);\n>      -+\tfsm_settings__set_socket_dir(r);\n>      -+\treason = check_for_incompatible(r);\n>      ++\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n>        \n>        \tif (reason != FSMONITOR_REASON_OK) {\n>        \t\tfsm_settings__set_incompatible(r, reason);\n>      @@ fsmonitor-settings.c: void fsm_settings__set_ipc(struct repository *r)\n>        void fsm_settings__set_hook(struct repository *r, const char *path)\n>        {\n>       -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n>      -+\tenum fsmonitor_reason reason;\n>      -+\n>      -+\tfsm_settings__set_allow_remote(r);\n>      -+\tfsm_settings__set_socket_dir(r);\n>      -+\treason = check_for_incompatible(r);\n>      ++\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n>        \n>        \tif (reason != FSMONITOR_REASON_OK) {\n>        \t\tfsm_settings__set_incompatible(r, reason);\n>       \n>        ## fsmonitor-settings.h ##\n>      -@@ fsmonitor-settings.h: enum fsmonitor_reason {\n>      - \tFSMONITOR_REASON_NOSOCKETS, /* NTFS,FAT32 do not support Unix sockets */\n>      - };\n>      - \n>      -+void fsm_settings__set_allow_remote(struct repository *r);\n>      -+void fsm_settings__set_socket_dir(struct repository *r);\n>      - void fsm_settings__set_ipc(struct repository *r);\n>      - void fsm_settings__set_hook(struct repository *r, const char *path);\n>      - void fsm_settings__set_disabled(struct repository *r);\n>      - void fsm_settings__set_incompatible(struct repository *r,\n>      - \t\t\t\t    enum fsmonitor_reason reason);\n>      - \n>      -+int fsm_settings__get_allow_remote(struct repository *r);\n>      -+const char *fsm_settings__get_socket_dir(struct repository *r);\n>      - enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n>      - const char *fsm_settings__get_hook_path(struct repository *r);\n>      +@@ fsmonitor-settings.h: struct fsmonitor_settings;\n>      +  * fsm_os__* routines should considered private to fsm_settings__\n>      +  * routines.\n>      +  */\n>      +-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n>      ++enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n>      + #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n>        \n>      + #endif /* FSMONITOR_SETTINGS_H */\n>  3:  a3110f1e25a < -:  ----------- fsmonitor: ensure filesystem and unix socket filesystem are compatible\n>  4:  56cabf3be3b ! 4:  b7d6cf44695 fsmonitor: normalize FSEvents event paths to the real path\n>      @@ Metadata\n>       Author: Eric DeCosta <edecosta@mathworks.com>\n>       \n>        ## Commit message ##\n>      -    fsmonitor: normalize FSEvents event paths to the real path\n>      +    fsmonitor: deal with synthetic firmlinks on macOS\n>       \n>      -    Consider the following network working directory that is mounted under\n>      -    /System/Volumes/Data:\n>      +    Starting with macOS 10.15 (Catalina), Apple introduced a new feature\n>      +    called 'firmlinks' in order to separate the boot volume into two\n>      +    volumes, one read-only and one writable but still present them to the\n>      +    user as a single volume. Along with this change, Apple removed the\n>      +    ability to create symlinks in the root directory and replaced them with\n>      +    'synthetic firmlinks'. See 'man synthetic.conf'\n>       \n>      -    /network/working/directory\n>      +    When FSEevents reports the path of changed files, if the path invloves\n>      +    a synthetic firmlink, the path is reported from the point of the\n>      +    synthetic firmlink and not the real path. For example:\n>       \n>      -    The git working directory path is:\n>      +    Real path:\n>      +    /System/Volumes/Data/network/working/directory/foo.txt\n>       \n>      -    /System/Volumes/Data/network/working/directory\n>      +    Synthetic firmlink:\n>      +    /network -> /System/Volumes/Data/network\n>       \n>      -    The paths reported by FSEvents always start with /network. fsmonitor\n>      -    expects paths to be under the working directory; therefore it\n>      -    fails to match /network/... and ignores the change.\n>      +    FSEvents path:\n>      +    /network/working/directory/foo.txt\n>       \n>      -    Change things such that if fsmonitor.allowRemote is true that the\n>      -    paths reported via FSEevents are normalized to the real path.\n>      +    This causes the FSEvents path to not match against the worktree\n>      +    directory.\n>      +\n>      +    There are several ways in which synthetic firmlinks can be created:\n>      +    they can be defined in /etc/synthetic.conf, the automounter can create\n>      +    them, and there may be other means. Simply reading /etc/synthetic.conf\n>      +    is insufficient. No matter what process creates synthetic firmlinks,\n>      +    they all get created in the root directory.\n>      +\n>      +    Therefore, in order to deal with synthetic firmlinks, the root directory\n>      +    is scanned and the first possible synthetic firmink that, when resolved,\n>      +    is a prefix of the worktree is used to map FSEvents paths to worktree\n>      +    paths.\n>       \n>           Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n>       \n>      + ## builtin/fsmonitor--daemon.c ##\n>      +@@\n>      + #include \"parse-options.h\"\n>      + #include \"fsmonitor.h\"\n>      + #include \"fsmonitor-ipc.h\"\n>      ++#include \"fsmonitor-path-utils.h\"\n>      + #include \"compat/fsmonitor/fsm-health.h\"\n>      + #include \"compat/fsmonitor/fsm-listen.h\"\n>      + #include \"fsmonitor--daemon.h\"\n>      +@@ builtin/fsmonitor--daemon.c: static int fsmonitor_run_daemon(void)\n>      + \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n>      + \tstate.nr_paths_watching = 1;\n>      + \n>      ++\tstrbuf_init(&state.alias.alias, 0);\n>      ++\tstrbuf_init(&state.alias.points_to, 0);\n>      ++\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n>      ++\t\terr = error(_(\"could not get worktree alias\"));\n>      ++\t\tgoto done;\n>      ++\t}\n>      ++\n>      + \t/*\n>      + \t * We create and delete cookie files somewhere inside the .git\n>      + \t * directory to help us keep sync with the file system.  If\n>      +\n>        ## compat/fsmonitor/fsm-listen-darwin.c ##\n>       @@\n>        #include \"fsmonitor.h\"\n>        #include \"fsm-listen.h\"\n>        #include \"fsmonitor--daemon.h\"\n>      -+#include \"fsmonitor-settings.h\"\n>      ++#include \"fsmonitor-path-utils.h\"\n>        \n>        struct fsm_listen_data\n>        {\n>      -@@ compat/fsmonitor/fsm-listen-darwin.c: static void my_add_path(struct fsmonitor_batch *batch, const char *path)\n>      - \tfree(composed);\n>      - }\n>      - \n>      --\n>      - static void fsevent_callback(ConstFSEventStreamRef streamRef,\n>      - \t\t\t     void *ctx,\n>      - \t\t\t     size_t num_of_events,\n>       @@ compat/fsmonitor/fsm-listen-darwin.c: static void fsevent_callback(ConstFSEventStreamRef streamRef,\n>        \t\t/*\n>        \t\t * On Mac, we receive an array of absolute paths.\n>        \t\t */\n>       -\t\tpath_k = paths[k];\n>      -+\t\tif (fsm_settings__get_allow_remote(the_repository) > 0) {\n>      -+\t\t\tstrbuf_reset(&tmp);\n>      -+\t\t\tstrbuf_realpath_forgiving(&tmp, paths[k], 0);\n>      -+\t\t\tpath_k = tmp.buf;\n>      -+\t\t} else\n>      ++\t\tpath_k = fsmonitor__resolve_alias(paths[k], &state->alias);\n>      ++\t\tif (!path_k)\n>       +\t\t\tpath_k = paths[k];\n>        \n>        \t\t/*\n>        \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n>       @@ compat/fsmonitor/fsm-listen-darwin.c: static void fsevent_callback(ConstFSEventStreamRef streamRef,\n>      - \n>        \t\t\tfsmonitor_force_resync(state);\n>        \t\t\tfsmonitor_batch__free_list(batch);\n>      -+\t\t\tbatch = NULL;\n>        \t\t\tstring_list_clear(&cookie_list, 0);\n>      ++\t\t\tbatch = NULL;\n>        \n>        \t\t\t/*\n>      -@@ compat/fsmonitor/fsm-listen-darwin.c: static void fsevent_callback(ConstFSEventStreamRef streamRef,\n>      + \t\t\t * We assume that any events that we received\n>      +\n>      + ## compat/fsmonitor/fsm-path-utils-darwin.c ##\n>      +@@\n>      + #include \"fsmonitor.h\"\n>      + #include \"fsmonitor-path-utils.h\"\n>      ++#include <dirent.h>\n>      ++#include <errno.h>\n>      ++#include <fcntl.h>\n>      + #include <sys/param.h>\n>      + #include <sys/mount.h>\n>      + \n>      +@@ compat/fsmonitor/fsm-path-utils-darwin.c: int fsmonitor__is_fs_remote(const char *path)\n>      + \t\treturn -1;\n>      + \treturn fs.is_remote;\n>      + }\n>      ++\n>      ++/*\n>      ++ * Scan the root directory for synthetic firmlinks that when resolved\n>      ++ * are a prefix of the path, stopping at the first one found.\n>      ++ *\n>      ++ * Some information about firmlinks and synthetic firmlinks:\n>      ++ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n>      ++ *\n>      ++ * macOS no longer allows symlinks in the root directory; any link found\n>      ++ * there is therefore a synthetic firmlink.\n>      ++ *\n>      ++ * If this function gets called often, will want to cache all the firmlink\n>      ++ * information, but for now there is only one caller of this function.\n>      ++ *\n>      ++ * If there is more than one alias for the path, that is another\n>      ++ * matter altogteher.\n>      ++ */\n>      ++int fsmonitor__get_alias(const char *path, struct alias_info *info)\n>      ++{\n>      ++\tDIR * dir;\n>      ++\tint read;\n>      ++\tint retval;\n>      ++\tstruct dirent *de;\n>      ++\tstruct strbuf alias;\n>      ++\tstruct strbuf points_to;\n>      ++\n>      ++\tretval = 0;\n>      ++\tdir = opendir(\"/\");\n>      ++\tif (!dir)\n>      ++\t\treturn -1;\n>      ++\n>      ++\tstrbuf_init(&alias, 256);\n>      ++\tstrbuf_init(&points_to, MAXPATHLEN);\n>      ++\n>      ++\twhile ((de = readdir(dir)) != NULL) {\n>      ++\t\tstrbuf_reset(&alias);\n>      ++\t\tstrbuf_addch(&alias, '/');\n>      ++\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n>      ++\n>      ++\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n>      ++\t\tif (read > 0) {\n>      ++\t\t\tstrbuf_setlen(&points_to, read);\n>      ++\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n>      ++\t\t\t\t&& path[points_to.len] == '/') {\n>      ++\t\t\t\tstrbuf_addbuf(&info->alias, &alias);\n>      ++\t\t\t\tstrbuf_addbuf(&info->points_to, &points_to);\n>      ++\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n>      ++\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n>      ++\t\t\t\t\tpath, info->alias.buf, info->points_to.buf);\n>      ++\t\t\t\tretval = 0;\n>      ++\t\t\t\tgoto done;\n>      ++\t\t\t}\n>      ++\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n>      ++\t\t\ttrace_printf_key(&trace_fsmonitor, \"Error %s\", strerror(errno));\n>      ++\t\t\tretval = -1;\n>      ++\t\t\tgoto done;\n>      ++\t\t}\n>      ++\t}\n>      ++\n>      ++\tdone:\n>      ++\tclosedir(dir);\n>      ++\tstrbuf_release(&alias);\n>      ++\tstrbuf_release(&points_to);\n>      ++\treturn retval;\n>      ++}\n>      ++\n>      ++char *fsmonitor__resolve_alias(const char *path,\n>      ++\tconst struct alias_info *info)\n>      ++{\n>      ++\tint len = info->alias.len;\n>      ++\n>      ++\tif (!len)\n>      ++\t\treturn NULL;\n>      ++\n>      ++\tif ((strncmp(info->alias.buf, path, len) == 0)\n>      ++\t\t&& path[len] == '/') {\n>      ++\t\tstruct strbuf tmp;\n>      ++\t\tconst char *remainder = path + len;\n>      ++\t\tint rem_len = strlen(remainder);\n>      ++\n>      ++\t\tstrbuf_init(&tmp, info->points_to.len + rem_len);\n>      ++\t\tstrbuf_addbuf(&tmp, &info->points_to);\n>      ++\t\tstrbuf_add(&tmp, remainder, rem_len);\n>      ++\t\treturn strbuf_detach(&tmp, NULL);\n>      ++\t}\n>      ++\n>      ++\treturn NULL;\n>      ++}\n>      +\n>      + ## compat/fsmonitor/fsm-path-utils-win32.c ##\n>      +@@ compat/fsmonitor/fsm-path-utils-win32.c: int fsmonitor__is_fs_remote(const char *path)\n>      + \t\treturn -1;\n>      + \treturn fs.is_remote;\n>      + }\n>      ++\n>      ++/*\n>      ++ * No-op for now.\n>      ++ */\n>      ++int fsmonitor__get_alias(const char *path, struct alias_info *info)\n>      ++{\n>      ++\treturn 0;\n>      ++}\n>      ++\n>      ++/*\n>      ++ * No-op for now.\n>      ++ */\n>      ++char *fsmonitor__resolve_alias(const char *path,\n>      ++\tconst struct alias_info *info)\n>      ++{\n>      ++\treturn NULL;\n>      ++}\n>      +\n>      + ## fsmonitor--daemon.h ##\n>      +@@\n>      + #include \"run-command.h\"\n>      + #include \"simple-ipc.h\"\n>      + #include \"thread-utils.h\"\n>      ++#include \"fsmonitor-path-utils.h\"\n>      + \n>      + struct fsmonitor_batch;\n>      + struct fsmonitor_token_data;\n>      +@@ fsmonitor--daemon.h: struct fsmonitor_daemon_state {\n>      + \n>      + \tstruct strbuf path_worktree_watch;\n>      + \tstruct strbuf path_gitdir_watch;\n>      ++\tstruct alias_info alias;\n>      + \tint nr_paths_watching;\n>      + \n>      + \tstruct fsmonitor_token_data *current_token_data;\n>      +@@ fsmonitor--daemon.h: struct fsmonitor_daemon_state {\n>      + \n>      + \tstruct ipc_server_data *ipc_server_data;\n>      + \tstruct strbuf path_ipc;\n>      ++\n>      + };\n>      + \n>      + /*\n>      +@@ fsmonitor--daemon.h: void fsmonitor_publish(struct fsmonitor_daemon_state *state,\n>      +  */\n>      + void fsmonitor_force_resync(struct fsmonitor_daemon_state *state);\n>      + \n>      ++char *fsmonitor_resolve_alias(const char *path,\n>      ++\tstruct alias_info *alias);\n>      ++\n>      + #endif /* HAVE_FSMONITOR_DAEMON_BACKEND */\n>      + #endif /* FSMONITOR_DAEMON_H */\n>      +\n>      + ## fsmonitor-path-utils.h ##\n>      +@@\n>      + #ifndef FSM_PATH_UTILS_H\n>      + #define FSM_PATH_UTILS_H\n>        \n>      - \t\tcase IS_WORKDIR_PATH:\n>      - \t\t\t/* try to queue normal pathnames */\n>      --\n>      - \t\t\tif (trace_pass_fl(&trace_fsmonitor))\n>      - \t\t\t\tlog_flags_set(path_k, event_flags[k]);\n>      ++#include \"strbuf.h\"\n>      ++\n>      ++struct alias_info\n>      ++{\n>      ++\tstruct strbuf alias;\n>      ++\tstruct strbuf points_to;\n>      ++};\n>      ++\n>      + struct fs_info {\n>      + \tint is_remote;\n>      + \tchar *typename;\n>      +@@ fsmonitor-path-utils.h: int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n>      +  */\n>      + int fsmonitor__is_fs_remote(const char *path);\n>        \n>      ++/*\n>      ++ * Get the alias in given path, if any.\n>      ++ *\n>      ++ * Sets alias to the first alias that matches any part of the path.\n>      ++ *\n>      ++ * Returns -1 on error, 0 otherwise.\n>      ++ */\n>      ++int fsmonitor__get_alias(const char *path, struct alias_info *info);\n>      ++\n>      ++/*\n>      ++ * Resolve the path against the given alias.\n>      ++ *\n>      ++ * Returns the resolved path if there is one, NULL otherwise.\n>      ++ */\n>      ++char *fsmonitor__resolve_alias(const char *path,\n>      ++\tconst struct alias_info *info);\n>      ++\n>      ++\n>      + #endif\n"},{"id":"462934","messageId":"xmqqa674hagf.fsf@gitster.g","threadId":"58324","inReplyTo":"pull.1326.v5.git.1662840031.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 0/4] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-12T15:35:44Z","receivedAt":"2022-09-12T15:36:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> Eric DeCosta (4):\n>   fsmonitor: refactor filesystem checks to common interface\n>   fsmonitor: relocate socket file if .git directory is remote\n>   fsmonitor: avoid socket location check if using hook\n>   fsmonitor: deal with synthetic firmlinks on macOS\n>\n>  Makefile                                 |   2 +\n>  builtin/fsmonitor--daemon.c              |   8 ++\n>  compat/fsmonitor/fsm-ipc-darwin.c        |  46 ++++++++\n>  compat/fsmonitor/fsm-ipc-win32.c         |   4 +\n>  compat/fsmonitor/fsm-listen-darwin.c     |   6 +-\n>  compat/fsmonitor/fsm-path-utils-darwin.c | 131 +++++++++++++++++++++++\n>  compat/fsmonitor/fsm-path-utils-win32.c  | 106 ++++++++++++++++++\n>  compat/fsmonitor/fsm-settings-darwin.c   |  70 ++++--------\n>  compat/fsmonitor/fsm-settings-win32.c    | 106 +-----------------\n>  contrib/buildsystems/CMakeLists.txt      |   4 +\n>  fsmonitor--daemon.h                      |   6 ++\n>  fsmonitor-ipc.c                          |   2 -\n>  fsmonitor-path-utils.h                   |  49 +++++++++\n>  fsmonitor-settings.c                     |  58 +++++++++-\n>  fsmonitor-settings.h                     |   2 +-\n>  15 files changed, 441 insertions(+), 159 deletions(-)\n>  create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n>  create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n>  create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n>  create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n>  create mode 100644 fsmonitor-path-utils.h\n>\n>\n> base-commit: be1a02a17ede4082a86dfbfee0f54f345e8b43ac\n\nAnother curious thing I need your help on.\n\nThe cover letter says that this work is a follow-up to extend the\nprevious work for Windows, and I would have expected these patches\nto build on 85dc0da6 (fsmonitor: option to allow fsmonitor to run\nagainst network-mounted repos, 2022-08-11), which is not yet in\nbe1a02a1 (The seventeenth batch, 2022-09-01).\n\nWhat is going on?\n\nThanks.\n\n"},{"id":"462946","messageId":"BL0PR05MB55719667F736618C49B00EBFD9449@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"xmqqa674hagf.fsf@gitster.g","subject":"RE: [PATCH v5 0/4] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-12T19:35:47Z","receivedAt":"2022-09-12T19:36:02Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Junio C Hamano <jch2355@gmail.com> On Behalf Of Junio C Hamano\n> Sent: Monday, September 12, 2022 11:36 AM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>\n> Cc: git@vger.kernel.org; Jeff Hostetler <git@jeffhostetler.com>; Eric Sunshine\n> <sunshine@sunshineco.com>; Torsten Bögershausen <tboegi@web.de>;\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com>; Ramsay Jones\n> <ramsay@ramsayjones.plus.com>; Johannes Schindelin\n> <Johannes.Schindelin@gmx.de>; Eric DeCosta <edecosta@mathworks.com>\n> Subject: Re: [PATCH v5 0/4] fsmonitor: option to allow fsmonitor to run\n> against network-mounted repos\n> \n> \"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n> > Eric DeCosta (4):\n> >   fsmonitor: refactor filesystem checks to common interface\n> >   fsmonitor: relocate socket file if .git directory is remote\n> >   fsmonitor: avoid socket location check if using hook\n> >   fsmonitor: deal with synthetic firmlinks on macOS\n> >\n> >  Makefile                                 |   2 +\n> >  builtin/fsmonitor--daemon.c              |   8 ++\n> >  compat/fsmonitor/fsm-ipc-darwin.c        |  46 ++++++++\n> >  compat/fsmonitor/fsm-ipc-win32.c         |   4 +\n> >  compat/fsmonitor/fsm-listen-darwin.c     |   6 +-\n> >  compat/fsmonitor/fsm-path-utils-darwin.c | 131\n> > +++++++++++++++++++++++  compat/fsmonitor/fsm-path-utils-win32.c  |\n> 106 ++++++++++++++++++\n> >  compat/fsmonitor/fsm-settings-darwin.c   |  70 ++++--------\n> >  compat/fsmonitor/fsm-settings-win32.c    | 106 +-----------------\n> >  contrib/buildsystems/CMakeLists.txt      |   4 +\n> >  fsmonitor--daemon.h                      |   6 ++\n> >  fsmonitor-ipc.c                          |   2 -\n> >  fsmonitor-path-utils.h                   |  49 +++++++++\n> >  fsmonitor-settings.c                     |  58 +++++++++-\n> >  fsmonitor-settings.h                     |   2 +-\n> >  15 files changed, 441 insertions(+), 159 deletions(-)  create mode\n> > 100644 compat/fsmonitor/fsm-ipc-darwin.c  create mode 100644\n> > compat/fsmonitor/fsm-ipc-win32.c  create mode 100644\n> > compat/fsmonitor/fsm-path-utils-darwin.c\n> >  create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n> >  create mode 100644 fsmonitor-path-utils.h\n> >\n> >\n> > base-commit: be1a02a17ede4082a86dfbfee0f54f345e8b43ac\n> \n> Another curious thing I need your help on.\n> \n> The cover letter says that this work is a follow-up to extend the previous\n> work for Windows, and I would have expected these patches to build on\n> 85dc0da6 (fsmonitor: option to allow fsmonitor to run against network-\n> mounted repos, 2022-08-11), which is not yet in\n> be1a02a1 (The seventeenth batch, 2022-09-01).\n> \n> What is going on?\n> \n> Thanks.\n\nI've been rolling that around in the back of my head over the last couple of days\nactually.  Initially I though the macOS changes were going to be independent of\nmy earlier changes, but with all the refactoring that has occurred, that is no\nlonger the case. I've rebased my current patch set on top of those earlier changes.\nv6 will incorporate that, the review feedback that I've received so far and add doc\nfor the new config options. I'll be sure to note all of this in the cover letter for v6.\n\n-Eric\n\n"},{"id":"462947","messageId":"xmqqr10gcrjp.fsf@gitster.g","threadId":"58324","inReplyTo":"xmqqedwghauv.fsf@gitster.g","subject":"Re: [PATCH v5 0/4] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-12T19:37:46Z","receivedAt":"2022-09-12T19:37:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> Eric DeCosta (4):\n>>   fsmonitor: refactor filesystem checks to common interface\n>>   fsmonitor: relocate socket file if .git directory is remote\n>>   fsmonitor: avoid socket location check if using hook\n>>   fsmonitor: deal with synthetic firmlinks on macOS\n>\n> It looks like this one is organized quite differently from the last\n> iteration <pull.1326.v4.git.1661962145.gitgitgadget@gmail.com>\n\nGiven that we are very close to -rc period, I am very tempted to say\nthat this follow-up effort can and should be separated out of the\nprevious \"windows only\" one, and we should merge the single patch\nthat has been in 'next' to 'master' without waiting for this \"macOS,\ntoo\" topic.  What do people think?\n\nThanks.\n"},{"id":"462948","messageId":"BL0PR05MB5571582512C460D95E1F1839D9449@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"xmqqr10gcrjp.fsf@gitster.g","subject":"RE: [PATCH v5 0/4] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-12T19:39:59Z","receivedAt":"2022-09-12T19:40:12Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Junio C Hamano <jch2355@gmail.com> On Behalf Of Junio C Hamano\n> Sent: Monday, September 12, 2022 3:38 PM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>\n> Cc: git@vger.kernel.org; Jeff Hostetler <git@jeffhostetler.com>; Eric Sunshine\n> <sunshine@sunshineco.com>; Torsten Bögershausen <tboegi@web.de>;\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com>; Ramsay Jones\n> <ramsay@ramsayjones.plus.com>; Johannes Schindelin\n> <Johannes.Schindelin@gmx.de>; Eric DeCosta <edecosta@mathworks.com>\n> Subject: Re: [PATCH v5 0/4] fsmonitor: option to allow fsmonitor to run\n> against network-mounted repos\n> \n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> >> Eric DeCosta (4):\n> >>   fsmonitor: refactor filesystem checks to common interface\n> >>   fsmonitor: relocate socket file if .git directory is remote\n> >>   fsmonitor: avoid socket location check if using hook\n> >>   fsmonitor: deal with synthetic firmlinks on macOS\n> >\n> > It looks like this one is organized quite differently from the last\n> > iteration <pull.1326.v4.git.1661962145.gitgitgadget@gmail.com>\n> \n> Given that we are very close to -rc period, I am very tempted to say that this\n> follow-up effort can and should be separated out of the previous \"windows\n> only\" one, and we should merge the single patch that has been in 'next' to\n> 'master' without waiting for this \"macOS, too\" topic.  What do people think?\n> \n> Thanks.\n\nI'm totally fine with that, turned out to be a bit more involved than I initially anticipated.\n\n-Eric\n\n"},{"id":"462993","messageId":"3233c908c4ab0521ff35df09f62adec25a49faa4.1663100859.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v6.git.1663100858.gitgitgadget@gmail.com","subject":"[PATCH v6 1/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-13T20:27:33Z","receivedAt":"2022-09-13T20:27:48Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nThough perhaps not common, there are use cases where users have large,\nnetwork-mounted repos. Having the ability to run fsmonitor against\nnetwork paths would benefit those users.\n\nMost modern Samba-based filers have the necessary support to enable\nfsmonitor on network-mounted repos. As a first step towards enabling\nfsmonitor to work against network-mounted repos, introduce a\nconfiguration option, 'fsmonitor.allowRemote'. Setting this option to\ntrue will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-win32.c | 68 +++++++++++++++++++++++++++\n 1 file changed, 68 insertions(+)\n\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex 907655720bb..e5ec5b0a9f7 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -24,6 +24,59 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n+/*\n+ * Check if monitoring remote working directories is allowed.\n+ *\n+ * By default, monitoring remote working directories is\n+ * disabled.  Users may override this behavior in enviroments where\n+ * they have proper support.\n+ */\n+static int check_config_allowremote(struct repository *r)\n+{\n+\tint allow;\n+\n+\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n+\t\treturn allow;\n+\n+\treturn -1; /* fsmonitor.allowremote not set */\n+}\n+\n+/*\n+ * Check remote working directory protocol.\n+ *\n+ * Error if client machine cannot get remote protocol information.\n+ */\n+static int check_remote_protocol(wchar_t *wpath)\n+{\n+\tHANDLE h;\n+\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n+\n+\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n+\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\n+\tif (h == INVALID_HANDLE_VALUE) {\n+\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n+\t\t&proto_info, sizeof(proto_info))) {\n+\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\tCloseHandle(h);\n+\t\treturn -1;\n+\t}\n+\n+\tCloseHandle(h);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n+\t\t\t\twpath, proto_info.Protocol);\n+\n+\treturn 0;\n+}\n+\n /*\n  * Remote working directories are problematic for FSMonitor.\n  *\n@@ -76,6 +129,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n  */\n static enum fsmonitor_reason check_remote(struct repository *r)\n {\n+\tint ret;\n \twchar_t wpath[MAX_PATH];\n \twchar_t wfullpath[MAX_PATH];\n \tsize_t wlen;\n@@ -115,6 +169,20 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n \t\ttrace_printf_key(&trace_fsmonitor,\n \t\t\t\t \"check_remote('%s') true\",\n \t\t\t\t r->worktree);\n+\n+\t\tret = check_remote_protocol(wfullpath);\n+\t\tif (ret < 0)\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\n+\t\tswitch (check_config_allowremote(r)) {\n+\t\tcase 0: /* config overrides and disables */\n+\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\tcase 1: /* config overrides and enables */\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\tbreak; /* config has no opinion */\n+\t\t}\n+\n \t\treturn FSMONITOR_REASON_REMOTE;\n \t}\n \n-- \ngitgitgadget\n\n"},{"id":"462994","messageId":"pull.1326.v6.git.1663100858.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v5.git.1662840031.gitgitgadget@gmail.com","subject":"[PATCH v6 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-13T20:27:32Z","receivedAt":"2022-09-13T20:27:49Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"Follow-on to the work done to allow Windows to work against network-mounted\nrepos for macOS.\n\nHave macOS take advantage of the same configuration option,\n'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\nto true will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for IPC\ncannot be created in a network location; instead $HOME is used if the\ndefault location is on the network. The user may, optionally, set the\n'fsmonitor.socketDir' configuration option to a valid, local directory if\n$HOME itself is on the network or is simply not the desired location for the\nUDS file.\n\nAn additional issue is that for mount points in the root directory, FSEvents\ndoes not report a path that matches the worktree directory due to the\nintroduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\nto the worktree directory by interrogating the root filesystem for synthetic\nfirmlinks and using that information to translate the path.\n\nv6 differs from v5:\n\n * incorporates earlier, Windows-specific changes that have not made it back\n   yet to the master branch\n * incorporates code review feedback\n * adds documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n\nv5 differs significantly from earlier versions:\n\n * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n   such that these options are no longer added to the settings data\n   structure but are rather read from config at point of use\n * refactoring of code for handling platform-specific file system checks via\n   a common interface to avoid platform #ifdef in IPC code and be in-model\n   with other platform-specific fsmonitor code\n * dealing with 'synthetic firmlinks' on macOS\n\nEric DeCosta (6):\n  fsmonitor: option to allow fsmonitor to run against network-mounted\n    repos\n  fsmonitor: refactor filesystem checks to common interface\n  fsmonitor: relocate socket file if .git directory is remote\n  fsmonitor: avoid socket location check if using hook\n  fsmonitor: deal with synthetic firmlinks on macOS\n  fsmonitor: add documentation for allowRemote and socketDir options\n\n Documentation/git-fsmonitor--daemon.txt  |  35 ++++++\n Makefile                                 |   2 +\n builtin/fsmonitor--daemon.c              |   8 ++\n compat/fsmonitor/fsm-ipc-darwin.c        |  46 +++++++\n compat/fsmonitor/fsm-ipc-win32.c         |   4 +\n compat/fsmonitor/fsm-listen-darwin.c     |   6 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 132 +++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 145 +++++++++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  70 ++++-------\n compat/fsmonitor/fsm-settings-win32.c    | 106 +----------------\n contrib/buildsystems/CMakeLists.txt      |   4 +\n fsmonitor--daemon.h                      |   6 +\n fsmonitor-ipc.c                          |   2 -\n fsmonitor-path-utils.h                   |  59 +++++++++\n fsmonitor-settings.c                     |  58 ++++++++-\n fsmonitor-settings.h                     |   2 +-\n 16 files changed, 526 insertions(+), 159 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\n\nbase-commit: dd3f6c4cae7e3b15ce984dce8593ff7569650e24\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v6\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v6\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v5:\n\n -:  ----------- > 1:  3233c908c4a fsmonitor: option to allow fsmonitor to run against network-mounted repos\n 1:  2f647b53e4d ! 2:  d2a8fc6b707 fsmonitor: refactor filesystem checks to common interface\n     @@ compat/fsmonitor/fsm-path-utils-win32.c (new)\n      +#include \"fsmonitor-path-utils.h\"\n      +\n      +/*\n     ++ * Check remote working directory protocol.\n     ++ *\n     ++ * Return -1 if client machine cannot get remote protocol information.\n     ++ */\n     ++static int check_remote_protocol(wchar_t *wpath)\n     ++{\n     ++\tHANDLE h;\n     ++\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n     ++\n     ++\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n     ++\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n     ++\n     ++\tif (h == INVALID_HANDLE_VALUE) {\n     ++\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n     ++\t\t      GetLastError(), wpath);\n     ++\t\treturn -1;\n     ++\t}\n     ++\n     ++\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n     ++\t\t&proto_info, sizeof(proto_info))) {\n     ++\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n     ++\t\t      GetLastError(), wpath);\n     ++\t\tCloseHandle(h);\n     ++\t\treturn -1;\n     ++\t}\n     ++\n     ++\tCloseHandle(h);\n     ++\n     ++\ttrace_printf_key(&trace_fsmonitor,\n     ++\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n     ++\t\t\t\twpath, proto_info.Protocol);\n     ++\n     ++\treturn 0;\n     ++}\n     ++\n     ++/*\n      + * Notes for testing:\n      + *\n      + * (a) Windows allows a network share to be mapped to a drive letter.\n     @@ compat/fsmonitor/fsm-path-utils-win32.c (new)\n      +\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n      +\t\t\t path, wfullpath, driveType);\n      +\n     -+\tif (driveType == DRIVE_REMOTE)\n     ++\tif (driveType == DRIVE_REMOTE) {\n      +\t\tfs_info->is_remote = 1;\n     -+\telse\n     ++\t\tif (check_remote_protocol(wfullpath) < 0)\n     ++\t\t\treturn -1;\n     ++\t} else {\n      +\t\tfs_info->is_remote = 0;\n     ++\t}\n      +\n      +\ttrace_printf_key(&trace_fsmonitor,\n      +\t\t\t\t\"'%s' is_remote: %d\",\n     @@ compat/fsmonitor/fsm-settings-win32.c: static enum fsmonitor_reason check_vfs4gi\n       \treturn FSMONITOR_REASON_OK;\n       }\n       \n     +-/*\n     +- * Check if monitoring remote working directories is allowed.\n     +- *\n     +- * By default, monitoring remote working directories is\n     +- * disabled.  Users may override this behavior in enviroments where\n     +- * they have proper support.\n     +- */\n     +-static int check_config_allowremote(struct repository *r)\n     +-{\n     +-\tint allow;\n     +-\n     +-\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n     +-\t\treturn allow;\n     +-\n     +-\treturn -1; /* fsmonitor.allowremote not set */\n     +-}\n     +-\n     +-/*\n     +- * Check remote working directory protocol.\n     +- *\n     +- * Error if client machine cannot get remote protocol information.\n     +- */\n     +-static int check_remote_protocol(wchar_t *wpath)\n     +-{\n     +-\tHANDLE h;\n     +-\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n     +-\n     +-\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n     +-\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n     +-\n     +-\tif (h == INVALID_HANDLE_VALUE) {\n     +-\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n     +-\t\t      GetLastError(), wpath);\n     +-\t\treturn -1;\n     +-\t}\n     +-\n     +-\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n     +-\t\t&proto_info, sizeof(proto_info))) {\n     +-\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n     +-\t\t      GetLastError(), wpath);\n     +-\t\tCloseHandle(h);\n     +-\t\treturn -1;\n     +-\t}\n     +-\n     +-\tCloseHandle(h);\n     +-\n     +-\ttrace_printf_key(&trace_fsmonitor,\n     +-\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n     +-\t\t\t\twpath, proto_info.Protocol);\n     +-\n     +-\treturn 0;\n     +-}\n     +-\n      -/*\n      - * Remote working directories are problematic for FSMonitor.\n      - *\n     @@ compat/fsmonitor/fsm-settings-win32.c: static enum fsmonitor_reason check_vfs4gi\n      - */\n      -static enum fsmonitor_reason check_remote(struct repository *r)\n      -{\n     +-\tint ret;\n      -\twchar_t wpath[MAX_PATH];\n      -\twchar_t wfullpath[MAX_PATH];\n      -\tsize_t wlen;\n     @@ compat/fsmonitor/fsm-settings-win32.c: static enum fsmonitor_reason check_vfs4gi\n      -\t\ttrace_printf_key(&trace_fsmonitor,\n      -\t\t\t\t \"check_remote('%s') true\",\n      -\t\t\t\t r->worktree);\n     +-\n     +-\t\tret = check_remote_protocol(wfullpath);\n     +-\t\tif (ret < 0)\n     +-\t\t\treturn FSMONITOR_REASON_ERROR;\n     +-\n     +-\t\tswitch (check_config_allowremote(r)) {\n     +-\t\tcase 0: /* config overrides and disables */\n     +-\t\t\treturn FSMONITOR_REASON_REMOTE;\n     +-\t\tcase 1: /* config overrides and enables */\n     +-\t\t\treturn FSMONITOR_REASON_OK;\n     +-\t\tdefault:\n     +-\t\t\tbreak; /* config has no opinion */\n     +-\t\t}\n     +-\n      -\t\treturn FSMONITOR_REASON_REMOTE;\n      -\t}\n      -\n 2:  28d08bcf808 = 3:  edef029a298 fsmonitor: relocate socket file if .git directory is remote\n 3:  ff54b6e0bb5 = 4:  3428bcf8763 fsmonitor: avoid socket location check if using hook\n 4:  b7d6cf44695 ! 5:  9c1f408ae6d fsmonitor: deal with synthetic firmlinks on macOS\n     @@ Commit message\n          ability to create symlinks in the root directory and replaced them with\n          'synthetic firmlinks'. See 'man synthetic.conf'\n      \n     -    When FSEevents reports the path of changed files, if the path invloves\n     +    When FSEevents reports the path of changed files, if the path involves\n          a synthetic firmlink, the path is reported from the point of the\n          synthetic firmlink and not the real path. For example:\n      \n     @@ builtin/fsmonitor--daemon.c: static int fsmonitor_run_daemon(void)\n       \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n       \tstate.nr_paths_watching = 1;\n       \n     -+\tstrbuf_init(&state.alias.alias, 0);\n     -+\tstrbuf_init(&state.alias.points_to, 0);\n     ++\tstate.alias.alias = NULL;\n     ++\tstate.alias.points_to = NULL;\n      +\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n      +\t\terr = error(_(\"could not get worktree alias\"));\n      +\t\tgoto done;\n     @@ compat/fsmonitor/fsm-path-utils-darwin.c: int fsmonitor__is_fs_remote(const char\n      + * information, but for now there is only one caller of this function.\n      + *\n      + * If there is more than one alias for the path, that is another\n     -+ * matter altogteher.\n     ++ * matter altogether.\n      + */\n      +int fsmonitor__get_alias(const char *path, struct alias_info *info)\n      +{\n     @@ compat/fsmonitor/fsm-path-utils-darwin.c: int fsmonitor__is_fs_remote(const char\n      +\t\t\tstrbuf_setlen(&points_to, read);\n      +\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n      +\t\t\t\t&& path[points_to.len] == '/') {\n     -+\t\t\t\tstrbuf_addbuf(&info->alias, &alias);\n     -+\t\t\t\tstrbuf_addbuf(&info->points_to, &points_to);\n     ++\t\t\t\tinfo->alias = strbuf_detach(&alias, NULL);\n     ++\t\t\t\tinfo->points_to = strbuf_detach(&points_to, NULL);\n      +\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n      +\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n     -+\t\t\t\t\tpath, info->alias.buf, info->points_to.buf);\n     ++\t\t\t\t\tpath, info->alias, info->points_to);\n      +\t\t\t\tretval = 0;\n      +\t\t\t\tgoto done;\n      +\t\t\t}\n     @@ compat/fsmonitor/fsm-path-utils-darwin.c: int fsmonitor__is_fs_remote(const char\n      +char *fsmonitor__resolve_alias(const char *path,\n      +\tconst struct alias_info *info)\n      +{\n     -+\tint len = info->alias.len;\n     ++\tint len = info->alias ? strlen(info->alias) : 0;\n      +\n      +\tif (!len)\n      +\t\treturn NULL;\n      +\n     -+\tif ((strncmp(info->alias.buf, path, len) == 0)\n     ++\tif ((strncmp(info->alias, path, len) == 0)\n      +\t\t&& path[len] == '/') {\n      +\t\tstruct strbuf tmp;\n      +\t\tconst char *remainder = path + len;\n     ++\t\tint ptr_len = strlen(info->points_to);\n      +\t\tint rem_len = strlen(remainder);\n      +\n     -+\t\tstrbuf_init(&tmp, info->points_to.len + rem_len);\n     -+\t\tstrbuf_addbuf(&tmp, &info->points_to);\n     ++\t\tstrbuf_init(&tmp, ptr_len + rem_len);\n     ++\t\tstrbuf_add(&tmp, info->points_to, ptr_len);\n      +\t\tstrbuf_add(&tmp, remainder, rem_len);\n      +\t\treturn strbuf_detach(&tmp, NULL);\n      +\t}\n     @@ fsmonitor-path-utils.h\n      +\n      +struct alias_info\n      +{\n     -+\tstruct strbuf alias;\n     -+\tstruct strbuf points_to;\n     ++\tchar *alias;\n     ++\tchar *points_to;\n      +};\n      +\n       struct fs_info {\n     @@ fsmonitor-path-utils.h: int fsmonitor__get_fs_info(const char *path, struct fs_i\n      + *\n      + * Sets alias to the first alias that matches any part of the path.\n      + *\n     ++ * If an alias is found, info.alias and info.points_to are set to the\n     ++ * found mapping.\n     ++ *\n      + * Returns -1 on error, 0 otherwise.\n     ++ *\n     ++ * The caller owns the storage that is occupied by set info.alias and\n     ++ * info.points_to and is responsible for releasing it with `free(3)`\n     ++ * when done.\n      + */\n      +int fsmonitor__get_alias(const char *path, struct alias_info *info);\n      +\n     @@ fsmonitor-path-utils.h: int fsmonitor__get_fs_info(const char *path, struct fs_i\n      + * Resolve the path against the given alias.\n      + *\n      + * Returns the resolved path if there is one, NULL otherwise.\n     ++ *\n     ++ * The caller owns the storage that the returned string occupies and\n     ++ * is responsible for releasing it with `free(3)` when done.\n      + */\n      +char *fsmonitor__resolve_alias(const char *path,\n      +\tconst struct alias_info *info);\n -:  ----------- > 6:  d2c95e34d3a fsmonitor: add documentation for allowRemote and socketDir options\n\n-- \ngitgitgadget\n"},{"id":"462995","messageId":"edef029a298a44ba59d19db53c2f7ba07e93aec6.1663100859.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v6.git.1663100858.gitgitgadget@gmail.com","subject":"[PATCH v6 3/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-13T20:27:35Z","receivedAt":"2022-09-13T20:27:52Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf the .git directory is on a remote file system, create the socket\nfile in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                            |  1 +\n compat/fsmonitor/fsm-ipc-darwin.c   | 46 +++++++++++++++++++++++++++++\n compat/fsmonitor/fsm-ipc-win32.c    |  4 +++\n contrib/buildsystems/CMakeLists.txt |  2 ++\n fsmonitor-ipc.c                     |  2 --\n 5 files changed, 53 insertions(+), 2 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n\ndiff --git a/Makefile b/Makefile\nindex b026f3e8cf0..5cd5ad818b8 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2033,6 +2033,7 @@ ifdef FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n endif\n \n ifdef FSMONITOR_OS_SETTINGS\ndiff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\nnew file mode 100644\nindex 00000000000..afaca96dab9\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-darwin.c\n@@ -0,0 +1,46 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"strbuf.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(void)\n+{\n+\tstatic const char *ipc_path;\n+\tSHA_CTX sha1ctx;\n+\tchar *sock_dir;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (ipc_path)\n+\t\treturn ipc_path;\n+\n+\tipc_path = fsmonitor_ipc__get_default_path();\n+\n+\t/* By default the socket file is created in the .git directory */\n+\tif (fsmonitor__is_fs_remote(ipc_path) < 1)\n+\t\treturn ipc_path;\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, the_repository->worktree, strlen(the_repository->worktree));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\trepo_config_get_string(the_repository, \"fsmonitor.socketdir\", &sock_dir);\n+\n+\t/* Create the socket file in either socketDir or $HOME */\n+\tif (sock_dir && *sock_dir)\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\telse\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\n+\tipc_path = interpolate_path(ipc_file.buf, 1);\n+\tif (!ipc_path)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\n+\tstrbuf_release(&ipc_file);\n+\treturn ipc_path;\n+}\ndiff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\nnew file mode 100644\nindex 00000000000..769a88639f6\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-win32.c\n@@ -0,0 +1,4 @@\n+#include \"cache.h\"\n+#include \"fsmonitor-ipc.h\"\n+\n+GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex b88494bf59b..7e7b6b9a362 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n@@ -316,6 +317,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..caad2e246a0 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -47,8 +47,6 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n-\n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\n \treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n-- \ngitgitgadget\n\n"},{"id":"462996","messageId":"d2a8fc6b7077bbcd80e4d1edcf8e25726b906985.1663100859.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v6.git.1663100858.gitgitgadget@gmail.com","subject":"[PATCH v6 2/6] fsmonitor: refactor filesystem checks to common interface","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-13T20:27:34Z","receivedAt":"2022-09-13T20:28:01Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nProvide a common interface for getting basic filesystem information\nincluding filesystem type and whether the filesystem is remote.\n\nRefactor existing code for getting basic filesystem info and detecting\nremote file systems to the new interface.\n\nRefactor filesystem checks to leverage new interface. For macOS,\nerror-out if the Unix Domain socket (UDS) file is on a remote\nfilesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                                 |   1 +\n compat/fsmonitor/fsm-path-utils-darwin.c |  40 ++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 128 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  62 +++-----\n compat/fsmonitor/fsm-settings-win32.c    | 172 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   2 +\n fsmonitor-path-utils.h                   |  23 +++\n fsmonitor-settings.c                     |  50 +++++++\n 8 files changed, 263 insertions(+), 215 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\ndiff --git a/Makefile b/Makefile\nindex c6e126e54c2..b026f3e8cf0 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2038,6 +2038,7 @@ endif\n ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n ifeq ($(TCLTK_PATH),)\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nnew file mode 100644\nindex 00000000000..067cbe6990a\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -0,0 +1,40 @@\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+#include <sys/param.h>\n+#include <sys/mount.h>\n+\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\tstruct statfs fs;\n+\tif (statfs(path, &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\treturn -1;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\n+\tif (!(fs.f_flags & MNT_LOCAL))\n+\t\tfs_info->is_remote = 1;\n+\telse\n+\t\tfs_info->is_remote = 0;\n+\n+\tfs_info->typename = fs.f_fstypename;\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nnew file mode 100644\nindex 00000000000..a90b8f7925b\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -0,0 +1,128 @@\n+#include \"cache.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+/*\n+ * Check remote working directory protocol.\n+ *\n+ * Return -1 if client machine cannot get remote protocol information.\n+ */\n+static int check_remote_protocol(wchar_t *wpath)\n+{\n+\tHANDLE h;\n+\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n+\n+\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n+\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\n+\tif (h == INVALID_HANDLE_VALUE) {\n+\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n+\t\t&proto_info, sizeof(proto_info))) {\n+\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\tCloseHandle(h);\n+\t\treturn -1;\n+\t}\n+\n+\tCloseHandle(h);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n+\t\t\t\twpath, proto_info.Protocol);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Notes for testing:\n+ *\n+ * (a) Windows allows a network share to be mapped to a drive letter.\n+ *     (This is the normal method to access it.)\n+ *\n+ *     $ NET USE Z: \\\\server\\share\n+ *     $ git -C Z:/repo status\n+ *\n+ * (b) Windows allows a network share to be referenced WITHOUT mapping\n+ *     it to drive letter.\n+ *\n+ *     $ NET USE \\\\server\\share\\dir\n+ *     $ git -C //server/share/repo status\n+ *\n+ * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n+ *     arbitrary path (which may be remote)\n+ *\n+ *     $ SUBST Q: Z:\\repo\n+ *     $ git -C Q:/ status\n+ *\n+ * (d) Windows allows a directory symlink to be created on a local\n+ *     file system that points to a remote repo.\n+ *\n+ *     $ mklink /d ./link //server/share/repo\n+ *     $ git -C ./link status\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\twchar_t wpath[MAX_PATH];\n+\twchar_t wfullpath[MAX_PATH];\n+\tsize_t wlen;\n+\tUINT driveType;\n+\n+\t/*\n+\t * Do everything in wide chars because the drive letter might be\n+\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n+\t */\n+\tif (xutftowcs_path(wpath, path) < 0) {\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * GetDriveTypeW() requires a final slash.  We assume that the\n+\t * worktree pathname points to an actual directory.\n+\t */\n+\twlen = wcslen(wpath);\n+\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n+\t\twpath[wlen++] = L'\\\\';\n+\t\twpath[wlen] = 0;\n+\t}\n+\n+\t/*\n+\t * Normalize the path.  If nothing else, this converts forward\n+\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n+\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n+\t */\n+\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {\n+\t\treturn -1;\n+\t}\n+\n+\tdriveType = GetDriveTypeW(wfullpath);\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n+\t\t\t path, wfullpath, driveType);\n+\n+\tif (driveType == DRIVE_REMOTE) {\n+\t\tfs_info->is_remote = 1;\n+\t\tif (check_remote_protocol(wfullpath) < 0)\n+\t\t\treturn -1;\n+\t} else {\n+\t\tfs_info->is_remote = 0;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dba3ced6bb7 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -1,32 +1,10 @@\n-#include \"cache.h\"\n #include \"config.h\"\n-#include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n-#include <sys/param.h>\n-#include <sys/mount.h>\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n-/*\n- * [1] Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type (NFS, SAMBA, etc.) dictates whether notification events\n- * are available at all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n+ /*\n  * For the builtin FSMonitor, we create the Unix domain socket for the\n  * IPC in the .git directory.  If the working directory is remote,\n  * then the socket will be created on the remote file system.  This\n@@ -38,40 +16,34 @@\n  * be taken to ensure that $HOME is actually local and not a managed\n  * file share.)\n  *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n+ * FAT32 and NTFS working directories are problematic too.\n  *\n  * The builtin FSMonitor uses a Unix domain socket in the .git\n  * directory for IPC.  These Windows drive formats do not support\n  * Unix domain sockets, so mark them as incompatible for the daemon.\n  *\n  */\n-static enum fsmonitor_reason check_volume(struct repository *r)\n+static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n-\tstruct statfs fs;\n+\tstruct fs_info fs;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \n-\tif (statfs(r->worktree, &fs) == -1) {\n-\t\tint saved_errno = errno;\n-\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n-\t\t\t\t r->worktree, strerror(saved_errno));\n-\t\terrno = saved_errno;\n+\tif (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {\n+\t\tstrbuf_release(&path);\n \t\treturn FSMONITOR_REASON_ERROR;\n \t}\n \n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n-\t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n \n-\tif (!(fs.f_flags & MNT_LOCAL))\n+\tif (fs.is_remote)\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n-\tif (!strcmp(fs.f_fstypename, \"msdos\")) /* aka FAT32 */\n+\tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n-\tif (!strcmp(fs.f_fstypename, \"ntfs\"))\n+\tif (!strcmp(fs.typename, \"ntfs\"))\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \treturn FSMONITOR_REASON_OK;\n@@ -81,7 +53,7 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_volume(r);\n+\treason = check_uds_volume(r);\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex e5ec5b0a9f7..d88b06ae610 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -1,8 +1,9 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * VFS for Git is incompatible with FSMonitor.\n@@ -24,171 +25,6 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-/*\n- * Check if monitoring remote working directories is allowed.\n- *\n- * By default, monitoring remote working directories is\n- * disabled.  Users may override this behavior in enviroments where\n- * they have proper support.\n- */\n-static int check_config_allowremote(struct repository *r)\n-{\n-\tint allow;\n-\n-\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n-\t\treturn allow;\n-\n-\treturn -1; /* fsmonitor.allowremote not set */\n-}\n-\n-/*\n- * Check remote working directory protocol.\n- *\n- * Error if client machine cannot get remote protocol information.\n- */\n-static int check_remote_protocol(wchar_t *wpath)\n-{\n-\tHANDLE h;\n-\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n-\n-\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n-\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n-\n-\tif (h == INVALID_HANDLE_VALUE) {\n-\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\treturn -1;\n-\t}\n-\n-\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n-\t\t&proto_info, sizeof(proto_info))) {\n-\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\tCloseHandle(h);\n-\t\treturn -1;\n-\t}\n-\n-\tCloseHandle(h);\n-\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n-\t\t\t\twpath, proto_info.Protocol);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type dictates whether notification events are available at\n- * all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- * Notes for testing:\n- *\n- * (a) Windows allows a network share to be mapped to a drive letter.\n- *     (This is the normal method to access it.)\n- *\n- *     $ NET USE Z: \\\\server\\share\n- *     $ git -C Z:/repo status\n- *\n- * (b) Windows allows a network share to be referenced WITHOUT mapping\n- *     it to drive letter.\n- *\n- *     $ NET USE \\\\server\\share\\dir\n- *     $ git -C //server/share/repo status\n- *\n- * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n- *     arbitrary path (which may be remote)\n- *\n- *     $ SUBST Q: Z:\\repo\n- *     $ git -C Q:/ status\n- *\n- * (d) Windows allows a directory symlink to be created on a local\n- *     file system that points to a remote repo.\n- *\n- *     $ mklink /d ./link //server/share/repo\n- *     $ git -C ./link status\n- */\n-static enum fsmonitor_reason check_remote(struct repository *r)\n-{\n-\tint ret;\n-\twchar_t wpath[MAX_PATH];\n-\twchar_t wfullpath[MAX_PATH];\n-\tsize_t wlen;\n-\tUINT driveType;\n-\n-\t/*\n-\t * Do everything in wide chars because the drive letter might be\n-\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n-\t */\n-\tif (xutftowcs_path(wpath, r->worktree) < 0)\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t/*\n-\t * GetDriveTypeW() requires a final slash.  We assume that the\n-\t * worktree pathname points to an actual directory.\n-\t */\n-\twlen = wcslen(wpath);\n-\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n-\t\twpath[wlen++] = L'\\\\';\n-\t\twpath[wlen] = 0;\n-\t}\n-\n-\t/*\n-\t * Normalize the path.  If nothing else, this converts forward\n-\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n-\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n-\t */\n-\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\tdriveType = GetDriveTypeW(wfullpath);\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n-\t\t\t r->worktree, wfullpath, driveType);\n-\n-\tif (driveType == DRIVE_REMOTE) {\n-\t\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t \"check_remote('%s') true\",\n-\t\t\t\t r->worktree);\n-\n-\t\tret = check_remote_protocol(wfullpath);\n-\t\tif (ret < 0)\n-\t\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t\tswitch (check_config_allowremote(r)) {\n-\t\tcase 0: /* config overrides and disables */\n-\t\t\treturn FSMONITOR_REASON_REMOTE;\n-\t\tcase 1: /* config overrides and enables */\n-\t\t\treturn FSMONITOR_REASON_OK;\n-\t\tdefault:\n-\t\t\tbreak; /* config has no opinion */\n-\t\t}\n-\n-\t\treturn FSMONITOR_REASON_REMOTE;\n-\t}\n-\n-\treturn FSMONITOR_REASON_OK;\n-}\n-\n enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n@@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n-\treason = check_remote(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n-\n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 2237109b57f..b88494bf59b 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)\n@@ -315,6 +316,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nnew file mode 100644\nindex 00000000000..e48592887e7\n--- /dev/null\n+++ b/fsmonitor-path-utils.h\n@@ -0,0 +1,23 @@\n+#ifndef FSM_PATH_UTILS_H\n+#define FSM_PATH_UTILS_H\n+\n+struct fs_info {\n+\tint is_remote;\n+\tchar *typename;\n+};\n+\n+/*\n+ * Get some basic filesystem informtion for the given path\n+ *\n+ * Returns -1 on error, zero otherwise.\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n+\n+/*\n+ * Determines if the filesystem that path resides on is remote.\n+ *\n+ * Returns -1 on error, 0 if not remote, 1 if remote.\n+ */\n+int fsmonitor__is_fs_remote(const char *path);\n+\n+#endif\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..d288cbad479 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * We keep this structure defintion private and have getters\n@@ -13,6 +14,52 @@ struct fsmonitor_settings {\n \tchar *hook_path;\n };\n \n+/*\n+ * Remote working directories are problematic for FSMonitor.\n+ *\n+ * The underlying file system on the server machine and/or the remote\n+ * mount type dictates whether notification events are available at\n+ * all to remote client machines.\n+ *\n+ * Kernel differences between the server and client machines also\n+ * dictate the how (buffering, frequency, de-dup) the events are\n+ * delivered to client machine processes.\n+ *\n+ * A client machine (such as a laptop) may choose to suspend/resume\n+ * and it is unclear (without lots of testing) whether the watcher can\n+ * resync after a resume.  We might be able to treat this as a normal\n+ * \"events were dropped by the kernel\" event and do our normal \"flush\n+ * and resync\" --or-- we might need to close the existing (zombie?)\n+ * notification fd and create a new one.\n+ *\n+ * In theory, the above issues need to be addressed whether we are\n+ * using the Hook or IPC API.\n+ *\n+ * So (for now at least), mark remote working directories as\n+ * incompatible unless 'fsmonitor.allowRemote' is true.\n+ *\n+ */\n+#ifdef HAVE_FSMONITOR_OS_SETTINGS\n+static enum fsmonitor_reason check_remote(struct repository *r)\n+{\n+\tint allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */\n+\tint is_remote = fsmonitor__is_fs_remote(r->worktree);\n+\n+\tswitch (is_remote) {\n+\t\tcase 0:\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tcase 1:\n+\t\t\trepo_config_get_bool(r, \"fsmonitor.allowremote\", &allow_remote);\n+\t\t\tif (allow_remote < 1)\n+\t\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\t\telse\n+\t\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+}\n+#endif\n+\n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n {\n \tif (!r->worktree) {\n@@ -27,6 +74,9 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t{\n \t\tenum fsmonitor_reason reason;\n \n+\t\treason = check_remote(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n \t\treason = fsm_os__incompatible(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-- \ngitgitgadget\n\n"},{"id":"462997","messageId":"3428bcf8763b553b722699d4095cb30cbf9709b6.1663100859.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v6.git.1663100858.gitgitgadget@gmail.com","subject":"[PATCH v6 4/6] fsmonitor: avoid socket location check if using hook","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-13T20:27:36Z","receivedAt":"2022-09-13T20:28:12Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf monitoring is done via fsmonitor hook rather than IPC there is no\nneed to check if the location of the Unix Domain socket (UDS) file is\non a remote filesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 10 ++++++----\n compat/fsmonitor/fsm-settings-win32.c  |  2 +-\n fsmonitor-settings.c                   |  8 ++++----\n fsmonitor-settings.h                   |  2 +-\n 4 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex dba3ced6bb7..3463c71763e 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -49,13 +49,15 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_uds_volume(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n+\tif (ipc) {\n+\t\treason = check_uds_volume(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n+\t}\n \n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex d88b06ae610..a8af31b71de 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex d288cbad479..531a1b6f956 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -60,7 +60,7 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n }\n #endif\n \n-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n+static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n {\n \tif (!r->worktree) {\n \t\t/*\n@@ -77,7 +77,7 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t\treason = check_remote(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-\t\treason = fsm_os__incompatible(r);\n+\t\treason = fsm_os__incompatible(r, ipc);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n \t}\n@@ -162,7 +162,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -185,7 +185,7 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..0721617b95a 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -48,7 +48,7 @@ struct fsmonitor_settings;\n  * fsm_os__* routines should considered private to fsm_settings__\n  * routines.\n  */\n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n \n #endif /* FSMONITOR_SETTINGS_H */\n-- \ngitgitgadget\n\n"},{"id":"462998","messageId":"d2c95e34d3ab927e69aa176981719df4fbc563e7.1663100859.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v6.git.1663100858.gitgitgadget@gmail.com","subject":"[PATCH v6 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-13T20:27:38Z","receivedAt":"2022-09-13T20:28:14Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nAdd documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\nCall-out experimental nature of 'fsmonitor.allowRemote' and limited file\nsystem support for 'fsmonitor.socketDir'.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Documentation/git-fsmonitor--daemon.txt | 35 +++++++++++++++++++++++++\n 1 file changed, 35 insertions(+)\n\ndiff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt\nindex cc142fb8612..0adccd0eced 100644\n--- a/Documentation/git-fsmonitor--daemon.txt\n+++ b/Documentation/git-fsmonitor--daemon.txt\n@@ -70,6 +70,41 @@ the change (as happening against the super repo).  However, the client\n will properly ignore these extra events, so performance may be affected\n but it will not cause an incorrect result.\n \n+By default, the fsmonitor daemon refuses to work against network-mounted\n+repositories; this my be overridden by setting `fsmonitor.allowRemote` to\n+`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n+correctly with all network-mounted repositores and such use is considered\n+experimental.\n+\n+On Mac OS, the inter-process communication (IPC) between various Git\n+commands and the fsmonitor daemon is done via a Unix domain socket (UDS).\n+Usage of UDS requires the creation of a file which, by default, is created\n+in the .git directory.  If the fsmonitor daemon detects that the .git directory\n+is on a network-mounted file system, it will create the UDS file in $HOME.  If\n+$HOME itself is on a network-mounted file system or if $HOME is not the desired\n+location for the UDS file, 'fsmonitor.socketDir' may be set to any valid, local\n+directory on a file system with proper support.  Mac OS native file systems have\n+the required support.  File systems known to lack support include FAT32 and\n+NTFS.  Other file systems may or many not have the needed support; the fsmonitor\n+daemon is not guaranteed to work with these file systems and such use is\n+considered experimental.\n+\n+CONFIGURATION\n+-------------\n+When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])\n+the fsmonitor daemon will pay attention to the following configuration\n+variables:\n+\n+fsmonitor.allowRemote::\n+\tBy default, the daemon refuses to work against network-mounted\n+\trepositories. Setting `fsmonitor.allowRemote` to `true` overrides\n+\tthis behavior.\n+\n+fsmonitor.socketDir::\n+\tThis option is only used by the Mac OS implementation of the fsmonitor\n+\tdaemon.\tIf set, 'fsmonitor.socketDir' must be set to a valid, local\n+\tdirectory on a file system that can support Unix domain sockets (UDS).\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \ngitgitgadget\n"},{"id":"462999","messageId":"9c1f408ae6dabaed26e57b6b1946dd2bb5d91bb8.1663100859.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v6.git.1663100858.gitgitgadget@gmail.com","subject":"[PATCH v6 5/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-13T20:27:37Z","receivedAt":"2022-09-13T20:28:17Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n builtin/fsmonitor--daemon.c              |  8 +++\n compat/fsmonitor/fsm-listen-darwin.c     |  6 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 92 ++++++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 17 +++++\n fsmonitor--daemon.h                      |  6 ++\n fsmonitor-path-utils.h                   | 36 ++++++++++\n 6 files changed, 164 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 2c109cf8b37..32475962c92 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"fsmonitor.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n #include \"compat/fsmonitor/fsm-health.h\"\n #include \"compat/fsmonitor/fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n@@ -1282,6 +1283,13 @@ static int fsmonitor_run_daemon(void)\n \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n \tstate.nr_paths_watching = 1;\n \n+\tstate.alias.alias = NULL;\n+\tstate.alias.points_to = NULL;\n+\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n+\t\terr = error(_(\"could not get worktree alias\"));\n+\t\tgoto done;\n+\t}\n+\n \t/*\n \t * We create and delete cookie files somewhere inside the .git\n \t * directory to help us keep sync with the file system.  If\ndiff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\nindex 8e208e8289e..179886bc15b 100644\n--- a/compat/fsmonitor/fsm-listen-darwin.c\n+++ b/compat/fsmonitor/fsm-listen-darwin.c\n@@ -26,6 +26,7 @@\n #include \"fsmonitor.h\"\n #include \"fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsm_listen_data\n {\n@@ -209,7 +210,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t/*\n \t\t * On Mac, we receive an array of absolute paths.\n \t\t */\n-\t\tpath_k = paths[k];\n+\t\tpath_k = fsmonitor__resolve_alias(paths[k], &state->alias);\n+\t\tif (!path_k)\n+\t\t\tpath_k = paths[k];\n \n \t\t/*\n \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n@@ -238,6 +241,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t\tfsmonitor_force_resync(state);\n \t\t\tfsmonitor_batch__free_list(batch);\n \t\t\tstring_list_clear(&cookie_list, 0);\n+\t\t\tbatch = NULL;\n \n \t\t\t/*\n \t\t\t * We assume that any events that we received\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nindex 067cbe6990a..13807f58e95 100644\n--- a/compat/fsmonitor/fsm-path-utils-darwin.c\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -1,5 +1,8 @@\n #include \"fsmonitor.h\"\n #include \"fsmonitor-path-utils.h\"\n+#include <dirent.h>\n+#include <errno.h>\n+#include <fcntl.h>\n #include <sys/param.h>\n #include <sys/mount.h>\n \n@@ -38,3 +41,92 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * Scan the root directory for synthetic firmlinks that when resolved\n+ * are a prefix of the path, stopping at the first one found.\n+ *\n+ * Some information about firmlinks and synthetic firmlinks:\n+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n+ *\n+ * macOS no longer allows symlinks in the root directory; any link found\n+ * there is therefore a synthetic firmlink.\n+ *\n+ * If this function gets called often, will want to cache all the firmlink\n+ * information, but for now there is only one caller of this function.\n+ *\n+ * If there is more than one alias for the path, that is another\n+ * matter altogether.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\tDIR * dir;\n+\tint read;\n+\tint retval;\n+\tstruct dirent *de;\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+\n+\tretval = 0;\n+\tdir = opendir(\"/\");\n+\tif (!dir)\n+\t\treturn -1;\n+\n+\tstrbuf_init(&alias, 256);\n+\tstrbuf_init(&points_to, MAXPATHLEN);\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstrbuf_reset(&alias);\n+\t\tstrbuf_addch(&alias, '/');\n+\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n+\n+\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n+\t\tif (read > 0) {\n+\t\t\tstrbuf_setlen(&points_to, read);\n+\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n+\t\t\t\t&& path[points_to.len] == '/') {\n+\t\t\t\tinfo->alias = strbuf_detach(&alias, NULL);\n+\t\t\t\tinfo->points_to = strbuf_detach(&points_to, NULL);\n+\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n+\t\t\t\t\tpath, info->alias, info->points_to);\n+\t\t\t\tretval = 0;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n+\t\t\ttrace_printf_key(&trace_fsmonitor, \"Error %s\", strerror(errno));\n+\t\t\tretval = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n+\tdone:\n+\tclosedir(dir);\n+\tstrbuf_release(&alias);\n+\tstrbuf_release(&points_to);\n+\treturn retval;\n+}\n+\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\tint len = info->alias ? strlen(info->alias) : 0;\n+\n+\tif (!len)\n+\t\treturn NULL;\n+\n+\tif ((strncmp(info->alias, path, len) == 0)\n+\t\t&& path[len] == '/') {\n+\t\tstruct strbuf tmp;\n+\t\tconst char *remainder = path + len;\n+\t\tint ptr_len = strlen(info->points_to);\n+\t\tint rem_len = strlen(remainder);\n+\n+\t\tstrbuf_init(&tmp, ptr_len + rem_len);\n+\t\tstrbuf_add(&tmp, info->points_to, ptr_len);\n+\t\tstrbuf_add(&tmp, remainder, rem_len);\n+\t\treturn strbuf_detach(&tmp, NULL);\n+\t}\n+\n+\treturn NULL;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nindex a90b8f7925b..0d95bbb416f 100644\n--- a/compat/fsmonitor/fsm-path-utils-win32.c\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -126,3 +126,20 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * No-op for now.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\treturn 0;\n+}\n+\n+/*\n+ * No-op for now.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\treturn NULL;\n+}\ndiff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\nindex 2102a5c9ff5..98cbb430083 100644\n--- a/fsmonitor--daemon.h\n+++ b/fsmonitor--daemon.h\n@@ -8,6 +8,7 @@\n #include \"run-command.h\"\n #include \"simple-ipc.h\"\n #include \"thread-utils.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsmonitor_batch;\n struct fsmonitor_token_data;\n@@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct strbuf path_worktree_watch;\n \tstruct strbuf path_gitdir_watch;\n+\tstruct alias_info alias;\n \tint nr_paths_watching;\n \n \tstruct fsmonitor_token_data *current_token_data;\n@@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct ipc_server_data *ipc_server_data;\n \tstruct strbuf path_ipc;\n+\n };\n \n /*\n@@ -167,5 +170,8 @@ void fsmonitor_publish(struct fsmonitor_daemon_state *state,\n  */\n void fsmonitor_force_resync(struct fsmonitor_daemon_state *state);\n \n+char *fsmonitor_resolve_alias(const char *path,\n+\tstruct alias_info *alias);\n+\n #endif /* HAVE_FSMONITOR_DAEMON_BACKEND */\n #endif /* FSMONITOR_DAEMON_H */\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nindex e48592887e7..50ef37e57bb 100644\n--- a/fsmonitor-path-utils.h\n+++ b/fsmonitor-path-utils.h\n@@ -1,6 +1,14 @@\n #ifndef FSM_PATH_UTILS_H\n #define FSM_PATH_UTILS_H\n \n+#include \"strbuf.h\"\n+\n+struct alias_info\n+{\n+\tchar *alias;\n+\tchar *points_to;\n+};\n+\n struct fs_info {\n \tint is_remote;\n \tchar *typename;\n@@ -20,4 +28,32 @@ int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n  */\n int fsmonitor__is_fs_remote(const char *path);\n \n+/*\n+ * Get the alias in given path, if any.\n+ *\n+ * Sets alias to the first alias that matches any part of the path.\n+ *\n+ * If an alias is found, info.alias and info.points_to are set to the\n+ * found mapping.\n+ *\n+ * Returns -1 on error, 0 otherwise.\n+ *\n+ * The caller owns the storage that is occupied by set info.alias and\n+ * info.points_to and is responsible for releasing it with `free(3)`\n+ * when done.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info);\n+\n+/*\n+ * Resolve the path against the given alias.\n+ *\n+ * Returns the resolved path if there is one, NULL otherwise.\n+ *\n+ * The caller owns the storage that the returned string occupies and\n+ * is responsible for releasing it with `free(3)` when done.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info);\n+\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"463008","messageId":"xmqqillq7pdi.fsf@gitster.g","threadId":"58324","inReplyTo":"edef029a298a44ba59d19db53c2f7ba07e93aec6.1663100859.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v6 3/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-14T00:48:09Z","receivedAt":"2022-09-14T00:48:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +const char *fsmonitor_ipc__get_path(void)\n> +{\n\nLooks like a bit klunky API.  I would have expected it to take at\nleast the path to the worktree or a pointer to struct repository.\n\n> +\tstatic const char *ipc_path;\n> +\tSHA_CTX sha1ctx;\n> +\tchar *sock_dir;\n> +\tstruct strbuf ipc_file = STRBUF_INIT;\n> +\tunsigned char hash[SHA_DIGEST_LENGTH];\n> +\n> +\tif (ipc_path)\n> +\t\treturn ipc_path;\n> +\n> +\tipc_path = fsmonitor_ipc__get_default_path();\n> +\n> +\t/* By default the socket file is created in the .git directory */\n> +\tif (fsmonitor__is_fs_remote(ipc_path) < 1)\n> +\t\treturn ipc_path;\n> +\n> +\tSHA1_Init(&sha1ctx);\n> +\tSHA1_Update(&sha1ctx, the_repository->worktree, strlen(the_repository->worktree));\n> +\tSHA1_Final(hash, &sha1ctx);\n\nI would not worry about SHA-1 hash collision for this use case, but\nwould worry more about .worktree possible being unique.\n\nCan the .worktree string be aliased for the same directory in some\nway (e.g. depending on the initialization sequence, can it be a full\npathname, a relative pathname, or can a pathname that looks like a\nfull-pathname have symbolic link component in it?) that ends up\ncreating two or more socket for the same worktree?\n\n> +\trepo_config_get_string(the_repository, \"fsmonitor.socketdir\", &sock_dir);\n> +\n> +\t/* Create the socket file in either socketDir or $HOME */\n> +\tif (sock_dir && *sock_dir)\n> +\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n> +\t\t\t\t\tsock_dir, hash_to_hex(hash));\n> +\telse\n> +\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n> +\n> +\tipc_path = interpolate_path(ipc_file.buf, 1);\n> +\tif (!ipc_path)\n> +\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n> +\n> +\tstrbuf_release(&ipc_file);\n> +\treturn ipc_path;\n> +}\n"},{"id":"463010","messageId":"xmqq7d267mli.fsf@gitster.g","threadId":"58324","inReplyTo":"3428bcf8763b553b722699d4095cb30cbf9709b6.1663100859.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v6 4/6] fsmonitor: avoid socket location check if using hook","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-14T01:48:09Z","receivedAt":"2022-09-14T01:48:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n>\n> If monitoring is done via fsmonitor hook rather than IPC there is no\n> need to check if the location of the Unix Domain socket (UDS) file is\n> on a remote filesystem.\n\nNicely done.\n\n\n>\n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n>  compat/fsmonitor/fsm-settings-darwin.c | 10 ++++++----\n>  compat/fsmonitor/fsm-settings-win32.c  |  2 +-\n>  fsmonitor-settings.c                   |  8 ++++----\n>  fsmonitor-settings.h                   |  2 +-\n>  4 files changed, 12 insertions(+), 10 deletions(-)\n>\n> diff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\n> index dba3ced6bb7..3463c71763e 100644\n> --- a/compat/fsmonitor/fsm-settings-darwin.c\n> +++ b/compat/fsmonitor/fsm-settings-darwin.c\n> @@ -49,13 +49,15 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n>  \treturn FSMONITOR_REASON_OK;\n>  }\n>  \n> -enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n> +enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n>  {\n>  \tenum fsmonitor_reason reason;\n>  \n> -\treason = check_uds_volume(r);\n> -\tif (reason != FSMONITOR_REASON_OK)\n> -\t\treturn reason;\n> +\tif (ipc) {\n> +\t\treason = check_uds_volume(r);\n> +\t\tif (reason != FSMONITOR_REASON_OK)\n> +\t\t\treturn reason;\n> +\t}\n>  \n>  \treturn FSMONITOR_REASON_OK;\n>  }\n> diff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\n> index d88b06ae610..a8af31b71de 100644\n> --- a/compat/fsmonitor/fsm-settings-win32.c\n> +++ b/compat/fsmonitor/fsm-settings-win32.c\n> @@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n>  \treturn FSMONITOR_REASON_OK;\n>  }\n>  \n> -enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n> +enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n>  {\n>  \tenum fsmonitor_reason reason;\n>  \n> diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\n> index d288cbad479..531a1b6f956 100644\n> --- a/fsmonitor-settings.c\n> +++ b/fsmonitor-settings.c\n> @@ -60,7 +60,7 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n>  }\n>  #endif\n>  \n> -static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n> +static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n>  {\n>  \tif (!r->worktree) {\n>  \t\t/*\n> @@ -77,7 +77,7 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n>  \t\treason = check_remote(r);\n>  \t\tif (reason != FSMONITOR_REASON_OK)\n>  \t\t\treturn reason;\n> -\t\treason = fsm_os__incompatible(r);\n> +\t\treason = fsm_os__incompatible(r, ipc);\n>  \t\tif (reason != FSMONITOR_REASON_OK)\n>  \t\t\treturn reason;\n>  \t}\n> @@ -162,7 +162,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n>  \n>  void fsm_settings__set_ipc(struct repository *r)\n>  {\n> -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n> +\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n>  \n>  \tif (reason != FSMONITOR_REASON_OK) {\n>  \t\tfsm_settings__set_incompatible(r, reason);\n> @@ -185,7 +185,7 @@ void fsm_settings__set_ipc(struct repository *r)\n>  \n>  void fsm_settings__set_hook(struct repository *r, const char *path)\n>  {\n> -\tenum fsmonitor_reason reason = check_for_incompatible(r);\n> +\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n>  \n>  \tif (reason != FSMONITOR_REASON_OK) {\n>  \t\tfsm_settings__set_incompatible(r, reason);\n> diff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\n> index d9c2605197f..0721617b95a 100644\n> --- a/fsmonitor-settings.h\n> +++ b/fsmonitor-settings.h\n> @@ -48,7 +48,7 @@ struct fsmonitor_settings;\n>   * fsm_os__* routines should considered private to fsm_settings__\n>   * routines.\n>   */\n> -enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n> +enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n>  #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n>  \n>  #endif /* FSMONITOR_SETTINGS_H */\n"},{"id":"463018","messageId":"BL0PR05MB557111B64B71717EA2D5CC91D9469@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"xmqqillq7pdi.fsf@gitster.g","subject":"RE: [PATCH v6 3/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-14T15:47:03Z","receivedAt":"2022-09-14T15:47:15Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Junio C Hamano <gitster@pobox.com>\n> Sent: Tuesday, September 13, 2022 8:48 PM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>\n> Cc: git@vger.kernel.org; Jeff Hostetler <git@jeffhostetler.com>; Eric Sunshine\n> <sunshine@sunshineco.com>; Torsten Bögershausen <tboegi@web.de>;\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com>; Ramsay Jones\n> <ramsay@ramsayjones.plus.com>; Johannes Schindelin\n> <Johannes.Schindelin@gmx.de>; Eric DeCosta <edecosta@mathworks.com>\n> Subject: Re: [PATCH v6 3/6] fsmonitor: relocate socket file if .git directory is\n> remote\n> \n> \"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n> > +const char *fsmonitor_ipc__get_path(void) {\n> \n> Looks like a bit klunky API.  I would have expected it to take at least the path\n> to the worktree or a pointer to struct repository.\n> \nOK, I'll change it to take a pointer to struct repository.\n\n> > +\tstatic const char *ipc_path;\n> > +\tSHA_CTX sha1ctx;\n> > +\tchar *sock_dir;\n> > +\tstruct strbuf ipc_file = STRBUF_INIT;\n> > +\tunsigned char hash[SHA_DIGEST_LENGTH];\n> > +\n> > +\tif (ipc_path)\n> > +\t\treturn ipc_path;\n> > +\n> > +\tipc_path = fsmonitor_ipc__get_default_path();\n> > +\n> > +\t/* By default the socket file is created in the .git directory */\n> > +\tif (fsmonitor__is_fs_remote(ipc_path) < 1)\n> > +\t\treturn ipc_path;\n> > +\n> > +\tSHA1_Init(&sha1ctx);\n> > +\tSHA1_Update(&sha1ctx, the_repository->worktree,\n> strlen(the_repository->worktree));\n> > +\tSHA1_Final(hash, &sha1ctx);\n> \n> I would not worry about SHA-1 hash collision for this use case, but would\n> worry more about .worktree possible being unique.\n> \n> Can the .worktree string be aliased for the same directory in some way (e.g.\n> depending on the initialization sequence, can it be a full pathname, a relative\n> pathname, or can a pathname that looks like a full-pathname have symbolic\n> link component in it?) that ends up creating two or more socket for the same\n> worktree?\n> \n\n.worktree is set to the real path and since hardlinks are not allowed across file systems, I think we are OK.\n\n> > +\trepo_config_get_string(the_repository, \"fsmonitor.socketdir\",\n> > +&sock_dir);\n> > +\n> > +\t/* Create the socket file in either socketDir or $HOME */\n> > +\tif (sock_dir && *sock_dir)\n> > +\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n> > +\t\t\t\t\tsock_dir, hash_to_hex(hash));\n> > +\telse\n> > +\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\",\n> hash_to_hex(hash));\n> > +\n> > +\tipc_path = interpolate_path(ipc_file.buf, 1);\n> > +\tif (!ipc_path)\n> > +\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n> > +\n> > +\tstrbuf_release(&ipc_file);\n> > +\treturn ipc_path;\n> > +}\n\n"},{"id":"463097","messageId":"ac9c12cf-866d-565f-fbf8-58dbfdd92ef1@jeffhostetler.com","threadId":"58324","inReplyTo":"pull.1326.v6.git.1663100858.gitgitgadget@gmail.com","subject":"Re: [PATCH v6 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-09-16T17:58:53Z","receivedAt":"2022-09-16T17:59:00Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 9/13/22 4:27 PM, Eric DeCosta via GitGitGadget wrote:\n> Follow-on to the work done to allow Windows to work against network-mounted\n> repos for macOS.\n> \n> Have macOS take advantage of the same configuration option,\n> 'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\n> to true will override the default behavior (erroring-out) when a\n> network-mounted repo is detected by fsmonitor.\n> \n> The added wrinkle being that the Unix domain socket (UDS) file used for IPC\n> cannot be created in a network location; instead $HOME is used if the\n> default location is on the network. The user may, optionally, set the\n> 'fsmonitor.socketDir' configuration option to a valid, local directory if\n> $HOME itself is on the network or is simply not the desired location for the\n> UDS file.\n> \n> An additional issue is that for mount points in the root directory, FSEvents\n> does not report a path that matches the worktree directory due to the\n> introduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\n> to the worktree directory by interrogating the root filesystem for synthetic\n> firmlinks and using that information to translate the path.\n> \n> v6 differs from v5:\n> \n>   * incorporates earlier, Windows-specific changes that have not made it back\n>     yet to the master branch\n>   * incorporates code review feedback\n>   * adds documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n> \n> v5 differs significantly from earlier versions:\n> \n>   * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n>     such that these options are no longer added to the settings data\n>     structure but are rather read from config at point of use\n>   * refactoring of code for handling platform-specific file system checks via\n>     a common interface to avoid platform #ifdef in IPC code and be in-model\n>     with other platform-specific fsmonitor code\n>   * dealing with 'synthetic firmlinks' on macOS\n> \n\nI've looked at v5 and v6 and I like the direction this is heading,\nso I'll mark this LGTM.  (I'm still traveling back from Git Merge,\nso I haven't had time to test it out, but I think we should proceed\nwith it.)\n\nThanks for you patience and attention to detail on this!\nJeff\n\n"},{"id":"463100","messageId":"pull.1326.v7.git.1663358014.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v6.git.1663100858.gitgitgadget@gmail.com","subject":"[PATCH v7 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-16T19:53:28Z","receivedAt":"2022-09-16T19:53:42Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"Follow-on to the work done to allow Windows to work against network-mounted\nrepos for macOS.\n\nHave macOS take advantage of the same configuration option,\n'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\nto true will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for IPC\ncannot be created in a network location; instead $HOME is used if the\ndefault location is on the network. The user may, optionally, set the\n'fsmonitor.socketDir' configuration option to a valid, local directory if\n$HOME itself is on the network or is simply not the desired location for the\nUDS file.\n\nAn additional issue is that for mount points in the root directory, FSEvents\ndoes not report a path that matches the worktree directory due to the\nintroduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\nto the worktree directory by interrogating the root filesystem for synthetic\nfirmlinks and using that information to translate the path.\n\nv7 differs from v6:\n\n * incorporates code review feedback\n\nv6 differs from v5:\n\n * incorporates earlier, Windows-specific changes that have not made it back\n   yet to the master branch\n * incorporates code review feedback\n * adds documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n\nv5 differs significantly from earlier versions:\n\n * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n   such that these options are no longer added to the settings data\n   structure but are rather read from config at point of use\n * refactoring of code for handling platform-specific file system checks via\n   a common interface to avoid platform #ifdef in IPC code and be in-model\n   with other platform-specific fsmonitor code\n * dealing with 'synthetic firmlinks' on macOS\n\nEric DeCosta (6):\n  fsmonitor: refactor filesystem checks to common interface\n  fsmonitor: relocate socket file if .git directory is remote\n  fsmonitor: avoid socket location check if using hook\n  fsmonitor: deal with synthetic firmlinks on macOS\n  fsmonitor: deal with synthetic firmlinks on macOS\n  fsmonitor: add documentation for allowRemote and socketDir options\n\n Documentation/git-fsmonitor--daemon.txt  |  35 +++++\n Makefile                                 |   2 +\n builtin/fsmonitor--daemon.c              |  11 +-\n compat/fsmonitor/fsm-ipc-darwin.c        |  49 +++++++\n compat/fsmonitor/fsm-ipc-win32.c         |   9 ++\n compat/fsmonitor/fsm-listen-darwin.c     |   6 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 132 +++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 145 +++++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  70 +++------\n compat/fsmonitor/fsm-settings-win32.c    | 174 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   4 +\n fsmonitor--daemon.h                      |   6 +\n fsmonitor-ipc.c                          |  18 ++-\n fsmonitor-ipc.h                          |   4 +-\n fsmonitor-path-utils.h                   |  59 ++++++++\n fsmonitor-settings.c                     |  58 +++++++-\n fsmonitor-settings.h                     |   2 +-\n 17 files changed, 547 insertions(+), 237 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\n\nbase-commit: d3fa443f97e3a8d75b51341e2d5bac380b7422df\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v7\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v7\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v6:\n\n 1:  3233c908c4a < -:  ----------- fsmonitor: option to allow fsmonitor to run against network-mounted repos\n 2:  d2a8fc6b707 = 1:  155a6890806 fsmonitor: refactor filesystem checks to common interface\n 3:  edef029a298 ! 2:  075340bd2a7 fsmonitor: relocate socket file if .git directory is remote\n     @@ Makefile: ifdef FSMONITOR_DAEMON_BACKEND\n       \n       ifdef FSMONITOR_OS_SETTINGS\n      \n     + ## builtin/fsmonitor--daemon.c ##\n     +@@ builtin/fsmonitor--daemon.c: static int fsmonitor_run_daemon(void)\n     + \t * directory.)\n     + \t */\n     + \tstrbuf_init(&state.path_ipc, 0);\n     +-\tstrbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));\n     ++\tstrbuf_addstr(&state.path_ipc,\n     ++\t\tabsolute_path(fsmonitor_ipc__get_path(the_repository)));\n     + \n     + \t/*\n     + \t * Confirm that we can create platform-specific resources for the\n     +\n       ## compat/fsmonitor/fsm-ipc-darwin.c (new) ##\n      @@\n      +#include \"cache.h\"\n     @@ compat/fsmonitor/fsm-ipc-darwin.c (new)\n      +\n      +static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n      +\n     -+const char *fsmonitor_ipc__get_path(void)\n     ++const char *fsmonitor_ipc__get_path(struct repository *r)\n      +{\n      +\tstatic const char *ipc_path;\n      +\tSHA_CTX sha1ctx;\n     @@ compat/fsmonitor/fsm-ipc-darwin.c (new)\n      +\tif (ipc_path)\n      +\t\treturn ipc_path;\n      +\n     ++\tif (!r)\n     ++\t\tr = the_repository;\n     ++\n      +\tipc_path = fsmonitor_ipc__get_default_path();\n      +\n      +\t/* By default the socket file is created in the .git directory */\n     @@ compat/fsmonitor/fsm-ipc-darwin.c (new)\n      +\t\treturn ipc_path;\n      +\n      +\tSHA1_Init(&sha1ctx);\n     -+\tSHA1_Update(&sha1ctx, the_repository->worktree, strlen(the_repository->worktree));\n     ++\tSHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n      +\tSHA1_Final(hash, &sha1ctx);\n      +\n     -+\trepo_config_get_string(the_repository, \"fsmonitor.socketdir\", &sock_dir);\n     ++\trepo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n      +\n      +\t/* Create the socket file in either socketDir or $HOME */\n      +\tif (sock_dir && *sock_dir)\n     @@ compat/fsmonitor/fsm-ipc-darwin.c (new)\n      \n       ## compat/fsmonitor/fsm-ipc-win32.c (new) ##\n      @@\n     -+#include \"cache.h\"\n     ++#include \"config.h\"\n      +#include \"fsmonitor-ipc.h\"\n      +\n     -+GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n     ++const char *fsmonitor_ipc__get_path(struct repository *r) {\n     ++\tstatic char *ret;\n     ++\tif (!ret)\n     ++\t\tret = git_pathdup(\"fsmonitor--daemon.ipc\");\n     ++\treturn ret;\n     ++}\n     + \\ No newline at end of file\n     +\n     + ## compat/fsmonitor/fsm-settings-darwin.c ##\n     +@@\n     + static enum fsmonitor_reason check_uds_volume(struct repository *r)\n     + {\n     + \tstruct fs_info fs;\n     +-\tconst char *ipc_path = fsmonitor_ipc__get_path();\n     ++\tconst char *ipc_path = fsmonitor_ipc__get_path(r);\n     + \tstruct strbuf path = STRBUF_INIT;\n     + \tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n     + \n      \n       ## contrib/buildsystems/CMakeLists.txt ##\n      @@ contrib/buildsystems/CMakeLists.txt: if(SUPPORTS_SIMPLE_IPC)\n     @@ contrib/buildsystems/CMakeLists.txt: if(SUPPORTS_SIMPLE_IPC)\n       \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n      \n       ## fsmonitor-ipc.c ##\n     +@@ fsmonitor-ipc.c: int fsmonitor_ipc__is_supported(void)\n     + \treturn 0;\n     + }\n     + \n     +-const char *fsmonitor_ipc__get_path(void)\n     ++const char *fsmonitor_ipc__get_path(struct repository *r)\n     + {\n     + \treturn NULL;\n     + }\n      @@ fsmonitor-ipc.c: int fsmonitor_ipc__is_supported(void)\n       \treturn 1;\n       }\n     @@ fsmonitor-ipc.c: int fsmonitor_ipc__is_supported(void)\n      -\n       enum ipc_active_state fsmonitor_ipc__get_state(void)\n       {\n     - \treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n     +-\treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n     ++\treturn ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));\n     + }\n     + \n     + static int spawn_daemon(void)\n     +@@ fsmonitor-ipc.c: int fsmonitor_ipc__send_query(const char *since_token,\n     + \ttrace2_data_string(\"fsm_client\", NULL, \"query/command\", tok);\n     + \n     + try_again:\n     +-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n     +-\t\t\t\t       &connection);\n     ++\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n     ++\t\t\t\t\t\t&options, &connection);\n     + \n     + \tswitch (state) {\n     + \tcase IPC_STATE__LISTENING:\n     +@@ fsmonitor-ipc.c: try_again:\n     + \n     + \tcase IPC_STATE__INVALID_PATH:\n     + \t\tret = error(_(\"fsmonitor_ipc__send_query: invalid path '%s'\"),\n     +-\t\t\t    fsmonitor_ipc__get_path());\n     ++\t\t\t    fsmonitor_ipc__get_path(the_repository));\n     + \t\tgoto done;\n     + \n     + \tcase IPC_STATE__OTHER_ERROR:\n     + \tdefault:\n     + \t\tret = error(_(\"fsmonitor_ipc__send_query: unspecified error on '%s'\"),\n     +-\t\t\t    fsmonitor_ipc__get_path());\n     ++\t\t\t    fsmonitor_ipc__get_path(the_repository));\n     + \t\tgoto done;\n     + \t}\n     + \n     +@@ fsmonitor-ipc.c: int fsmonitor_ipc__send_command(const char *command,\n     + \toptions.wait_if_busy = 1;\n     + \toptions.wait_if_not_found = 0;\n     + \n     +-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n     +-\t\t\t\t       &connection);\n     ++\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n     ++\t\t\t\t\t\t&options, &connection);\n     + \tif (state != IPC_STATE__LISTENING) {\n     + \t\tdie(_(\"fsmonitor--daemon is not running\"));\n     + \t\treturn -1;\n     +\n     + ## fsmonitor-ipc.h ##\n     +@@\n     + \n     + #include \"simple-ipc.h\"\n     + \n     ++struct repository;\n     ++\n     + /*\n     +  * Returns true if built-in file system monitor daemon is defined\n     +  * for this platform.\n     +@@ fsmonitor-ipc.h: int fsmonitor_ipc__is_supported(void);\n     +  *\n     +  * Returns NULL if the daemon is not supported on this platform.\n     +  */\n     +-const char *fsmonitor_ipc__get_path(void);\n     ++const char *fsmonitor_ipc__get_path(struct repository *r);\n     + \n     + /*\n     +  * Try to determine whether there is a `git-fsmonitor--daemon` process\n 4:  3428bcf8763 = 3:  5518d2f3e03 fsmonitor: avoid socket location check if using hook\n 5:  9c1f408ae6d ! 4:  3a9fe473cf4 fsmonitor: deal with synthetic firmlinks on macOS\n     @@ fsmonitor--daemon.h: struct fsmonitor_daemon_state {\n       };\n       \n       /*\n     -@@ fsmonitor--daemon.h: void fsmonitor_publish(struct fsmonitor_daemon_state *state,\n     -  */\n     - void fsmonitor_force_resync(struct fsmonitor_daemon_state *state);\n     - \n     -+char *fsmonitor_resolve_alias(const char *path,\n     -+\tstruct alias_info *alias);\n     -+\n     - #endif /* HAVE_FSMONITOR_DAEMON_BACKEND */\n     - #endif /* FSMONITOR_DAEMON_H */\n      \n       ## fsmonitor-path-utils.h ##\n      @@\n -:  ----------- > 5:  4d00adb1deb fsmonitor: deal with synthetic firmlinks on macOS\n 6:  d2c95e34d3a = 6:  260591f5820 fsmonitor: add documentation for allowRemote and socketDir options\n\n-- \ngitgitgadget\n"},{"id":"463101","messageId":"155a689080658e1953fc4434016f746f371ba08a.1663358014.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v7.git.1663358014.gitgitgadget@gmail.com","subject":"[PATCH v7 1/6] fsmonitor: refactor filesystem checks to common interface","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-16T19:53:29Z","receivedAt":"2022-09-16T19:53:46Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nProvide a common interface for getting basic filesystem information\nincluding filesystem type and whether the filesystem is remote.\n\nRefactor existing code for getting basic filesystem info and detecting\nremote file systems to the new interface.\n\nRefactor filesystem checks to leverage new interface. For macOS,\nerror-out if the Unix Domain socket (UDS) file is on a remote\nfilesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                                 |   1 +\n compat/fsmonitor/fsm-path-utils-darwin.c |  40 ++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 128 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  62 +++-----\n compat/fsmonitor/fsm-settings-win32.c    | 172 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   2 +\n fsmonitor-path-utils.h                   |  23 +++\n fsmonitor-settings.c                     |  50 +++++++\n 8 files changed, 263 insertions(+), 215 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\ndiff --git a/Makefile b/Makefile\nindex d9247ead45b..6e492a67547 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2039,6 +2039,7 @@ endif\n ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n ifeq ($(TCLTK_PATH),)\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nnew file mode 100644\nindex 00000000000..067cbe6990a\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -0,0 +1,40 @@\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+#include <sys/param.h>\n+#include <sys/mount.h>\n+\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\tstruct statfs fs;\n+\tif (statfs(path, &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\treturn -1;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\n+\tif (!(fs.f_flags & MNT_LOCAL))\n+\t\tfs_info->is_remote = 1;\n+\telse\n+\t\tfs_info->is_remote = 0;\n+\n+\tfs_info->typename = fs.f_fstypename;\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nnew file mode 100644\nindex 00000000000..a90b8f7925b\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -0,0 +1,128 @@\n+#include \"cache.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+/*\n+ * Check remote working directory protocol.\n+ *\n+ * Return -1 if client machine cannot get remote protocol information.\n+ */\n+static int check_remote_protocol(wchar_t *wpath)\n+{\n+\tHANDLE h;\n+\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n+\n+\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n+\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\n+\tif (h == INVALID_HANDLE_VALUE) {\n+\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n+\t\t&proto_info, sizeof(proto_info))) {\n+\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\tCloseHandle(h);\n+\t\treturn -1;\n+\t}\n+\n+\tCloseHandle(h);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n+\t\t\t\twpath, proto_info.Protocol);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Notes for testing:\n+ *\n+ * (a) Windows allows a network share to be mapped to a drive letter.\n+ *     (This is the normal method to access it.)\n+ *\n+ *     $ NET USE Z: \\\\server\\share\n+ *     $ git -C Z:/repo status\n+ *\n+ * (b) Windows allows a network share to be referenced WITHOUT mapping\n+ *     it to drive letter.\n+ *\n+ *     $ NET USE \\\\server\\share\\dir\n+ *     $ git -C //server/share/repo status\n+ *\n+ * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n+ *     arbitrary path (which may be remote)\n+ *\n+ *     $ SUBST Q: Z:\\repo\n+ *     $ git -C Q:/ status\n+ *\n+ * (d) Windows allows a directory symlink to be created on a local\n+ *     file system that points to a remote repo.\n+ *\n+ *     $ mklink /d ./link //server/share/repo\n+ *     $ git -C ./link status\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\twchar_t wpath[MAX_PATH];\n+\twchar_t wfullpath[MAX_PATH];\n+\tsize_t wlen;\n+\tUINT driveType;\n+\n+\t/*\n+\t * Do everything in wide chars because the drive letter might be\n+\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n+\t */\n+\tif (xutftowcs_path(wpath, path) < 0) {\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * GetDriveTypeW() requires a final slash.  We assume that the\n+\t * worktree pathname points to an actual directory.\n+\t */\n+\twlen = wcslen(wpath);\n+\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n+\t\twpath[wlen++] = L'\\\\';\n+\t\twpath[wlen] = 0;\n+\t}\n+\n+\t/*\n+\t * Normalize the path.  If nothing else, this converts forward\n+\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n+\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n+\t */\n+\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {\n+\t\treturn -1;\n+\t}\n+\n+\tdriveType = GetDriveTypeW(wfullpath);\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n+\t\t\t path, wfullpath, driveType);\n+\n+\tif (driveType == DRIVE_REMOTE) {\n+\t\tfs_info->is_remote = 1;\n+\t\tif (check_remote_protocol(wfullpath) < 0)\n+\t\t\treturn -1;\n+\t} else {\n+\t\tfs_info->is_remote = 0;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dba3ced6bb7 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -1,32 +1,10 @@\n-#include \"cache.h\"\n #include \"config.h\"\n-#include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n-#include <sys/param.h>\n-#include <sys/mount.h>\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n-/*\n- * [1] Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type (NFS, SAMBA, etc.) dictates whether notification events\n- * are available at all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n+ /*\n  * For the builtin FSMonitor, we create the Unix domain socket for the\n  * IPC in the .git directory.  If the working directory is remote,\n  * then the socket will be created on the remote file system.  This\n@@ -38,40 +16,34 @@\n  * be taken to ensure that $HOME is actually local and not a managed\n  * file share.)\n  *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n+ * FAT32 and NTFS working directories are problematic too.\n  *\n  * The builtin FSMonitor uses a Unix domain socket in the .git\n  * directory for IPC.  These Windows drive formats do not support\n  * Unix domain sockets, so mark them as incompatible for the daemon.\n  *\n  */\n-static enum fsmonitor_reason check_volume(struct repository *r)\n+static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n-\tstruct statfs fs;\n+\tstruct fs_info fs;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \n-\tif (statfs(r->worktree, &fs) == -1) {\n-\t\tint saved_errno = errno;\n-\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n-\t\t\t\t r->worktree, strerror(saved_errno));\n-\t\terrno = saved_errno;\n+\tif (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {\n+\t\tstrbuf_release(&path);\n \t\treturn FSMONITOR_REASON_ERROR;\n \t}\n \n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n-\t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n \n-\tif (!(fs.f_flags & MNT_LOCAL))\n+\tif (fs.is_remote)\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n-\tif (!strcmp(fs.f_fstypename, \"msdos\")) /* aka FAT32 */\n+\tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n-\tif (!strcmp(fs.f_fstypename, \"ntfs\"))\n+\tif (!strcmp(fs.typename, \"ntfs\"))\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \treturn FSMONITOR_REASON_OK;\n@@ -81,7 +53,7 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_volume(r);\n+\treason = check_uds_volume(r);\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex e5ec5b0a9f7..d88b06ae610 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -1,8 +1,9 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * VFS for Git is incompatible with FSMonitor.\n@@ -24,171 +25,6 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-/*\n- * Check if monitoring remote working directories is allowed.\n- *\n- * By default, monitoring remote working directories is\n- * disabled.  Users may override this behavior in enviroments where\n- * they have proper support.\n- */\n-static int check_config_allowremote(struct repository *r)\n-{\n-\tint allow;\n-\n-\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n-\t\treturn allow;\n-\n-\treturn -1; /* fsmonitor.allowremote not set */\n-}\n-\n-/*\n- * Check remote working directory protocol.\n- *\n- * Error if client machine cannot get remote protocol information.\n- */\n-static int check_remote_protocol(wchar_t *wpath)\n-{\n-\tHANDLE h;\n-\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n-\n-\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n-\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n-\n-\tif (h == INVALID_HANDLE_VALUE) {\n-\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\treturn -1;\n-\t}\n-\n-\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n-\t\t&proto_info, sizeof(proto_info))) {\n-\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\tCloseHandle(h);\n-\t\treturn -1;\n-\t}\n-\n-\tCloseHandle(h);\n-\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n-\t\t\t\twpath, proto_info.Protocol);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type dictates whether notification events are available at\n- * all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- * Notes for testing:\n- *\n- * (a) Windows allows a network share to be mapped to a drive letter.\n- *     (This is the normal method to access it.)\n- *\n- *     $ NET USE Z: \\\\server\\share\n- *     $ git -C Z:/repo status\n- *\n- * (b) Windows allows a network share to be referenced WITHOUT mapping\n- *     it to drive letter.\n- *\n- *     $ NET USE \\\\server\\share\\dir\n- *     $ git -C //server/share/repo status\n- *\n- * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n- *     arbitrary path (which may be remote)\n- *\n- *     $ SUBST Q: Z:\\repo\n- *     $ git -C Q:/ status\n- *\n- * (d) Windows allows a directory symlink to be created on a local\n- *     file system that points to a remote repo.\n- *\n- *     $ mklink /d ./link //server/share/repo\n- *     $ git -C ./link status\n- */\n-static enum fsmonitor_reason check_remote(struct repository *r)\n-{\n-\tint ret;\n-\twchar_t wpath[MAX_PATH];\n-\twchar_t wfullpath[MAX_PATH];\n-\tsize_t wlen;\n-\tUINT driveType;\n-\n-\t/*\n-\t * Do everything in wide chars because the drive letter might be\n-\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n-\t */\n-\tif (xutftowcs_path(wpath, r->worktree) < 0)\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t/*\n-\t * GetDriveTypeW() requires a final slash.  We assume that the\n-\t * worktree pathname points to an actual directory.\n-\t */\n-\twlen = wcslen(wpath);\n-\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n-\t\twpath[wlen++] = L'\\\\';\n-\t\twpath[wlen] = 0;\n-\t}\n-\n-\t/*\n-\t * Normalize the path.  If nothing else, this converts forward\n-\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n-\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n-\t */\n-\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\tdriveType = GetDriveTypeW(wfullpath);\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n-\t\t\t r->worktree, wfullpath, driveType);\n-\n-\tif (driveType == DRIVE_REMOTE) {\n-\t\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t \"check_remote('%s') true\",\n-\t\t\t\t r->worktree);\n-\n-\t\tret = check_remote_protocol(wfullpath);\n-\t\tif (ret < 0)\n-\t\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t\tswitch (check_config_allowremote(r)) {\n-\t\tcase 0: /* config overrides and disables */\n-\t\t\treturn FSMONITOR_REASON_REMOTE;\n-\t\tcase 1: /* config overrides and enables */\n-\t\t\treturn FSMONITOR_REASON_OK;\n-\t\tdefault:\n-\t\t\tbreak; /* config has no opinion */\n-\t\t}\n-\n-\t\treturn FSMONITOR_REASON_REMOTE;\n-\t}\n-\n-\treturn FSMONITOR_REASON_OK;\n-}\n-\n enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n@@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n-\treason = check_remote(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n-\n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 2237109b57f..b88494bf59b 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)\n@@ -315,6 +316,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nnew file mode 100644\nindex 00000000000..e48592887e7\n--- /dev/null\n+++ b/fsmonitor-path-utils.h\n@@ -0,0 +1,23 @@\n+#ifndef FSM_PATH_UTILS_H\n+#define FSM_PATH_UTILS_H\n+\n+struct fs_info {\n+\tint is_remote;\n+\tchar *typename;\n+};\n+\n+/*\n+ * Get some basic filesystem informtion for the given path\n+ *\n+ * Returns -1 on error, zero otherwise.\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n+\n+/*\n+ * Determines if the filesystem that path resides on is remote.\n+ *\n+ * Returns -1 on error, 0 if not remote, 1 if remote.\n+ */\n+int fsmonitor__is_fs_remote(const char *path);\n+\n+#endif\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..d288cbad479 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * We keep this structure defintion private and have getters\n@@ -13,6 +14,52 @@ struct fsmonitor_settings {\n \tchar *hook_path;\n };\n \n+/*\n+ * Remote working directories are problematic for FSMonitor.\n+ *\n+ * The underlying file system on the server machine and/or the remote\n+ * mount type dictates whether notification events are available at\n+ * all to remote client machines.\n+ *\n+ * Kernel differences between the server and client machines also\n+ * dictate the how (buffering, frequency, de-dup) the events are\n+ * delivered to client machine processes.\n+ *\n+ * A client machine (such as a laptop) may choose to suspend/resume\n+ * and it is unclear (without lots of testing) whether the watcher can\n+ * resync after a resume.  We might be able to treat this as a normal\n+ * \"events were dropped by the kernel\" event and do our normal \"flush\n+ * and resync\" --or-- we might need to close the existing (zombie?)\n+ * notification fd and create a new one.\n+ *\n+ * In theory, the above issues need to be addressed whether we are\n+ * using the Hook or IPC API.\n+ *\n+ * So (for now at least), mark remote working directories as\n+ * incompatible unless 'fsmonitor.allowRemote' is true.\n+ *\n+ */\n+#ifdef HAVE_FSMONITOR_OS_SETTINGS\n+static enum fsmonitor_reason check_remote(struct repository *r)\n+{\n+\tint allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */\n+\tint is_remote = fsmonitor__is_fs_remote(r->worktree);\n+\n+\tswitch (is_remote) {\n+\t\tcase 0:\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tcase 1:\n+\t\t\trepo_config_get_bool(r, \"fsmonitor.allowremote\", &allow_remote);\n+\t\t\tif (allow_remote < 1)\n+\t\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\t\telse\n+\t\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+}\n+#endif\n+\n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n {\n \tif (!r->worktree) {\n@@ -27,6 +74,9 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t{\n \t\tenum fsmonitor_reason reason;\n \n+\t\treason = check_remote(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n \t\treason = fsm_os__incompatible(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-- \ngitgitgadget\n\n"},{"id":"463102","messageId":"075340bd2a713905d8bee4f53765dcbcba9a17c4.1663358014.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v7.git.1663358014.gitgitgadget@gmail.com","subject":"[PATCH v7 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-16T19:53:30Z","receivedAt":"2022-09-16T19:53:50Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf the .git directory is on a remote file system, create the socket\nfile in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                               |  1 +\n builtin/fsmonitor--daemon.c            |  3 +-\n compat/fsmonitor/fsm-ipc-darwin.c      | 49 ++++++++++++++++++++++++++\n compat/fsmonitor/fsm-ipc-win32.c       |  9 +++++\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n contrib/buildsystems/CMakeLists.txt    |  2 ++\n fsmonitor-ipc.c                        | 18 +++++-----\n fsmonitor-ipc.h                        |  4 ++-\n 8 files changed, 75 insertions(+), 13 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n\ndiff --git a/Makefile b/Makefile\nindex 6e492a67547..58bb9248471 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2034,6 +2034,7 @@ ifdef FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n endif\n \n ifdef FSMONITOR_OS_SETTINGS\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 2c109cf8b37..0123fc33ed2 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -1343,7 +1343,8 @@ static int fsmonitor_run_daemon(void)\n \t * directory.)\n \t */\n \tstrbuf_init(&state.path_ipc, 0);\n-\tstrbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));\n+\tstrbuf_addstr(&state.path_ipc,\n+\t\tabsolute_path(fsmonitor_ipc__get_path(the_repository)));\n \n \t/*\n \t * Confirm that we can create platform-specific resources for the\ndiff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\nnew file mode 100644\nindex 00000000000..c238ed9c5d7\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-darwin.c\n@@ -0,0 +1,49 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"strbuf.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n+{\n+\tstatic const char *ipc_path;\n+\tSHA_CTX sha1ctx;\n+\tchar *sock_dir;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (ipc_path)\n+\t\treturn ipc_path;\n+\n+\tif (!r)\n+\t\tr = the_repository;\n+\n+\tipc_path = fsmonitor_ipc__get_default_path();\n+\n+\t/* By default the socket file is created in the .git directory */\n+\tif (fsmonitor__is_fs_remote(ipc_path) < 1)\n+\t\treturn ipc_path;\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\trepo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n+\n+\t/* Create the socket file in either socketDir or $HOME */\n+\tif (sock_dir && *sock_dir)\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\telse\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\n+\tipc_path = interpolate_path(ipc_file.buf, 1);\n+\tif (!ipc_path)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\n+\tstrbuf_release(&ipc_file);\n+\treturn ipc_path;\n+}\ndiff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\nnew file mode 100644\nindex 00000000000..3a3a46db209\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-win32.c\n@@ -0,0 +1,9 @@\n+#include \"config.h\"\n+#include \"fsmonitor-ipc.h\"\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r) {\n+\tstatic char *ret;\n+\tif (!ret)\n+\t\tret = git_pathdup(\"fsmonitor--daemon.ipc\");\n+\treturn ret;\n+}\n\\ No newline at end of file\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex dba3ced6bb7..681d8bf963e 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -26,7 +26,7 @@\n static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n \tstruct fs_info fs;\n-\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tconst char *ipc_path = fsmonitor_ipc__get_path(r);\n \tstruct strbuf path = STRBUF_INIT;\n \tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex b88494bf59b..7e7b6b9a362 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n@@ -316,6 +317,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..c0f42301c84 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -18,7 +18,7 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 0;\n }\n \n-const char *fsmonitor_ipc__get_path(void)\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n {\n \treturn NULL;\n }\n@@ -47,11 +47,9 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n-\n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\n-\treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n+\treturn ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));\n }\n \n static int spawn_daemon(void)\n@@ -81,8 +79,8 @@ int fsmonitor_ipc__send_query(const char *since_token,\n \ttrace2_data_string(\"fsm_client\", NULL, \"query/command\", tok);\n \n try_again:\n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \n \tswitch (state) {\n \tcase IPC_STATE__LISTENING:\n@@ -117,13 +115,13 @@ try_again:\n \n \tcase IPC_STATE__INVALID_PATH:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: invalid path '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \n \tcase IPC_STATE__OTHER_ERROR:\n \tdefault:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: unspecified error on '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \t}\n \n@@ -149,8 +147,8 @@ int fsmonitor_ipc__send_command(const char *command,\n \toptions.wait_if_busy = 1;\n \toptions.wait_if_not_found = 0;\n \n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \tif (state != IPC_STATE__LISTENING) {\n \t\tdie(_(\"fsmonitor--daemon is not running\"));\n \t\treturn -1;\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..8b489da762b 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -3,6 +3,8 @@\n \n #include \"simple-ipc.h\"\n \n+struct repository;\n+\n /*\n  * Returns true if built-in file system monitor daemon is defined\n  * for this platform.\n@@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);\n  *\n  * Returns NULL if the daemon is not supported on this platform.\n  */\n-const char *fsmonitor_ipc__get_path(void);\n+const char *fsmonitor_ipc__get_path(struct repository *r);\n \n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n-- \ngitgitgadget\n\n"},{"id":"463103","messageId":"5518d2f3e03e5e3ea121827cc0c148b481f353f3.1663358014.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v7.git.1663358014.gitgitgadget@gmail.com","subject":"[PATCH v7 3/6] fsmonitor: avoid socket location check if using hook","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-16T19:53:31Z","receivedAt":"2022-09-16T19:54:00Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf monitoring is done via fsmonitor hook rather than IPC there is no\nneed to check if the location of the Unix Domain socket (UDS) file is\non a remote filesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 10 ++++++----\n compat/fsmonitor/fsm-settings-win32.c  |  2 +-\n fsmonitor-settings.c                   |  8 ++++----\n fsmonitor-settings.h                   |  2 +-\n 4 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 681d8bf963e..40da2d3b533 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -49,13 +49,15 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_uds_volume(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n+\tif (ipc) {\n+\t\treason = check_uds_volume(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n+\t}\n \n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex d88b06ae610..a8af31b71de 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex d288cbad479..531a1b6f956 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -60,7 +60,7 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n }\n #endif\n \n-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n+static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n {\n \tif (!r->worktree) {\n \t\t/*\n@@ -77,7 +77,7 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t\treason = check_remote(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-\t\treason = fsm_os__incompatible(r);\n+\t\treason = fsm_os__incompatible(r, ipc);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n \t}\n@@ -162,7 +162,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -185,7 +185,7 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..0721617b95a 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -48,7 +48,7 @@ struct fsmonitor_settings;\n  * fsm_os__* routines should considered private to fsm_settings__\n  * routines.\n  */\n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n \n #endif /* FSMONITOR_SETTINGS_H */\n-- \ngitgitgadget\n\n"},{"id":"463104","messageId":"260591f58202c2a771f59beb09f11b63e853a1f9.1663358014.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v7.git.1663358014.gitgitgadget@gmail.com","subject":"[PATCH v7 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-16T19:53:34Z","receivedAt":"2022-09-16T19:54:02Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nAdd documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\nCall-out experimental nature of 'fsmonitor.allowRemote' and limited file\nsystem support for 'fsmonitor.socketDir'.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Documentation/git-fsmonitor--daemon.txt | 35 +++++++++++++++++++++++++\n 1 file changed, 35 insertions(+)\n\ndiff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt\nindex cc142fb8612..0adccd0eced 100644\n--- a/Documentation/git-fsmonitor--daemon.txt\n+++ b/Documentation/git-fsmonitor--daemon.txt\n@@ -70,6 +70,41 @@ the change (as happening against the super repo).  However, the client\n will properly ignore these extra events, so performance may be affected\n but it will not cause an incorrect result.\n \n+By default, the fsmonitor daemon refuses to work against network-mounted\n+repositories; this my be overridden by setting `fsmonitor.allowRemote` to\n+`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n+correctly with all network-mounted repositores and such use is considered\n+experimental.\n+\n+On Mac OS, the inter-process communication (IPC) between various Git\n+commands and the fsmonitor daemon is done via a Unix domain socket (UDS).\n+Usage of UDS requires the creation of a file which, by default, is created\n+in the .git directory.  If the fsmonitor daemon detects that the .git directory\n+is on a network-mounted file system, it will create the UDS file in $HOME.  If\n+$HOME itself is on a network-mounted file system or if $HOME is not the desired\n+location for the UDS file, 'fsmonitor.socketDir' may be set to any valid, local\n+directory on a file system with proper support.  Mac OS native file systems have\n+the required support.  File systems known to lack support include FAT32 and\n+NTFS.  Other file systems may or many not have the needed support; the fsmonitor\n+daemon is not guaranteed to work with these file systems and such use is\n+considered experimental.\n+\n+CONFIGURATION\n+-------------\n+When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])\n+the fsmonitor daemon will pay attention to the following configuration\n+variables:\n+\n+fsmonitor.allowRemote::\n+\tBy default, the daemon refuses to work against network-mounted\n+\trepositories. Setting `fsmonitor.allowRemote` to `true` overrides\n+\tthis behavior.\n+\n+fsmonitor.socketDir::\n+\tThis option is only used by the Mac OS implementation of the fsmonitor\n+\tdaemon.\tIf set, 'fsmonitor.socketDir' must be set to a valid, local\n+\tdirectory on a file system that can support Unix domain sockets (UDS).\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \ngitgitgadget\n"},{"id":"463105","messageId":"4d00adb1deb61c3af58bed81271dd3337db7037e.1663358014.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v7.git.1663358014.gitgitgadget@gmail.com","subject":"[PATCH v7 5/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-16T19:53:33Z","receivedAt":"2022-09-16T19:54:05Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n fsmonitor--daemon.h | 3 +++\n 1 file changed, 3 insertions(+)\n\ndiff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\nindex e24838f9a86..98cbb430083 100644\n--- a/fsmonitor--daemon.h\n+++ b/fsmonitor--daemon.h\n@@ -170,5 +170,8 @@ void fsmonitor_publish(struct fsmonitor_daemon_state *state,\n  */\n void fsmonitor_force_resync(struct fsmonitor_daemon_state *state);\n \n+char *fsmonitor_resolve_alias(const char *path,\n+\tstruct alias_info *alias);\n+\n #endif /* HAVE_FSMONITOR_DAEMON_BACKEND */\n #endif /* FSMONITOR_DAEMON_H */\n-- \ngitgitgadget\n\n"},{"id":"463106","messageId":"3a9fe473cf4f5f29099771883e53fd40ebe3b511.1663358014.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v7.git.1663358014.gitgitgadget@gmail.com","subject":"[PATCH v7 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-16T19:53:32Z","receivedAt":"2022-09-16T19:54:07Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n builtin/fsmonitor--daemon.c              |  8 +++\n compat/fsmonitor/fsm-listen-darwin.c     |  6 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 92 ++++++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 17 +++++\n fsmonitor--daemon.h                      |  3 +\n fsmonitor-path-utils.h                   | 36 ++++++++++\n 6 files changed, 161 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 0123fc33ed2..56fcd1c2baa 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"fsmonitor.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n #include \"compat/fsmonitor/fsm-health.h\"\n #include \"compat/fsmonitor/fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n@@ -1282,6 +1283,13 @@ static int fsmonitor_run_daemon(void)\n \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n \tstate.nr_paths_watching = 1;\n \n+\tstate.alias.alias = NULL;\n+\tstate.alias.points_to = NULL;\n+\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n+\t\terr = error(_(\"could not get worktree alias\"));\n+\t\tgoto done;\n+\t}\n+\n \t/*\n \t * We create and delete cookie files somewhere inside the .git\n \t * directory to help us keep sync with the file system.  If\ndiff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\nindex 8e208e8289e..179886bc15b 100644\n--- a/compat/fsmonitor/fsm-listen-darwin.c\n+++ b/compat/fsmonitor/fsm-listen-darwin.c\n@@ -26,6 +26,7 @@\n #include \"fsmonitor.h\"\n #include \"fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsm_listen_data\n {\n@@ -209,7 +210,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t/*\n \t\t * On Mac, we receive an array of absolute paths.\n \t\t */\n-\t\tpath_k = paths[k];\n+\t\tpath_k = fsmonitor__resolve_alias(paths[k], &state->alias);\n+\t\tif (!path_k)\n+\t\t\tpath_k = paths[k];\n \n \t\t/*\n \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n@@ -238,6 +241,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t\tfsmonitor_force_resync(state);\n \t\t\tfsmonitor_batch__free_list(batch);\n \t\t\tstring_list_clear(&cookie_list, 0);\n+\t\t\tbatch = NULL;\n \n \t\t\t/*\n \t\t\t * We assume that any events that we received\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nindex 067cbe6990a..13807f58e95 100644\n--- a/compat/fsmonitor/fsm-path-utils-darwin.c\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -1,5 +1,8 @@\n #include \"fsmonitor.h\"\n #include \"fsmonitor-path-utils.h\"\n+#include <dirent.h>\n+#include <errno.h>\n+#include <fcntl.h>\n #include <sys/param.h>\n #include <sys/mount.h>\n \n@@ -38,3 +41,92 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * Scan the root directory for synthetic firmlinks that when resolved\n+ * are a prefix of the path, stopping at the first one found.\n+ *\n+ * Some information about firmlinks and synthetic firmlinks:\n+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n+ *\n+ * macOS no longer allows symlinks in the root directory; any link found\n+ * there is therefore a synthetic firmlink.\n+ *\n+ * If this function gets called often, will want to cache all the firmlink\n+ * information, but for now there is only one caller of this function.\n+ *\n+ * If there is more than one alias for the path, that is another\n+ * matter altogether.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\tDIR * dir;\n+\tint read;\n+\tint retval;\n+\tstruct dirent *de;\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+\n+\tretval = 0;\n+\tdir = opendir(\"/\");\n+\tif (!dir)\n+\t\treturn -1;\n+\n+\tstrbuf_init(&alias, 256);\n+\tstrbuf_init(&points_to, MAXPATHLEN);\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstrbuf_reset(&alias);\n+\t\tstrbuf_addch(&alias, '/');\n+\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n+\n+\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n+\t\tif (read > 0) {\n+\t\t\tstrbuf_setlen(&points_to, read);\n+\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n+\t\t\t\t&& path[points_to.len] == '/') {\n+\t\t\t\tinfo->alias = strbuf_detach(&alias, NULL);\n+\t\t\t\tinfo->points_to = strbuf_detach(&points_to, NULL);\n+\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n+\t\t\t\t\tpath, info->alias, info->points_to);\n+\t\t\t\tretval = 0;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n+\t\t\ttrace_printf_key(&trace_fsmonitor, \"Error %s\", strerror(errno));\n+\t\t\tretval = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n+\tdone:\n+\tclosedir(dir);\n+\tstrbuf_release(&alias);\n+\tstrbuf_release(&points_to);\n+\treturn retval;\n+}\n+\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\tint len = info->alias ? strlen(info->alias) : 0;\n+\n+\tif (!len)\n+\t\treturn NULL;\n+\n+\tif ((strncmp(info->alias, path, len) == 0)\n+\t\t&& path[len] == '/') {\n+\t\tstruct strbuf tmp;\n+\t\tconst char *remainder = path + len;\n+\t\tint ptr_len = strlen(info->points_to);\n+\t\tint rem_len = strlen(remainder);\n+\n+\t\tstrbuf_init(&tmp, ptr_len + rem_len);\n+\t\tstrbuf_add(&tmp, info->points_to, ptr_len);\n+\t\tstrbuf_add(&tmp, remainder, rem_len);\n+\t\treturn strbuf_detach(&tmp, NULL);\n+\t}\n+\n+\treturn NULL;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nindex a90b8f7925b..0d95bbb416f 100644\n--- a/compat/fsmonitor/fsm-path-utils-win32.c\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -126,3 +126,20 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * No-op for now.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\treturn 0;\n+}\n+\n+/*\n+ * No-op for now.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\treturn NULL;\n+}\ndiff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\nindex 2102a5c9ff5..e24838f9a86 100644\n--- a/fsmonitor--daemon.h\n+++ b/fsmonitor--daemon.h\n@@ -8,6 +8,7 @@\n #include \"run-command.h\"\n #include \"simple-ipc.h\"\n #include \"thread-utils.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsmonitor_batch;\n struct fsmonitor_token_data;\n@@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct strbuf path_worktree_watch;\n \tstruct strbuf path_gitdir_watch;\n+\tstruct alias_info alias;\n \tint nr_paths_watching;\n \n \tstruct fsmonitor_token_data *current_token_data;\n@@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct ipc_server_data *ipc_server_data;\n \tstruct strbuf path_ipc;\n+\n };\n \n /*\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nindex e48592887e7..50ef37e57bb 100644\n--- a/fsmonitor-path-utils.h\n+++ b/fsmonitor-path-utils.h\n@@ -1,6 +1,14 @@\n #ifndef FSM_PATH_UTILS_H\n #define FSM_PATH_UTILS_H\n \n+#include \"strbuf.h\"\n+\n+struct alias_info\n+{\n+\tchar *alias;\n+\tchar *points_to;\n+};\n+\n struct fs_info {\n \tint is_remote;\n \tchar *typename;\n@@ -20,4 +28,32 @@ int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n  */\n int fsmonitor__is_fs_remote(const char *path);\n \n+/*\n+ * Get the alias in given path, if any.\n+ *\n+ * Sets alias to the first alias that matches any part of the path.\n+ *\n+ * If an alias is found, info.alias and info.points_to are set to the\n+ * found mapping.\n+ *\n+ * Returns -1 on error, 0 otherwise.\n+ *\n+ * The caller owns the storage that is occupied by set info.alias and\n+ * info.points_to and is responsible for releasing it with `free(3)`\n+ * when done.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info);\n+\n+/*\n+ * Resolve the path against the given alias.\n+ *\n+ * Returns the resolved path if there is one, NULL otherwise.\n+ *\n+ * The caller owns the storage that the returned string occupies and\n+ * is responsible for releasing it with `free(3)` when done.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info);\n+\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"463107","messageId":"BL0PR05MB55713024EE4938946AD9DEC8D9489@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"ac9c12cf-866d-565f-fbf8-58dbfdd92ef1@jeffhostetler.com","subject":"RE: [PATCH v6 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-16T20:02:15Z","receivedAt":"2022-09-16T20:02:26Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Jeff Hostetler <git@jeffhostetler.com>\n> Sent: Friday, September 16, 2022 1:59 PM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n> git@vger.kernel.org\n> Cc: Eric Sunshine <sunshine@sunshineco.com>; Torsten Bögershausen\n> <tboegi@web.de>; Ævar Arnfjörð Bjarmason <avarab@gmail.com>; Ramsay\n> Jones <ramsay@ramsayjones.plus.com>; Johannes Schindelin\n> <Johannes.Schindelin@gmx.de>; Eric DeCosta <edecosta@mathworks.com>\n> Subject: Re: [PATCH v6 0/6] fsmonitor: option to allow fsmonitor to run\n> against network-mounted repos\n> \n> \n> \n> On 9/13/22 4:27 PM, Eric DeCosta via GitGitGadget wrote:\n> > Follow-on to the work done to allow Windows to work against\n> > network-mounted repos for macOS.\n> >\n> > Have macOS take advantage of the same configuration option,\n> > 'fsmonitor.allowRemote' that was introduced for Windows. Setting this\n> > option to true will override the default behavior (erroring-out) when\n> > a network-mounted repo is detected by fsmonitor.\n> >\n> > The added wrinkle being that the Unix domain socket (UDS) file used\n> > for IPC cannot be created in a network location; instead $HOME is used\n> > if the default location is on the network. The user may, optionally,\n> > set the 'fsmonitor.socketDir' configuration option to a valid, local\n> > directory if $HOME itself is on the network or is simply not the\n> > desired location for the UDS file.\n> >\n> > An additional issue is that for mount points in the root directory,\n> > FSEvents does not report a path that matches the worktree directory\n> > due to the introduction of 'synthetic firmlinks'. fsmonitor must map\n> > the FSEvents paths to the worktree directory by interrogating the root\n> > filesystem for synthetic firmlinks and using that information to translate the\n> path.\n> >\n> > v6 differs from v5:\n> >\n> >   * incorporates earlier, Windows-specific changes that have not made it\n> back\n> >     yet to the master branch\n> >   * incorporates code review feedback\n> >   * adds documentation for 'fsmonitor.allowRemote' and\n> 'fsmonitor.socketDir'\n> >\n> > v5 differs significantly from earlier versions:\n> >\n> >   * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n> >     such that these options are no longer added to the settings data\n> >     structure but are rather read from config at point of use\n> >   * refactoring of code for handling platform-specific file system checks via\n> >     a common interface to avoid platform #ifdef in IPC code and be in-model\n> >     with other platform-specific fsmonitor code\n> >   * dealing with 'synthetic firmlinks' on macOS\n> >\n> \n> I've looked at v5 and v6 and I like the direction this is heading, so I'll mark this\n> LGTM.  (I'm still traveling back from Git Merge, so I haven't had time to test it\n> out, but I think we should proceed with it.)\n> \n> Thanks for you patience and attention to detail on this!\n> Jeff\n\nGreat! I just created v7 with some small updates based on Junio's feedback.\n\nI think this landed in a pretty good place. I don't know that the plans are for fsmonitor on Linux, but I've already started tinkering around with an inotify-based implementation for that platform. The devil is in the details, of course, but it looks fairly straight-forward.\n\n-Eric\n"},{"id":"463108","messageId":"xmqq5yhngjz1.fsf@gitster.g","threadId":"58324","inReplyTo":"pull.1326.v7.git.1663358014.gitgitgadget@gmail.com","subject":"Re: [PATCH v7 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-16T20:09:06Z","receivedAt":"2022-09-16T20:09:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n>  5:  9c1f408ae6d ! 4:  3a9fe473cf4 fsmonitor: deal with synthetic firmlinks on macOS\n>      @@ fsmonitor--daemon.h: struct fsmonitor_daemon_state {\n>        };\n>        \n>        /*\n>      -@@ fsmonitor--daemon.h: void fsmonitor_publish(struct fsmonitor_daemon_state *state,\n>      -  */\n>      - void fsmonitor_force_resync(struct fsmonitor_daemon_state *state);\n>      - \n>      -+char *fsmonitor_resolve_alias(const char *path,\n>      -+\tstruct alias_info *alias);\n>      -+\n>      - #endif /* HAVE_FSMONITOR_DAEMON_BACKEND */\n>      - #endif /* FSMONITOR_DAEMON_H */\n>       \n>        ## fsmonitor-path-utils.h ##\n>       @@\n>  -:  ----------- > 5:  4d00adb1deb fsmonitor: deal with synthetic firmlinks on macOS\n\n\"rebase -i\" mistake or something?\n"},{"id":"463109","messageId":"xmqqy1ujf5a9.fsf@gitster.g","threadId":"58324","inReplyTo":"075340bd2a713905d8bee4f53765dcbcba9a17c4.1663358014.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v7 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-16T20:11:42Z","receivedAt":"2022-09-16T20:11:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +const char *fsmonitor_ipc__get_path(struct repository *r)\n> +{\n> +\tstatic const char *ipc_path;\n> +\tSHA_CTX sha1ctx;\n> +\tchar *sock_dir;\n> +\tstruct strbuf ipc_file = STRBUF_INIT;\n> +\tunsigned char hash[SHA_DIGEST_LENGTH];\n> +\n> +\tif (ipc_path)\n> +\t\treturn ipc_path;\n> +\n> +\tif (!r)\n> +\t\tr = the_repository;\n\nI'd prefer not to see this \"NULL means the_repository\".  It would be\na different story if the caller does not necessarily have a ready\naccess to the_repository, but it is a global, so the caller can pass\nthe_repository and be more explicit.  Giving two ways to the caller\nto express same thing is not a good idea.\n\nThanks.\n\n"},{"id":"463110","messageId":"xmqqtu57f54m.fsf@gitster.g","threadId":"58324","inReplyTo":"4d00adb1deb61c3af58bed81271dd3337db7037e.1663358014.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v7 5/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-16T20:15:05Z","receivedAt":"2022-09-16T20:15:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> Starting with macOS 10.15 (Catalina), Apple introduced a new feature\n> called 'firmlinks' in order to separate the boot volume into two\n> volumes, one read-only and one writable but still present them to the\n> user as a single volume. Along with this change, Apple removed the\n> ...\n> Therefore, in order to deal with synthetic firmlinks, the root directory\n> is scanned and the first possible synthetic firmink that, when resolved,\n> is a prefix of the worktree is used to map FSEvents paths to worktree\n> paths.\n\nThe proposed log message for this step seems fairly similar to the\nprevious one.\n\n> diff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\n> index e24838f9a86..98cbb430083 100644\n> --- a/fsmonitor--daemon.h\n> +++ b/fsmonitor--daemon.h\n> @@ -170,5 +170,8 @@ void fsmonitor_publish(struct fsmonitor_daemon_state *state,\n>   */\n>  void fsmonitor_force_resync(struct fsmonitor_daemon_state *state);\n>  \n> +char *fsmonitor_resolve_alias(const char *path,\n> +\tstruct alias_info *alias);\n> +\n\nAfter applying all the 6 steps, this function is declared here\nwithout any implementation.  Should we drop this step, I have to\nwonder.\n\nThanks.\n"},{"id":"463125","messageId":"pull.1326.v8.git.1663377141.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v7.git.1663358014.gitgitgadget@gmail.com","subject":"[PATCH v8 0/5] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-17T01:12:16Z","receivedAt":"2022-09-17T01:12:28Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"Follow-on to the work done to allow Windows to work against network-mounted\nrepos for macOS.\n\nHave macOS take advantage of the same configuration option,\n'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\nto true will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for IPC\ncannot be created in a network location; instead $HOME is used if the\ndefault location is on the network. The user may, optionally, set the\n'fsmonitor.socketDir' configuration option to a valid, local directory if\n$HOME itself is on the network or is simply not the desired location for the\nUDS file.\n\nAn additional issue is that for mount points in the root directory, FSEvents\ndoes not report a path that matches the worktree directory due to the\nintroduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\nto the worktree directory by interrogating the root filesystem for synthetic\nfirmlinks and using that information to translate the path.\n\nv8 differs from v7:\n\n * incorporates code review feedback\n * gets the rebase right\n\nv7 differs from v6:\n\n * incorporates code review feedback\n\nv6 differs from v5:\n\n * incorporates earlier, Windows-specific changes that have not made it back\n   yet to the master branch\n * incorporates code review feedback\n * adds documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n\nv5 differs significantly from earlier versions:\n\n * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n   such that these options are no longer added to the settings data\n   structure but are rather read from config at point of use\n * refactoring of code for handling platform-specific file system checks via\n   a common interface to avoid platform #ifdef in IPC code and be in-model\n   with other platform-specific fsmonitor code\n * dealing with 'synthetic firmlinks' on macOS\n\nEric DeCosta (5):\n  fsmonitor: refactor filesystem checks to common interface\n  fsmonitor: relocate socket file if .git directory is remote\n  fsmonitor: avoid socket location check if using hook\n  fsmonitor: deal with synthetic firmlinks on macOS\n  fsmonitor: add documentation for allowRemote and socketDir options\n\n Documentation/git-fsmonitor--daemon.txt  |  35 +++++\n Makefile                                 |   2 +\n builtin/fsmonitor--daemon.c              |  11 +-\n compat/fsmonitor/fsm-ipc-darwin.c        |  46 ++++++\n compat/fsmonitor/fsm-ipc-win32.c         |   9 ++\n compat/fsmonitor/fsm-listen-darwin.c     |   6 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 132 +++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 145 +++++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  70 +++------\n compat/fsmonitor/fsm-settings-win32.c    | 174 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   4 +\n fsmonitor--daemon.h                      |   3 +\n fsmonitor-ipc.c                          |  18 ++-\n fsmonitor-ipc.h                          |   4 +-\n fsmonitor-path-utils.h                   |  59 ++++++++\n fsmonitor-settings.c                     |  58 +++++++-\n fsmonitor-settings.h                     |   2 +-\n 17 files changed, 541 insertions(+), 237 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\n\nbase-commit: d3fa443f97e3a8d75b51341e2d5bac380b7422df\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v8\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v8\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v7:\n\n 1:  155a6890806 = 1:  155a6890806 fsmonitor: refactor filesystem checks to common interface\n 2:  075340bd2a7 ! 2:  b5356497228 fsmonitor: relocate socket file if .git directory is remote\n     @@ compat/fsmonitor/fsm-ipc-darwin.c (new)\n      +\tif (ipc_path)\n      +\t\treturn ipc_path;\n      +\n     -+\tif (!r)\n     -+\t\tr = the_repository;\n     -+\n      +\tipc_path = fsmonitor_ipc__get_default_path();\n      +\n      +\t/* By default the socket file is created in the .git directory */\n 3:  5518d2f3e03 = 3:  6719ca2b24d fsmonitor: avoid socket location check if using hook\n 4:  3a9fe473cf4 ! 4:  d736cb8fa90 fsmonitor: deal with synthetic firmlinks on macOS\n     @@ Commit message\n      \n          Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n      \n     +    fsmonitor: deal with synthetic firmlinks on macOS\n     +\n     +    Starting with macOS 10.15 (Catalina), Apple introduced a new feature\n     +    called 'firmlinks' in order to separate the boot volume into two\n     +    volumes, one read-only and one writable but still present them to the\n     +    user as a single volume. Along with this change, Apple removed the\n     +    ability to create symlinks in the root directory and replaced them with\n     +    'synthetic firmlinks'. See 'man synthetic.conf'\n     +\n     +    When FSEevents reports the path of changed files, if the path involves\n     +    a synthetic firmlink, the path is reported from the point of the\n     +    synthetic firmlink and not the real path. For example:\n     +\n     +    Real path:\n     +    /System/Volumes/Data/network/working/directory/foo.txt\n     +\n     +    Synthetic firmlink:\n     +    /network -> /System/Volumes/Data/network\n     +\n     +    FSEvents path:\n     +    /network/working/directory/foo.txt\n     +\n     +    This causes the FSEvents path to not match against the worktree\n     +    directory.\n     +\n     +    There are several ways in which synthetic firmlinks can be created:\n     +    they can be defined in /etc/synthetic.conf, the automounter can create\n     +    them, and there may be other means. Simply reading /etc/synthetic.conf\n     +    is insufficient. No matter what process creates synthetic firmlinks,\n     +    they all get created in the root directory.\n     +\n     +    Therefore, in order to deal with synthetic firmlinks, the root directory\n     +    is scanned and the first possible synthetic firmink that, when resolved,\n     +    is a prefix of the worktree is used to map FSEvents paths to worktree\n     +    paths.\n     +\n     +    Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n     +\n     +    fsmonitor: deal with synthetic firmlinks on macOS\n     +\n     +    Starting with macOS 10.15 (Catalina), Apple introduced a new feature\n     +    called 'firmlinks' in order to separate the boot volume into two\n     +    volumes, one read-only and one writable but still present them to the\n     +    user as a single volume. Along with this change, Apple removed the\n     +    ability to create symlinks in the root directory and replaced them with\n     +    'synthetic firmlinks'. See 'man synthetic.conf'\n     +\n     +    When FSEevents reports the path of changed files, if the path involves\n     +    a synthetic firmlink, the path is reported from the point of the\n     +    synthetic firmlink and not the real path. For example:\n     +\n     +    Real path:\n     +    /System/Volumes/Data/network/working/directory/foo.txt\n     +\n     +    Synthetic firmlink:\n     +    /network -> /System/Volumes/Data/network\n     +\n     +    FSEvents path:\n     +    /network/working/directory/foo.txt\n     +\n     +    This causes the FSEvents path to not match against the worktree\n     +    directory.\n     +\n     +    There are several ways in which synthetic firmlinks can be created:\n     +    they can be defined in /etc/synthetic.conf, the automounter can create\n     +    them, and there may be other means. Simply reading /etc/synthetic.conf\n     +    is insufficient. No matter what process creates synthetic firmlinks,\n     +    they all get created in the root directory.\n     +\n     +    Therefore, in order to deal with synthetic firmlinks, the root directory\n     +    is scanned and the first possible synthetic firmink that, when resolved,\n     +    is a prefix of the worktree is used to map FSEvents paths to worktree\n     +    paths.\n     +\n     +    Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n     +\n       ## builtin/fsmonitor--daemon.c ##\n      @@\n       #include \"parse-options.h\"\n 5:  4d00adb1deb < -:  ----------- fsmonitor: deal with synthetic firmlinks on macOS\n 6:  260591f5820 = 5:  ddf4e3e6442 fsmonitor: add documentation for allowRemote and socketDir options\n\n-- \ngitgitgadget\n"},{"id":"463126","messageId":"b535649722826d7317061da7d8b0cf3d6e2a51ff.1663377141.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v8.git.1663377141.gitgitgadget@gmail.com","subject":"[PATCH v8 2/5] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-17T01:12:18Z","receivedAt":"2022-09-17T01:12:33Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf the .git directory is on a remote file system, create the socket\nfile in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                               |  1 +\n builtin/fsmonitor--daemon.c            |  3 +-\n compat/fsmonitor/fsm-ipc-darwin.c      | 46 ++++++++++++++++++++++++++\n compat/fsmonitor/fsm-ipc-win32.c       |  9 +++++\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n contrib/buildsystems/CMakeLists.txt    |  2 ++\n fsmonitor-ipc.c                        | 18 +++++-----\n fsmonitor-ipc.h                        |  4 ++-\n 8 files changed, 72 insertions(+), 13 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n\ndiff --git a/Makefile b/Makefile\nindex 6e492a67547..58bb9248471 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2034,6 +2034,7 @@ ifdef FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n endif\n \n ifdef FSMONITOR_OS_SETTINGS\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 2c109cf8b37..0123fc33ed2 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -1343,7 +1343,8 @@ static int fsmonitor_run_daemon(void)\n \t * directory.)\n \t */\n \tstrbuf_init(&state.path_ipc, 0);\n-\tstrbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));\n+\tstrbuf_addstr(&state.path_ipc,\n+\t\tabsolute_path(fsmonitor_ipc__get_path(the_repository)));\n \n \t/*\n \t * Confirm that we can create platform-specific resources for the\ndiff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\nnew file mode 100644\nindex 00000000000..d6628185000\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-darwin.c\n@@ -0,0 +1,46 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"strbuf.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n+{\n+\tstatic const char *ipc_path;\n+\tSHA_CTX sha1ctx;\n+\tchar *sock_dir;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (ipc_path)\n+\t\treturn ipc_path;\n+\n+\tipc_path = fsmonitor_ipc__get_default_path();\n+\n+\t/* By default the socket file is created in the .git directory */\n+\tif (fsmonitor__is_fs_remote(ipc_path) < 1)\n+\t\treturn ipc_path;\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\trepo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n+\n+\t/* Create the socket file in either socketDir or $HOME */\n+\tif (sock_dir && *sock_dir)\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\telse\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\n+\tipc_path = interpolate_path(ipc_file.buf, 1);\n+\tif (!ipc_path)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\n+\tstrbuf_release(&ipc_file);\n+\treturn ipc_path;\n+}\ndiff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\nnew file mode 100644\nindex 00000000000..3a3a46db209\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-win32.c\n@@ -0,0 +1,9 @@\n+#include \"config.h\"\n+#include \"fsmonitor-ipc.h\"\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r) {\n+\tstatic char *ret;\n+\tif (!ret)\n+\t\tret = git_pathdup(\"fsmonitor--daemon.ipc\");\n+\treturn ret;\n+}\n\\ No newline at end of file\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex dba3ced6bb7..681d8bf963e 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -26,7 +26,7 @@\n static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n \tstruct fs_info fs;\n-\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tconst char *ipc_path = fsmonitor_ipc__get_path(r);\n \tstruct strbuf path = STRBUF_INIT;\n \tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex b88494bf59b..7e7b6b9a362 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n@@ -316,6 +317,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..c0f42301c84 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -18,7 +18,7 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 0;\n }\n \n-const char *fsmonitor_ipc__get_path(void)\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n {\n \treturn NULL;\n }\n@@ -47,11 +47,9 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n-\n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\n-\treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n+\treturn ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));\n }\n \n static int spawn_daemon(void)\n@@ -81,8 +79,8 @@ int fsmonitor_ipc__send_query(const char *since_token,\n \ttrace2_data_string(\"fsm_client\", NULL, \"query/command\", tok);\n \n try_again:\n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \n \tswitch (state) {\n \tcase IPC_STATE__LISTENING:\n@@ -117,13 +115,13 @@ try_again:\n \n \tcase IPC_STATE__INVALID_PATH:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: invalid path '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \n \tcase IPC_STATE__OTHER_ERROR:\n \tdefault:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: unspecified error on '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \t}\n \n@@ -149,8 +147,8 @@ int fsmonitor_ipc__send_command(const char *command,\n \toptions.wait_if_busy = 1;\n \toptions.wait_if_not_found = 0;\n \n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \tif (state != IPC_STATE__LISTENING) {\n \t\tdie(_(\"fsmonitor--daemon is not running\"));\n \t\treturn -1;\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..8b489da762b 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -3,6 +3,8 @@\n \n #include \"simple-ipc.h\"\n \n+struct repository;\n+\n /*\n  * Returns true if built-in file system monitor daemon is defined\n  * for this platform.\n@@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);\n  *\n  * Returns NULL if the daemon is not supported on this platform.\n  */\n-const char *fsmonitor_ipc__get_path(void);\n+const char *fsmonitor_ipc__get_path(struct repository *r);\n \n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n-- \ngitgitgadget\n\n"},{"id":"463127","messageId":"155a689080658e1953fc4434016f746f371ba08a.1663377141.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v8.git.1663377141.gitgitgadget@gmail.com","subject":"[PATCH v8 1/5] fsmonitor: refactor filesystem checks to common interface","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-17T01:12:17Z","receivedAt":"2022-09-17T01:12:36Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nProvide a common interface for getting basic filesystem information\nincluding filesystem type and whether the filesystem is remote.\n\nRefactor existing code for getting basic filesystem info and detecting\nremote file systems to the new interface.\n\nRefactor filesystem checks to leverage new interface. For macOS,\nerror-out if the Unix Domain socket (UDS) file is on a remote\nfilesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                                 |   1 +\n compat/fsmonitor/fsm-path-utils-darwin.c |  40 ++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 128 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  62 +++-----\n compat/fsmonitor/fsm-settings-win32.c    | 172 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   2 +\n fsmonitor-path-utils.h                   |  23 +++\n fsmonitor-settings.c                     |  50 +++++++\n 8 files changed, 263 insertions(+), 215 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\ndiff --git a/Makefile b/Makefile\nindex d9247ead45b..6e492a67547 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2039,6 +2039,7 @@ endif\n ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n ifeq ($(TCLTK_PATH),)\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nnew file mode 100644\nindex 00000000000..067cbe6990a\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -0,0 +1,40 @@\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+#include <sys/param.h>\n+#include <sys/mount.h>\n+\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\tstruct statfs fs;\n+\tif (statfs(path, &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\treturn -1;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\n+\tif (!(fs.f_flags & MNT_LOCAL))\n+\t\tfs_info->is_remote = 1;\n+\telse\n+\t\tfs_info->is_remote = 0;\n+\n+\tfs_info->typename = fs.f_fstypename;\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nnew file mode 100644\nindex 00000000000..a90b8f7925b\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -0,0 +1,128 @@\n+#include \"cache.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+/*\n+ * Check remote working directory protocol.\n+ *\n+ * Return -1 if client machine cannot get remote protocol information.\n+ */\n+static int check_remote_protocol(wchar_t *wpath)\n+{\n+\tHANDLE h;\n+\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n+\n+\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n+\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\n+\tif (h == INVALID_HANDLE_VALUE) {\n+\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n+\t\t&proto_info, sizeof(proto_info))) {\n+\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\tCloseHandle(h);\n+\t\treturn -1;\n+\t}\n+\n+\tCloseHandle(h);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n+\t\t\t\twpath, proto_info.Protocol);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Notes for testing:\n+ *\n+ * (a) Windows allows a network share to be mapped to a drive letter.\n+ *     (This is the normal method to access it.)\n+ *\n+ *     $ NET USE Z: \\\\server\\share\n+ *     $ git -C Z:/repo status\n+ *\n+ * (b) Windows allows a network share to be referenced WITHOUT mapping\n+ *     it to drive letter.\n+ *\n+ *     $ NET USE \\\\server\\share\\dir\n+ *     $ git -C //server/share/repo status\n+ *\n+ * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n+ *     arbitrary path (which may be remote)\n+ *\n+ *     $ SUBST Q: Z:\\repo\n+ *     $ git -C Q:/ status\n+ *\n+ * (d) Windows allows a directory symlink to be created on a local\n+ *     file system that points to a remote repo.\n+ *\n+ *     $ mklink /d ./link //server/share/repo\n+ *     $ git -C ./link status\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\twchar_t wpath[MAX_PATH];\n+\twchar_t wfullpath[MAX_PATH];\n+\tsize_t wlen;\n+\tUINT driveType;\n+\n+\t/*\n+\t * Do everything in wide chars because the drive letter might be\n+\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n+\t */\n+\tif (xutftowcs_path(wpath, path) < 0) {\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * GetDriveTypeW() requires a final slash.  We assume that the\n+\t * worktree pathname points to an actual directory.\n+\t */\n+\twlen = wcslen(wpath);\n+\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n+\t\twpath[wlen++] = L'\\\\';\n+\t\twpath[wlen] = 0;\n+\t}\n+\n+\t/*\n+\t * Normalize the path.  If nothing else, this converts forward\n+\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n+\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n+\t */\n+\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {\n+\t\treturn -1;\n+\t}\n+\n+\tdriveType = GetDriveTypeW(wfullpath);\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n+\t\t\t path, wfullpath, driveType);\n+\n+\tif (driveType == DRIVE_REMOTE) {\n+\t\tfs_info->is_remote = 1;\n+\t\tif (check_remote_protocol(wfullpath) < 0)\n+\t\t\treturn -1;\n+\t} else {\n+\t\tfs_info->is_remote = 0;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dba3ced6bb7 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -1,32 +1,10 @@\n-#include \"cache.h\"\n #include \"config.h\"\n-#include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n-#include <sys/param.h>\n-#include <sys/mount.h>\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n-/*\n- * [1] Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type (NFS, SAMBA, etc.) dictates whether notification events\n- * are available at all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n+ /*\n  * For the builtin FSMonitor, we create the Unix domain socket for the\n  * IPC in the .git directory.  If the working directory is remote,\n  * then the socket will be created on the remote file system.  This\n@@ -38,40 +16,34 @@\n  * be taken to ensure that $HOME is actually local and not a managed\n  * file share.)\n  *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n+ * FAT32 and NTFS working directories are problematic too.\n  *\n  * The builtin FSMonitor uses a Unix domain socket in the .git\n  * directory for IPC.  These Windows drive formats do not support\n  * Unix domain sockets, so mark them as incompatible for the daemon.\n  *\n  */\n-static enum fsmonitor_reason check_volume(struct repository *r)\n+static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n-\tstruct statfs fs;\n+\tstruct fs_info fs;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \n-\tif (statfs(r->worktree, &fs) == -1) {\n-\t\tint saved_errno = errno;\n-\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n-\t\t\t\t r->worktree, strerror(saved_errno));\n-\t\terrno = saved_errno;\n+\tif (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {\n+\t\tstrbuf_release(&path);\n \t\treturn FSMONITOR_REASON_ERROR;\n \t}\n \n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n-\t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n \n-\tif (!(fs.f_flags & MNT_LOCAL))\n+\tif (fs.is_remote)\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n-\tif (!strcmp(fs.f_fstypename, \"msdos\")) /* aka FAT32 */\n+\tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n-\tif (!strcmp(fs.f_fstypename, \"ntfs\"))\n+\tif (!strcmp(fs.typename, \"ntfs\"))\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \treturn FSMONITOR_REASON_OK;\n@@ -81,7 +53,7 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_volume(r);\n+\treason = check_uds_volume(r);\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex e5ec5b0a9f7..d88b06ae610 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -1,8 +1,9 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * VFS for Git is incompatible with FSMonitor.\n@@ -24,171 +25,6 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-/*\n- * Check if monitoring remote working directories is allowed.\n- *\n- * By default, monitoring remote working directories is\n- * disabled.  Users may override this behavior in enviroments where\n- * they have proper support.\n- */\n-static int check_config_allowremote(struct repository *r)\n-{\n-\tint allow;\n-\n-\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n-\t\treturn allow;\n-\n-\treturn -1; /* fsmonitor.allowremote not set */\n-}\n-\n-/*\n- * Check remote working directory protocol.\n- *\n- * Error if client machine cannot get remote protocol information.\n- */\n-static int check_remote_protocol(wchar_t *wpath)\n-{\n-\tHANDLE h;\n-\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n-\n-\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n-\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n-\n-\tif (h == INVALID_HANDLE_VALUE) {\n-\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\treturn -1;\n-\t}\n-\n-\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n-\t\t&proto_info, sizeof(proto_info))) {\n-\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\tCloseHandle(h);\n-\t\treturn -1;\n-\t}\n-\n-\tCloseHandle(h);\n-\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n-\t\t\t\twpath, proto_info.Protocol);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type dictates whether notification events are available at\n- * all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- * Notes for testing:\n- *\n- * (a) Windows allows a network share to be mapped to a drive letter.\n- *     (This is the normal method to access it.)\n- *\n- *     $ NET USE Z: \\\\server\\share\n- *     $ git -C Z:/repo status\n- *\n- * (b) Windows allows a network share to be referenced WITHOUT mapping\n- *     it to drive letter.\n- *\n- *     $ NET USE \\\\server\\share\\dir\n- *     $ git -C //server/share/repo status\n- *\n- * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n- *     arbitrary path (which may be remote)\n- *\n- *     $ SUBST Q: Z:\\repo\n- *     $ git -C Q:/ status\n- *\n- * (d) Windows allows a directory symlink to be created on a local\n- *     file system that points to a remote repo.\n- *\n- *     $ mklink /d ./link //server/share/repo\n- *     $ git -C ./link status\n- */\n-static enum fsmonitor_reason check_remote(struct repository *r)\n-{\n-\tint ret;\n-\twchar_t wpath[MAX_PATH];\n-\twchar_t wfullpath[MAX_PATH];\n-\tsize_t wlen;\n-\tUINT driveType;\n-\n-\t/*\n-\t * Do everything in wide chars because the drive letter might be\n-\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n-\t */\n-\tif (xutftowcs_path(wpath, r->worktree) < 0)\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t/*\n-\t * GetDriveTypeW() requires a final slash.  We assume that the\n-\t * worktree pathname points to an actual directory.\n-\t */\n-\twlen = wcslen(wpath);\n-\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n-\t\twpath[wlen++] = L'\\\\';\n-\t\twpath[wlen] = 0;\n-\t}\n-\n-\t/*\n-\t * Normalize the path.  If nothing else, this converts forward\n-\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n-\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n-\t */\n-\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\tdriveType = GetDriveTypeW(wfullpath);\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n-\t\t\t r->worktree, wfullpath, driveType);\n-\n-\tif (driveType == DRIVE_REMOTE) {\n-\t\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t \"check_remote('%s') true\",\n-\t\t\t\t r->worktree);\n-\n-\t\tret = check_remote_protocol(wfullpath);\n-\t\tif (ret < 0)\n-\t\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t\tswitch (check_config_allowremote(r)) {\n-\t\tcase 0: /* config overrides and disables */\n-\t\t\treturn FSMONITOR_REASON_REMOTE;\n-\t\tcase 1: /* config overrides and enables */\n-\t\t\treturn FSMONITOR_REASON_OK;\n-\t\tdefault:\n-\t\t\tbreak; /* config has no opinion */\n-\t\t}\n-\n-\t\treturn FSMONITOR_REASON_REMOTE;\n-\t}\n-\n-\treturn FSMONITOR_REASON_OK;\n-}\n-\n enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n@@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n-\treason = check_remote(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n-\n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 2237109b57f..b88494bf59b 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)\n@@ -315,6 +316,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nnew file mode 100644\nindex 00000000000..e48592887e7\n--- /dev/null\n+++ b/fsmonitor-path-utils.h\n@@ -0,0 +1,23 @@\n+#ifndef FSM_PATH_UTILS_H\n+#define FSM_PATH_UTILS_H\n+\n+struct fs_info {\n+\tint is_remote;\n+\tchar *typename;\n+};\n+\n+/*\n+ * Get some basic filesystem informtion for the given path\n+ *\n+ * Returns -1 on error, zero otherwise.\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n+\n+/*\n+ * Determines if the filesystem that path resides on is remote.\n+ *\n+ * Returns -1 on error, 0 if not remote, 1 if remote.\n+ */\n+int fsmonitor__is_fs_remote(const char *path);\n+\n+#endif\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..d288cbad479 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * We keep this structure defintion private and have getters\n@@ -13,6 +14,52 @@ struct fsmonitor_settings {\n \tchar *hook_path;\n };\n \n+/*\n+ * Remote working directories are problematic for FSMonitor.\n+ *\n+ * The underlying file system on the server machine and/or the remote\n+ * mount type dictates whether notification events are available at\n+ * all to remote client machines.\n+ *\n+ * Kernel differences between the server and client machines also\n+ * dictate the how (buffering, frequency, de-dup) the events are\n+ * delivered to client machine processes.\n+ *\n+ * A client machine (such as a laptop) may choose to suspend/resume\n+ * and it is unclear (without lots of testing) whether the watcher can\n+ * resync after a resume.  We might be able to treat this as a normal\n+ * \"events were dropped by the kernel\" event and do our normal \"flush\n+ * and resync\" --or-- we might need to close the existing (zombie?)\n+ * notification fd and create a new one.\n+ *\n+ * In theory, the above issues need to be addressed whether we are\n+ * using the Hook or IPC API.\n+ *\n+ * So (for now at least), mark remote working directories as\n+ * incompatible unless 'fsmonitor.allowRemote' is true.\n+ *\n+ */\n+#ifdef HAVE_FSMONITOR_OS_SETTINGS\n+static enum fsmonitor_reason check_remote(struct repository *r)\n+{\n+\tint allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */\n+\tint is_remote = fsmonitor__is_fs_remote(r->worktree);\n+\n+\tswitch (is_remote) {\n+\t\tcase 0:\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tcase 1:\n+\t\t\trepo_config_get_bool(r, \"fsmonitor.allowremote\", &allow_remote);\n+\t\t\tif (allow_remote < 1)\n+\t\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\t\telse\n+\t\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+}\n+#endif\n+\n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n {\n \tif (!r->worktree) {\n@@ -27,6 +74,9 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t{\n \t\tenum fsmonitor_reason reason;\n \n+\t\treason = check_remote(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n \t\treason = fsm_os__incompatible(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-- \ngitgitgadget\n\n"},{"id":"463128","messageId":"6719ca2b24d07449b8469b94f99e99ede01bce60.1663377141.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v8.git.1663377141.gitgitgadget@gmail.com","subject":"[PATCH v8 3/5] fsmonitor: avoid socket location check if using hook","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-17T01:12:19Z","receivedAt":"2022-09-17T01:12:39Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf monitoring is done via fsmonitor hook rather than IPC there is no\nneed to check if the location of the Unix Domain socket (UDS) file is\non a remote filesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 10 ++++++----\n compat/fsmonitor/fsm-settings-win32.c  |  2 +-\n fsmonitor-settings.c                   |  8 ++++----\n fsmonitor-settings.h                   |  2 +-\n 4 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 681d8bf963e..40da2d3b533 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -49,13 +49,15 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_uds_volume(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n+\tif (ipc) {\n+\t\treason = check_uds_volume(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n+\t}\n \n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex d88b06ae610..a8af31b71de 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex d288cbad479..531a1b6f956 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -60,7 +60,7 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n }\n #endif\n \n-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n+static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n {\n \tif (!r->worktree) {\n \t\t/*\n@@ -77,7 +77,7 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t\treason = check_remote(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-\t\treason = fsm_os__incompatible(r);\n+\t\treason = fsm_os__incompatible(r, ipc);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n \t}\n@@ -162,7 +162,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -185,7 +185,7 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..0721617b95a 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -48,7 +48,7 @@ struct fsmonitor_settings;\n  * fsm_os__* routines should considered private to fsm_settings__\n  * routines.\n  */\n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n \n #endif /* FSMONITOR_SETTINGS_H */\n-- \ngitgitgadget\n\n"},{"id":"463129","messageId":"d736cb8fa90ae9df400b3585188475dcf8dcb053.1663377141.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v8.git.1663377141.gitgitgadget@gmail.com","subject":"[PATCH v8 4/5] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-17T01:12:20Z","receivedAt":"2022-09-17T01:12:43Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n\nfsmonitor: deal with synthetic firmlinks on macOS\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n\nfsmonitor: deal with synthetic firmlinks on macOS\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n builtin/fsmonitor--daemon.c              |  8 +++\n compat/fsmonitor/fsm-listen-darwin.c     |  6 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 92 ++++++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 17 +++++\n fsmonitor--daemon.h                      |  3 +\n fsmonitor-path-utils.h                   | 36 ++++++++++\n 6 files changed, 161 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 0123fc33ed2..56fcd1c2baa 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"fsmonitor.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n #include \"compat/fsmonitor/fsm-health.h\"\n #include \"compat/fsmonitor/fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n@@ -1282,6 +1283,13 @@ static int fsmonitor_run_daemon(void)\n \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n \tstate.nr_paths_watching = 1;\n \n+\tstate.alias.alias = NULL;\n+\tstate.alias.points_to = NULL;\n+\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n+\t\terr = error(_(\"could not get worktree alias\"));\n+\t\tgoto done;\n+\t}\n+\n \t/*\n \t * We create and delete cookie files somewhere inside the .git\n \t * directory to help us keep sync with the file system.  If\ndiff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\nindex 8e208e8289e..179886bc15b 100644\n--- a/compat/fsmonitor/fsm-listen-darwin.c\n+++ b/compat/fsmonitor/fsm-listen-darwin.c\n@@ -26,6 +26,7 @@\n #include \"fsmonitor.h\"\n #include \"fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsm_listen_data\n {\n@@ -209,7 +210,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t/*\n \t\t * On Mac, we receive an array of absolute paths.\n \t\t */\n-\t\tpath_k = paths[k];\n+\t\tpath_k = fsmonitor__resolve_alias(paths[k], &state->alias);\n+\t\tif (!path_k)\n+\t\t\tpath_k = paths[k];\n \n \t\t/*\n \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n@@ -238,6 +241,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t\tfsmonitor_force_resync(state);\n \t\t\tfsmonitor_batch__free_list(batch);\n \t\t\tstring_list_clear(&cookie_list, 0);\n+\t\t\tbatch = NULL;\n \n \t\t\t/*\n \t\t\t * We assume that any events that we received\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nindex 067cbe6990a..13807f58e95 100644\n--- a/compat/fsmonitor/fsm-path-utils-darwin.c\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -1,5 +1,8 @@\n #include \"fsmonitor.h\"\n #include \"fsmonitor-path-utils.h\"\n+#include <dirent.h>\n+#include <errno.h>\n+#include <fcntl.h>\n #include <sys/param.h>\n #include <sys/mount.h>\n \n@@ -38,3 +41,92 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * Scan the root directory for synthetic firmlinks that when resolved\n+ * are a prefix of the path, stopping at the first one found.\n+ *\n+ * Some information about firmlinks and synthetic firmlinks:\n+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n+ *\n+ * macOS no longer allows symlinks in the root directory; any link found\n+ * there is therefore a synthetic firmlink.\n+ *\n+ * If this function gets called often, will want to cache all the firmlink\n+ * information, but for now there is only one caller of this function.\n+ *\n+ * If there is more than one alias for the path, that is another\n+ * matter altogether.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\tDIR * dir;\n+\tint read;\n+\tint retval;\n+\tstruct dirent *de;\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+\n+\tretval = 0;\n+\tdir = opendir(\"/\");\n+\tif (!dir)\n+\t\treturn -1;\n+\n+\tstrbuf_init(&alias, 256);\n+\tstrbuf_init(&points_to, MAXPATHLEN);\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstrbuf_reset(&alias);\n+\t\tstrbuf_addch(&alias, '/');\n+\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n+\n+\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n+\t\tif (read > 0) {\n+\t\t\tstrbuf_setlen(&points_to, read);\n+\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n+\t\t\t\t&& path[points_to.len] == '/') {\n+\t\t\t\tinfo->alias = strbuf_detach(&alias, NULL);\n+\t\t\t\tinfo->points_to = strbuf_detach(&points_to, NULL);\n+\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n+\t\t\t\t\tpath, info->alias, info->points_to);\n+\t\t\t\tretval = 0;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n+\t\t\ttrace_printf_key(&trace_fsmonitor, \"Error %s\", strerror(errno));\n+\t\t\tretval = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n+\tdone:\n+\tclosedir(dir);\n+\tstrbuf_release(&alias);\n+\tstrbuf_release(&points_to);\n+\treturn retval;\n+}\n+\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\tint len = info->alias ? strlen(info->alias) : 0;\n+\n+\tif (!len)\n+\t\treturn NULL;\n+\n+\tif ((strncmp(info->alias, path, len) == 0)\n+\t\t&& path[len] == '/') {\n+\t\tstruct strbuf tmp;\n+\t\tconst char *remainder = path + len;\n+\t\tint ptr_len = strlen(info->points_to);\n+\t\tint rem_len = strlen(remainder);\n+\n+\t\tstrbuf_init(&tmp, ptr_len + rem_len);\n+\t\tstrbuf_add(&tmp, info->points_to, ptr_len);\n+\t\tstrbuf_add(&tmp, remainder, rem_len);\n+\t\treturn strbuf_detach(&tmp, NULL);\n+\t}\n+\n+\treturn NULL;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nindex a90b8f7925b..0d95bbb416f 100644\n--- a/compat/fsmonitor/fsm-path-utils-win32.c\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -126,3 +126,20 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * No-op for now.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\treturn 0;\n+}\n+\n+/*\n+ * No-op for now.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\treturn NULL;\n+}\ndiff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\nindex 2102a5c9ff5..e24838f9a86 100644\n--- a/fsmonitor--daemon.h\n+++ b/fsmonitor--daemon.h\n@@ -8,6 +8,7 @@\n #include \"run-command.h\"\n #include \"simple-ipc.h\"\n #include \"thread-utils.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsmonitor_batch;\n struct fsmonitor_token_data;\n@@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct strbuf path_worktree_watch;\n \tstruct strbuf path_gitdir_watch;\n+\tstruct alias_info alias;\n \tint nr_paths_watching;\n \n \tstruct fsmonitor_token_data *current_token_data;\n@@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct ipc_server_data *ipc_server_data;\n \tstruct strbuf path_ipc;\n+\n };\n \n /*\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nindex e48592887e7..50ef37e57bb 100644\n--- a/fsmonitor-path-utils.h\n+++ b/fsmonitor-path-utils.h\n@@ -1,6 +1,14 @@\n #ifndef FSM_PATH_UTILS_H\n #define FSM_PATH_UTILS_H\n \n+#include \"strbuf.h\"\n+\n+struct alias_info\n+{\n+\tchar *alias;\n+\tchar *points_to;\n+};\n+\n struct fs_info {\n \tint is_remote;\n \tchar *typename;\n@@ -20,4 +28,32 @@ int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n  */\n int fsmonitor__is_fs_remote(const char *path);\n \n+/*\n+ * Get the alias in given path, if any.\n+ *\n+ * Sets alias to the first alias that matches any part of the path.\n+ *\n+ * If an alias is found, info.alias and info.points_to are set to the\n+ * found mapping.\n+ *\n+ * Returns -1 on error, 0 otherwise.\n+ *\n+ * The caller owns the storage that is occupied by set info.alias and\n+ * info.points_to and is responsible for releasing it with `free(3)`\n+ * when done.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info);\n+\n+/*\n+ * Resolve the path against the given alias.\n+ *\n+ * Returns the resolved path if there is one, NULL otherwise.\n+ *\n+ * The caller owns the storage that the returned string occupies and\n+ * is responsible for releasing it with `free(3)` when done.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info);\n+\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"463130","messageId":"ddf4e3e6442b104447154b0a5d4954f274f4b5ef.1663377141.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v8.git.1663377141.gitgitgadget@gmail.com","subject":"[PATCH v8 5/5] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-17T01:12:21Z","receivedAt":"2022-09-17T01:12:45Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nAdd documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\nCall-out experimental nature of 'fsmonitor.allowRemote' and limited file\nsystem support for 'fsmonitor.socketDir'.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Documentation/git-fsmonitor--daemon.txt | 35 +++++++++++++++++++++++++\n 1 file changed, 35 insertions(+)\n\ndiff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt\nindex cc142fb8612..0adccd0eced 100644\n--- a/Documentation/git-fsmonitor--daemon.txt\n+++ b/Documentation/git-fsmonitor--daemon.txt\n@@ -70,6 +70,41 @@ the change (as happening against the super repo).  However, the client\n will properly ignore these extra events, so performance may be affected\n but it will not cause an incorrect result.\n \n+By default, the fsmonitor daemon refuses to work against network-mounted\n+repositories; this my be overridden by setting `fsmonitor.allowRemote` to\n+`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n+correctly with all network-mounted repositores and such use is considered\n+experimental.\n+\n+On Mac OS, the inter-process communication (IPC) between various Git\n+commands and the fsmonitor daemon is done via a Unix domain socket (UDS).\n+Usage of UDS requires the creation of a file which, by default, is created\n+in the .git directory.  If the fsmonitor daemon detects that the .git directory\n+is on a network-mounted file system, it will create the UDS file in $HOME.  If\n+$HOME itself is on a network-mounted file system or if $HOME is not the desired\n+location for the UDS file, 'fsmonitor.socketDir' may be set to any valid, local\n+directory on a file system with proper support.  Mac OS native file systems have\n+the required support.  File systems known to lack support include FAT32 and\n+NTFS.  Other file systems may or many not have the needed support; the fsmonitor\n+daemon is not guaranteed to work with these file systems and such use is\n+considered experimental.\n+\n+CONFIGURATION\n+-------------\n+When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])\n+the fsmonitor daemon will pay attention to the following configuration\n+variables:\n+\n+fsmonitor.allowRemote::\n+\tBy default, the daemon refuses to work against network-mounted\n+\trepositories. Setting `fsmonitor.allowRemote` to `true` overrides\n+\tthis behavior.\n+\n+fsmonitor.socketDir::\n+\tThis option is only used by the Mac OS implementation of the fsmonitor\n+\tdaemon.\tIf set, 'fsmonitor.socketDir' must be set to a valid, local\n+\tdirectory on a file system that can support Unix domain sockets (UDS).\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \ngitgitgadget\n"},{"id":"463133","messageId":"CAPig+cScMtqKg2Ke8XK2FQ+XSeLRof0mvKk_uGPFC6eriUOdDw@mail.gmail.com","threadId":"58324","inReplyTo":"b535649722826d7317061da7d8b0cf3d6e2a51ff.1663377141.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v8 2/5] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-09-17T04:13:45Z","receivedAt":"2022-09-17T04:14:02Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Sep 16, 2022 at 9:12 PM Eric DeCosta via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> If the .git directory is on a remote file system, create the socket\n> file in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n>\n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n> diff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\n> @@ -0,0 +1,9 @@\n> +#include \"config.h\"\n> +#include \"fsmonitor-ipc.h\"\n> +\n> +const char *fsmonitor_ipc__get_path(struct repository *r) {\n> +       static char *ret;\n> +       if (!ret)\n> +               ret = git_pathdup(\"fsmonitor--daemon.ipc\");\n> +       return ret;\n> +}\n> \\ No newline at end of file\n\nThis file probably wants a final line terminator.\n"},{"id":"463135","messageId":"CAPig+cQ5SMw+0Cwtw47LQM59-mQjJaOPe_LTybAC2j=3F9OywA@mail.gmail.com","threadId":"58324","inReplyTo":"ddf4e3e6442b104447154b0a5d4954f274f4b5ef.1663377141.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v8 5/5] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-09-17T06:08:43Z","receivedAt":"2022-09-17T06:09:00Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Sep 16, 2022 at 9:12 PM Eric DeCosta via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> Add documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\n> Call-out experimental nature of 'fsmonitor.allowRemote' and limited file\n> system support for 'fsmonitor.socketDir'.\n>\n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n> diff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt\n> @@ -70,6 +70,41 @@ the change (as happening against the super repo).  However, the client\n> +By default, the fsmonitor daemon refuses to work against network-mounted\n> +repositories; this my be overridden by setting `fsmonitor.allowRemote` to\n> +`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n> +correctly with all network-mounted repositores and such use is considered\n> +experimental.\n\ns/repositores/repositories/\n\n> +On Mac OS, the inter-process communication (IPC) between various Git\n> +commands and the fsmonitor daemon is done via a Unix domain socket (UDS).\n> +Usage of UDS requires the creation of a file which, by default, is created\n> +in the .git directory.  If the fsmonitor daemon detects that the .git directory\n\nTypesetting: s/.git/`.git`/g\n\n> +is on a network-mounted file system, it will create the UDS file in $HOME.  If\n\nThere's a gap in the explanation as to _why_ fsmonitor won't use the\n.git directory for this file when on a network-mounted filesystem and\ninstead chooses $HOME. For the reader who is not well-versed in Unix\nsockets/filesystems, it may be difficult to understand the logic\nbehind this. This gap is somewhat filled in a few sentences later, but\nit makes for potentially confusing reading until then.\n\nShould the reader know the name of the socket file or at least the\ntemplated form of the name? The first question which popped into my\nhead upon reading this was whether it was going to pollute my home\ndirectory with non-hidden files. If this had mentioned something along\nthe lines of \"creation of a file named `.git-fsmonitor-*`\" or\n\"creation of a hidden file\" then I would have understood immediately\nthat the file would have been hidden.\n\n> +$HOME itself is on a network-mounted file system or if $HOME is not the desired\n\nTo be consistent with formatting elsewhere in the Git documentation,\nlet's typeset this as `$HOME` (with backticks).\n\nAside: The spelling \"filesystem\" appears almost five times as often as\n\"file system\" in Git documentation, however, this particular file\nalready uses \"file system\" and does so consistently, so it makes sense\nto follow suit as you do here. Changing to use \"filesystem\" instead,\nif such a task is desirable, is outside the scope of this patch\nseries.\n\n> +location for the UDS file, 'fsmonitor.socketDir' may be set to any valid, local\n\nFor consistency, let's use backticks here, as well: `fsmonitor.socketDir`\n\n> +directory on a file system with proper support.  Mac OS native file systems have\n\nTogether with the above comment about a gap in the explanation, I\nfound myself scratching my head about what \"proper support\" meant\n(when pretending to read this as a person not particularly familiar\nwith Unix sockets or filesystems).\n\nAlso, although this explains how to work around the case when $HOME is\nitself network-mounted, what happens if $HOME is network-mounted and\nthe user does not set `fsmonitor.socketDir`? Does it error out? Does\nit simply misbehave in some way? Should it error out? (I would think\n\"yes\".)\n\n> +the required support.  File systems known to lack support include FAT32 and\n> +NTFS.  Other file systems may or many not have the needed support; the fsmonitor\n\ns/many/may/\n\n> +daemon is not guaranteed to work with these file systems and such use is\n> +considered experimental.\n\nTaking the above comments into account, here's my attempt at a rewrite:\n\n    On Mac OS, the inter-process communication (IPC) between various\n    Git commands and the fsmonitor daemon occurs via a Unix domain\n    socket (UDS) -- a special type of file -- which is supported by\n    the native Mac OS filesystems but not by network-mounted\n    filesystems, NTFS or FAT32.  Other file systems may or many not\n    have the needed support; the fsmonitor daemon is not guaranteed to\n    work with these file systems and such use is considered\n    experimental.\n\n    By default, the socket is created in the `.git` directory,\n    however, if the `.git` directory is on a network-mounted file\n    system, it will instead be created at `$HOME/.git-fsmonitor-*`\n    unless `$HOME` itself is on a network-mounted file system, in\n    which case you must set the configuration variable\n    `fsmonitor.socketDir` to the path of a directory on a Mac OS\n    native filesystem in which to create the socket file.\n\n> +CONFIGURATION\n> +-------------\n> +When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])\n> +the fsmonitor daemon will pay attention to the following configuration\n> +variables:\n\nWe probably want a blank line after the header underline and before\nthis paragraph.\n\n> +fsmonitor.allowRemote::\n> +       By default, the daemon refuses to work against network-mounted\n> +       repositories. Setting `fsmonitor.allowRemote` to `true` overrides\n> +       this behavior.\n> +\n> +fsmonitor.socketDir::\n> +       This option is only used by the Mac OS implementation of the fsmonitor\n> +       daemon. If set, 'fsmonitor.socketDir' must be set to a valid, local\n> +       directory on a file system that can support Unix domain sockets (UDS).\n\nTypeset with backticks: `fsmonitor.socketDir`\n\nThe word \"valid\" seems unnecessary. A possible rewrite:\n\n    This Mac OS-specific option, if set, specifies the directory in\n    which to create the Unix domain socket used for communication\n    between fsmonitor and various Git commands. The directory must\n    reside on a native Mac OS filesystem as discussed above.\n"},{"id":"463136","messageId":"CAPig+cTRNfewL01GOVTjFLaFq-vGgdeV2OX6Bn+StGrgb1JVMA@mail.gmail.com","threadId":"58324","inReplyTo":"b535649722826d7317061da7d8b0cf3d6e2a51ff.1663377141.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v8 2/5] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-09-17T06:29:44Z","receivedAt":"2022-09-17T06:30:02Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Sep 16, 2022 at 9:12 PM Eric DeCosta via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> If the .git directory is on a remote file system, create the socket\n> file in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n>\n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n> diff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\n> @@ -0,0 +1,46 @@\n> +static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n> +\n> +const char *fsmonitor_ipc__get_path(struct repository *r)\n> +{\n> +       static const char *ipc_path;\n> +       SHA_CTX sha1ctx;\n> +       char *sock_dir;\n> +       struct strbuf ipc_file = STRBUF_INIT;\n> +       unsigned char hash[SHA_DIGEST_LENGTH];\n> +\n> +       if (ipc_path)\n> +               return ipc_path;\n> +\n> +       ipc_path = fsmonitor_ipc__get_default_path();\n> +\n> +       /* By default the socket file is created in the .git directory */\n> +       if (fsmonitor__is_fs_remote(ipc_path) < 1)\n> +               return ipc_path;\n> +\n> +       SHA1_Init(&sha1ctx);\n> +       SHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n> +       SHA1_Final(hash, &sha1ctx);\n> +\n> +       repo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n> +\n> +       /* Create the socket file in either socketDir or $HOME */\n> +       if (sock_dir && *sock_dir)\n> +               strbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n> +                                       sock_dir, hash_to_hex(hash));\n> +       else\n> +               strbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n\nA couple comments...\n\nIn my mind, the directory specified by `fsmonitor.socketdir` is likely\nto be dedicated to this purpose (i.e. housing Git administrative\njunk). As such, it feels somewhat odd for the socket file to be\nhidden; I would instead expect the socket name to be non-hidden (say,\n\"git-fsmonitor-daemon-{hash}.ipc\") rather than hidden\n(\".git-fsmonitor-*\"). The directory specified by `fsmonitor.socketdir`\nmay or may not be hidden (i.e. start with a dot), but that's the\nuser's decision. For the $HOME case, it almost feels cleaner to create\na hidden directory (say, \"$HOME/.git-fsmonitor\") in which to house the\nsocket files (\"git-fsmonitor-daemon-{hash}.ipc\"). Anyhow, this comment\nis quite subjective; perhaps not actionable.\n\nWhat happens if either $HOME or `fsmonitor.socketdir` are\nnetwork-mounted? Should this code be checking for that case? If they\nare network-mounted, should it error out? At minimum, I would think a\nwarning is warranted in order to save users the headache of wondering\nwhy fsmonitor isn't working correctly.\n\n> +       ipc_path = interpolate_path(ipc_file.buf, 1);\n> +       if (!ipc_path)\n> +               die(_(\"Invalid path: %s\"), ipc_file.buf);\n> +\n> +       strbuf_release(&ipc_file);\n\n`sock_dir` is being leaked, isn't it?\n\n> +       return ipc_path;\n> +}\n> diff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\n> @@ -0,0 +1,9 @@\n> +const char *fsmonitor_ipc__get_path(struct repository *r) {\n> +       static char *ret;\n> +       if (!ret)\n> +               ret = git_pathdup(\"fsmonitor--daemon.ipc\");\n> +       return ret;\n> +}\n> \\ No newline at end of file\n\nMentioned already.\n"},{"id":"463141","messageId":"BL0PR05MB55710118DBE7AE6A44795D7ED94B9@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"CAPig+cTRNfewL01GOVTjFLaFq-vGgdeV2OX6Bn+StGrgb1JVMA@mail.gmail.com","subject":"RE: [PATCH v8 2/5] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-17T16:29:58Z","receivedAt":"2022-09-17T16:30:10Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Eric Sunshine <sunshine@sunshineco.com>\n> Sent: Saturday, September 17, 2022 2:30 AM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>\n> Cc: Git List <git@vger.kernel.org>; Jeff Hostetler <git@jeffhostetler.com>;\n> Torsten Bögershausen <tboegi@web.de>; Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com>; Ramsay Jones <ramsay@ramsayjones.plus.com>;\n> Johannes Schindelin <Johannes.Schindelin@gmx.de>; Eric DeCosta\n> <edecosta@mathworks.com>\n> Subject: Re: [PATCH v8 2/5] fsmonitor: relocate socket file if .git directory is\n> remote\n> \n> On Fri, Sep 16, 2022 at 9:12 PM Eric DeCosta via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> > If the .git directory is on a remote file system, create the socket\n> > file in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n> >\n> > Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> > ---\n> > diff --git a/compat/fsmonitor/fsm-ipc-darwin.c\n> > b/compat/fsmonitor/fsm-ipc-darwin.c\n> > @@ -0,0 +1,46 @@\n> > +static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path,\n> > +\"fsmonitor--daemon.ipc\")\n> > +\n> > +const char *fsmonitor_ipc__get_path(struct repository *r) {\n> > +       static const char *ipc_path;\n> > +       SHA_CTX sha1ctx;\n> > +       char *sock_dir;\n> > +       struct strbuf ipc_file = STRBUF_INIT;\n> > +       unsigned char hash[SHA_DIGEST_LENGTH];\n> > +\n> > +       if (ipc_path)\n> > +               return ipc_path;\n> > +\n> > +       ipc_path = fsmonitor_ipc__get_default_path();\n> > +\n> > +       /* By default the socket file is created in the .git directory */\n> > +       if (fsmonitor__is_fs_remote(ipc_path) < 1)\n> > +               return ipc_path;\n> > +\n> > +       SHA1_Init(&sha1ctx);\n> > +       SHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n> > +       SHA1_Final(hash, &sha1ctx);\n> > +\n> > +       repo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n> > +\n> > +       /* Create the socket file in either socketDir or $HOME */\n> > +       if (sock_dir && *sock_dir)\n> > +               strbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n> > +                                       sock_dir, hash_to_hex(hash));\n> > +       else\n> > +               strbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\",\n> > + hash_to_hex(hash));\n> \n> A couple comments...\n> \n> In my mind, the directory specified by `fsmonitor.socketdir` is likely to be\n> dedicated to this purpose (i.e. housing Git administrative junk). As such, it\n> feels somewhat odd for the socket file to be hidden; I would instead expect\n> the socket name to be non-hidden (say,\n> \"git-fsmonitor-daemon-{hash}.ipc\") rather than hidden (\".git-fsmonitor-*\").\n> The directory specified by `fsmonitor.socketdir` may or may not be hidden\n> (i.e. start with a dot), but that's the user's decision. For the $HOME case, it\n> almost feels cleaner to create a hidden directory (say, \"$HOME/.git-\n> fsmonitor\") in which to house the socket files (\"git-fsmonitor-daemon-\n> {hash}.ipc\"). Anyhow, this comment is quite subjective; perhaps not\n> actionable.\n\n> What happens if either $HOME or `fsmonitor.socketdir` are network-\n> mounted? Should this code be checking for that case? If they are network-\n> mounted, should it error out? At minimum, I would think a warning is\n> warranted in order to save users the headache of wondering why fsmonitor\n> isn't working correctly.\n> \nUltimately, the UDS file location is checked by check_uds_volume in fsm-settings-darwin as part of the overall settings checks; it will error-out there if the path is on the network.\n\n> > +       ipc_path = interpolate_path(ipc_file.buf, 1);\n> > +       if (!ipc_path)\n> > +               die(_(\"Invalid path: %s\"), ipc_file.buf);\n> > +\n> > +       strbuf_release(&ipc_file);\n> \n> `sock_dir` is being leaked, isn't it?\n>\n\nSure is, thanks.\n\n\n> > +       return ipc_path;\n> > +}\n> > diff --git a/compat/fsmonitor/fsm-ipc-win32.c\n> > b/compat/fsmonitor/fsm-ipc-win32.c\n> > @@ -0,0 +1,9 @@\n> > +const char *fsmonitor_ipc__get_path(struct repository *r) {\n> > +       static char *ret;\n> > +       if (!ret)\n> > +               ret = git_pathdup(\"fsmonitor--daemon.ipc\");\n> > +       return ret;\n> > +}\n> > \\ No newline at end of file\n> \n> Mentioned already.\n\n"},{"id":"463172","messageId":"5c50a3cf-c5ac-6b11-2548-8400e5574b32@jeffhostetler.com","threadId":"58324","inReplyTo":"xmqqy1ujf5a9.fsf@gitster.g","subject":"Re: [PATCH v7 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-09-19T12:31:04Z","receivedAt":"2022-09-19T12:31:33Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 9/16/22 4:11 PM, Junio C Hamano wrote:\n> \"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n>> +const char *fsmonitor_ipc__get_path(struct repository *r)\n>> +{\n>> +\tstatic const char *ipc_path;\n>> +\tSHA_CTX sha1ctx;\n>> +\tchar *sock_dir;\n>> +\tstruct strbuf ipc_file = STRBUF_INIT;\n>> +\tunsigned char hash[SHA_DIGEST_LENGTH];\n>> +\n>> +\tif (ipc_path)\n>> +\t\treturn ipc_path;\n>> +\n>> +\tif (!r)\n>> +\t\tr = the_repository;\n> \n> I'd prefer not to see this \"NULL means the_repository\".  It would be\n> a different story if the caller does not necessarily have a ready\n> access to the_repository, but it is a global, so the caller can pass\n> the_repository and be more explicit.  Giving two ways to the caller\n> to express same thing is not a good idea.\n> \n> Thanks.\n> \n\nTo be fair, I added several \"if (!r) r = the_repository;\" statements\nto the original public FSMonitor routines.  There were obscure cases\nwhere tests would sometimes randomly fail because \"r\" wasn't completely\npassed down via some hard to isolate call stack.  Offlist, AEvar told me\nthat he managed to isolate it and has a fix.\n\nSo eventually, we'll be able to get rid of all of these direct\nreferences to \"the_repository\" and properly assume that \"r\" is\nalways passed down.\n\nBut for now, I think we should let this stay for safety.\n\nJeff\n\n"},{"id":"463196","messageId":"xmqq35cnco3h.fsf@gitster.g","threadId":"58324","inReplyTo":"5c50a3cf-c5ac-6b11-2548-8400e5574b32@jeffhostetler.com","subject":"Re: [PATCH v7 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-19T16:42:42Z","receivedAt":"2022-09-19T16:42:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Hostetler <git@jeffhostetler.com> writes:\n\n> On 9/16/22 4:11 PM, Junio C Hamano wrote:\n>> \"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>> \n>>> +const char *fsmonitor_ipc__get_path(struct repository *r)\n>>> +{\n>>> +\tstatic const char *ipc_path;\n>>> +\tSHA_CTX sha1ctx;\n>>> +\tchar *sock_dir;\n>>> +\tstruct strbuf ipc_file = STRBUF_INIT;\n>>> +\tunsigned char hash[SHA_DIGEST_LENGTH];\n>>> +\n>>> +\tif (ipc_path)\n>>> +\t\treturn ipc_path;\n>>> +\n>>> +\tif (!r)\n>>> +\t\tr = the_repository;\n>> I'd prefer not to see this \"NULL means the_repository\".  It would be\n>> a different story if the caller does not necessarily have a ready\n>> access to the_repository, but it is a global, so the caller can pass\n>> the_repository and be more explicit.  Giving two ways to the caller\n>> to express same thing is not a good idea.\n>> Thanks.\n>> \n>\n> To be fair, I added several \"if (!r) r = the_repository;\" statements\n> to the original public FSMonitor routines.  There were obscure cases\n> where tests would sometimes randomly fail because \"r\" wasn't completely\n> passed down via some hard to isolate call stack.  Offlist, AEvar told me\n> that he managed to isolate it and has a fix.\n>\n> So eventually, we'll be able to get rid of all of these direct\n> references to \"the_repository\" and properly assume that \"r\" is\n> always passed down.\n>\n> But for now, I think we should let this stay for safety.\n\nI wouldn't call \"sweeping a breakage under the rug\" a \"safety\",\nthough.  If the caller cannot decide which repository instance is\nthe right thing to pass, or the caller does not yet have a good one\nto pass when making a call down the codepath, how can it be safer to\nuse the_repository that may or may not be the appropriate one than\nnoticing the problem by dying and stopping the spread of damage?\n"},{"id":"463198","messageId":"xmqqy1ufb960.fsf@gitster.g","threadId":"58324","inReplyTo":"CAPig+cScMtqKg2Ke8XK2FQ+XSeLRof0mvKk_uGPFC6eriUOdDw@mail.gmail.com","subject":"Re: [PATCH v8 2/5] fsmonitor: relocate socket file if .git directory is remote","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-19T16:50:31Z","receivedAt":"2022-09-19T16:52:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> On Fri, Sep 16, 2022 at 9:12 PM Eric DeCosta via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>> +}\n>> \\ No newline at end of file\n>\n> This file probably wants a final line terminator.\n\ndefinitely ;-)\n"},{"id":"463199","messageId":"xmqqsfknb8sw.fsf@gitster.g","threadId":"58324","inReplyTo":"CAPig+cTRNfewL01GOVTjFLaFq-vGgdeV2OX6Bn+StGrgb1JVMA@mail.gmail.com","subject":"Re: [PATCH v8 2/5] fsmonitor: relocate socket file if .git directory is remote","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-19T16:58:23Z","receivedAt":"2022-09-19T16:59:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> A couple comments...\n>\n> In my mind, the directory specified by `fsmonitor.socketdir` is likely\n> to be dedicated to this purpose (i.e. housing Git administrative\n> junk). As such, it feels somewhat odd for the socket file to be\n> hidden; I would instead expect the socket name to be non-hidden (say,\n> \"git-fsmonitor-daemon-{hash}.ipc\") rather than hidden\n> (\".git-fsmonitor-*\"). The directory specified by `fsmonitor.socketdir`\n> may or may not be hidden (i.e. start with a dot), but that's the\n> user's decision. For the $HOME case, it almost feels cleaner to create\n> a hidden directory (say, \"$HOME/.git-fsmonitor\") in which to house the\n> socket files (\"git-fsmonitor-daemon-{hash}.ipc\"). Anyhow, this comment\n> is quite subjective; perhaps not actionable.\n\nYeah, dot-prefixed files are appropriate if they are to be placed at\nthe top of some tree without the user having any say in how that\ntree is chosen (e.g. the working tree or $HOME).  If the user has\nthe power to specify the location, the equation changes.\n\n> What happens if either $HOME or `fsmonitor.socketdir` are\n> network-mounted? Should this code be checking for that case? If they\n> are network-mounted, should it error out? At minimum, I would think a\n> warning is warranted in order to save users the headache of wondering\n> why fsmonitor isn't working correctly.\n\nThat's a good point.  If one default position (e.g. repository) is\nchecked if it is usable and can be rejected if it isn't, the\nfallback position should at least satisfy the same \"is it usable?\"\ncriteria.\n\nThanks.\n"},{"id":"463200","messageId":"40b47246-adb6-cf56-02ef-25ab1429a42e@jeffhostetler.com","threadId":"58324","inReplyTo":"xmqq35cnco3h.fsf@gitster.g","subject":"Re: [PATCH v7 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-09-19T17:08:53Z","receivedAt":"2022-09-19T17:11:55Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 9/19/22 12:42 PM, Junio C Hamano wrote:\n> Jeff Hostetler <git@jeffhostetler.com> writes:\n> \n>> On 9/16/22 4:11 PM, Junio C Hamano wrote:\n>>> \"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>>>\n>>>> +const char *fsmonitor_ipc__get_path(struct repository *r)\n>>>> +{\n>>>> +\tstatic const char *ipc_path;\n>>>> +\tSHA_CTX sha1ctx;\n>>>> +\tchar *sock_dir;\n>>>> +\tstruct strbuf ipc_file = STRBUF_INIT;\n>>>> +\tunsigned char hash[SHA_DIGEST_LENGTH];\n>>>> +\n>>>> +\tif (ipc_path)\n>>>> +\t\treturn ipc_path;\n>>>> +\n>>>> +\tif (!r)\n>>>> +\t\tr = the_repository;\n>>> I'd prefer not to see this \"NULL means the_repository\".  It would be\n>>> a different story if the caller does not necessarily have a ready\n>>> access to the_repository, but it is a global, so the caller can pass\n>>> the_repository and be more explicit.  Giving two ways to the caller\n>>> to express same thing is not a good idea.\n>>> Thanks.\n>>>\n>>\n>> To be fair, I added several \"if (!r) r = the_repository;\" statements\n>> to the original public FSMonitor routines.  There were obscure cases\n>> where tests would sometimes randomly fail because \"r\" wasn't completely\n>> passed down via some hard to isolate call stack.  Offlist, AEvar told me\n>> that he managed to isolate it and has a fix.\n>>\n>> So eventually, we'll be able to get rid of all of these direct\n>> references to \"the_repository\" and properly assume that \"r\" is\n>> always passed down.\n>>\n>> But for now, I think we should let this stay for safety.\n> \n> I wouldn't call \"sweeping a breakage under the rug\" a \"safety\",\n> though.  If the caller cannot decide which repository instance is\n> the right thing to pass, or the caller does not yet have a good one\n> to pass when making a call down the codepath, how can it be safer to\n> use the_repository that may or may not be the appropriate one than\n> noticing the problem by dying and stopping the spread of damage?\n> \n\nAren't we in the middle of a transition from always\nusing the global \"the_repository\" to a passed \"r\" variable?\nWe're getting closer to being able to hide the the global\nsymbol, but we're not there yet, right?\n\nI'm thinking that at as long as the global exists, we are not\nsafe to have multiple \"struct repository\" instances, right?\n\nAll I'm saying is that there are obscure/edge code paths where\na valid \"r\" is not being passed down.  Or, more likely, someone\nhas an \"istate\" that doesn't yet have \"istate->repo\" set at the\npoint of the call, so they might be passing \"istate->repo\", but\nit is null.\n\nTracking down these nulls is important, but shouldn't it be\nindependent of this one?\n\nJeff\n"},{"id":"463210","messageId":"xmqq35cnb6fw.fsf@gitster.g","threadId":"58324","inReplyTo":"40b47246-adb6-cf56-02ef-25ab1429a42e@jeffhostetler.com","subject":"Re: [PATCH v7 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-19T17:49:23Z","receivedAt":"2022-09-19T17:49:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Hostetler <git@jeffhostetler.com> writes:\n\n> Aren't we in the middle of a transition from always\n> using the global \"the_repository\" to a passed \"r\" variable?\n> We're getting closer to being able to hide the the global\n> symbol, but we're not there yet, right?\n\nWe may still have code that works ONLY on the_repository, but\nletting a function take \"r\" and lettin it ignore is worse than\nleaving it explicitly limited to the_repository only, no?\n\n> I'm thinking that at as long as the global exists, we are not\n> safe to have multiple \"struct repository\" instances, right?\n\nBy itself, Not at all.  It is the code like I am criticizing that\nmakes it unsafe.\n\nI do not mind adding\n\n\tif (!r)\n\t\tBUG(...);\n\nat the place you have the \"sweep it under the rug\" band-aid, though.\n"},{"id":"463226","messageId":"pull.1326.v9.git.1663616277.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v8.git.1663377141.gitgitgadget@gmail.com","subject":"[PATCH v9 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T19:37:51Z","receivedAt":"2022-09-19T19:38:04Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"Follow-on to the work done to allow Windows to work against network-mounted\nrepos for macOS.\n\nHave macOS take advantage of the same configuration option,\n'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\nto true will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for IPC\ncannot be created in a network location; instead $HOME is used if the\ndefault location is on the network. The user may, optionally, set the\n'fsmonitor.socketDir' configuration option to a valid, local directory if\n$HOME itself is on the network or is simply not the desired location for the\nUDS file.\n\nAn additional issue is that for mount points in the root directory, FSEvents\ndoes not report a path that matches the worktree directory due to the\nintroduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\nto the worktree directory by interrogating the root filesystem for synthetic\nfirmlinks and using that information to translate the path.\n\nv9 differs from v8:\n\n * incorporates code review feedback\n * check for incompatibility before communicating with fsmonitor\n\nv8 differs from v7:\n\n * incorporates code review feedback\n * gets the rebase right\n\nv7 differs from v6:\n\n * incorporates code review feedback\n\nv6 differs from v5:\n\n * incorporates earlier, Windows-specific changes that have not made it back\n   yet to the master branch\n * incorporates code review feedback\n * adds documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n\nv5 differs significantly from earlier versions:\n\n * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n   such that these options are no longer added to the settings data\n   structure but are rather read from config at point of use\n * refactoring of code for handling platform-specific file system checks via\n   a common interface to avoid platform #ifdef in IPC code and be in-model\n   with other platform-specific fsmonitor code\n * dealing with 'synthetic firmlinks' on macOS\n\nEric DeCosta (6):\n  fsmonitor: refactor filesystem checks to common interface\n  fsmonitor: relocate socket file if .git directory is remote\n  fsmonitor: avoid socket location check if using hook\n  fsmonitor: deal with synthetic firmlinks on macOS\n  fsmonitor: check for compatability before communicating with fsmonitor\n  fsmonitor: add documentation for allowRemote and socketDir options\n\n Documentation/git-fsmonitor--daemon.txt  |  48 ++++++-\n Makefile                                 |   2 +\n builtin/fsmonitor--daemon.c              |  11 +-\n compat/fsmonitor/fsm-ipc-darwin.c        |  48 +++++++\n compat/fsmonitor/fsm-ipc-win32.c         |   9 ++\n compat/fsmonitor/fsm-listen-darwin.c     |   6 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 132 +++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 145 +++++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  70 +++------\n compat/fsmonitor/fsm-settings-win32.c    | 174 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   4 +\n fsmonitor--daemon.h                      |   3 +\n fsmonitor-ipc.c                          |  18 ++-\n fsmonitor-ipc.h                          |   4 +-\n fsmonitor-path-utils.h                   |  59 ++++++++\n fsmonitor-settings.c                     |  60 +++++++-\n fsmonitor-settings.h                     |   2 +-\n fsmonitor.c                              |   4 +\n 18 files changed, 559 insertions(+), 240 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\n\nbase-commit: d3fa443f97e3a8d75b51341e2d5bac380b7422df\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v9\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v9\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v8:\n\n 1:  155a6890806 = 1:  155a6890806 fsmonitor: refactor filesystem checks to common interface\n 2:  b5356497228 ! 2:  68709d788d5 fsmonitor: relocate socket file if .git directory is remote\n     @@ compat/fsmonitor/fsm-ipc-darwin.c (new)\n      +\trepo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n      +\n      +\t/* Create the socket file in either socketDir or $HOME */\n     -+\tif (sock_dir && *sock_dir)\n     ++\tif (sock_dir && *sock_dir) {\n      +\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n      +\t\t\t\t\tsock_dir, hash_to_hex(hash));\n     -+\telse\n     ++\t\tfree(sock_dir);\n     ++\t} else {\n      +\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n     ++\t}\n      +\n      +\tipc_path = interpolate_path(ipc_file.buf, 1);\n      +\tif (!ipc_path)\n 3:  6719ca2b24d = 3:  6ddd922917a fsmonitor: avoid socket location check if using hook\n 4:  d736cb8fa90 ! 4:  73afd9f3122 fsmonitor: deal with synthetic firmlinks on macOS\n     @@ Commit message\n      \n          Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n      \n     -    fsmonitor: deal with synthetic firmlinks on macOS\n     -\n     -    Starting with macOS 10.15 (Catalina), Apple introduced a new feature\n     -    called 'firmlinks' in order to separate the boot volume into two\n     -    volumes, one read-only and one writable but still present them to the\n     -    user as a single volume. Along with this change, Apple removed the\n     -    ability to create symlinks in the root directory and replaced them with\n     -    'synthetic firmlinks'. See 'man synthetic.conf'\n     -\n     -    When FSEevents reports the path of changed files, if the path involves\n     -    a synthetic firmlink, the path is reported from the point of the\n     -    synthetic firmlink and not the real path. For example:\n     -\n     -    Real path:\n     -    /System/Volumes/Data/network/working/directory/foo.txt\n     -\n     -    Synthetic firmlink:\n     -    /network -> /System/Volumes/Data/network\n     -\n     -    FSEvents path:\n     -    /network/working/directory/foo.txt\n     -\n     -    This causes the FSEvents path to not match against the worktree\n     -    directory.\n     -\n     -    There are several ways in which synthetic firmlinks can be created:\n     -    they can be defined in /etc/synthetic.conf, the automounter can create\n     -    them, and there may be other means. Simply reading /etc/synthetic.conf\n     -    is insufficient. No matter what process creates synthetic firmlinks,\n     -    they all get created in the root directory.\n     -\n     -    Therefore, in order to deal with synthetic firmlinks, the root directory\n     -    is scanned and the first possible synthetic firmink that, when resolved,\n     -    is a prefix of the worktree is used to map FSEvents paths to worktree\n     -    paths.\n     -\n     -    Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n     -\n     -    fsmonitor: deal with synthetic firmlinks on macOS\n     -\n     -    Starting with macOS 10.15 (Catalina), Apple introduced a new feature\n     -    called 'firmlinks' in order to separate the boot volume into two\n     -    volumes, one read-only and one writable but still present them to the\n     -    user as a single volume. Along with this change, Apple removed the\n     -    ability to create symlinks in the root directory and replaced them with\n     -    'synthetic firmlinks'. See 'man synthetic.conf'\n     -\n     -    When FSEevents reports the path of changed files, if the path involves\n     -    a synthetic firmlink, the path is reported from the point of the\n     -    synthetic firmlink and not the real path. For example:\n     -\n     -    Real path:\n     -    /System/Volumes/Data/network/working/directory/foo.txt\n     -\n     -    Synthetic firmlink:\n     -    /network -> /System/Volumes/Data/network\n     -\n     -    FSEvents path:\n     -    /network/working/directory/foo.txt\n     -\n     -    This causes the FSEvents path to not match against the worktree\n     -    directory.\n     -\n     -    There are several ways in which synthetic firmlinks can be created:\n     -    they can be defined in /etc/synthetic.conf, the automounter can create\n     -    them, and there may be other means. Simply reading /etc/synthetic.conf\n     -    is insufficient. No matter what process creates synthetic firmlinks,\n     -    they all get created in the root directory.\n     -\n     -    Therefore, in order to deal with synthetic firmlinks, the root directory\n     -    is scanned and the first possible synthetic firmink that, when resolved,\n     -    is a prefix of the worktree is used to map FSEvents paths to worktree\n     -    paths.\n     -\n     -    Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n     -\n       ## builtin/fsmonitor--daemon.c ##\n      @@\n       #include \"parse-options.h\"\n -:  ----------- > 5:  02afeaa01be fsmonitor: check for compatability before communicating with fsmonitor\n 5:  ddf4e3e6442 ! 6:  0e8ea28acc1 fsmonitor: add documentation for allowRemote and socketDir options\n     @@ Commit message\n          Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n      \n       ## Documentation/git-fsmonitor--daemon.txt ##\n     -@@ Documentation/git-fsmonitor--daemon.txt: the change (as happening against the super repo).  However, the client\n     +@@ Documentation/git-fsmonitor--daemon.txt: git-fsmonitor{litdd}daemon(1)\n     + \n     + NAME\n     + ----\n     +-git-fsmonitor--daemon - A Built-in File System Monitor\n     ++git-fsmonitor--daemon - A Built-in Filesystem Monitor\n     + \n     + SYNOPSIS\n     + --------\n     +@@ Documentation/git-fsmonitor--daemon.txt: DESCRIPTION\n     + -----------\n     + \n     + A daemon to watch the working directory for file and directory\n     +-changes using platform-specific file system notification facilities.\n     ++changes using platform-specific filesystem notification facilities.\n     + \n     + This daemon communicates directly with commands like `git status`\n     + using the link:technical/api-simple-ipc.html[simple IPC] interface\n     +@@ Documentation/git-fsmonitor--daemon.txt: CAVEATS\n     + -------\n     + \n     + The fsmonitor daemon does not currently know about submodules and does\n     +-not know to filter out file system events that happen within a\n     ++not know to filter out filesystem events that happen within a\n     + submodule.  If fsmonitor daemon is watching a super repo and a file is\n     + modified within the working directory of a submodule, it will report\n     + the change (as happening against the super repo).  However, the client\n       will properly ignore these extra events, so performance may be affected\n       but it will not cause an incorrect result.\n       \n      +By default, the fsmonitor daemon refuses to work against network-mounted\n     -+repositories; this my be overridden by setting `fsmonitor.allowRemote` to\n     ++repositories; this may be overridden by setting `fsmonitor.allowRemote` to\n      +`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n     -+correctly with all network-mounted repositores and such use is considered\n     ++correctly with all network-mounted repositories and such use is considered\n      +experimental.\n      +\n      +On Mac OS, the inter-process communication (IPC) between various Git\n     -+commands and the fsmonitor daemon is done via a Unix domain socket (UDS).\n     -+Usage of UDS requires the creation of a file which, by default, is created\n     -+in the .git directory.  If the fsmonitor daemon detects that the .git directory\n     -+is on a network-mounted file system, it will create the UDS file in $HOME.  If\n     -+$HOME itself is on a network-mounted file system or if $HOME is not the desired\n     -+location for the UDS file, 'fsmonitor.socketDir' may be set to any valid, local\n     -+directory on a file system with proper support.  Mac OS native file systems have\n     -+the required support.  File systems known to lack support include FAT32 and\n     -+NTFS.  Other file systems may or many not have the needed support; the fsmonitor\n     -+daemon is not guaranteed to work with these file systems and such use is\n     -+considered experimental.\n     ++commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a\n     ++special type of file -- which is supported by native Mac OS filesystems,\n     ++but not on network-mounted filesystems, NTFS, or FAT32.  Other filesystems\n     ++may or may not have the needed support; the fsmonitor daemon is not guaranteed\n     ++to work with these filesystems and such use is considered experimental.\n     ++\n     ++By default, the socket is created in the `.git` directory, however, if the\n     ++`.git` directory is on a network-mounted filesystem, it will be instead be\n     ++created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a\n     ++network-mounted filesystem in which case you must set the configuration\n     ++variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native\n     ++filesystem in which to create the socket file.\n     ++\n     ++If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)\n     ++is on a native Mac OS file filesystem the fsmonitor daemon will report an\n     ++error that will cause the daemon and the currently running command to exit.\n      +\n      +CONFIGURATION\n      +-------------\n     ++\n      +When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])\n      +the fsmonitor daemon will pay attention to the following configuration\n      +variables:\n      +\n     -+fsmonitor.allowRemote::\n     ++`fsmonitor.allowRemote`::\n      +\tBy default, the daemon refuses to work against network-mounted\n      +\trepositories. Setting `fsmonitor.allowRemote` to `true` overrides\n      +\tthis behavior.\n      +\n     -+fsmonitor.socketDir::\n     -+\tThis option is only used by the Mac OS implementation of the fsmonitor\n     -+\tdaemon.\tIf set, 'fsmonitor.socketDir' must be set to a valid, local\n     -+\tdirectory on a file system that can support Unix domain sockets (UDS).\n     ++`fsmonitor.socketDir`::\n     ++    This Mac OS-specific option, if set, specifies the directory in\n     ++    which to create the Unix domain socket used for communication\n     ++    between fsmonitor and various Git commands. The directory must\n     ++    reside on a native Mac OS filesystem as discussed above.\n      +\n       GIT\n       ---\n\n-- \ngitgitgadget\n"},{"id":"463227","messageId":"155a689080658e1953fc4434016f746f371ba08a.1663616277.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v9.git.1663616277.gitgitgadget@gmail.com","subject":"[PATCH v9 1/6] fsmonitor: refactor filesystem checks to common interface","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T19:37:52Z","receivedAt":"2022-09-19T19:38:07Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nProvide a common interface for getting basic filesystem information\nincluding filesystem type and whether the filesystem is remote.\n\nRefactor existing code for getting basic filesystem info and detecting\nremote file systems to the new interface.\n\nRefactor filesystem checks to leverage new interface. For macOS,\nerror-out if the Unix Domain socket (UDS) file is on a remote\nfilesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                                 |   1 +\n compat/fsmonitor/fsm-path-utils-darwin.c |  40 ++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 128 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  62 +++-----\n compat/fsmonitor/fsm-settings-win32.c    | 172 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   2 +\n fsmonitor-path-utils.h                   |  23 +++\n fsmonitor-settings.c                     |  50 +++++++\n 8 files changed, 263 insertions(+), 215 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\ndiff --git a/Makefile b/Makefile\nindex d9247ead45b..6e492a67547 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2039,6 +2039,7 @@ endif\n ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n ifeq ($(TCLTK_PATH),)\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nnew file mode 100644\nindex 00000000000..067cbe6990a\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -0,0 +1,40 @@\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+#include <sys/param.h>\n+#include <sys/mount.h>\n+\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\tstruct statfs fs;\n+\tif (statfs(path, &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\treturn -1;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\n+\tif (!(fs.f_flags & MNT_LOCAL))\n+\t\tfs_info->is_remote = 1;\n+\telse\n+\t\tfs_info->is_remote = 0;\n+\n+\tfs_info->typename = fs.f_fstypename;\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nnew file mode 100644\nindex 00000000000..a90b8f7925b\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -0,0 +1,128 @@\n+#include \"cache.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+/*\n+ * Check remote working directory protocol.\n+ *\n+ * Return -1 if client machine cannot get remote protocol information.\n+ */\n+static int check_remote_protocol(wchar_t *wpath)\n+{\n+\tHANDLE h;\n+\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n+\n+\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n+\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\n+\tif (h == INVALID_HANDLE_VALUE) {\n+\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n+\t\t&proto_info, sizeof(proto_info))) {\n+\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\tCloseHandle(h);\n+\t\treturn -1;\n+\t}\n+\n+\tCloseHandle(h);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n+\t\t\t\twpath, proto_info.Protocol);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Notes for testing:\n+ *\n+ * (a) Windows allows a network share to be mapped to a drive letter.\n+ *     (This is the normal method to access it.)\n+ *\n+ *     $ NET USE Z: \\\\server\\share\n+ *     $ git -C Z:/repo status\n+ *\n+ * (b) Windows allows a network share to be referenced WITHOUT mapping\n+ *     it to drive letter.\n+ *\n+ *     $ NET USE \\\\server\\share\\dir\n+ *     $ git -C //server/share/repo status\n+ *\n+ * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n+ *     arbitrary path (which may be remote)\n+ *\n+ *     $ SUBST Q: Z:\\repo\n+ *     $ git -C Q:/ status\n+ *\n+ * (d) Windows allows a directory symlink to be created on a local\n+ *     file system that points to a remote repo.\n+ *\n+ *     $ mklink /d ./link //server/share/repo\n+ *     $ git -C ./link status\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\twchar_t wpath[MAX_PATH];\n+\twchar_t wfullpath[MAX_PATH];\n+\tsize_t wlen;\n+\tUINT driveType;\n+\n+\t/*\n+\t * Do everything in wide chars because the drive letter might be\n+\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n+\t */\n+\tif (xutftowcs_path(wpath, path) < 0) {\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * GetDriveTypeW() requires a final slash.  We assume that the\n+\t * worktree pathname points to an actual directory.\n+\t */\n+\twlen = wcslen(wpath);\n+\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n+\t\twpath[wlen++] = L'\\\\';\n+\t\twpath[wlen] = 0;\n+\t}\n+\n+\t/*\n+\t * Normalize the path.  If nothing else, this converts forward\n+\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n+\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n+\t */\n+\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {\n+\t\treturn -1;\n+\t}\n+\n+\tdriveType = GetDriveTypeW(wfullpath);\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n+\t\t\t path, wfullpath, driveType);\n+\n+\tif (driveType == DRIVE_REMOTE) {\n+\t\tfs_info->is_remote = 1;\n+\t\tif (check_remote_protocol(wfullpath) < 0)\n+\t\t\treturn -1;\n+\t} else {\n+\t\tfs_info->is_remote = 0;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dba3ced6bb7 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -1,32 +1,10 @@\n-#include \"cache.h\"\n #include \"config.h\"\n-#include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n-#include <sys/param.h>\n-#include <sys/mount.h>\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n-/*\n- * [1] Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type (NFS, SAMBA, etc.) dictates whether notification events\n- * are available at all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n+ /*\n  * For the builtin FSMonitor, we create the Unix domain socket for the\n  * IPC in the .git directory.  If the working directory is remote,\n  * then the socket will be created on the remote file system.  This\n@@ -38,40 +16,34 @@\n  * be taken to ensure that $HOME is actually local and not a managed\n  * file share.)\n  *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n+ * FAT32 and NTFS working directories are problematic too.\n  *\n  * The builtin FSMonitor uses a Unix domain socket in the .git\n  * directory for IPC.  These Windows drive formats do not support\n  * Unix domain sockets, so mark them as incompatible for the daemon.\n  *\n  */\n-static enum fsmonitor_reason check_volume(struct repository *r)\n+static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n-\tstruct statfs fs;\n+\tstruct fs_info fs;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \n-\tif (statfs(r->worktree, &fs) == -1) {\n-\t\tint saved_errno = errno;\n-\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n-\t\t\t\t r->worktree, strerror(saved_errno));\n-\t\terrno = saved_errno;\n+\tif (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {\n+\t\tstrbuf_release(&path);\n \t\treturn FSMONITOR_REASON_ERROR;\n \t}\n \n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n-\t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n \n-\tif (!(fs.f_flags & MNT_LOCAL))\n+\tif (fs.is_remote)\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n-\tif (!strcmp(fs.f_fstypename, \"msdos\")) /* aka FAT32 */\n+\tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n-\tif (!strcmp(fs.f_fstypename, \"ntfs\"))\n+\tif (!strcmp(fs.typename, \"ntfs\"))\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \treturn FSMONITOR_REASON_OK;\n@@ -81,7 +53,7 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_volume(r);\n+\treason = check_uds_volume(r);\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex e5ec5b0a9f7..d88b06ae610 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -1,8 +1,9 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * VFS for Git is incompatible with FSMonitor.\n@@ -24,171 +25,6 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-/*\n- * Check if monitoring remote working directories is allowed.\n- *\n- * By default, monitoring remote working directories is\n- * disabled.  Users may override this behavior in enviroments where\n- * they have proper support.\n- */\n-static int check_config_allowremote(struct repository *r)\n-{\n-\tint allow;\n-\n-\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n-\t\treturn allow;\n-\n-\treturn -1; /* fsmonitor.allowremote not set */\n-}\n-\n-/*\n- * Check remote working directory protocol.\n- *\n- * Error if client machine cannot get remote protocol information.\n- */\n-static int check_remote_protocol(wchar_t *wpath)\n-{\n-\tHANDLE h;\n-\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n-\n-\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n-\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n-\n-\tif (h == INVALID_HANDLE_VALUE) {\n-\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\treturn -1;\n-\t}\n-\n-\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n-\t\t&proto_info, sizeof(proto_info))) {\n-\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\tCloseHandle(h);\n-\t\treturn -1;\n-\t}\n-\n-\tCloseHandle(h);\n-\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n-\t\t\t\twpath, proto_info.Protocol);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type dictates whether notification events are available at\n- * all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- * Notes for testing:\n- *\n- * (a) Windows allows a network share to be mapped to a drive letter.\n- *     (This is the normal method to access it.)\n- *\n- *     $ NET USE Z: \\\\server\\share\n- *     $ git -C Z:/repo status\n- *\n- * (b) Windows allows a network share to be referenced WITHOUT mapping\n- *     it to drive letter.\n- *\n- *     $ NET USE \\\\server\\share\\dir\n- *     $ git -C //server/share/repo status\n- *\n- * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n- *     arbitrary path (which may be remote)\n- *\n- *     $ SUBST Q: Z:\\repo\n- *     $ git -C Q:/ status\n- *\n- * (d) Windows allows a directory symlink to be created on a local\n- *     file system that points to a remote repo.\n- *\n- *     $ mklink /d ./link //server/share/repo\n- *     $ git -C ./link status\n- */\n-static enum fsmonitor_reason check_remote(struct repository *r)\n-{\n-\tint ret;\n-\twchar_t wpath[MAX_PATH];\n-\twchar_t wfullpath[MAX_PATH];\n-\tsize_t wlen;\n-\tUINT driveType;\n-\n-\t/*\n-\t * Do everything in wide chars because the drive letter might be\n-\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n-\t */\n-\tif (xutftowcs_path(wpath, r->worktree) < 0)\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t/*\n-\t * GetDriveTypeW() requires a final slash.  We assume that the\n-\t * worktree pathname points to an actual directory.\n-\t */\n-\twlen = wcslen(wpath);\n-\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n-\t\twpath[wlen++] = L'\\\\';\n-\t\twpath[wlen] = 0;\n-\t}\n-\n-\t/*\n-\t * Normalize the path.  If nothing else, this converts forward\n-\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n-\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n-\t */\n-\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\tdriveType = GetDriveTypeW(wfullpath);\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n-\t\t\t r->worktree, wfullpath, driveType);\n-\n-\tif (driveType == DRIVE_REMOTE) {\n-\t\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t \"check_remote('%s') true\",\n-\t\t\t\t r->worktree);\n-\n-\t\tret = check_remote_protocol(wfullpath);\n-\t\tif (ret < 0)\n-\t\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t\tswitch (check_config_allowremote(r)) {\n-\t\tcase 0: /* config overrides and disables */\n-\t\t\treturn FSMONITOR_REASON_REMOTE;\n-\t\tcase 1: /* config overrides and enables */\n-\t\t\treturn FSMONITOR_REASON_OK;\n-\t\tdefault:\n-\t\t\tbreak; /* config has no opinion */\n-\t\t}\n-\n-\t\treturn FSMONITOR_REASON_REMOTE;\n-\t}\n-\n-\treturn FSMONITOR_REASON_OK;\n-}\n-\n enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n@@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n-\treason = check_remote(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n-\n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 2237109b57f..b88494bf59b 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)\n@@ -315,6 +316,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nnew file mode 100644\nindex 00000000000..e48592887e7\n--- /dev/null\n+++ b/fsmonitor-path-utils.h\n@@ -0,0 +1,23 @@\n+#ifndef FSM_PATH_UTILS_H\n+#define FSM_PATH_UTILS_H\n+\n+struct fs_info {\n+\tint is_remote;\n+\tchar *typename;\n+};\n+\n+/*\n+ * Get some basic filesystem informtion for the given path\n+ *\n+ * Returns -1 on error, zero otherwise.\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n+\n+/*\n+ * Determines if the filesystem that path resides on is remote.\n+ *\n+ * Returns -1 on error, 0 if not remote, 1 if remote.\n+ */\n+int fsmonitor__is_fs_remote(const char *path);\n+\n+#endif\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..d288cbad479 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * We keep this structure defintion private and have getters\n@@ -13,6 +14,52 @@ struct fsmonitor_settings {\n \tchar *hook_path;\n };\n \n+/*\n+ * Remote working directories are problematic for FSMonitor.\n+ *\n+ * The underlying file system on the server machine and/or the remote\n+ * mount type dictates whether notification events are available at\n+ * all to remote client machines.\n+ *\n+ * Kernel differences between the server and client machines also\n+ * dictate the how (buffering, frequency, de-dup) the events are\n+ * delivered to client machine processes.\n+ *\n+ * A client machine (such as a laptop) may choose to suspend/resume\n+ * and it is unclear (without lots of testing) whether the watcher can\n+ * resync after a resume.  We might be able to treat this as a normal\n+ * \"events were dropped by the kernel\" event and do our normal \"flush\n+ * and resync\" --or-- we might need to close the existing (zombie?)\n+ * notification fd and create a new one.\n+ *\n+ * In theory, the above issues need to be addressed whether we are\n+ * using the Hook or IPC API.\n+ *\n+ * So (for now at least), mark remote working directories as\n+ * incompatible unless 'fsmonitor.allowRemote' is true.\n+ *\n+ */\n+#ifdef HAVE_FSMONITOR_OS_SETTINGS\n+static enum fsmonitor_reason check_remote(struct repository *r)\n+{\n+\tint allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */\n+\tint is_remote = fsmonitor__is_fs_remote(r->worktree);\n+\n+\tswitch (is_remote) {\n+\t\tcase 0:\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tcase 1:\n+\t\t\trepo_config_get_bool(r, \"fsmonitor.allowremote\", &allow_remote);\n+\t\t\tif (allow_remote < 1)\n+\t\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\t\telse\n+\t\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+}\n+#endif\n+\n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n {\n \tif (!r->worktree) {\n@@ -27,6 +74,9 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t{\n \t\tenum fsmonitor_reason reason;\n \n+\t\treason = check_remote(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n \t\treason = fsm_os__incompatible(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-- \ngitgitgadget\n\n"},{"id":"463228","messageId":"68709d788d531805d9328e16f53381fcd8d7bd5e.1663616277.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v9.git.1663616277.gitgitgadget@gmail.com","subject":"[PATCH v9 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T19:37:53Z","receivedAt":"2022-09-19T19:38:09Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf the .git directory is on a remote file system, create the socket\nfile in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                               |  1 +\n builtin/fsmonitor--daemon.c            |  3 +-\n compat/fsmonitor/fsm-ipc-darwin.c      | 48 ++++++++++++++++++++++++++\n compat/fsmonitor/fsm-ipc-win32.c       |  9 +++++\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n contrib/buildsystems/CMakeLists.txt    |  2 ++\n fsmonitor-ipc.c                        | 18 +++++-----\n fsmonitor-ipc.h                        |  4 ++-\n 8 files changed, 74 insertions(+), 13 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n\ndiff --git a/Makefile b/Makefile\nindex 6e492a67547..58bb9248471 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2034,6 +2034,7 @@ ifdef FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n endif\n \n ifdef FSMONITOR_OS_SETTINGS\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 2c109cf8b37..0123fc33ed2 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -1343,7 +1343,8 @@ static int fsmonitor_run_daemon(void)\n \t * directory.)\n \t */\n \tstrbuf_init(&state.path_ipc, 0);\n-\tstrbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));\n+\tstrbuf_addstr(&state.path_ipc,\n+\t\tabsolute_path(fsmonitor_ipc__get_path(the_repository)));\n \n \t/*\n \t * Confirm that we can create platform-specific resources for the\ndiff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\nnew file mode 100644\nindex 00000000000..d48d67690ee\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-darwin.c\n@@ -0,0 +1,48 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"strbuf.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n+{\n+\tstatic const char *ipc_path;\n+\tSHA_CTX sha1ctx;\n+\tchar *sock_dir;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (ipc_path)\n+\t\treturn ipc_path;\n+\n+\tipc_path = fsmonitor_ipc__get_default_path();\n+\n+\t/* By default the socket file is created in the .git directory */\n+\tif (fsmonitor__is_fs_remote(ipc_path) < 1)\n+\t\treturn ipc_path;\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\trepo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n+\n+\t/* Create the socket file in either socketDir or $HOME */\n+\tif (sock_dir && *sock_dir) {\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\t\tfree(sock_dir);\n+\t} else {\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\t}\n+\n+\tipc_path = interpolate_path(ipc_file.buf, 1);\n+\tif (!ipc_path)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\n+\tstrbuf_release(&ipc_file);\n+\treturn ipc_path;\n+}\ndiff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\nnew file mode 100644\nindex 00000000000..3a3a46db209\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-win32.c\n@@ -0,0 +1,9 @@\n+#include \"config.h\"\n+#include \"fsmonitor-ipc.h\"\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r) {\n+\tstatic char *ret;\n+\tif (!ret)\n+\t\tret = git_pathdup(\"fsmonitor--daemon.ipc\");\n+\treturn ret;\n+}\n\\ No newline at end of file\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex dba3ced6bb7..681d8bf963e 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -26,7 +26,7 @@\n static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n \tstruct fs_info fs;\n-\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tconst char *ipc_path = fsmonitor_ipc__get_path(r);\n \tstruct strbuf path = STRBUF_INIT;\n \tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex b88494bf59b..7e7b6b9a362 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n@@ -316,6 +317,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..c0f42301c84 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -18,7 +18,7 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 0;\n }\n \n-const char *fsmonitor_ipc__get_path(void)\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n {\n \treturn NULL;\n }\n@@ -47,11 +47,9 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n-\n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\n-\treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n+\treturn ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));\n }\n \n static int spawn_daemon(void)\n@@ -81,8 +79,8 @@ int fsmonitor_ipc__send_query(const char *since_token,\n \ttrace2_data_string(\"fsm_client\", NULL, \"query/command\", tok);\n \n try_again:\n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \n \tswitch (state) {\n \tcase IPC_STATE__LISTENING:\n@@ -117,13 +115,13 @@ try_again:\n \n \tcase IPC_STATE__INVALID_PATH:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: invalid path '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \n \tcase IPC_STATE__OTHER_ERROR:\n \tdefault:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: unspecified error on '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \t}\n \n@@ -149,8 +147,8 @@ int fsmonitor_ipc__send_command(const char *command,\n \toptions.wait_if_busy = 1;\n \toptions.wait_if_not_found = 0;\n \n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \tif (state != IPC_STATE__LISTENING) {\n \t\tdie(_(\"fsmonitor--daemon is not running\"));\n \t\treturn -1;\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..8b489da762b 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -3,6 +3,8 @@\n \n #include \"simple-ipc.h\"\n \n+struct repository;\n+\n /*\n  * Returns true if built-in file system monitor daemon is defined\n  * for this platform.\n@@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);\n  *\n  * Returns NULL if the daemon is not supported on this platform.\n  */\n-const char *fsmonitor_ipc__get_path(void);\n+const char *fsmonitor_ipc__get_path(struct repository *r);\n \n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n-- \ngitgitgadget\n\n"},{"id":"463229","messageId":"6ddd922917a144f2339af47b9f5c17c122c1ebbd.1663616277.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v9.git.1663616277.gitgitgadget@gmail.com","subject":"[PATCH v9 3/6] fsmonitor: avoid socket location check if using hook","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T19:37:54Z","receivedAt":"2022-09-19T19:38:10Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf monitoring is done via fsmonitor hook rather than IPC there is no\nneed to check if the location of the Unix Domain socket (UDS) file is\non a remote filesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 10 ++++++----\n compat/fsmonitor/fsm-settings-win32.c  |  2 +-\n fsmonitor-settings.c                   |  8 ++++----\n fsmonitor-settings.h                   |  2 +-\n 4 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 681d8bf963e..40da2d3b533 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -49,13 +49,15 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_uds_volume(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n+\tif (ipc) {\n+\t\treason = check_uds_volume(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n+\t}\n \n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex d88b06ae610..a8af31b71de 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex d288cbad479..531a1b6f956 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -60,7 +60,7 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n }\n #endif\n \n-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n+static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n {\n \tif (!r->worktree) {\n \t\t/*\n@@ -77,7 +77,7 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t\treason = check_remote(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-\t\treason = fsm_os__incompatible(r);\n+\t\treason = fsm_os__incompatible(r, ipc);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n \t}\n@@ -162,7 +162,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -185,7 +185,7 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..0721617b95a 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -48,7 +48,7 @@ struct fsmonitor_settings;\n  * fsm_os__* routines should considered private to fsm_settings__\n  * routines.\n  */\n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n \n #endif /* FSMONITOR_SETTINGS_H */\n-- \ngitgitgadget\n\n"},{"id":"463231","messageId":"73afd9f31229a50a7c23271a66e842647378628a.1663616277.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v9.git.1663616277.gitgitgadget@gmail.com","subject":"[PATCH v9 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T19:37:55Z","receivedAt":"2022-09-19T19:38:24Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n builtin/fsmonitor--daemon.c              |  8 +++\n compat/fsmonitor/fsm-listen-darwin.c     |  6 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 92 ++++++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 17 +++++\n fsmonitor--daemon.h                      |  3 +\n fsmonitor-path-utils.h                   | 36 ++++++++++\n 6 files changed, 161 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 0123fc33ed2..56fcd1c2baa 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"fsmonitor.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n #include \"compat/fsmonitor/fsm-health.h\"\n #include \"compat/fsmonitor/fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n@@ -1282,6 +1283,13 @@ static int fsmonitor_run_daemon(void)\n \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n \tstate.nr_paths_watching = 1;\n \n+\tstate.alias.alias = NULL;\n+\tstate.alias.points_to = NULL;\n+\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n+\t\terr = error(_(\"could not get worktree alias\"));\n+\t\tgoto done;\n+\t}\n+\n \t/*\n \t * We create and delete cookie files somewhere inside the .git\n \t * directory to help us keep sync with the file system.  If\ndiff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\nindex 8e208e8289e..179886bc15b 100644\n--- a/compat/fsmonitor/fsm-listen-darwin.c\n+++ b/compat/fsmonitor/fsm-listen-darwin.c\n@@ -26,6 +26,7 @@\n #include \"fsmonitor.h\"\n #include \"fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsm_listen_data\n {\n@@ -209,7 +210,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t/*\n \t\t * On Mac, we receive an array of absolute paths.\n \t\t */\n-\t\tpath_k = paths[k];\n+\t\tpath_k = fsmonitor__resolve_alias(paths[k], &state->alias);\n+\t\tif (!path_k)\n+\t\t\tpath_k = paths[k];\n \n \t\t/*\n \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n@@ -238,6 +241,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t\tfsmonitor_force_resync(state);\n \t\t\tfsmonitor_batch__free_list(batch);\n \t\t\tstring_list_clear(&cookie_list, 0);\n+\t\t\tbatch = NULL;\n \n \t\t\t/*\n \t\t\t * We assume that any events that we received\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nindex 067cbe6990a..13807f58e95 100644\n--- a/compat/fsmonitor/fsm-path-utils-darwin.c\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -1,5 +1,8 @@\n #include \"fsmonitor.h\"\n #include \"fsmonitor-path-utils.h\"\n+#include <dirent.h>\n+#include <errno.h>\n+#include <fcntl.h>\n #include <sys/param.h>\n #include <sys/mount.h>\n \n@@ -38,3 +41,92 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * Scan the root directory for synthetic firmlinks that when resolved\n+ * are a prefix of the path, stopping at the first one found.\n+ *\n+ * Some information about firmlinks and synthetic firmlinks:\n+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n+ *\n+ * macOS no longer allows symlinks in the root directory; any link found\n+ * there is therefore a synthetic firmlink.\n+ *\n+ * If this function gets called often, will want to cache all the firmlink\n+ * information, but for now there is only one caller of this function.\n+ *\n+ * If there is more than one alias for the path, that is another\n+ * matter altogether.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\tDIR * dir;\n+\tint read;\n+\tint retval;\n+\tstruct dirent *de;\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+\n+\tretval = 0;\n+\tdir = opendir(\"/\");\n+\tif (!dir)\n+\t\treturn -1;\n+\n+\tstrbuf_init(&alias, 256);\n+\tstrbuf_init(&points_to, MAXPATHLEN);\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstrbuf_reset(&alias);\n+\t\tstrbuf_addch(&alias, '/');\n+\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n+\n+\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n+\t\tif (read > 0) {\n+\t\t\tstrbuf_setlen(&points_to, read);\n+\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n+\t\t\t\t&& path[points_to.len] == '/') {\n+\t\t\t\tinfo->alias = strbuf_detach(&alias, NULL);\n+\t\t\t\tinfo->points_to = strbuf_detach(&points_to, NULL);\n+\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n+\t\t\t\t\tpath, info->alias, info->points_to);\n+\t\t\t\tretval = 0;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n+\t\t\ttrace_printf_key(&trace_fsmonitor, \"Error %s\", strerror(errno));\n+\t\t\tretval = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n+\tdone:\n+\tclosedir(dir);\n+\tstrbuf_release(&alias);\n+\tstrbuf_release(&points_to);\n+\treturn retval;\n+}\n+\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\tint len = info->alias ? strlen(info->alias) : 0;\n+\n+\tif (!len)\n+\t\treturn NULL;\n+\n+\tif ((strncmp(info->alias, path, len) == 0)\n+\t\t&& path[len] == '/') {\n+\t\tstruct strbuf tmp;\n+\t\tconst char *remainder = path + len;\n+\t\tint ptr_len = strlen(info->points_to);\n+\t\tint rem_len = strlen(remainder);\n+\n+\t\tstrbuf_init(&tmp, ptr_len + rem_len);\n+\t\tstrbuf_add(&tmp, info->points_to, ptr_len);\n+\t\tstrbuf_add(&tmp, remainder, rem_len);\n+\t\treturn strbuf_detach(&tmp, NULL);\n+\t}\n+\n+\treturn NULL;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nindex a90b8f7925b..0d95bbb416f 100644\n--- a/compat/fsmonitor/fsm-path-utils-win32.c\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -126,3 +126,20 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * No-op for now.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\treturn 0;\n+}\n+\n+/*\n+ * No-op for now.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\treturn NULL;\n+}\ndiff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\nindex 2102a5c9ff5..e24838f9a86 100644\n--- a/fsmonitor--daemon.h\n+++ b/fsmonitor--daemon.h\n@@ -8,6 +8,7 @@\n #include \"run-command.h\"\n #include \"simple-ipc.h\"\n #include \"thread-utils.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsmonitor_batch;\n struct fsmonitor_token_data;\n@@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct strbuf path_worktree_watch;\n \tstruct strbuf path_gitdir_watch;\n+\tstruct alias_info alias;\n \tint nr_paths_watching;\n \n \tstruct fsmonitor_token_data *current_token_data;\n@@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct ipc_server_data *ipc_server_data;\n \tstruct strbuf path_ipc;\n+\n };\n \n /*\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nindex e48592887e7..50ef37e57bb 100644\n--- a/fsmonitor-path-utils.h\n+++ b/fsmonitor-path-utils.h\n@@ -1,6 +1,14 @@\n #ifndef FSM_PATH_UTILS_H\n #define FSM_PATH_UTILS_H\n \n+#include \"strbuf.h\"\n+\n+struct alias_info\n+{\n+\tchar *alias;\n+\tchar *points_to;\n+};\n+\n struct fs_info {\n \tint is_remote;\n \tchar *typename;\n@@ -20,4 +28,32 @@ int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n  */\n int fsmonitor__is_fs_remote(const char *path);\n \n+/*\n+ * Get the alias in given path, if any.\n+ *\n+ * Sets alias to the first alias that matches any part of the path.\n+ *\n+ * If an alias is found, info.alias and info.points_to are set to the\n+ * found mapping.\n+ *\n+ * Returns -1 on error, 0 otherwise.\n+ *\n+ * The caller owns the storage that is occupied by set info.alias and\n+ * info.points_to and is responsible for releasing it with `free(3)`\n+ * when done.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info);\n+\n+/*\n+ * Resolve the path against the given alias.\n+ *\n+ * Returns the resolved path if there is one, NULL otherwise.\n+ *\n+ * The caller owns the storage that the returned string occupies and\n+ * is responsible for releasing it with `free(3)` when done.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info);\n+\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"463230","messageId":"02afeaa01bef0a033c17a2ab37cc37e5e7818eb9.1663616277.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v9.git.1663616277.gitgitgadget@gmail.com","subject":"[PATCH v9 5/6] fsmonitor: check for compatability before communicating with fsmonitor","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T19:37:56Z","receivedAt":"2022-09-19T19:38:25Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf fsmonitor is not in a compatible state, die with an appropriate error\nmessge.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n fsmonitor-settings.c | 2 ++\n fsmonitor.c          | 4 ++++\n 2 files changed, 6 insertions(+)\n\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 531a1b6f956..aaa204e0352 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -247,6 +247,8 @@ char *fsm_settings__get_incompatible_msg(const struct repository *r,\n {\n \tstruct strbuf msg = STRBUF_INIT;\n \n+\tstrbuf_add(&msg, \"fsmonitor: \", strlen(\"fsmonitor: \"));\n+\n \tswitch (reason) {\n \tcase FSMONITOR_REASON_UNTESTED:\n \tcase FSMONITOR_REASON_OK:\ndiff --git a/fsmonitor.c b/fsmonitor.c\nindex 57d6a483bee..43d580132fb 100644\n--- a/fsmonitor.c\n+++ b/fsmonitor.c\n@@ -305,6 +305,10 @@ void refresh_fsmonitor(struct index_state *istate)\n \tint is_trivial = 0;\n \tstruct repository *r = istate->repo ? istate->repo : the_repository;\n \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);\n+\tenum fsmonitor_reason reason = fsm_settings__get_reason(r);\n+\n+\tif (reason > FSMONITOR_REASON_OK)\n+\t\tdie(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n \n \tif (fsm_mode <= FSMONITOR_MODE_DISABLED ||\n \t    istate->fsmonitor_has_run_once)\n-- \ngitgitgadget\n\n"},{"id":"463232","messageId":"0e8ea28acc12850f2a6b347c6bce509beb756d81.1663616277.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v9.git.1663616277.gitgitgadget@gmail.com","subject":"[PATCH v9 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T19:37:57Z","receivedAt":"2022-09-19T19:38:29Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nAdd documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\nCall-out experimental nature of 'fsmonitor.allowRemote' and limited file\nsystem support for 'fsmonitor.socketDir'.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Documentation/git-fsmonitor--daemon.txt | 48 +++++++++++++++++++++++--\n 1 file changed, 45 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt\nindex cc142fb8612..6ad3e518ae0 100644\n--- a/Documentation/git-fsmonitor--daemon.txt\n+++ b/Documentation/git-fsmonitor--daemon.txt\n@@ -3,7 +3,7 @@ git-fsmonitor{litdd}daemon(1)\n \n NAME\n ----\n-git-fsmonitor--daemon - A Built-in File System Monitor\n+git-fsmonitor--daemon - A Built-in Filesystem Monitor\n \n SYNOPSIS\n --------\n@@ -17,7 +17,7 @@ DESCRIPTION\n -----------\n \n A daemon to watch the working directory for file and directory\n-changes using platform-specific file system notification facilities.\n+changes using platform-specific filesystem notification facilities.\n \n This daemon communicates directly with commands like `git status`\n using the link:technical/api-simple-ipc.html[simple IPC] interface\n@@ -63,13 +63,55 @@ CAVEATS\n -------\n \n The fsmonitor daemon does not currently know about submodules and does\n-not know to filter out file system events that happen within a\n+not know to filter out filesystem events that happen within a\n submodule.  If fsmonitor daemon is watching a super repo and a file is\n modified within the working directory of a submodule, it will report\n the change (as happening against the super repo).  However, the client\n will properly ignore these extra events, so performance may be affected\n but it will not cause an incorrect result.\n \n+By default, the fsmonitor daemon refuses to work against network-mounted\n+repositories; this may be overridden by setting `fsmonitor.allowRemote` to\n+`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n+correctly with all network-mounted repositories and such use is considered\n+experimental.\n+\n+On Mac OS, the inter-process communication (IPC) between various Git\n+commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a\n+special type of file -- which is supported by native Mac OS filesystems,\n+but not on network-mounted filesystems, NTFS, or FAT32.  Other filesystems\n+may or may not have the needed support; the fsmonitor daemon is not guaranteed\n+to work with these filesystems and such use is considered experimental.\n+\n+By default, the socket is created in the `.git` directory, however, if the\n+`.git` directory is on a network-mounted filesystem, it will be instead be\n+created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a\n+network-mounted filesystem in which case you must set the configuration\n+variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native\n+filesystem in which to create the socket file.\n+\n+If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)\n+is on a native Mac OS file filesystem the fsmonitor daemon will report an\n+error that will cause the daemon and the currently running command to exit.\n+\n+CONFIGURATION\n+-------------\n+\n+When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])\n+the fsmonitor daemon will pay attention to the following configuration\n+variables:\n+\n+`fsmonitor.allowRemote`::\n+\tBy default, the daemon refuses to work against network-mounted\n+\trepositories. Setting `fsmonitor.allowRemote` to `true` overrides\n+\tthis behavior.\n+\n+`fsmonitor.socketDir`::\n+    This Mac OS-specific option, if set, specifies the directory in\n+    which to create the Unix domain socket used for communication\n+    between fsmonitor and various Git commands. The directory must\n+    reside on a native Mac OS filesystem as discussed above.\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \ngitgitgadget\n"},{"id":"463233","messageId":"CAPig+cRzbXJ+npc29bXi4Jtmp9O+vhRF2wJQX6y4QFDVnzAkBg@mail.gmail.com","threadId":"58324","inReplyTo":"68709d788d531805d9328e16f53381fcd8d7bd5e.1663616277.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v9 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-09-19T19:57:58Z","receivedAt":"2022-09-19T19:58:14Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Sep 19, 2022 at 3:38 PM Eric DeCosta via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> If the .git directory is on a remote file system, create the socket\n> file in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n>\n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n> diff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\n> @@ -0,0 +1,48 @@\n> +const char *fsmonitor_ipc__get_path(struct repository *r)\n> +{\n> +       char *sock_dir;\n> +\n> +       repo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n> +\n> +       /* Create the socket file in either socketDir or $HOME */\n> +       if (sock_dir && *sock_dir) {\n> +               strbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n> +                                       sock_dir, hash_to_hex(hash));\n> +               free(sock_dir);\n> +       } else {\n> +               strbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n> +       }\n\nThis is still leaking `sock_dir`, isn't it, if\nrepo_config_get_string() returns it as a zero-length string?\n\nProbably want to move free() below the entire conditional.\n\n> diff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\n> @@ -0,0 +1,9 @@\n> +#include \"config.h\"\n> +#include \"fsmonitor-ipc.h\"\n> +\n> +const char *fsmonitor_ipc__get_path(struct repository *r) {\n> +       static char *ret;\n> +       if (!ret)\n> +               ret = git_pathdup(\"fsmonitor--daemon.ipc\");\n> +       return ret;\n> +}\n> \\ No newline at end of file\n\nStill an incomplete line.\n"},{"id":"463242","messageId":"BL0PR05MB557100B0802A8407C9CA55BFD94D9@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"xmqq35cnb6fw.fsf@gitster.g","subject":"RE: [PATCH v7 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-19T23:51:56Z","receivedAt":"2022-09-19T23:52:09Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Junio C Hamano <gitster@pobox.com>\n> Sent: Monday, September 19, 2022 1:49 PM\n> To: Jeff Hostetler <git@jeffhostetler.com>\n> Cc: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n> git@vger.kernel.org; Eric Sunshine <sunshine@sunshineco.com>; Torsten\n> Bögershausen <tboegi@web.de>; Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com>; Ramsay Jones <ramsay@ramsayjones.plus.com>;\n> Johannes Schindelin <Johannes.Schindelin@gmx.de>; Eric DeCosta\n> <edecosta@mathworks.com>\n> Subject: Re: [PATCH v7 2/6] fsmonitor: relocate socket file if .git directory is\n> remote\n> \n> Jeff Hostetler <git@jeffhostetler.com> writes:\n> \n> > Aren't we in the middle of a transition from always using the global\n> > \"the_repository\" to a passed \"r\" variable?\n> > We're getting closer to being able to hide the the global symbol, but\n> > we're not there yet, right?\n> \n> We may still have code that works ONLY on the_repository, but letting a\n> function take \"r\" and lettin it ignore is worse than leaving it explicitly limited\n> to the_repository only, no?\n> \n> > I'm thinking that at as long as the global exists, we are not safe to\n> > have multiple \"struct repository\" instances, right?\n> \n> By itself, Not at all.  It is the code like I am criticizing that makes it unsafe.\n> \n> I do not mind adding\n> \n> \tif (!r)\n> \t\tBUG(...);\n> \n> at the place you have the \"sweep it under the rug\" band-aid, though.\n\nAppreciate all the insights and comments. Where are we landing with this? Very close to the finish line and I'd like to be able to push these changes over that line.\n\n-Eric\n\n"},{"id":"463243","messageId":"BL0PR05MB5571531460B45E1AFB15F460D94D9@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"CAPig+cQ5SMw+0Cwtw47LQM59-mQjJaOPe_LTybAC2j=3F9OywA@mail.gmail.com","subject":"RE: [PATCH v8 5/5] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-19T23:55:01Z","receivedAt":"2022-09-19T23:55:12Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Eric Sunshine <sunshine@sunshineco.com>\n> Sent: Saturday, September 17, 2022 2:09 AM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>\n> Cc: Git List <git@vger.kernel.org>; Jeff Hostetler <git@jeffhostetler.com>;\n> Torsten Bögershausen <tboegi@web.de>; Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com>; Ramsay Jones <ramsay@ramsayjones.plus.com>;\n> Johannes Schindelin <Johannes.Schindelin@gmx.de>; Eric DeCosta\n> <edecosta@mathworks.com>\n> Subject: Re: [PATCH v8 5/5] fsmonitor: add documentation for allowRemote\n> and socketDir options\n> \n> On Fri, Sep 16, 2022 at 9:12 PM Eric DeCosta via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n> > Add documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\n> > Call-out experimental nature of 'fsmonitor.allowRemote' and limited\n> > file system support for 'fsmonitor.socketDir'.\n> >\n> > Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> > ---\n> > diff --git a/Documentation/git-fsmonitor--daemon.txt\n> > b/Documentation/git-fsmonitor--daemon.txt\n> > @@ -70,6 +70,41 @@ the change (as happening against the super repo).\n> > However, the client\n> > +By default, the fsmonitor daemon refuses to work against\n> > +network-mounted repositories; this my be overridden by setting\n> > +`fsmonitor.allowRemote` to `true`. Note, however, that the fsmonitor\n> > +daemon is not guaranteed to work correctly with all network-mounted\n> > +repositores and such use is considered experimental.\n> \n> s/repositores/repositories/\n> \n> > +On Mac OS, the inter-process communication (IPC) between various Git\n> > +commands and the fsmonitor daemon is done via a Unix domain socket\n> (UDS).\n> > +Usage of UDS requires the creation of a file which, by default, is\n> > +created in the .git directory.  If the fsmonitor daemon detects that\n> > +the .git directory\n> \n> Typesetting: s/.git/`.git`/g\n> \n> > +is on a network-mounted file system, it will create the UDS file in\n> > +$HOME.  If\n> \n> There's a gap in the explanation as to _why_ fsmonitor won't use the .git\n> directory for this file when on a network-mounted filesystem and instead\n> chooses $HOME. For the reader who is not well-versed in Unix\n> sockets/filesystems, it may be difficult to understand the logic behind this.\n> This gap is somewhat filled in a few sentences later, but it makes for\n> potentially confusing reading until then.\n> \n> Should the reader know the name of the socket file or at least the templated\n> form of the name? The first question which popped into my head upon\n> reading this was whether it was going to pollute my home directory with\n> non-hidden files. If this had mentioned something along the lines of\n> \"creation of a file named `.git-fsmonitor-*`\" or \"creation of a hidden file\"\n> then I would have understood immediately that the file would have been\n> hidden.\n> \n> > +$HOME itself is on a network-mounted file system or if $HOME is not\n> > +the desired\n> \n> To be consistent with formatting elsewhere in the Git documentation, let's\n> typeset this as `$HOME` (with backticks).\n> \n> Aside: The spelling \"filesystem\" appears almost five times as often as \"file\n> system\" in Git documentation, however, this particular file already uses \"file\n> system\" and does so consistently, so it makes sense to follow suit as you do\n> here. Changing to use \"filesystem\" instead, if such a task is desirable, is\n> outside the scope of this patch series.\n> \n> > +location for the UDS file, 'fsmonitor.socketDir' may be set to any\n> > +valid, local\n> \n> For consistency, let's use backticks here, as well: `fsmonitor.socketDir`\n> \n> > +directory on a file system with proper support.  Mac OS native file\n> > +systems have\n> \n> Together with the above comment about a gap in the explanation, I found\n> myself scratching my head about what \"proper support\" meant (when\n> pretending to read this as a person not particularly familiar with Unix sockets\n> or filesystems).\n> \n> Also, although this explains how to work around the case when $HOME is\n> itself network-mounted, what happens if $HOME is network-mounted and\n> the user does not set `fsmonitor.socketDir`? Does it error out? Does it simply\n> misbehave in some way? Should it error out? (I would think\n> \"yes\".)\n> \n> > +the required support.  File systems known to lack support include\n> > +FAT32 and NTFS.  Other file systems may or many not have the needed\n> > +support; the fsmonitor\n> \n> s/many/may/\n> \n> > +daemon is not guaranteed to work with these file systems and such use\n> > +is considered experimental.\n> \n> Taking the above comments into account, here's my attempt at a rewrite:\n> \n>     On Mac OS, the inter-process communication (IPC) between various\n>     Git commands and the fsmonitor daemon occurs via a Unix domain\n>     socket (UDS) -- a special type of file -- which is supported by\n>     the native Mac OS filesystems but not by network-mounted\n>     filesystems, NTFS or FAT32.  Other file systems may or many not\n>     have the needed support; the fsmonitor daemon is not guaranteed to\n>     work with these file systems and such use is considered\n>     experimental.\n> \n>     By default, the socket is created in the `.git` directory,\n>     however, if the `.git` directory is on a network-mounted file\n>     system, it will instead be created at `$HOME/.git-fsmonitor-*`\n>     unless `$HOME` itself is on a network-mounted file system, in\n>     which case you must set the configuration variable\n>     `fsmonitor.socketDir` to the path of a directory on a Mac OS\n>     native filesystem in which to create the socket file.\n> \n> > +CONFIGURATION\n> > +-------------\n> > +When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])\n> > +the fsmonitor daemon will pay attention to the following\n> > +configuration\n> > +variables:\n> \n> We probably want a blank line after the header underline and before this\n> paragraph.\n> \n> > +fsmonitor.allowRemote::\n> > +       By default, the daemon refuses to work against network-mounted\n> > +       repositories. Setting `fsmonitor.allowRemote` to `true` overrides\n> > +       this behavior.\n> > +\n> > +fsmonitor.socketDir::\n> > +       This option is only used by the Mac OS implementation of the\n> fsmonitor\n> > +       daemon. If set, 'fsmonitor.socketDir' must be set to a valid, local\n> > +       directory on a file system that can support Unix domain sockets (UDS).\n> \n> Typeset with backticks: `fsmonitor.socketDir`\n> \n> The word \"valid\" seems unnecessary. A possible rewrite:\n> \n>     This Mac OS-specific option, if set, specifies the directory in\n>     which to create the Unix domain socket used for communication\n>     between fsmonitor and various Git commands. The directory must\n>     reside on a native Mac OS filesystem as discussed above.\n\nThe way you've re-written things is so much clearer! Much appreciated.\n\n-Eric\n"},{"id":"463305","messageId":"454ed375-471e-44b9-f636-d1992e4a3c34@jeffhostetler.com","threadId":"58324","inReplyTo":"BL0PR05MB557100B0802A8407C9CA55BFD94D9@BL0PR05MB5571.namprd05.prod.outlook.com","subject":"Re: [PATCH v7 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-09-20T14:35:55Z","receivedAt":"2022-09-20T14:36:01Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 9/19/22 7:51 PM, Eric DeCosta wrote:\n> \n> \n>> -----Original Message-----\n>> From: Junio C Hamano <gitster@pobox.com>\n>> Sent: Monday, September 19, 2022 1:49 PM\n>> To: Jeff Hostetler <git@jeffhostetler.com>\n>> Cc: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n>> git@vger.kernel.org; Eric Sunshine <sunshine@sunshineco.com>; Torsten\n>> Bögershausen <tboegi@web.de>; Ævar Arnfjörð Bjarmason\n>> <avarab@gmail.com>; Ramsay Jones <ramsay@ramsayjones.plus.com>;\n>> Johannes Schindelin <Johannes.Schindelin@gmx.de>; Eric DeCosta\n>> <edecosta@mathworks.com>\n>> Subject: Re: [PATCH v7 2/6] fsmonitor: relocate socket file if .git directory is\n>> remote\n>>\n>> Jeff Hostetler <git@jeffhostetler.com> writes:\n>>\n>>> Aren't we in the middle of a transition from always using the global\n>>> \"the_repository\" to a passed \"r\" variable?\n>>> We're getting closer to being able to hide the the global symbol, but\n>>> we're not there yet, right?\n>>\n>> We may still have code that works ONLY on the_repository, but letting a\n>> function take \"r\" and lettin it ignore is worse than leaving it explicitly limited\n>> to the_repository only, no?\n>>\n>>> I'm thinking that at as long as the global exists, we are not safe to\n>>> have multiple \"struct repository\" instances, right?\n>>\n>> By itself, Not at all.  It is the code like I am criticizing that makes it unsafe.\n>>\n>> I do not mind adding\n>>\n>> \tif (!r)\n>> \t\tBUG(...);\n>>\n>> at the place you have the \"sweep it under the rug\" band-aid, though.\n> \n> Appreciate all the insights and comments. Where are we landing with this? Very close to the finish line and I'd like to be able to push these changes over that line.\n> \n> -Eric\n> \n\nI'm OK doing it either way.  Junio seems to prefer the BUG() version,\nso let's go with that.  That lets us make progress on getting rid of\ndirect references to \"the_repository\".\n\nJeff\n"},{"id":"463310","messageId":"BL0PR05MB557135D925C638CC0A05D155D94C9@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"454ed375-471e-44b9-f636-d1992e4a3c34@jeffhostetler.com","subject":"RE: [PATCH v7 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-20T15:49:46Z","receivedAt":"2022-09-20T15:50:00Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Jeff Hostetler <git@jeffhostetler.com>\n> Sent: Tuesday, September 20, 2022 10:36 AM\n> To: Eric DeCosta <edecosta@mathworks.com>; Junio C Hamano\n> <gitster@pobox.com>\n> Cc: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n> git@vger.kernel.org; Eric Sunshine <sunshine@sunshineco.com>; Torsten\n> Bögershausen <tboegi@web.de>; Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com>; Ramsay Jones <ramsay@ramsayjones.plus.com>;\n> Johannes Schindelin <Johannes.Schindelin@gmx.de>\n> Subject: Re: [PATCH v7 2/6] fsmonitor: relocate socket file if .git directory is\n> remote\n> \n> \n> \n> On 9/19/22 7:51 PM, Eric DeCosta wrote:\n> >\n> >\n> >> -----Original Message-----\n> >> From: Junio C Hamano <gitster@pobox.com>\n> >> Sent: Monday, September 19, 2022 1:49 PM\n> >> To: Jeff Hostetler <git@jeffhostetler.com>\n> >> Cc: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n> >> git@vger.kernel.org; Eric Sunshine <sunshine@sunshineco.com>; Torsten\n> >> Bögershausen <tboegi@web.de>; Ævar Arnfjörð Bjarmason\n> >> <avarab@gmail.com>; Ramsay Jones <ramsay@ramsayjones.plus.com>;\n> >> Johannes Schindelin <Johannes.Schindelin@gmx.de>; Eric DeCosta\n> >> <edecosta@mathworks.com>\n> >> Subject: Re: [PATCH v7 2/6] fsmonitor: relocate socket file if .git\n> >> directory is remote\n> >>\n> >> Jeff Hostetler <git@jeffhostetler.com> writes:\n> >>\n> >>> Aren't we in the middle of a transition from always using the global\n> >>> \"the_repository\" to a passed \"r\" variable?\n> >>> We're getting closer to being able to hide the the global symbol,\n> >>> but we're not there yet, right?\n> >>\n> >> We may still have code that works ONLY on the_repository, but letting\n> >> a function take \"r\" and lettin it ignore is worse than leaving it\n> >> explicitly limited to the_repository only, no?\n> >>\n> >>> I'm thinking that at as long as the global exists, we are not safe\n> >>> to have multiple \"struct repository\" instances, right?\n> >>\n> >> By itself, Not at all.  It is the code like I am criticizing that makes it unsafe.\n> >>\n> >> I do not mind adding\n> >>\n> >> \tif (!r)\n> >> \t\tBUG(...);\n> >>\n> >> at the place you have the \"sweep it under the rug\" band-aid, though.\n> >\n> > Appreciate all the insights and comments. Where are we landing with this?\n> Very close to the finish line and I'd like to be able to push these changes over\n> that line.\n> >\n> > -Eric\n> >\n> \n> I'm OK doing it either way.  Junio seems to prefer the BUG() version, so let's\n> go with that.  That lets us make progress on getting rid of direct references to\n> \"the_repository\".\n> \n> Jeff\n\nSounds like a plan!\n\n-Eric\n\n"},{"id":"463332","messageId":"pull.1326.v10.git.1663705986.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v9.git.1663616277.gitgitgadget@gmail.com","subject":"[PATCH v10 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-20T20:33:00Z","receivedAt":"2022-09-20T20:33:13Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"Follow-on to the work done to allow Windows to work against network-mounted\nrepos for macOS.\n\nHave macOS take advantage of the same configuration option,\n'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\nto true will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for IPC\ncannot be created in a network location; instead $HOME is used if the\ndefault location is on the network. The user may, optionally, set the\n'fsmonitor.socketDir' configuration option to a valid, local directory if\n$HOME itself is on the network or is simply not the desired location for the\nUDS file.\n\nAn additional issue is that for mount points in the root directory, FSEvents\ndoes not report a path that matches the worktree directory due to the\nintroduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\nto the worktree directory by interrogating the root filesystem for synthetic\nfirmlinks and using that information to translate the path.\n\nv10 differs from v9:\n\n * incorporates code review feedback\n * improves error messaging for incompatible socket directory\n\nv9 differs from v8:\n\n * incorporates code review feedback\n * check for incompatibility before communicating with fsmonitor\n\nv8 differs from v7:\n\n * incorporates code review feedback\n * gets the rebase right\n\nv7 differs from v6:\n\n * incorporates code review feedback\n\nv6 differs from v5:\n\n * incorporates earlier, Windows-specific changes that have not made it back\n   yet to the master branch\n * incorporates code review feedback\n * adds documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n\nv5 differs significantly from earlier versions:\n\n * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n   such that these options are no longer added to the settings data\n   structure but are rather read from config at point of use\n * refactoring of code for handling platform-specific file system checks via\n   a common interface to avoid platform #ifdef in IPC code and be in-model\n   with other platform-specific fsmonitor code\n * dealing with 'synthetic firmlinks' on macOS\n\nEric DeCosta (6):\n  fsmonitor: refactor filesystem checks to common interface\n  fsmonitor: relocate socket file if .git directory is remote\n  fsmonitor: avoid socket location check if using hook\n  fsmonitor: deal with synthetic firmlinks on macOS\n  fsmonitor: check for compatability before communicating with fsmonitor\n  fsmonitor: add documentation for allowRemote and socketDir options\n\n Documentation/git-fsmonitor--daemon.txt  |  48 ++++++-\n Makefile                                 |   2 +\n builtin/fsmonitor--daemon.c              |  11 +-\n compat/fsmonitor/fsm-ipc-darwin.c        |  51 +++++++\n compat/fsmonitor/fsm-ipc-win32.c         |   9 ++\n compat/fsmonitor/fsm-listen-darwin.c     |   6 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 132 +++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 145 +++++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  72 +++-------\n compat/fsmonitor/fsm-settings-win32.c    | 174 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   4 +\n fsmonitor--daemon.h                      |   3 +\n fsmonitor-ipc.c                          |  18 ++-\n fsmonitor-ipc.h                          |   4 +-\n fsmonitor-path-utils.h                   |  59 ++++++++\n fsmonitor-settings.c                     |  68 ++++++++-\n fsmonitor-settings.h                     |   4 +-\n fsmonitor.c                              |   4 +\n 18 files changed, 569 insertions(+), 245 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\n\nbase-commit: d3fa443f97e3a8d75b51341e2d5bac380b7422df\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v10\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v10\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v9:\n\n 1:  155a6890806 = 1:  155a6890806 fsmonitor: refactor filesystem checks to common interface\n 2:  68709d788d5 ! 2:  dbe113abb87 fsmonitor: relocate socket file if .git directory is remote\n     @@ compat/fsmonitor/fsm-ipc-darwin.c (new)\n      +{\n      +\tstatic const char *ipc_path;\n      +\tSHA_CTX sha1ctx;\n     -+\tchar *sock_dir;\n     ++\tchar *sock_dir = NULL;\n      +\tstruct strbuf ipc_file = STRBUF_INIT;\n      +\tunsigned char hash[SHA_DIGEST_LENGTH];\n      +\n     ++\tif (!r)\n     ++\t\tBUG(\"No repository passed into fsmonitor_ipc__get_path\");\n     ++\n      +\tif (ipc_path)\n      +\t\treturn ipc_path;\n      +\n     @@ compat/fsmonitor/fsm-ipc-darwin.c (new)\n      +\tif (sock_dir && *sock_dir) {\n      +\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n      +\t\t\t\t\tsock_dir, hash_to_hex(hash));\n     -+\t\tfree(sock_dir);\n      +\t} else {\n      +\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n      +\t}\n     ++\tfree(sock_dir);\n      +\n      +\tipc_path = interpolate_path(ipc_file.buf, 1);\n      +\tif (!ipc_path)\n     @@ compat/fsmonitor/fsm-ipc-win32.c (new)\n      +\t\tret = git_pathdup(\"fsmonitor--daemon.ipc\");\n      +\treturn ret;\n      +}\n     - \\ No newline at end of file\n      \n       ## compat/fsmonitor/fsm-settings-darwin.c ##\n      @@\n 3:  6ddd922917a = 3:  86c15299ae8 fsmonitor: avoid socket location check if using hook\n 4:  73afd9f3122 = 4:  4f6c98cf834 fsmonitor: deal with synthetic firmlinks on macOS\n 5:  02afeaa01be ! 5:  8f6c1fbacbf fsmonitor: check for compatability before communicating with fsmonitor\n     @@ Commit message\n      \n          Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n      \n     + ## compat/fsmonitor/fsm-settings-darwin.c ##\n     +@@ compat/fsmonitor/fsm-settings-darwin.c: static enum fsmonitor_reason check_uds_volume(struct repository *r)\n     + \tstrbuf_release(&path);\n     + \n     + \tif (fs.is_remote)\n     +-\t\treturn FSMONITOR_REASON_REMOTE;\n     ++\t\treturn FSMONITOR_REASON_NOSOCKETS;\n     + \n     + \tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n     + \t\treturn FSMONITOR_REASON_NOSOCKETS;\n     +\n       ## fsmonitor-settings.c ##\n     -@@ fsmonitor-settings.c: char *fsm_settings__get_incompatible_msg(const struct repository *r,\n     +@@\n     + #include \"cache.h\"\n     + #include \"config.h\"\n     + #include \"repository.h\"\n     ++#include \"fsmonitor-ipc.h\"\n     + #include \"fsmonitor-settings.h\"\n     + #include \"fsmonitor-path-utils.h\"\n     + \n     +@@ fsmonitor-settings.c: enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)\n     + \treturn r->settings.fsmonitor->reason;\n     + }\n     + \n     +-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n     ++char *fsm_settings__get_incompatible_msg(struct repository *r,\n     + \t\t\t\t\t enum fsmonitor_reason reason)\n       {\n       \tstruct strbuf msg = STRBUF_INIT;\n     ++\tconst char *socket_dir;\n       \n     -+\tstrbuf_add(&msg, \"fsmonitor: \", strlen(\"fsmonitor: \"));\n     -+\n       \tswitch (reason) {\n       \tcase FSMONITOR_REASON_UNTESTED:\n     - \tcase FSMONITOR_REASON_OK:\n     +@@ fsmonitor-settings.c: char *fsm_settings__get_incompatible_msg(const struct repository *r,\n     + \t\tgoto done;\n     + \n     + \tcase FSMONITOR_REASON_NOSOCKETS:\n     ++\t\tsocket_dir = dirname((char *)fsmonitor_ipc__get_path(r));\n     + \t\tstrbuf_addf(&msg,\n     +-\t\t\t    _(\"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets\"),\n     +-\t\t\t    r->worktree);\n     ++\t\t\t    _(\"socket directory '%s' is incompatible with fsmonitor due\"),\n     ++\t\t\t    socket_dir);\n     ++\t\tstrbuf_add(&msg, _(\" to lack of Unix sockets support\"), 32);\n     + \t\tgoto done;\n     + \t}\n     + \n     +\n     + ## fsmonitor-settings.h ##\n     +@@ fsmonitor-settings.h: enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n     + const char *fsm_settings__get_hook_path(struct repository *r);\n     + \n     + enum fsmonitor_reason fsm_settings__get_reason(struct repository *r);\n     +-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n     ++char *fsm_settings__get_incompatible_msg(struct repository *r,\n     + \t\t\t\t\t enum fsmonitor_reason reason);\n     + \n     + struct fsmonitor_settings;\n      \n       ## fsmonitor.c ##\n      @@ fsmonitor.c: void refresh_fsmonitor(struct index_state *istate)\n 6:  0e8ea28acc1 = 6:  d7c25bf96c6 fsmonitor: add documentation for allowRemote and socketDir options\n\n-- \ngitgitgadget\n"},{"id":"463333","messageId":"dbe113abb875bae568d75c23f0eadd363c3eb905.1663705986.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v10.git.1663705986.gitgitgadget@gmail.com","subject":"[PATCH v10 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-20T20:33:02Z","receivedAt":"2022-09-20T20:33:18Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf the .git directory is on a remote file system, create the socket\nfile in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                               |  1 +\n builtin/fsmonitor--daemon.c            |  3 +-\n compat/fsmonitor/fsm-ipc-darwin.c      | 51 ++++++++++++++++++++++++++\n compat/fsmonitor/fsm-ipc-win32.c       |  9 +++++\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n contrib/buildsystems/CMakeLists.txt    |  2 +\n fsmonitor-ipc.c                        | 18 ++++-----\n fsmonitor-ipc.h                        |  4 +-\n 8 files changed, 77 insertions(+), 13 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n\ndiff --git a/Makefile b/Makefile\nindex 6e492a67547..58bb9248471 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2034,6 +2034,7 @@ ifdef FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n endif\n \n ifdef FSMONITOR_OS_SETTINGS\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 2c109cf8b37..0123fc33ed2 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -1343,7 +1343,8 @@ static int fsmonitor_run_daemon(void)\n \t * directory.)\n \t */\n \tstrbuf_init(&state.path_ipc, 0);\n-\tstrbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));\n+\tstrbuf_addstr(&state.path_ipc,\n+\t\tabsolute_path(fsmonitor_ipc__get_path(the_repository)));\n \n \t/*\n \t * Confirm that we can create platform-specific resources for the\ndiff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\nnew file mode 100644\nindex 00000000000..0f551e5e572\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-darwin.c\n@@ -0,0 +1,51 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"strbuf.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n+{\n+\tstatic const char *ipc_path;\n+\tSHA_CTX sha1ctx;\n+\tchar *sock_dir = NULL;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (!r)\n+\t\tBUG(\"No repository passed into fsmonitor_ipc__get_path\");\n+\n+\tif (ipc_path)\n+\t\treturn ipc_path;\n+\n+\tipc_path = fsmonitor_ipc__get_default_path();\n+\n+\t/* By default the socket file is created in the .git directory */\n+\tif (fsmonitor__is_fs_remote(ipc_path) < 1)\n+\t\treturn ipc_path;\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\trepo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n+\n+\t/* Create the socket file in either socketDir or $HOME */\n+\tif (sock_dir && *sock_dir) {\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\t} else {\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\t}\n+\tfree(sock_dir);\n+\n+\tipc_path = interpolate_path(ipc_file.buf, 1);\n+\tif (!ipc_path)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\n+\tstrbuf_release(&ipc_file);\n+\treturn ipc_path;\n+}\ndiff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\nnew file mode 100644\nindex 00000000000..e08c505c148\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-win32.c\n@@ -0,0 +1,9 @@\n+#include \"config.h\"\n+#include \"fsmonitor-ipc.h\"\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r) {\n+\tstatic char *ret;\n+\tif (!ret)\n+\t\tret = git_pathdup(\"fsmonitor--daemon.ipc\");\n+\treturn ret;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex dba3ced6bb7..681d8bf963e 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -26,7 +26,7 @@\n static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n \tstruct fs_info fs;\n-\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tconst char *ipc_path = fsmonitor_ipc__get_path(r);\n \tstruct strbuf path = STRBUF_INIT;\n \tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex b88494bf59b..7e7b6b9a362 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n@@ -316,6 +317,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..c0f42301c84 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -18,7 +18,7 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 0;\n }\n \n-const char *fsmonitor_ipc__get_path(void)\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n {\n \treturn NULL;\n }\n@@ -47,11 +47,9 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n-\n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\n-\treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n+\treturn ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));\n }\n \n static int spawn_daemon(void)\n@@ -81,8 +79,8 @@ int fsmonitor_ipc__send_query(const char *since_token,\n \ttrace2_data_string(\"fsm_client\", NULL, \"query/command\", tok);\n \n try_again:\n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \n \tswitch (state) {\n \tcase IPC_STATE__LISTENING:\n@@ -117,13 +115,13 @@ try_again:\n \n \tcase IPC_STATE__INVALID_PATH:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: invalid path '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \n \tcase IPC_STATE__OTHER_ERROR:\n \tdefault:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: unspecified error on '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \t}\n \n@@ -149,8 +147,8 @@ int fsmonitor_ipc__send_command(const char *command,\n \toptions.wait_if_busy = 1;\n \toptions.wait_if_not_found = 0;\n \n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \tif (state != IPC_STATE__LISTENING) {\n \t\tdie(_(\"fsmonitor--daemon is not running\"));\n \t\treturn -1;\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..8b489da762b 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -3,6 +3,8 @@\n \n #include \"simple-ipc.h\"\n \n+struct repository;\n+\n /*\n  * Returns true if built-in file system monitor daemon is defined\n  * for this platform.\n@@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);\n  *\n  * Returns NULL if the daemon is not supported on this platform.\n  */\n-const char *fsmonitor_ipc__get_path(void);\n+const char *fsmonitor_ipc__get_path(struct repository *r);\n \n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n-- \ngitgitgadget\n\n"},{"id":"463334","messageId":"155a689080658e1953fc4434016f746f371ba08a.1663705986.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v10.git.1663705986.gitgitgadget@gmail.com","subject":"[PATCH v10 1/6] fsmonitor: refactor filesystem checks to common interface","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-20T20:33:01Z","receivedAt":"2022-09-20T20:33:21Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nProvide a common interface for getting basic filesystem information\nincluding filesystem type and whether the filesystem is remote.\n\nRefactor existing code for getting basic filesystem info and detecting\nremote file systems to the new interface.\n\nRefactor filesystem checks to leverage new interface. For macOS,\nerror-out if the Unix Domain socket (UDS) file is on a remote\nfilesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                                 |   1 +\n compat/fsmonitor/fsm-path-utils-darwin.c |  40 ++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 128 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  62 +++-----\n compat/fsmonitor/fsm-settings-win32.c    | 172 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   2 +\n fsmonitor-path-utils.h                   |  23 +++\n fsmonitor-settings.c                     |  50 +++++++\n 8 files changed, 263 insertions(+), 215 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\ndiff --git a/Makefile b/Makefile\nindex d9247ead45b..6e492a67547 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2039,6 +2039,7 @@ endif\n ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n ifeq ($(TCLTK_PATH),)\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nnew file mode 100644\nindex 00000000000..067cbe6990a\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -0,0 +1,40 @@\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+#include <sys/param.h>\n+#include <sys/mount.h>\n+\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\tstruct statfs fs;\n+\tif (statfs(path, &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\treturn -1;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\n+\tif (!(fs.f_flags & MNT_LOCAL))\n+\t\tfs_info->is_remote = 1;\n+\telse\n+\t\tfs_info->is_remote = 0;\n+\n+\tfs_info->typename = fs.f_fstypename;\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nnew file mode 100644\nindex 00000000000..a90b8f7925b\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -0,0 +1,128 @@\n+#include \"cache.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+/*\n+ * Check remote working directory protocol.\n+ *\n+ * Return -1 if client machine cannot get remote protocol information.\n+ */\n+static int check_remote_protocol(wchar_t *wpath)\n+{\n+\tHANDLE h;\n+\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n+\n+\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n+\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\n+\tif (h == INVALID_HANDLE_VALUE) {\n+\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n+\t\t&proto_info, sizeof(proto_info))) {\n+\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\tCloseHandle(h);\n+\t\treturn -1;\n+\t}\n+\n+\tCloseHandle(h);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n+\t\t\t\twpath, proto_info.Protocol);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Notes for testing:\n+ *\n+ * (a) Windows allows a network share to be mapped to a drive letter.\n+ *     (This is the normal method to access it.)\n+ *\n+ *     $ NET USE Z: \\\\server\\share\n+ *     $ git -C Z:/repo status\n+ *\n+ * (b) Windows allows a network share to be referenced WITHOUT mapping\n+ *     it to drive letter.\n+ *\n+ *     $ NET USE \\\\server\\share\\dir\n+ *     $ git -C //server/share/repo status\n+ *\n+ * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n+ *     arbitrary path (which may be remote)\n+ *\n+ *     $ SUBST Q: Z:\\repo\n+ *     $ git -C Q:/ status\n+ *\n+ * (d) Windows allows a directory symlink to be created on a local\n+ *     file system that points to a remote repo.\n+ *\n+ *     $ mklink /d ./link //server/share/repo\n+ *     $ git -C ./link status\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\twchar_t wpath[MAX_PATH];\n+\twchar_t wfullpath[MAX_PATH];\n+\tsize_t wlen;\n+\tUINT driveType;\n+\n+\t/*\n+\t * Do everything in wide chars because the drive letter might be\n+\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n+\t */\n+\tif (xutftowcs_path(wpath, path) < 0) {\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * GetDriveTypeW() requires a final slash.  We assume that the\n+\t * worktree pathname points to an actual directory.\n+\t */\n+\twlen = wcslen(wpath);\n+\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n+\t\twpath[wlen++] = L'\\\\';\n+\t\twpath[wlen] = 0;\n+\t}\n+\n+\t/*\n+\t * Normalize the path.  If nothing else, this converts forward\n+\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n+\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n+\t */\n+\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {\n+\t\treturn -1;\n+\t}\n+\n+\tdriveType = GetDriveTypeW(wfullpath);\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n+\t\t\t path, wfullpath, driveType);\n+\n+\tif (driveType == DRIVE_REMOTE) {\n+\t\tfs_info->is_remote = 1;\n+\t\tif (check_remote_protocol(wfullpath) < 0)\n+\t\t\treturn -1;\n+\t} else {\n+\t\tfs_info->is_remote = 0;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dba3ced6bb7 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -1,32 +1,10 @@\n-#include \"cache.h\"\n #include \"config.h\"\n-#include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n-#include <sys/param.h>\n-#include <sys/mount.h>\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n-/*\n- * [1] Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type (NFS, SAMBA, etc.) dictates whether notification events\n- * are available at all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n+ /*\n  * For the builtin FSMonitor, we create the Unix domain socket for the\n  * IPC in the .git directory.  If the working directory is remote,\n  * then the socket will be created on the remote file system.  This\n@@ -38,40 +16,34 @@\n  * be taken to ensure that $HOME is actually local and not a managed\n  * file share.)\n  *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n+ * FAT32 and NTFS working directories are problematic too.\n  *\n  * The builtin FSMonitor uses a Unix domain socket in the .git\n  * directory for IPC.  These Windows drive formats do not support\n  * Unix domain sockets, so mark them as incompatible for the daemon.\n  *\n  */\n-static enum fsmonitor_reason check_volume(struct repository *r)\n+static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n-\tstruct statfs fs;\n+\tstruct fs_info fs;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \n-\tif (statfs(r->worktree, &fs) == -1) {\n-\t\tint saved_errno = errno;\n-\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n-\t\t\t\t r->worktree, strerror(saved_errno));\n-\t\terrno = saved_errno;\n+\tif (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {\n+\t\tstrbuf_release(&path);\n \t\treturn FSMONITOR_REASON_ERROR;\n \t}\n \n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n-\t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n \n-\tif (!(fs.f_flags & MNT_LOCAL))\n+\tif (fs.is_remote)\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n-\tif (!strcmp(fs.f_fstypename, \"msdos\")) /* aka FAT32 */\n+\tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n-\tif (!strcmp(fs.f_fstypename, \"ntfs\"))\n+\tif (!strcmp(fs.typename, \"ntfs\"))\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \treturn FSMONITOR_REASON_OK;\n@@ -81,7 +53,7 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_volume(r);\n+\treason = check_uds_volume(r);\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex e5ec5b0a9f7..d88b06ae610 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -1,8 +1,9 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * VFS for Git is incompatible with FSMonitor.\n@@ -24,171 +25,6 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-/*\n- * Check if monitoring remote working directories is allowed.\n- *\n- * By default, monitoring remote working directories is\n- * disabled.  Users may override this behavior in enviroments where\n- * they have proper support.\n- */\n-static int check_config_allowremote(struct repository *r)\n-{\n-\tint allow;\n-\n-\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n-\t\treturn allow;\n-\n-\treturn -1; /* fsmonitor.allowremote not set */\n-}\n-\n-/*\n- * Check remote working directory protocol.\n- *\n- * Error if client machine cannot get remote protocol information.\n- */\n-static int check_remote_protocol(wchar_t *wpath)\n-{\n-\tHANDLE h;\n-\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n-\n-\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n-\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n-\n-\tif (h == INVALID_HANDLE_VALUE) {\n-\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\treturn -1;\n-\t}\n-\n-\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n-\t\t&proto_info, sizeof(proto_info))) {\n-\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\tCloseHandle(h);\n-\t\treturn -1;\n-\t}\n-\n-\tCloseHandle(h);\n-\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n-\t\t\t\twpath, proto_info.Protocol);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type dictates whether notification events are available at\n- * all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- * Notes for testing:\n- *\n- * (a) Windows allows a network share to be mapped to a drive letter.\n- *     (This is the normal method to access it.)\n- *\n- *     $ NET USE Z: \\\\server\\share\n- *     $ git -C Z:/repo status\n- *\n- * (b) Windows allows a network share to be referenced WITHOUT mapping\n- *     it to drive letter.\n- *\n- *     $ NET USE \\\\server\\share\\dir\n- *     $ git -C //server/share/repo status\n- *\n- * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n- *     arbitrary path (which may be remote)\n- *\n- *     $ SUBST Q: Z:\\repo\n- *     $ git -C Q:/ status\n- *\n- * (d) Windows allows a directory symlink to be created on a local\n- *     file system that points to a remote repo.\n- *\n- *     $ mklink /d ./link //server/share/repo\n- *     $ git -C ./link status\n- */\n-static enum fsmonitor_reason check_remote(struct repository *r)\n-{\n-\tint ret;\n-\twchar_t wpath[MAX_PATH];\n-\twchar_t wfullpath[MAX_PATH];\n-\tsize_t wlen;\n-\tUINT driveType;\n-\n-\t/*\n-\t * Do everything in wide chars because the drive letter might be\n-\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n-\t */\n-\tif (xutftowcs_path(wpath, r->worktree) < 0)\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t/*\n-\t * GetDriveTypeW() requires a final slash.  We assume that the\n-\t * worktree pathname points to an actual directory.\n-\t */\n-\twlen = wcslen(wpath);\n-\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n-\t\twpath[wlen++] = L'\\\\';\n-\t\twpath[wlen] = 0;\n-\t}\n-\n-\t/*\n-\t * Normalize the path.  If nothing else, this converts forward\n-\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n-\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n-\t */\n-\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\tdriveType = GetDriveTypeW(wfullpath);\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n-\t\t\t r->worktree, wfullpath, driveType);\n-\n-\tif (driveType == DRIVE_REMOTE) {\n-\t\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t \"check_remote('%s') true\",\n-\t\t\t\t r->worktree);\n-\n-\t\tret = check_remote_protocol(wfullpath);\n-\t\tif (ret < 0)\n-\t\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t\tswitch (check_config_allowremote(r)) {\n-\t\tcase 0: /* config overrides and disables */\n-\t\t\treturn FSMONITOR_REASON_REMOTE;\n-\t\tcase 1: /* config overrides and enables */\n-\t\t\treturn FSMONITOR_REASON_OK;\n-\t\tdefault:\n-\t\t\tbreak; /* config has no opinion */\n-\t\t}\n-\n-\t\treturn FSMONITOR_REASON_REMOTE;\n-\t}\n-\n-\treturn FSMONITOR_REASON_OK;\n-}\n-\n enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n@@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n-\treason = check_remote(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n-\n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 2237109b57f..b88494bf59b 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)\n@@ -315,6 +316,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nnew file mode 100644\nindex 00000000000..e48592887e7\n--- /dev/null\n+++ b/fsmonitor-path-utils.h\n@@ -0,0 +1,23 @@\n+#ifndef FSM_PATH_UTILS_H\n+#define FSM_PATH_UTILS_H\n+\n+struct fs_info {\n+\tint is_remote;\n+\tchar *typename;\n+};\n+\n+/*\n+ * Get some basic filesystem informtion for the given path\n+ *\n+ * Returns -1 on error, zero otherwise.\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n+\n+/*\n+ * Determines if the filesystem that path resides on is remote.\n+ *\n+ * Returns -1 on error, 0 if not remote, 1 if remote.\n+ */\n+int fsmonitor__is_fs_remote(const char *path);\n+\n+#endif\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..d288cbad479 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * We keep this structure defintion private and have getters\n@@ -13,6 +14,52 @@ struct fsmonitor_settings {\n \tchar *hook_path;\n };\n \n+/*\n+ * Remote working directories are problematic for FSMonitor.\n+ *\n+ * The underlying file system on the server machine and/or the remote\n+ * mount type dictates whether notification events are available at\n+ * all to remote client machines.\n+ *\n+ * Kernel differences between the server and client machines also\n+ * dictate the how (buffering, frequency, de-dup) the events are\n+ * delivered to client machine processes.\n+ *\n+ * A client machine (such as a laptop) may choose to suspend/resume\n+ * and it is unclear (without lots of testing) whether the watcher can\n+ * resync after a resume.  We might be able to treat this as a normal\n+ * \"events were dropped by the kernel\" event and do our normal \"flush\n+ * and resync\" --or-- we might need to close the existing (zombie?)\n+ * notification fd and create a new one.\n+ *\n+ * In theory, the above issues need to be addressed whether we are\n+ * using the Hook or IPC API.\n+ *\n+ * So (for now at least), mark remote working directories as\n+ * incompatible unless 'fsmonitor.allowRemote' is true.\n+ *\n+ */\n+#ifdef HAVE_FSMONITOR_OS_SETTINGS\n+static enum fsmonitor_reason check_remote(struct repository *r)\n+{\n+\tint allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */\n+\tint is_remote = fsmonitor__is_fs_remote(r->worktree);\n+\n+\tswitch (is_remote) {\n+\t\tcase 0:\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tcase 1:\n+\t\t\trepo_config_get_bool(r, \"fsmonitor.allowremote\", &allow_remote);\n+\t\t\tif (allow_remote < 1)\n+\t\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\t\telse\n+\t\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+}\n+#endif\n+\n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n {\n \tif (!r->worktree) {\n@@ -27,6 +74,9 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t{\n \t\tenum fsmonitor_reason reason;\n \n+\t\treason = check_remote(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n \t\treason = fsm_os__incompatible(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-- \ngitgitgadget\n\n"},{"id":"463335","messageId":"86c15299ae8094d077ff97ea79d6e54451ee071d.1663705986.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v10.git.1663705986.gitgitgadget@gmail.com","subject":"[PATCH v10 3/6] fsmonitor: avoid socket location check if using hook","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-20T20:33:03Z","receivedAt":"2022-09-20T20:33:37Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf monitoring is done via fsmonitor hook rather than IPC there is no\nneed to check if the location of the Unix Domain socket (UDS) file is\non a remote filesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 10 ++++++----\n compat/fsmonitor/fsm-settings-win32.c  |  2 +-\n fsmonitor-settings.c                   |  8 ++++----\n fsmonitor-settings.h                   |  2 +-\n 4 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 681d8bf963e..40da2d3b533 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -49,13 +49,15 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_uds_volume(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n+\tif (ipc) {\n+\t\treason = check_uds_volume(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n+\t}\n \n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex d88b06ae610..a8af31b71de 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex d288cbad479..531a1b6f956 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -60,7 +60,7 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n }\n #endif\n \n-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n+static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n {\n \tif (!r->worktree) {\n \t\t/*\n@@ -77,7 +77,7 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t\treason = check_remote(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-\t\treason = fsm_os__incompatible(r);\n+\t\treason = fsm_os__incompatible(r, ipc);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n \t}\n@@ -162,7 +162,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -185,7 +185,7 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..0721617b95a 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -48,7 +48,7 @@ struct fsmonitor_settings;\n  * fsm_os__* routines should considered private to fsm_settings__\n  * routines.\n  */\n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n \n #endif /* FSMONITOR_SETTINGS_H */\n-- \ngitgitgadget\n\n"},{"id":"463336","messageId":"8f6c1fbacbfd3df67d1a7591788d98f1af0e26f4.1663705986.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v10.git.1663705986.gitgitgadget@gmail.com","subject":"[PATCH v10 5/6] fsmonitor: check for compatability before communicating with fsmonitor","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-20T20:33:05Z","receivedAt":"2022-09-20T20:33:42Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf fsmonitor is not in a compatible state, die with an appropriate error\nmessge.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n fsmonitor-settings.c                   | 10 +++++++---\n fsmonitor-settings.h                   |  2 +-\n fsmonitor.c                            |  4 ++++\n 4 files changed, 13 insertions(+), 5 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 40da2d3b533..44233125df8 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -38,7 +38,7 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \tstrbuf_release(&path);\n \n \tif (fs.is_remote)\n-\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 531a1b6f956..24480b9806d 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n+#include \"fsmonitor-ipc.h\"\n #include \"fsmonitor-settings.h\"\n #include \"fsmonitor-path-utils.h\"\n \n@@ -242,10 +243,11 @@ enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)\n \treturn r->settings.fsmonitor->reason;\n }\n \n-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n+char *fsm_settings__get_incompatible_msg(struct repository *r,\n \t\t\t\t\t enum fsmonitor_reason reason)\n {\n \tstruct strbuf msg = STRBUF_INIT;\n+\tconst char *socket_dir;\n \n \tswitch (reason) {\n \tcase FSMONITOR_REASON_UNTESTED:\n@@ -281,9 +283,11 @@ char *fsm_settings__get_incompatible_msg(const struct repository *r,\n \t\tgoto done;\n \n \tcase FSMONITOR_REASON_NOSOCKETS:\n+\t\tsocket_dir = dirname((char *)fsmonitor_ipc__get_path(r));\n \t\tstrbuf_addf(&msg,\n-\t\t\t    _(\"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets\"),\n-\t\t\t    r->worktree);\n+\t\t\t    _(\"socket directory '%s' is incompatible with fsmonitor due\"),\n+\t\t\t    socket_dir);\n+\t\tstrbuf_add(&msg, _(\" to lack of Unix sockets support\"), 32);\n \t\tgoto done;\n \t}\n \ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex 0721617b95a..ab02e3995ee 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -33,7 +33,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n const char *fsm_settings__get_hook_path(struct repository *r);\n \n enum fsmonitor_reason fsm_settings__get_reason(struct repository *r);\n-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n+char *fsm_settings__get_incompatible_msg(struct repository *r,\n \t\t\t\t\t enum fsmonitor_reason reason);\n \n struct fsmonitor_settings;\ndiff --git a/fsmonitor.c b/fsmonitor.c\nindex 57d6a483bee..43d580132fb 100644\n--- a/fsmonitor.c\n+++ b/fsmonitor.c\n@@ -305,6 +305,10 @@ void refresh_fsmonitor(struct index_state *istate)\n \tint is_trivial = 0;\n \tstruct repository *r = istate->repo ? istate->repo : the_repository;\n \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);\n+\tenum fsmonitor_reason reason = fsm_settings__get_reason(r);\n+\n+\tif (reason > FSMONITOR_REASON_OK)\n+\t\tdie(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n \n \tif (fsm_mode <= FSMONITOR_MODE_DISABLED ||\n \t    istate->fsmonitor_has_run_once)\n-- \ngitgitgadget\n\n"},{"id":"463337","messageId":"4f6c98cf834019ba7aab3406fbb8e7e237ddf3a9.1663705986.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v10.git.1663705986.gitgitgadget@gmail.com","subject":"[PATCH v10 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-20T20:33:04Z","receivedAt":"2022-09-20T20:33:43Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n builtin/fsmonitor--daemon.c              |  8 +++\n compat/fsmonitor/fsm-listen-darwin.c     |  6 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 92 ++++++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 17 +++++\n fsmonitor--daemon.h                      |  3 +\n fsmonitor-path-utils.h                   | 36 ++++++++++\n 6 files changed, 161 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 0123fc33ed2..56fcd1c2baa 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"fsmonitor.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n #include \"compat/fsmonitor/fsm-health.h\"\n #include \"compat/fsmonitor/fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n@@ -1282,6 +1283,13 @@ static int fsmonitor_run_daemon(void)\n \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n \tstate.nr_paths_watching = 1;\n \n+\tstate.alias.alias = NULL;\n+\tstate.alias.points_to = NULL;\n+\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n+\t\terr = error(_(\"could not get worktree alias\"));\n+\t\tgoto done;\n+\t}\n+\n \t/*\n \t * We create and delete cookie files somewhere inside the .git\n \t * directory to help us keep sync with the file system.  If\ndiff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\nindex 8e208e8289e..179886bc15b 100644\n--- a/compat/fsmonitor/fsm-listen-darwin.c\n+++ b/compat/fsmonitor/fsm-listen-darwin.c\n@@ -26,6 +26,7 @@\n #include \"fsmonitor.h\"\n #include \"fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsm_listen_data\n {\n@@ -209,7 +210,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t/*\n \t\t * On Mac, we receive an array of absolute paths.\n \t\t */\n-\t\tpath_k = paths[k];\n+\t\tpath_k = fsmonitor__resolve_alias(paths[k], &state->alias);\n+\t\tif (!path_k)\n+\t\t\tpath_k = paths[k];\n \n \t\t/*\n \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n@@ -238,6 +241,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t\tfsmonitor_force_resync(state);\n \t\t\tfsmonitor_batch__free_list(batch);\n \t\t\tstring_list_clear(&cookie_list, 0);\n+\t\t\tbatch = NULL;\n \n \t\t\t/*\n \t\t\t * We assume that any events that we received\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nindex 067cbe6990a..13807f58e95 100644\n--- a/compat/fsmonitor/fsm-path-utils-darwin.c\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -1,5 +1,8 @@\n #include \"fsmonitor.h\"\n #include \"fsmonitor-path-utils.h\"\n+#include <dirent.h>\n+#include <errno.h>\n+#include <fcntl.h>\n #include <sys/param.h>\n #include <sys/mount.h>\n \n@@ -38,3 +41,92 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * Scan the root directory for synthetic firmlinks that when resolved\n+ * are a prefix of the path, stopping at the first one found.\n+ *\n+ * Some information about firmlinks and synthetic firmlinks:\n+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n+ *\n+ * macOS no longer allows symlinks in the root directory; any link found\n+ * there is therefore a synthetic firmlink.\n+ *\n+ * If this function gets called often, will want to cache all the firmlink\n+ * information, but for now there is only one caller of this function.\n+ *\n+ * If there is more than one alias for the path, that is another\n+ * matter altogether.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\tDIR * dir;\n+\tint read;\n+\tint retval;\n+\tstruct dirent *de;\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+\n+\tretval = 0;\n+\tdir = opendir(\"/\");\n+\tif (!dir)\n+\t\treturn -1;\n+\n+\tstrbuf_init(&alias, 256);\n+\tstrbuf_init(&points_to, MAXPATHLEN);\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstrbuf_reset(&alias);\n+\t\tstrbuf_addch(&alias, '/');\n+\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n+\n+\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n+\t\tif (read > 0) {\n+\t\t\tstrbuf_setlen(&points_to, read);\n+\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n+\t\t\t\t&& path[points_to.len] == '/') {\n+\t\t\t\tinfo->alias = strbuf_detach(&alias, NULL);\n+\t\t\t\tinfo->points_to = strbuf_detach(&points_to, NULL);\n+\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n+\t\t\t\t\tpath, info->alias, info->points_to);\n+\t\t\t\tretval = 0;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n+\t\t\ttrace_printf_key(&trace_fsmonitor, \"Error %s\", strerror(errno));\n+\t\t\tretval = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n+\tdone:\n+\tclosedir(dir);\n+\tstrbuf_release(&alias);\n+\tstrbuf_release(&points_to);\n+\treturn retval;\n+}\n+\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\tint len = info->alias ? strlen(info->alias) : 0;\n+\n+\tif (!len)\n+\t\treturn NULL;\n+\n+\tif ((strncmp(info->alias, path, len) == 0)\n+\t\t&& path[len] == '/') {\n+\t\tstruct strbuf tmp;\n+\t\tconst char *remainder = path + len;\n+\t\tint ptr_len = strlen(info->points_to);\n+\t\tint rem_len = strlen(remainder);\n+\n+\t\tstrbuf_init(&tmp, ptr_len + rem_len);\n+\t\tstrbuf_add(&tmp, info->points_to, ptr_len);\n+\t\tstrbuf_add(&tmp, remainder, rem_len);\n+\t\treturn strbuf_detach(&tmp, NULL);\n+\t}\n+\n+\treturn NULL;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nindex a90b8f7925b..0d95bbb416f 100644\n--- a/compat/fsmonitor/fsm-path-utils-win32.c\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -126,3 +126,20 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * No-op for now.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\treturn 0;\n+}\n+\n+/*\n+ * No-op for now.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\treturn NULL;\n+}\ndiff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\nindex 2102a5c9ff5..e24838f9a86 100644\n--- a/fsmonitor--daemon.h\n+++ b/fsmonitor--daemon.h\n@@ -8,6 +8,7 @@\n #include \"run-command.h\"\n #include \"simple-ipc.h\"\n #include \"thread-utils.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsmonitor_batch;\n struct fsmonitor_token_data;\n@@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct strbuf path_worktree_watch;\n \tstruct strbuf path_gitdir_watch;\n+\tstruct alias_info alias;\n \tint nr_paths_watching;\n \n \tstruct fsmonitor_token_data *current_token_data;\n@@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct ipc_server_data *ipc_server_data;\n \tstruct strbuf path_ipc;\n+\n };\n \n /*\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nindex e48592887e7..50ef37e57bb 100644\n--- a/fsmonitor-path-utils.h\n+++ b/fsmonitor-path-utils.h\n@@ -1,6 +1,14 @@\n #ifndef FSM_PATH_UTILS_H\n #define FSM_PATH_UTILS_H\n \n+#include \"strbuf.h\"\n+\n+struct alias_info\n+{\n+\tchar *alias;\n+\tchar *points_to;\n+};\n+\n struct fs_info {\n \tint is_remote;\n \tchar *typename;\n@@ -20,4 +28,32 @@ int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n  */\n int fsmonitor__is_fs_remote(const char *path);\n \n+/*\n+ * Get the alias in given path, if any.\n+ *\n+ * Sets alias to the first alias that matches any part of the path.\n+ *\n+ * If an alias is found, info.alias and info.points_to are set to the\n+ * found mapping.\n+ *\n+ * Returns -1 on error, 0 otherwise.\n+ *\n+ * The caller owns the storage that is occupied by set info.alias and\n+ * info.points_to and is responsible for releasing it with `free(3)`\n+ * when done.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info);\n+\n+/*\n+ * Resolve the path against the given alias.\n+ *\n+ * Returns the resolved path if there is one, NULL otherwise.\n+ *\n+ * The caller owns the storage that the returned string occupies and\n+ * is responsible for releasing it with `free(3)` when done.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info);\n+\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"463338","messageId":"d7c25bf96c6e952e1dd6f886b58cdaa5d9a6422e.1663705986.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v10.git.1663705986.gitgitgadget@gmail.com","subject":"[PATCH v10 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-20T20:33:06Z","receivedAt":"2022-09-20T20:33:45Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nAdd documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\nCall-out experimental nature of 'fsmonitor.allowRemote' and limited file\nsystem support for 'fsmonitor.socketDir'.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Documentation/git-fsmonitor--daemon.txt | 48 +++++++++++++++++++++++--\n 1 file changed, 45 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt\nindex cc142fb8612..6ad3e518ae0 100644\n--- a/Documentation/git-fsmonitor--daemon.txt\n+++ b/Documentation/git-fsmonitor--daemon.txt\n@@ -3,7 +3,7 @@ git-fsmonitor{litdd}daemon(1)\n \n NAME\n ----\n-git-fsmonitor--daemon - A Built-in File System Monitor\n+git-fsmonitor--daemon - A Built-in Filesystem Monitor\n \n SYNOPSIS\n --------\n@@ -17,7 +17,7 @@ DESCRIPTION\n -----------\n \n A daemon to watch the working directory for file and directory\n-changes using platform-specific file system notification facilities.\n+changes using platform-specific filesystem notification facilities.\n \n This daemon communicates directly with commands like `git status`\n using the link:technical/api-simple-ipc.html[simple IPC] interface\n@@ -63,13 +63,55 @@ CAVEATS\n -------\n \n The fsmonitor daemon does not currently know about submodules and does\n-not know to filter out file system events that happen within a\n+not know to filter out filesystem events that happen within a\n submodule.  If fsmonitor daemon is watching a super repo and a file is\n modified within the working directory of a submodule, it will report\n the change (as happening against the super repo).  However, the client\n will properly ignore these extra events, so performance may be affected\n but it will not cause an incorrect result.\n \n+By default, the fsmonitor daemon refuses to work against network-mounted\n+repositories; this may be overridden by setting `fsmonitor.allowRemote` to\n+`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n+correctly with all network-mounted repositories and such use is considered\n+experimental.\n+\n+On Mac OS, the inter-process communication (IPC) between various Git\n+commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a\n+special type of file -- which is supported by native Mac OS filesystems,\n+but not on network-mounted filesystems, NTFS, or FAT32.  Other filesystems\n+may or may not have the needed support; the fsmonitor daemon is not guaranteed\n+to work with these filesystems and such use is considered experimental.\n+\n+By default, the socket is created in the `.git` directory, however, if the\n+`.git` directory is on a network-mounted filesystem, it will be instead be\n+created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a\n+network-mounted filesystem in which case you must set the configuration\n+variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native\n+filesystem in which to create the socket file.\n+\n+If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)\n+is on a native Mac OS file filesystem the fsmonitor daemon will report an\n+error that will cause the daemon and the currently running command to exit.\n+\n+CONFIGURATION\n+-------------\n+\n+When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])\n+the fsmonitor daemon will pay attention to the following configuration\n+variables:\n+\n+`fsmonitor.allowRemote`::\n+\tBy default, the daemon refuses to work against network-mounted\n+\trepositories. Setting `fsmonitor.allowRemote` to `true` overrides\n+\tthis behavior.\n+\n+`fsmonitor.socketDir`::\n+    This Mac OS-specific option, if set, specifies the directory in\n+    which to create the Unix domain socket used for communication\n+    between fsmonitor and various Git commands. The directory must\n+    reside on a native Mac OS filesystem as discussed above.\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \ngitgitgadget\n"},{"id":"463361","messageId":"28d36af9-f493-f4a5-4aaf-18758e1c46db@jeffhostetler.com","threadId":"58324","inReplyTo":"8f6c1fbacbfd3df67d1a7591788d98f1af0e26f4.1663705986.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v10 5/6] fsmonitor: check for compatability before communicating with fsmonitor","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2022-09-21T11:22:16Z","receivedAt":"2022-09-21T11:22:43Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 9/20/22 4:33 PM, Eric DeCosta via GitGitGadget wrote:\n> From: Eric DeCosta <edecosta@mathworks.com>\n> \n> If fsmonitor is not in a compatible state, die with an appropriate error\n> messge.\n[...]\n> diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\n> index 531a1b6f956..24480b9806d 100644\n> --- a/fsmonitor-settings.c\n> +++ b/fsmonitor-settings.c\n[...]\n> +char *fsm_settings__get_incompatible_msg(struct repository *r,\n>   \t\t\t\t\t enum fsmonitor_reason reason)\n>   {\n>   \tstruct strbuf msg = STRBUF_INIT;\n> +\tconst char *socket_dir;\n>   \n>   \tswitch (reason) {\n>   \tcase FSMONITOR_REASON_UNTESTED:\n> @@ -281,9 +283,11 @@ char *fsm_settings__get_incompatible_msg(const struct repository *r,\n>   \t\tgoto done;\n>   \n>   \tcase FSMONITOR_REASON_NOSOCKETS:\n> +\t\tsocket_dir = dirname((char *)fsmonitor_ipc__get_path(r));\n>   \t\tstrbuf_addf(&msg,\n> -\t\t\t    _(\"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets\"),\n> -\t\t\t    r->worktree);\n> +\t\t\t    _(\"socket directory '%s' is incompatible with fsmonitor due\"),\n> +\t\t\t    socket_dir);\n> +\t\tstrbuf_add(&msg, _(\" to lack of Unix sockets support\"), 32);\n>   \t\tgoto done;\n\nI don't think we should split the error message between two\ncalls to strbuf_add().  I realize that this was probably done\nbecause of line length concerns.  But this makes assumptions\non language word order during translations.\n\nInstead, we can use C string literal joining before passing\nit to the translation macro.  Something like:\n\n\tstrbuf_addf(&msg,\n\t\t_(\"socket directory '%s' is incompatible with \"\n\t\t  \"fsmonitor due to lack of Unix sockets support\"),\n\t\tsocket_dir);\n\n[...]\n> diff --git a/fsmonitor.c b/fsmonitor.c\n> index 57d6a483bee..43d580132fb 100644\n> --- a/fsmonitor.c\n> +++ b/fsmonitor.c\n> @@ -305,6 +305,10 @@ void refresh_fsmonitor(struct index_state *istate)\n>   \tint is_trivial = 0;\n>   \tstruct repository *r = istate->repo ? istate->repo : the_repository;\n>   \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);\n> +\tenum fsmonitor_reason reason = fsm_settings__get_reason(r);\n> +\n> +\tif (reason > FSMONITOR_REASON_OK)\n> +\t\tdie(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n\nWe don't want to call die() here.  Maybe just silently return\nwithout doing anything or issue a warning() and return.  (But\nI'm favoring a silent return here.)\n\n From the clients' (`git status`, `git diff`, etc.) point of view,\nthey just want a speed-up, if possible, but we shouldn't kill them;\nwe should just let them do the normal scan that would have done\nif the feature were turned off.\n\nJeff\n"},{"id":"463367","messageId":"BL0PR05MB557160F62C59B02E116BC219D94F9@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"28d36af9-f493-f4a5-4aaf-18758e1c46db@jeffhostetler.com","subject":"RE: [PATCH v10 5/6] fsmonitor: check for compatability before communicating with fsmonitor","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-21T13:03:18Z","receivedAt":"2022-09-21T13:03:43Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Jeff Hostetler <git@jeffhostetler.com>\n> Sent: Wednesday, September 21, 2022 7:22 AM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>;\n> git@vger.kernel.org\n> Cc: Eric Sunshine <sunshine@sunshineco.com>; Torsten Bögershausen\n> <tboegi@web.de>; Ævar Arnfjörð Bjarmason <avarab@gmail.com>; Ramsay\n> Jones <ramsay@ramsayjones.plus.com>; Johannes Schindelin\n> <Johannes.Schindelin@gmx.de>; Eric DeCosta <edecosta@mathworks.com>\n> Subject: Re: [PATCH v10 5/6] fsmonitor: check for compatability before\n> communicating with fsmonitor\n> \n> \n> \n> On 9/20/22 4:33 PM, Eric DeCosta via GitGitGadget wrote:\n> > From: Eric DeCosta <edecosta@mathworks.com>\n> >\n> > If fsmonitor is not in a compatible state, die with an appropriate\n> > error messge.\n> [...]\n> > diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c index\n> > 531a1b6f956..24480b9806d 100644\n> > --- a/fsmonitor-settings.c\n> > +++ b/fsmonitor-settings.c\n> [...]\n> > +char *fsm_settings__get_incompatible_msg(struct repository *r,\n> >   \t\t\t\t\t enum fsmonitor_reason reason)\n> >   {\n> >   \tstruct strbuf msg = STRBUF_INIT;\n> > +\tconst char *socket_dir;\n> >\n> >   \tswitch (reason) {\n> >   \tcase FSMONITOR_REASON_UNTESTED:\n> > @@ -281,9 +283,11 @@ char *fsm_settings__get_incompatible_msg(const\n> struct repository *r,\n> >   \t\tgoto done;\n> >\n> >   \tcase FSMONITOR_REASON_NOSOCKETS:\n> > +\t\tsocket_dir = dirname((char *)fsmonitor_ipc__get_path(r));\n> >   \t\tstrbuf_addf(&msg,\n> > -\t\t\t    _(\"repository '%s' is incompatible with fsmonitor\n> due to lack of Unix sockets\"),\n> > -\t\t\t    r->worktree);\n> > +\t\t\t    _(\"socket directory '%s' is incompatible with\n> fsmonitor due\"),\n> > +\t\t\t    socket_dir);\n> > +\t\tstrbuf_add(&msg, _(\" to lack of Unix sockets support\"), 32);\n> >   \t\tgoto done;\n> \n> I don't think we should split the error message between two calls to\n> strbuf_add().  I realize that this was probably done because of line length\n> concerns.  But this makes assumptions on language word order during\n> translations.\n> \n> Instead, we can use C string literal joining before passing it to the translation\n> macro.  Something like:\n> \n> \tstrbuf_addf(&msg,\n> \t\t_(\"socket directory '%s' is incompatible with \"\n> \t\t  \"fsmonitor due to lack of Unix sockets support\"),\n> \t\tsocket_dir);\n> \n> [...]\n> > diff --git a/fsmonitor.c b/fsmonitor.c index 57d6a483bee..43d580132fb\n> > 100644f\n> > --- a/fsmonitor.c\n> > +++ b/fsmonitor.c\n> > @@ -305,6 +305,10 @@ void refresh_fsmonitor(struct index_state *istate)\n> >   \tint is_trivial = 0;\n> >   \tstruct repository *r = istate->repo ? istate->repo : the_repository;\n> >   \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);\n> > +\tenum fsmonitor_reason reason = fsm_settings__get_reason(r);\n> > +\n> > +\tif (reason > FSMONITOR_REASON_OK)\n> > +\t\tdie(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n> \n> We don't want to call die() here.  Maybe just silently return without doing\n> anything or issue a warning() and return.  (But I'm favoring a silent return\n> here.)\n> \n>  From the clients' (`git status`, `git diff`, etc.) point of view, they just want a\n> speed-up, if possible, but we shouldn't kill them; we should just let them do\n> the normal scan that would have done if the feature were turned off.\n> \n> Jeff\n\nIf we just silently return then fsmonitor is in a perpetual incompatible state and the user gets no benefit from fsmonitor (in fact it could be worse as fsmonitor will attempt to spawn over and over again). I would think that it would be better to at least inform the user so that they can update fsmonitor's settings and have a more pleasant experience going forward.\n\n-Eric\n"},{"id":"463394","messageId":"pull.1326.v11.git.1663798722.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v10.git.1663705986.gitgitgadget@gmail.com","subject":"[PATCH v11 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-21T22:18:36Z","receivedAt":"2022-09-21T22:18:50Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"Follow-on to the work done to allow Windows to work against network-mounted\nrepos for macOS.\n\nHave macOS take advantage of the same configuration option,\n'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\nto true will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for IPC\ncannot be created in a network location; instead $HOME is used if the\ndefault location is on the network. The user may, optionally, set the\n'fsmonitor.socketDir' configuration option to a valid, local directory if\n$HOME itself is on the network or is simply not the desired location for the\nUDS file.\n\nAn additional issue is that for mount points in the root directory, FSEvents\ndoes not report a path that matches the worktree directory due to the\nintroduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\nto the worktree directory by interrogating the root filesystem for synthetic\nfirmlinks and using that information to translate the path.\n\nv11 differs from v10:\n\n * incorporates code review feedback\n * fix memory leak in fsm-listen-darwin.c\n\nv10 differs from v9:\n\n * incorporates code review feedback\n * improves error messaging for incompatible socket directory\n\nv9 differs from v8:\n\n * incorporates code review feedback\n * check for incompatibility before communicating with fsmonitor\n\nv8 differs from v7:\n\n * incorporates code review feedback\n * gets the rebase right\n\nv7 differs from v6:\n\n * incorporates code review feedback\n\nv6 differs from v5:\n\n * incorporates earlier, Windows-specific changes that have not made it back\n   yet to the master branch\n * incorporates code review feedback\n * adds documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n\nv5 differs significantly from earlier versions:\n\n * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n   such that these options are no longer added to the settings data\n   structure but are rather read from config at point of use\n * refactoring of code for handling platform-specific file system checks via\n   a common interface to avoid platform #ifdef in IPC code and be in-model\n   with other platform-specific fsmonitor code\n * dealing with 'synthetic firmlinks' on macOS\n\nEric DeCosta (6):\n  fsmonitor: refactor filesystem checks to common interface\n  fsmonitor: relocate socket file if .git directory is remote\n  fsmonitor: avoid socket location check if using hook\n  fsmonitor: deal with synthetic firmlinks on macOS\n  fsmonitor: check for compatability before communicating with fsmonitor\n  fsmonitor: add documentation for allowRemote and socketDir options\n\n Documentation/git-fsmonitor--daemon.txt  |  48 ++++++-\n Makefile                                 |   2 +\n builtin/fsmonitor--daemon.c              |  11 +-\n compat/fsmonitor/fsm-ipc-darwin.c        |  51 +++++++\n compat/fsmonitor/fsm-ipc-win32.c         |   9 ++\n compat/fsmonitor/fsm-listen-darwin.c     |  14 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 132 +++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 145 +++++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  72 +++-------\n compat/fsmonitor/fsm-settings-win32.c    | 174 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   4 +\n fsmonitor--daemon.h                      |   3 +\n fsmonitor-ipc.c                          |  18 ++-\n fsmonitor-ipc.h                          |   4 +-\n fsmonitor-path-utils.h                   |  59 ++++++++\n fsmonitor-settings.c                     |  68 ++++++++-\n fsmonitor-settings.h                     |   4 +-\n fsmonitor.c                              |   4 +\n 18 files changed, 576 insertions(+), 246 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\n\nbase-commit: d3fa443f97e3a8d75b51341e2d5bac380b7422df\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v11\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v11\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v10:\n\n 1:  155a6890806 = 1:  155a6890806 fsmonitor: refactor filesystem checks to common interface\n 2:  dbe113abb87 = 2:  dbe113abb87 fsmonitor: relocate socket file if .git directory is remote\n 3:  86c15299ae8 = 3:  86c15299ae8 fsmonitor: avoid socket location check if using hook\n 4:  4f6c98cf834 ! 4:  1a516fd9214 fsmonitor: deal with synthetic firmlinks on macOS\n     @@ compat/fsmonitor/fsm-listen-darwin.c\n       \n       struct fsm_listen_data\n       {\n     +@@ compat/fsmonitor/fsm-listen-darwin.c: static void fsevent_callback(ConstFSEventStreamRef streamRef,\n     + \tstruct string_list cookie_list = STRING_LIST_INIT_DUP;\n     + \tconst char *path_k;\n     + \tconst char *slash;\n     +-\tint k;\n     ++\tchar *resolved = NULL;\n     + \tstruct strbuf tmp = STRBUF_INIT;\n     ++\tint k;\n     + \n     + \t/*\n     + \t * Build a list of all filesystem changes into a private/local\n      @@ compat/fsmonitor/fsm-listen-darwin.c: static void fsevent_callback(ConstFSEventStreamRef streamRef,\n       \t\t/*\n       \t\t * On Mac, we receive an array of absolute paths.\n       \t\t */\n      -\t\tpath_k = paths[k];\n     -+\t\tpath_k = fsmonitor__resolve_alias(paths[k], &state->alias);\n     -+\t\tif (!path_k)\n     ++\t\tfree(resolved);\n     ++\t\tresolved = fsmonitor__resolve_alias(paths[k], &state->alias);\n     ++\t\tif (resolved)\n     ++\t\t\tpath_k = resolved;\n     ++\t\telse\n      +\t\t\tpath_k = paths[k];\n       \n       \t\t/*\n     @@ compat/fsmonitor/fsm-listen-darwin.c: static void fsevent_callback(ConstFSEventS\n       \n       \t\t\t/*\n       \t\t\t * We assume that any events that we received\n     +@@ compat/fsmonitor/fsm-listen-darwin.c: static void fsevent_callback(ConstFSEventStreamRef streamRef,\n     + \t\t}\n     + \t}\n     + \n     ++\tfree(resolved);\n     + \tfsmonitor_publish(state, batch, &cookie_list);\n     + \tstring_list_clear(&cookie_list, 0);\n     + \tstrbuf_release(&tmp);\n     + \treturn;\n     + \n     + force_shutdown:\n     ++\tfree(resolved);\n     + \tfsmonitor_batch__free_list(batch);\n     + \tstring_list_clear(&cookie_list, 0);\n     + \n      \n       ## compat/fsmonitor/fsm-path-utils-darwin.c ##\n      @@\n 5:  8f6c1fbacbf ! 5:  e0fe05dabef fsmonitor: check for compatability before communicating with fsmonitor\n     @@ fsmonitor-settings.c: char *fsm_settings__get_incompatible_msg(const struct repo\n       \t\tstrbuf_addf(&msg,\n      -\t\t\t    _(\"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets\"),\n      -\t\t\t    r->worktree);\n     -+\t\t\t    _(\"socket directory '%s' is incompatible with fsmonitor due\"),\n     ++\t\t\t    _(\"socket directory '%s' is incompatible with fsmonitor due\"\n     ++\t\t\t\t  \" to lack of Unix sockets support\"),\n      +\t\t\t    socket_dir);\n     -+\t\tstrbuf_add(&msg, _(\" to lack of Unix sockets support\"), 32);\n       \t\tgoto done;\n       \t}\n       \n     @@ fsmonitor.c: void refresh_fsmonitor(struct index_state *istate)\n      +\tenum fsmonitor_reason reason = fsm_settings__get_reason(r);\n      +\n      +\tif (reason > FSMONITOR_REASON_OK)\n     -+\t\tdie(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n     ++\t\twarning(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n       \n       \tif (fsm_mode <= FSMONITOR_MODE_DISABLED ||\n       \t    istate->fsmonitor_has_run_once)\n 6:  d7c25bf96c6 = 6:  3200b505988 fsmonitor: add documentation for allowRemote and socketDir options\n\n-- \ngitgitgadget\n"},{"id":"463396","messageId":"155a689080658e1953fc4434016f746f371ba08a.1663798722.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v11.git.1663798722.gitgitgadget@gmail.com","subject":"[PATCH v11 1/6] fsmonitor: refactor filesystem checks to common interface","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-21T22:18:37Z","receivedAt":"2022-09-21T22:18:52Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nProvide a common interface for getting basic filesystem information\nincluding filesystem type and whether the filesystem is remote.\n\nRefactor existing code for getting basic filesystem info and detecting\nremote file systems to the new interface.\n\nRefactor filesystem checks to leverage new interface. For macOS,\nerror-out if the Unix Domain socket (UDS) file is on a remote\nfilesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                                 |   1 +\n compat/fsmonitor/fsm-path-utils-darwin.c |  40 ++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 128 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  62 +++-----\n compat/fsmonitor/fsm-settings-win32.c    | 172 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   2 +\n fsmonitor-path-utils.h                   |  23 +++\n fsmonitor-settings.c                     |  50 +++++++\n 8 files changed, 263 insertions(+), 215 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\ndiff --git a/Makefile b/Makefile\nindex d9247ead45b..6e492a67547 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2039,6 +2039,7 @@ endif\n ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n ifeq ($(TCLTK_PATH),)\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nnew file mode 100644\nindex 00000000000..067cbe6990a\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -0,0 +1,40 @@\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+#include <sys/param.h>\n+#include <sys/mount.h>\n+\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\tstruct statfs fs;\n+\tif (statfs(path, &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\treturn -1;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\n+\tif (!(fs.f_flags & MNT_LOCAL))\n+\t\tfs_info->is_remote = 1;\n+\telse\n+\t\tfs_info->is_remote = 0;\n+\n+\tfs_info->typename = fs.f_fstypename;\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nnew file mode 100644\nindex 00000000000..a90b8f7925b\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -0,0 +1,128 @@\n+#include \"cache.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+/*\n+ * Check remote working directory protocol.\n+ *\n+ * Return -1 if client machine cannot get remote protocol information.\n+ */\n+static int check_remote_protocol(wchar_t *wpath)\n+{\n+\tHANDLE h;\n+\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n+\n+\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n+\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\n+\tif (h == INVALID_HANDLE_VALUE) {\n+\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n+\t\t&proto_info, sizeof(proto_info))) {\n+\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\tCloseHandle(h);\n+\t\treturn -1;\n+\t}\n+\n+\tCloseHandle(h);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n+\t\t\t\twpath, proto_info.Protocol);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Notes for testing:\n+ *\n+ * (a) Windows allows a network share to be mapped to a drive letter.\n+ *     (This is the normal method to access it.)\n+ *\n+ *     $ NET USE Z: \\\\server\\share\n+ *     $ git -C Z:/repo status\n+ *\n+ * (b) Windows allows a network share to be referenced WITHOUT mapping\n+ *     it to drive letter.\n+ *\n+ *     $ NET USE \\\\server\\share\\dir\n+ *     $ git -C //server/share/repo status\n+ *\n+ * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n+ *     arbitrary path (which may be remote)\n+ *\n+ *     $ SUBST Q: Z:\\repo\n+ *     $ git -C Q:/ status\n+ *\n+ * (d) Windows allows a directory symlink to be created on a local\n+ *     file system that points to a remote repo.\n+ *\n+ *     $ mklink /d ./link //server/share/repo\n+ *     $ git -C ./link status\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\twchar_t wpath[MAX_PATH];\n+\twchar_t wfullpath[MAX_PATH];\n+\tsize_t wlen;\n+\tUINT driveType;\n+\n+\t/*\n+\t * Do everything in wide chars because the drive letter might be\n+\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n+\t */\n+\tif (xutftowcs_path(wpath, path) < 0) {\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * GetDriveTypeW() requires a final slash.  We assume that the\n+\t * worktree pathname points to an actual directory.\n+\t */\n+\twlen = wcslen(wpath);\n+\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n+\t\twpath[wlen++] = L'\\\\';\n+\t\twpath[wlen] = 0;\n+\t}\n+\n+\t/*\n+\t * Normalize the path.  If nothing else, this converts forward\n+\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n+\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n+\t */\n+\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {\n+\t\treturn -1;\n+\t}\n+\n+\tdriveType = GetDriveTypeW(wfullpath);\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n+\t\t\t path, wfullpath, driveType);\n+\n+\tif (driveType == DRIVE_REMOTE) {\n+\t\tfs_info->is_remote = 1;\n+\t\tif (check_remote_protocol(wfullpath) < 0)\n+\t\t\treturn -1;\n+\t} else {\n+\t\tfs_info->is_remote = 0;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dba3ced6bb7 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -1,32 +1,10 @@\n-#include \"cache.h\"\n #include \"config.h\"\n-#include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n-#include <sys/param.h>\n-#include <sys/mount.h>\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n-/*\n- * [1] Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type (NFS, SAMBA, etc.) dictates whether notification events\n- * are available at all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n+ /*\n  * For the builtin FSMonitor, we create the Unix domain socket for the\n  * IPC in the .git directory.  If the working directory is remote,\n  * then the socket will be created on the remote file system.  This\n@@ -38,40 +16,34 @@\n  * be taken to ensure that $HOME is actually local and not a managed\n  * file share.)\n  *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n+ * FAT32 and NTFS working directories are problematic too.\n  *\n  * The builtin FSMonitor uses a Unix domain socket in the .git\n  * directory for IPC.  These Windows drive formats do not support\n  * Unix domain sockets, so mark them as incompatible for the daemon.\n  *\n  */\n-static enum fsmonitor_reason check_volume(struct repository *r)\n+static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n-\tstruct statfs fs;\n+\tstruct fs_info fs;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \n-\tif (statfs(r->worktree, &fs) == -1) {\n-\t\tint saved_errno = errno;\n-\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n-\t\t\t\t r->worktree, strerror(saved_errno));\n-\t\terrno = saved_errno;\n+\tif (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {\n+\t\tstrbuf_release(&path);\n \t\treturn FSMONITOR_REASON_ERROR;\n \t}\n \n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n-\t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n \n-\tif (!(fs.f_flags & MNT_LOCAL))\n+\tif (fs.is_remote)\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n-\tif (!strcmp(fs.f_fstypename, \"msdos\")) /* aka FAT32 */\n+\tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n-\tif (!strcmp(fs.f_fstypename, \"ntfs\"))\n+\tif (!strcmp(fs.typename, \"ntfs\"))\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \treturn FSMONITOR_REASON_OK;\n@@ -81,7 +53,7 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_volume(r);\n+\treason = check_uds_volume(r);\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex e5ec5b0a9f7..d88b06ae610 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -1,8 +1,9 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * VFS for Git is incompatible with FSMonitor.\n@@ -24,171 +25,6 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-/*\n- * Check if monitoring remote working directories is allowed.\n- *\n- * By default, monitoring remote working directories is\n- * disabled.  Users may override this behavior in enviroments where\n- * they have proper support.\n- */\n-static int check_config_allowremote(struct repository *r)\n-{\n-\tint allow;\n-\n-\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n-\t\treturn allow;\n-\n-\treturn -1; /* fsmonitor.allowremote not set */\n-}\n-\n-/*\n- * Check remote working directory protocol.\n- *\n- * Error if client machine cannot get remote protocol information.\n- */\n-static int check_remote_protocol(wchar_t *wpath)\n-{\n-\tHANDLE h;\n-\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n-\n-\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n-\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n-\n-\tif (h == INVALID_HANDLE_VALUE) {\n-\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\treturn -1;\n-\t}\n-\n-\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n-\t\t&proto_info, sizeof(proto_info))) {\n-\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\tCloseHandle(h);\n-\t\treturn -1;\n-\t}\n-\n-\tCloseHandle(h);\n-\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n-\t\t\t\twpath, proto_info.Protocol);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type dictates whether notification events are available at\n- * all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- * Notes for testing:\n- *\n- * (a) Windows allows a network share to be mapped to a drive letter.\n- *     (This is the normal method to access it.)\n- *\n- *     $ NET USE Z: \\\\server\\share\n- *     $ git -C Z:/repo status\n- *\n- * (b) Windows allows a network share to be referenced WITHOUT mapping\n- *     it to drive letter.\n- *\n- *     $ NET USE \\\\server\\share\\dir\n- *     $ git -C //server/share/repo status\n- *\n- * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n- *     arbitrary path (which may be remote)\n- *\n- *     $ SUBST Q: Z:\\repo\n- *     $ git -C Q:/ status\n- *\n- * (d) Windows allows a directory symlink to be created on a local\n- *     file system that points to a remote repo.\n- *\n- *     $ mklink /d ./link //server/share/repo\n- *     $ git -C ./link status\n- */\n-static enum fsmonitor_reason check_remote(struct repository *r)\n-{\n-\tint ret;\n-\twchar_t wpath[MAX_PATH];\n-\twchar_t wfullpath[MAX_PATH];\n-\tsize_t wlen;\n-\tUINT driveType;\n-\n-\t/*\n-\t * Do everything in wide chars because the drive letter might be\n-\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n-\t */\n-\tif (xutftowcs_path(wpath, r->worktree) < 0)\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t/*\n-\t * GetDriveTypeW() requires a final slash.  We assume that the\n-\t * worktree pathname points to an actual directory.\n-\t */\n-\twlen = wcslen(wpath);\n-\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n-\t\twpath[wlen++] = L'\\\\';\n-\t\twpath[wlen] = 0;\n-\t}\n-\n-\t/*\n-\t * Normalize the path.  If nothing else, this converts forward\n-\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n-\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n-\t */\n-\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\tdriveType = GetDriveTypeW(wfullpath);\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n-\t\t\t r->worktree, wfullpath, driveType);\n-\n-\tif (driveType == DRIVE_REMOTE) {\n-\t\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t \"check_remote('%s') true\",\n-\t\t\t\t r->worktree);\n-\n-\t\tret = check_remote_protocol(wfullpath);\n-\t\tif (ret < 0)\n-\t\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t\tswitch (check_config_allowremote(r)) {\n-\t\tcase 0: /* config overrides and disables */\n-\t\t\treturn FSMONITOR_REASON_REMOTE;\n-\t\tcase 1: /* config overrides and enables */\n-\t\t\treturn FSMONITOR_REASON_OK;\n-\t\tdefault:\n-\t\t\tbreak; /* config has no opinion */\n-\t\t}\n-\n-\t\treturn FSMONITOR_REASON_REMOTE;\n-\t}\n-\n-\treturn FSMONITOR_REASON_OK;\n-}\n-\n enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n@@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n-\treason = check_remote(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n-\n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 2237109b57f..b88494bf59b 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)\n@@ -315,6 +316,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nnew file mode 100644\nindex 00000000000..e48592887e7\n--- /dev/null\n+++ b/fsmonitor-path-utils.h\n@@ -0,0 +1,23 @@\n+#ifndef FSM_PATH_UTILS_H\n+#define FSM_PATH_UTILS_H\n+\n+struct fs_info {\n+\tint is_remote;\n+\tchar *typename;\n+};\n+\n+/*\n+ * Get some basic filesystem informtion for the given path\n+ *\n+ * Returns -1 on error, zero otherwise.\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n+\n+/*\n+ * Determines if the filesystem that path resides on is remote.\n+ *\n+ * Returns -1 on error, 0 if not remote, 1 if remote.\n+ */\n+int fsmonitor__is_fs_remote(const char *path);\n+\n+#endif\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..d288cbad479 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * We keep this structure defintion private and have getters\n@@ -13,6 +14,52 @@ struct fsmonitor_settings {\n \tchar *hook_path;\n };\n \n+/*\n+ * Remote working directories are problematic for FSMonitor.\n+ *\n+ * The underlying file system on the server machine and/or the remote\n+ * mount type dictates whether notification events are available at\n+ * all to remote client machines.\n+ *\n+ * Kernel differences between the server and client machines also\n+ * dictate the how (buffering, frequency, de-dup) the events are\n+ * delivered to client machine processes.\n+ *\n+ * A client machine (such as a laptop) may choose to suspend/resume\n+ * and it is unclear (without lots of testing) whether the watcher can\n+ * resync after a resume.  We might be able to treat this as a normal\n+ * \"events were dropped by the kernel\" event and do our normal \"flush\n+ * and resync\" --or-- we might need to close the existing (zombie?)\n+ * notification fd and create a new one.\n+ *\n+ * In theory, the above issues need to be addressed whether we are\n+ * using the Hook or IPC API.\n+ *\n+ * So (for now at least), mark remote working directories as\n+ * incompatible unless 'fsmonitor.allowRemote' is true.\n+ *\n+ */\n+#ifdef HAVE_FSMONITOR_OS_SETTINGS\n+static enum fsmonitor_reason check_remote(struct repository *r)\n+{\n+\tint allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */\n+\tint is_remote = fsmonitor__is_fs_remote(r->worktree);\n+\n+\tswitch (is_remote) {\n+\t\tcase 0:\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tcase 1:\n+\t\t\trepo_config_get_bool(r, \"fsmonitor.allowremote\", &allow_remote);\n+\t\t\tif (allow_remote < 1)\n+\t\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\t\telse\n+\t\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+}\n+#endif\n+\n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n {\n \tif (!r->worktree) {\n@@ -27,6 +74,9 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t{\n \t\tenum fsmonitor_reason reason;\n \n+\t\treason = check_remote(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n \t\treason = fsm_os__incompatible(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-- \ngitgitgadget\n\n"},{"id":"463395","messageId":"dbe113abb875bae568d75c23f0eadd363c3eb905.1663798722.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v11.git.1663798722.gitgitgadget@gmail.com","subject":"[PATCH v11 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-21T22:18:38Z","receivedAt":"2022-09-21T22:18:55Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf the .git directory is on a remote file system, create the socket\nfile in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                               |  1 +\n builtin/fsmonitor--daemon.c            |  3 +-\n compat/fsmonitor/fsm-ipc-darwin.c      | 51 ++++++++++++++++++++++++++\n compat/fsmonitor/fsm-ipc-win32.c       |  9 +++++\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n contrib/buildsystems/CMakeLists.txt    |  2 +\n fsmonitor-ipc.c                        | 18 ++++-----\n fsmonitor-ipc.h                        |  4 +-\n 8 files changed, 77 insertions(+), 13 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n\ndiff --git a/Makefile b/Makefile\nindex 6e492a67547..58bb9248471 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2034,6 +2034,7 @@ ifdef FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n endif\n \n ifdef FSMONITOR_OS_SETTINGS\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 2c109cf8b37..0123fc33ed2 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -1343,7 +1343,8 @@ static int fsmonitor_run_daemon(void)\n \t * directory.)\n \t */\n \tstrbuf_init(&state.path_ipc, 0);\n-\tstrbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));\n+\tstrbuf_addstr(&state.path_ipc,\n+\t\tabsolute_path(fsmonitor_ipc__get_path(the_repository)));\n \n \t/*\n \t * Confirm that we can create platform-specific resources for the\ndiff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\nnew file mode 100644\nindex 00000000000..0f551e5e572\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-darwin.c\n@@ -0,0 +1,51 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"strbuf.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n+{\n+\tstatic const char *ipc_path;\n+\tSHA_CTX sha1ctx;\n+\tchar *sock_dir = NULL;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (!r)\n+\t\tBUG(\"No repository passed into fsmonitor_ipc__get_path\");\n+\n+\tif (ipc_path)\n+\t\treturn ipc_path;\n+\n+\tipc_path = fsmonitor_ipc__get_default_path();\n+\n+\t/* By default the socket file is created in the .git directory */\n+\tif (fsmonitor__is_fs_remote(ipc_path) < 1)\n+\t\treturn ipc_path;\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\trepo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n+\n+\t/* Create the socket file in either socketDir or $HOME */\n+\tif (sock_dir && *sock_dir) {\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\t} else {\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\t}\n+\tfree(sock_dir);\n+\n+\tipc_path = interpolate_path(ipc_file.buf, 1);\n+\tif (!ipc_path)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\n+\tstrbuf_release(&ipc_file);\n+\treturn ipc_path;\n+}\ndiff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\nnew file mode 100644\nindex 00000000000..e08c505c148\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-win32.c\n@@ -0,0 +1,9 @@\n+#include \"config.h\"\n+#include \"fsmonitor-ipc.h\"\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r) {\n+\tstatic char *ret;\n+\tif (!ret)\n+\t\tret = git_pathdup(\"fsmonitor--daemon.ipc\");\n+\treturn ret;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex dba3ced6bb7..681d8bf963e 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -26,7 +26,7 @@\n static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n \tstruct fs_info fs;\n-\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tconst char *ipc_path = fsmonitor_ipc__get_path(r);\n \tstruct strbuf path = STRBUF_INIT;\n \tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex b88494bf59b..7e7b6b9a362 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n@@ -316,6 +317,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..c0f42301c84 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -18,7 +18,7 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 0;\n }\n \n-const char *fsmonitor_ipc__get_path(void)\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n {\n \treturn NULL;\n }\n@@ -47,11 +47,9 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n-\n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\n-\treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n+\treturn ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));\n }\n \n static int spawn_daemon(void)\n@@ -81,8 +79,8 @@ int fsmonitor_ipc__send_query(const char *since_token,\n \ttrace2_data_string(\"fsm_client\", NULL, \"query/command\", tok);\n \n try_again:\n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \n \tswitch (state) {\n \tcase IPC_STATE__LISTENING:\n@@ -117,13 +115,13 @@ try_again:\n \n \tcase IPC_STATE__INVALID_PATH:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: invalid path '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \n \tcase IPC_STATE__OTHER_ERROR:\n \tdefault:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: unspecified error on '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \t}\n \n@@ -149,8 +147,8 @@ int fsmonitor_ipc__send_command(const char *command,\n \toptions.wait_if_busy = 1;\n \toptions.wait_if_not_found = 0;\n \n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \tif (state != IPC_STATE__LISTENING) {\n \t\tdie(_(\"fsmonitor--daemon is not running\"));\n \t\treturn -1;\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..8b489da762b 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -3,6 +3,8 @@\n \n #include \"simple-ipc.h\"\n \n+struct repository;\n+\n /*\n  * Returns true if built-in file system monitor daemon is defined\n  * for this platform.\n@@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);\n  *\n  * Returns NULL if the daemon is not supported on this platform.\n  */\n-const char *fsmonitor_ipc__get_path(void);\n+const char *fsmonitor_ipc__get_path(struct repository *r);\n \n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n-- \ngitgitgadget\n\n"},{"id":"463397","messageId":"86c15299ae8094d077ff97ea79d6e54451ee071d.1663798722.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v11.git.1663798722.gitgitgadget@gmail.com","subject":"[PATCH v11 3/6] fsmonitor: avoid socket location check if using hook","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-21T22:18:39Z","receivedAt":"2022-09-21T22:18:57Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf monitoring is done via fsmonitor hook rather than IPC there is no\nneed to check if the location of the Unix Domain socket (UDS) file is\non a remote filesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 10 ++++++----\n compat/fsmonitor/fsm-settings-win32.c  |  2 +-\n fsmonitor-settings.c                   |  8 ++++----\n fsmonitor-settings.h                   |  2 +-\n 4 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 681d8bf963e..40da2d3b533 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -49,13 +49,15 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_uds_volume(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n+\tif (ipc) {\n+\t\treason = check_uds_volume(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n+\t}\n \n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex d88b06ae610..a8af31b71de 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex d288cbad479..531a1b6f956 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -60,7 +60,7 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n }\n #endif\n \n-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n+static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n {\n \tif (!r->worktree) {\n \t\t/*\n@@ -77,7 +77,7 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t\treason = check_remote(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-\t\treason = fsm_os__incompatible(r);\n+\t\treason = fsm_os__incompatible(r, ipc);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n \t}\n@@ -162,7 +162,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -185,7 +185,7 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..0721617b95a 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -48,7 +48,7 @@ struct fsmonitor_settings;\n  * fsm_os__* routines should considered private to fsm_settings__\n  * routines.\n  */\n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n \n #endif /* FSMONITOR_SETTINGS_H */\n-- \ngitgitgadget\n\n"},{"id":"463398","messageId":"1a516fd9214492c95c6159695b989c724314bdb9.1663798722.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v11.git.1663798722.gitgitgadget@gmail.com","subject":"[PATCH v11 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-21T22:18:40Z","receivedAt":"2022-09-21T22:19:11Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n builtin/fsmonitor--daemon.c              |  8 +++\n compat/fsmonitor/fsm-listen-darwin.c     | 14 +++-\n compat/fsmonitor/fsm-path-utils-darwin.c | 92 ++++++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 17 +++++\n fsmonitor--daemon.h                      |  3 +\n fsmonitor-path-utils.h                   | 36 ++++++++++\n 6 files changed, 168 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 0123fc33ed2..56fcd1c2baa 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"fsmonitor.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n #include \"compat/fsmonitor/fsm-health.h\"\n #include \"compat/fsmonitor/fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n@@ -1282,6 +1283,13 @@ static int fsmonitor_run_daemon(void)\n \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n \tstate.nr_paths_watching = 1;\n \n+\tstate.alias.alias = NULL;\n+\tstate.alias.points_to = NULL;\n+\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n+\t\terr = error(_(\"could not get worktree alias\"));\n+\t\tgoto done;\n+\t}\n+\n \t/*\n \t * We create and delete cookie files somewhere inside the .git\n \t * directory to help us keep sync with the file system.  If\ndiff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\nindex 8e208e8289e..daeee4e465c 100644\n--- a/compat/fsmonitor/fsm-listen-darwin.c\n+++ b/compat/fsmonitor/fsm-listen-darwin.c\n@@ -26,6 +26,7 @@\n #include \"fsmonitor.h\"\n #include \"fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsm_listen_data\n {\n@@ -198,8 +199,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \tstruct string_list cookie_list = STRING_LIST_INIT_DUP;\n \tconst char *path_k;\n \tconst char *slash;\n-\tint k;\n+\tchar *resolved = NULL;\n \tstruct strbuf tmp = STRBUF_INIT;\n+\tint k;\n \n \t/*\n \t * Build a list of all filesystem changes into a private/local\n@@ -209,7 +211,12 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t/*\n \t\t * On Mac, we receive an array of absolute paths.\n \t\t */\n-\t\tpath_k = paths[k];\n+\t\tfree(resolved);\n+\t\tresolved = fsmonitor__resolve_alias(paths[k], &state->alias);\n+\t\tif (resolved)\n+\t\t\tpath_k = resolved;\n+\t\telse\n+\t\t\tpath_k = paths[k];\n \n \t\t/*\n \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n@@ -238,6 +245,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t\tfsmonitor_force_resync(state);\n \t\t\tfsmonitor_batch__free_list(batch);\n \t\t\tstring_list_clear(&cookie_list, 0);\n+\t\t\tbatch = NULL;\n \n \t\t\t/*\n \t\t\t * We assume that any events that we received\n@@ -360,12 +368,14 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t}\n \t}\n \n+\tfree(resolved);\n \tfsmonitor_publish(state, batch, &cookie_list);\n \tstring_list_clear(&cookie_list, 0);\n \tstrbuf_release(&tmp);\n \treturn;\n \n force_shutdown:\n+\tfree(resolved);\n \tfsmonitor_batch__free_list(batch);\n \tstring_list_clear(&cookie_list, 0);\n \ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nindex 067cbe6990a..13807f58e95 100644\n--- a/compat/fsmonitor/fsm-path-utils-darwin.c\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -1,5 +1,8 @@\n #include \"fsmonitor.h\"\n #include \"fsmonitor-path-utils.h\"\n+#include <dirent.h>\n+#include <errno.h>\n+#include <fcntl.h>\n #include <sys/param.h>\n #include <sys/mount.h>\n \n@@ -38,3 +41,92 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * Scan the root directory for synthetic firmlinks that when resolved\n+ * are a prefix of the path, stopping at the first one found.\n+ *\n+ * Some information about firmlinks and synthetic firmlinks:\n+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n+ *\n+ * macOS no longer allows symlinks in the root directory; any link found\n+ * there is therefore a synthetic firmlink.\n+ *\n+ * If this function gets called often, will want to cache all the firmlink\n+ * information, but for now there is only one caller of this function.\n+ *\n+ * If there is more than one alias for the path, that is another\n+ * matter altogether.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\tDIR * dir;\n+\tint read;\n+\tint retval;\n+\tstruct dirent *de;\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+\n+\tretval = 0;\n+\tdir = opendir(\"/\");\n+\tif (!dir)\n+\t\treturn -1;\n+\n+\tstrbuf_init(&alias, 256);\n+\tstrbuf_init(&points_to, MAXPATHLEN);\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstrbuf_reset(&alias);\n+\t\tstrbuf_addch(&alias, '/');\n+\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n+\n+\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n+\t\tif (read > 0) {\n+\t\t\tstrbuf_setlen(&points_to, read);\n+\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n+\t\t\t\t&& path[points_to.len] == '/') {\n+\t\t\t\tinfo->alias = strbuf_detach(&alias, NULL);\n+\t\t\t\tinfo->points_to = strbuf_detach(&points_to, NULL);\n+\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n+\t\t\t\t\tpath, info->alias, info->points_to);\n+\t\t\t\tretval = 0;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n+\t\t\ttrace_printf_key(&trace_fsmonitor, \"Error %s\", strerror(errno));\n+\t\t\tretval = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n+\tdone:\n+\tclosedir(dir);\n+\tstrbuf_release(&alias);\n+\tstrbuf_release(&points_to);\n+\treturn retval;\n+}\n+\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\tint len = info->alias ? strlen(info->alias) : 0;\n+\n+\tif (!len)\n+\t\treturn NULL;\n+\n+\tif ((strncmp(info->alias, path, len) == 0)\n+\t\t&& path[len] == '/') {\n+\t\tstruct strbuf tmp;\n+\t\tconst char *remainder = path + len;\n+\t\tint ptr_len = strlen(info->points_to);\n+\t\tint rem_len = strlen(remainder);\n+\n+\t\tstrbuf_init(&tmp, ptr_len + rem_len);\n+\t\tstrbuf_add(&tmp, info->points_to, ptr_len);\n+\t\tstrbuf_add(&tmp, remainder, rem_len);\n+\t\treturn strbuf_detach(&tmp, NULL);\n+\t}\n+\n+\treturn NULL;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nindex a90b8f7925b..0d95bbb416f 100644\n--- a/compat/fsmonitor/fsm-path-utils-win32.c\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -126,3 +126,20 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * No-op for now.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\treturn 0;\n+}\n+\n+/*\n+ * No-op for now.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\treturn NULL;\n+}\ndiff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\nindex 2102a5c9ff5..e24838f9a86 100644\n--- a/fsmonitor--daemon.h\n+++ b/fsmonitor--daemon.h\n@@ -8,6 +8,7 @@\n #include \"run-command.h\"\n #include \"simple-ipc.h\"\n #include \"thread-utils.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsmonitor_batch;\n struct fsmonitor_token_data;\n@@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct strbuf path_worktree_watch;\n \tstruct strbuf path_gitdir_watch;\n+\tstruct alias_info alias;\n \tint nr_paths_watching;\n \n \tstruct fsmonitor_token_data *current_token_data;\n@@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct ipc_server_data *ipc_server_data;\n \tstruct strbuf path_ipc;\n+\n };\n \n /*\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nindex e48592887e7..50ef37e57bb 100644\n--- a/fsmonitor-path-utils.h\n+++ b/fsmonitor-path-utils.h\n@@ -1,6 +1,14 @@\n #ifndef FSM_PATH_UTILS_H\n #define FSM_PATH_UTILS_H\n \n+#include \"strbuf.h\"\n+\n+struct alias_info\n+{\n+\tchar *alias;\n+\tchar *points_to;\n+};\n+\n struct fs_info {\n \tint is_remote;\n \tchar *typename;\n@@ -20,4 +28,32 @@ int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n  */\n int fsmonitor__is_fs_remote(const char *path);\n \n+/*\n+ * Get the alias in given path, if any.\n+ *\n+ * Sets alias to the first alias that matches any part of the path.\n+ *\n+ * If an alias is found, info.alias and info.points_to are set to the\n+ * found mapping.\n+ *\n+ * Returns -1 on error, 0 otherwise.\n+ *\n+ * The caller owns the storage that is occupied by set info.alias and\n+ * info.points_to and is responsible for releasing it with `free(3)`\n+ * when done.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info);\n+\n+/*\n+ * Resolve the path against the given alias.\n+ *\n+ * Returns the resolved path if there is one, NULL otherwise.\n+ *\n+ * The caller owns the storage that the returned string occupies and\n+ * is responsible for releasing it with `free(3)` when done.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info);\n+\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"463399","messageId":"e0fe05dabef92535ab42437d3a04025c5a1cc464.1663798722.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v11.git.1663798722.gitgitgadget@gmail.com","subject":"[PATCH v11 5/6] fsmonitor: check for compatability before communicating with fsmonitor","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-21T22:18:41Z","receivedAt":"2022-09-21T22:19:12Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf fsmonitor is not in a compatible state, die with an appropriate error\nmessge.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n fsmonitor-settings.c                   | 10 +++++++---\n fsmonitor-settings.h                   |  2 +-\n fsmonitor.c                            |  4 ++++\n 4 files changed, 13 insertions(+), 5 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 40da2d3b533..44233125df8 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -38,7 +38,7 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \tstrbuf_release(&path);\n \n \tif (fs.is_remote)\n-\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 531a1b6f956..8592a4d9bad 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n+#include \"fsmonitor-ipc.h\"\n #include \"fsmonitor-settings.h\"\n #include \"fsmonitor-path-utils.h\"\n \n@@ -242,10 +243,11 @@ enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)\n \treturn r->settings.fsmonitor->reason;\n }\n \n-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n+char *fsm_settings__get_incompatible_msg(struct repository *r,\n \t\t\t\t\t enum fsmonitor_reason reason)\n {\n \tstruct strbuf msg = STRBUF_INIT;\n+\tconst char *socket_dir;\n \n \tswitch (reason) {\n \tcase FSMONITOR_REASON_UNTESTED:\n@@ -281,9 +283,11 @@ char *fsm_settings__get_incompatible_msg(const struct repository *r,\n \t\tgoto done;\n \n \tcase FSMONITOR_REASON_NOSOCKETS:\n+\t\tsocket_dir = dirname((char *)fsmonitor_ipc__get_path(r));\n \t\tstrbuf_addf(&msg,\n-\t\t\t    _(\"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets\"),\n-\t\t\t    r->worktree);\n+\t\t\t    _(\"socket directory '%s' is incompatible with fsmonitor due\"\n+\t\t\t\t  \" to lack of Unix sockets support\"),\n+\t\t\t    socket_dir);\n \t\tgoto done;\n \t}\n \ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex 0721617b95a..ab02e3995ee 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -33,7 +33,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n const char *fsm_settings__get_hook_path(struct repository *r);\n \n enum fsmonitor_reason fsm_settings__get_reason(struct repository *r);\n-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n+char *fsm_settings__get_incompatible_msg(struct repository *r,\n \t\t\t\t\t enum fsmonitor_reason reason);\n \n struct fsmonitor_settings;\ndiff --git a/fsmonitor.c b/fsmonitor.c\nindex 57d6a483bee..1a86b9789a9 100644\n--- a/fsmonitor.c\n+++ b/fsmonitor.c\n@@ -305,6 +305,10 @@ void refresh_fsmonitor(struct index_state *istate)\n \tint is_trivial = 0;\n \tstruct repository *r = istate->repo ? istate->repo : the_repository;\n \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);\n+\tenum fsmonitor_reason reason = fsm_settings__get_reason(r);\n+\n+\tif (reason > FSMONITOR_REASON_OK)\n+\t\twarning(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n \n \tif (fsm_mode <= FSMONITOR_MODE_DISABLED ||\n \t    istate->fsmonitor_has_run_once)\n-- \ngitgitgadget\n\n"},{"id":"463400","messageId":"3200b505988d3b46909f7a038a10e0ee9b50ede0.1663798722.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v11.git.1663798722.gitgitgadget@gmail.com","subject":"[PATCH v11 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-21T22:18:42Z","receivedAt":"2022-09-21T22:19:16Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nAdd documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\nCall-out experimental nature of 'fsmonitor.allowRemote' and limited file\nsystem support for 'fsmonitor.socketDir'.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Documentation/git-fsmonitor--daemon.txt | 48 +++++++++++++++++++++++--\n 1 file changed, 45 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt\nindex cc142fb8612..6ad3e518ae0 100644\n--- a/Documentation/git-fsmonitor--daemon.txt\n+++ b/Documentation/git-fsmonitor--daemon.txt\n@@ -3,7 +3,7 @@ git-fsmonitor{litdd}daemon(1)\n \n NAME\n ----\n-git-fsmonitor--daemon - A Built-in File System Monitor\n+git-fsmonitor--daemon - A Built-in Filesystem Monitor\n \n SYNOPSIS\n --------\n@@ -17,7 +17,7 @@ DESCRIPTION\n -----------\n \n A daemon to watch the working directory for file and directory\n-changes using platform-specific file system notification facilities.\n+changes using platform-specific filesystem notification facilities.\n \n This daemon communicates directly with commands like `git status`\n using the link:technical/api-simple-ipc.html[simple IPC] interface\n@@ -63,13 +63,55 @@ CAVEATS\n -------\n \n The fsmonitor daemon does not currently know about submodules and does\n-not know to filter out file system events that happen within a\n+not know to filter out filesystem events that happen within a\n submodule.  If fsmonitor daemon is watching a super repo and a file is\n modified within the working directory of a submodule, it will report\n the change (as happening against the super repo).  However, the client\n will properly ignore these extra events, so performance may be affected\n but it will not cause an incorrect result.\n \n+By default, the fsmonitor daemon refuses to work against network-mounted\n+repositories; this may be overridden by setting `fsmonitor.allowRemote` to\n+`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n+correctly with all network-mounted repositories and such use is considered\n+experimental.\n+\n+On Mac OS, the inter-process communication (IPC) between various Git\n+commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a\n+special type of file -- which is supported by native Mac OS filesystems,\n+but not on network-mounted filesystems, NTFS, or FAT32.  Other filesystems\n+may or may not have the needed support; the fsmonitor daemon is not guaranteed\n+to work with these filesystems and such use is considered experimental.\n+\n+By default, the socket is created in the `.git` directory, however, if the\n+`.git` directory is on a network-mounted filesystem, it will be instead be\n+created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a\n+network-mounted filesystem in which case you must set the configuration\n+variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native\n+filesystem in which to create the socket file.\n+\n+If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)\n+is on a native Mac OS file filesystem the fsmonitor daemon will report an\n+error that will cause the daemon and the currently running command to exit.\n+\n+CONFIGURATION\n+-------------\n+\n+When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])\n+the fsmonitor daemon will pay attention to the following configuration\n+variables:\n+\n+`fsmonitor.allowRemote`::\n+\tBy default, the daemon refuses to work against network-mounted\n+\trepositories. Setting `fsmonitor.allowRemote` to `true` overrides\n+\tthis behavior.\n+\n+`fsmonitor.socketDir`::\n+    This Mac OS-specific option, if set, specifies the directory in\n+    which to create the Unix domain socket used for communication\n+    between fsmonitor and various Git commands. The directory must\n+    reside on a native Mac OS filesystem as discussed above.\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \ngitgitgadget\n"},{"id":"463576","messageId":"pull.1326.v12.git.1664048782.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v11.git.1663798722.gitgitgadget@gmail.com","subject":"[PATCH v12 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-24T19:46:16Z","receivedAt":"2022-09-24T19:46:30Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"Follow-on to the work done to allow Windows to work against network-mounted\nrepos for macOS.\n\nHave macOS take advantage of the same configuration option,\n'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\nto true will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for IPC\ncannot be created in a network location; instead $HOME is used if the\ndefault location is on the network. The user may, optionally, set the\n'fsmonitor.socketDir' configuration option to a valid, local directory if\n$HOME itself is on the network or is simply not the desired location for the\nUDS file.\n\nAn additional issue is that for mount points in the root directory, FSEvents\ndoes not report a path that matches the worktree directory due to the\nintroduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\nto the worktree directory by interrogating the root filesystem for synthetic\nfirmlinks and using that information to translate the path.\n\nv12 differs from v11:\n\n * bug fixes\n\nv11 differs from v10:\n\n * incorporates code review feedback\n * fix memory leak in fsm-listen-darwin.c\n\nv10 differs from v9:\n\n * incorporates code review feedback\n * improves error messaging for incompatible socket directory\n\nv9 differs from v8:\n\n * incorporates code review feedback\n * check for incompatibility before communicating with fsmonitor\n\nv8 differs from v7:\n\n * incorporates code review feedback\n * gets the rebase right\n\nv7 differs from v6:\n\n * incorporates code review feedback\n\nv6 differs from v5:\n\n * incorporates earlier, Windows-specific changes that have not made it back\n   yet to the master branch\n * incorporates code review feedback\n * adds documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n\nv5 differs significantly from earlier versions:\n\n * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n   such that these options are no longer added to the settings data\n   structure but are rather read from config at point of use\n * refactoring of code for handling platform-specific file system checks via\n   a common interface to avoid platform #ifdef in IPC code and be in-model\n   with other platform-specific fsmonitor code\n * dealing with 'synthetic firmlinks' on macOS\n\nEric DeCosta (6):\n  fsmonitor: refactor filesystem checks to common interface\n  fsmonitor: relocate socket file if .git directory is remote\n  fsmonitor: avoid socket location check if using hook\n  fsmonitor: deal with synthetic firmlinks on macOS\n  fsmonitor: check for compatability before communicating with fsmonitor\n  fsmonitor: add documentation for allowRemote and socketDir options\n\n Documentation/git-fsmonitor--daemon.txt  |  48 ++++++-\n Makefile                                 |   2 +\n builtin/fsmonitor--daemon.c              |  11 +-\n compat/fsmonitor/fsm-ipc-darwin.c        |  52 +++++++\n compat/fsmonitor/fsm-ipc-win32.c         |   9 ++\n compat/fsmonitor/fsm-listen-darwin.c     |  14 +-\n compat/fsmonitor/fsm-path-utils-darwin.c | 132 +++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 145 +++++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  72 +++-------\n compat/fsmonitor/fsm-settings-win32.c    | 174 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   4 +\n fsmonitor--daemon.h                      |   3 +\n fsmonitor-ipc.c                          |  18 ++-\n fsmonitor-ipc.h                          |   4 +-\n fsmonitor-path-utils.h                   |  59 ++++++++\n fsmonitor-settings.c                     |  68 ++++++++-\n fsmonitor-settings.h                     |   4 +-\n fsmonitor.c                              |   7 +\n 18 files changed, 580 insertions(+), 246 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\n\nbase-commit: 4b79ee4b0cd1130ba8907029cdc5f6a1632aca26\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v12\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v12\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v11:\n\n 1:  155a6890806 = 1:  5958dab0163 fsmonitor: refactor filesystem checks to common interface\n 2:  dbe113abb87 ! 2:  20220b08edb fsmonitor: relocate socket file if .git directory is remote\n     @@ compat/fsmonitor/fsm-ipc-darwin.c (new)\n      +\n      +const char *fsmonitor_ipc__get_path(struct repository *r)\n      +{\n     -+\tstatic const char *ipc_path;\n     ++\tstatic const char *ipc_path = NULL;\n      +\tSHA_CTX sha1ctx;\n      +\tchar *sock_dir = NULL;\n      +\tstruct strbuf ipc_file = STRBUF_INIT;\n     @@ compat/fsmonitor/fsm-ipc-darwin.c (new)\n      +\tif (ipc_path)\n      +\t\treturn ipc_path;\n      +\n     -+\tipc_path = fsmonitor_ipc__get_default_path();\n      +\n      +\t/* By default the socket file is created in the .git directory */\n     -+\tif (fsmonitor__is_fs_remote(ipc_path) < 1)\n     ++\tif (fsmonitor__is_fs_remote(r->gitdir) < 1) {\n     ++\t\tipc_path = fsmonitor_ipc__get_default_path();\n      +\t\treturn ipc_path;\n     ++\t}\n      +\n      +\tSHA1_Init(&sha1ctx);\n      +\tSHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n 3:  86c15299ae8 = 3:  e9921550a67 fsmonitor: avoid socket location check if using hook\n 4:  1a516fd9214 = 4:  6efdc6ed74e fsmonitor: deal with synthetic firmlinks on macOS\n 5:  e0fe05dabef ! 5:  421d77775dc fsmonitor: check for compatability before communicating with fsmonitor\n     @@ fsmonitor-settings.h: enum fsmonitor_mode fsm_settings__get_mode(struct reposito\n       struct fsmonitor_settings;\n      \n       ## fsmonitor.c ##\n     +@@ fsmonitor.c: static int fsmonitor_force_update_threshold = 100;\n     + \n     + void refresh_fsmonitor(struct index_state *istate)\n     + {\n     ++\tstatic int warn_once = 0;\n     + \tstruct strbuf query_result = STRBUF_INIT;\n     + \tint query_success = 0, hook_version = -1;\n     + \tsize_t bol = 0; /* beginning of line */\n      @@ fsmonitor.c: void refresh_fsmonitor(struct index_state *istate)\n       \tint is_trivial = 0;\n       \tstruct repository *r = istate->repo ? istate->repo : the_repository;\n       \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);\n      +\tenum fsmonitor_reason reason = fsm_settings__get_reason(r);\n      +\n     -+\tif (reason > FSMONITOR_REASON_OK)\n     ++\tif (!warn_once && reason > FSMONITOR_REASON_OK) {\n     ++\t\twarn_once = 1;\n      +\t\twarning(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n     ++\t}\n       \n       \tif (fsm_mode <= FSMONITOR_MODE_DISABLED ||\n       \t    istate->fsmonitor_has_run_once)\n 6:  3200b505988 = 6:  b375b0ac798 fsmonitor: add documentation for allowRemote and socketDir options\n\n-- \ngitgitgadget\n"},{"id":"463577","messageId":"5958dab0163dd33073bb04fb6ad81148bad26613.1664048782.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v12.git.1664048782.gitgitgadget@gmail.com","subject":"[PATCH v12 1/6] fsmonitor: refactor filesystem checks to common interface","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-24T19:46:17Z","receivedAt":"2022-09-24T19:46:34Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nProvide a common interface for getting basic filesystem information\nincluding filesystem type and whether the filesystem is remote.\n\nRefactor existing code for getting basic filesystem info and detecting\nremote file systems to the new interface.\n\nRefactor filesystem checks to leverage new interface. For macOS,\nerror-out if the Unix Domain socket (UDS) file is on a remote\nfilesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                                 |   1 +\n compat/fsmonitor/fsm-path-utils-darwin.c |  40 ++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 128 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  62 +++-----\n compat/fsmonitor/fsm-settings-win32.c    | 172 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   2 +\n fsmonitor-path-utils.h                   |  23 +++\n fsmonitor-settings.c                     |  50 +++++++\n 8 files changed, 263 insertions(+), 215 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\ndiff --git a/Makefile b/Makefile\nindex cac3452edb9..ffab427ea5b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2044,6 +2044,7 @@ endif\n ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n ifeq ($(TCLTK_PATH),)\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nnew file mode 100644\nindex 00000000000..067cbe6990a\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -0,0 +1,40 @@\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+#include <sys/param.h>\n+#include <sys/mount.h>\n+\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\tstruct statfs fs;\n+\tif (statfs(path, &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\treturn -1;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\n+\tif (!(fs.f_flags & MNT_LOCAL))\n+\t\tfs_info->is_remote = 1;\n+\telse\n+\t\tfs_info->is_remote = 0;\n+\n+\tfs_info->typename = fs.f_fstypename;\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nnew file mode 100644\nindex 00000000000..a90b8f7925b\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -0,0 +1,128 @@\n+#include \"cache.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+/*\n+ * Check remote working directory protocol.\n+ *\n+ * Return -1 if client machine cannot get remote protocol information.\n+ */\n+static int check_remote_protocol(wchar_t *wpath)\n+{\n+\tHANDLE h;\n+\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n+\n+\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n+\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\n+\tif (h == INVALID_HANDLE_VALUE) {\n+\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n+\t\t&proto_info, sizeof(proto_info))) {\n+\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\tCloseHandle(h);\n+\t\treturn -1;\n+\t}\n+\n+\tCloseHandle(h);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n+\t\t\t\twpath, proto_info.Protocol);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Notes for testing:\n+ *\n+ * (a) Windows allows a network share to be mapped to a drive letter.\n+ *     (This is the normal method to access it.)\n+ *\n+ *     $ NET USE Z: \\\\server\\share\n+ *     $ git -C Z:/repo status\n+ *\n+ * (b) Windows allows a network share to be referenced WITHOUT mapping\n+ *     it to drive letter.\n+ *\n+ *     $ NET USE \\\\server\\share\\dir\n+ *     $ git -C //server/share/repo status\n+ *\n+ * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n+ *     arbitrary path (which may be remote)\n+ *\n+ *     $ SUBST Q: Z:\\repo\n+ *     $ git -C Q:/ status\n+ *\n+ * (d) Windows allows a directory symlink to be created on a local\n+ *     file system that points to a remote repo.\n+ *\n+ *     $ mklink /d ./link //server/share/repo\n+ *     $ git -C ./link status\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\twchar_t wpath[MAX_PATH];\n+\twchar_t wfullpath[MAX_PATH];\n+\tsize_t wlen;\n+\tUINT driveType;\n+\n+\t/*\n+\t * Do everything in wide chars because the drive letter might be\n+\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n+\t */\n+\tif (xutftowcs_path(wpath, path) < 0) {\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * GetDriveTypeW() requires a final slash.  We assume that the\n+\t * worktree pathname points to an actual directory.\n+\t */\n+\twlen = wcslen(wpath);\n+\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n+\t\twpath[wlen++] = L'\\\\';\n+\t\twpath[wlen] = 0;\n+\t}\n+\n+\t/*\n+\t * Normalize the path.  If nothing else, this converts forward\n+\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n+\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n+\t */\n+\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {\n+\t\treturn -1;\n+\t}\n+\n+\tdriveType = GetDriveTypeW(wfullpath);\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n+\t\t\t path, wfullpath, driveType);\n+\n+\tif (driveType == DRIVE_REMOTE) {\n+\t\tfs_info->is_remote = 1;\n+\t\tif (check_remote_protocol(wfullpath) < 0)\n+\t\t\treturn -1;\n+\t} else {\n+\t\tfs_info->is_remote = 0;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dba3ced6bb7 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -1,32 +1,10 @@\n-#include \"cache.h\"\n #include \"config.h\"\n-#include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n-#include <sys/param.h>\n-#include <sys/mount.h>\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n-/*\n- * [1] Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type (NFS, SAMBA, etc.) dictates whether notification events\n- * are available at all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n+ /*\n  * For the builtin FSMonitor, we create the Unix domain socket for the\n  * IPC in the .git directory.  If the working directory is remote,\n  * then the socket will be created on the remote file system.  This\n@@ -38,40 +16,34 @@\n  * be taken to ensure that $HOME is actually local and not a managed\n  * file share.)\n  *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n+ * FAT32 and NTFS working directories are problematic too.\n  *\n  * The builtin FSMonitor uses a Unix domain socket in the .git\n  * directory for IPC.  These Windows drive formats do not support\n  * Unix domain sockets, so mark them as incompatible for the daemon.\n  *\n  */\n-static enum fsmonitor_reason check_volume(struct repository *r)\n+static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n-\tstruct statfs fs;\n+\tstruct fs_info fs;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \n-\tif (statfs(r->worktree, &fs) == -1) {\n-\t\tint saved_errno = errno;\n-\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n-\t\t\t\t r->worktree, strerror(saved_errno));\n-\t\terrno = saved_errno;\n+\tif (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {\n+\t\tstrbuf_release(&path);\n \t\treturn FSMONITOR_REASON_ERROR;\n \t}\n \n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n-\t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n \n-\tif (!(fs.f_flags & MNT_LOCAL))\n+\tif (fs.is_remote)\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n-\tif (!strcmp(fs.f_fstypename, \"msdos\")) /* aka FAT32 */\n+\tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n-\tif (!strcmp(fs.f_fstypename, \"ntfs\"))\n+\tif (!strcmp(fs.typename, \"ntfs\"))\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \treturn FSMONITOR_REASON_OK;\n@@ -81,7 +53,7 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_volume(r);\n+\treason = check_uds_volume(r);\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex e5ec5b0a9f7..d88b06ae610 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -1,8 +1,9 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * VFS for Git is incompatible with FSMonitor.\n@@ -24,171 +25,6 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-/*\n- * Check if monitoring remote working directories is allowed.\n- *\n- * By default, monitoring remote working directories is\n- * disabled.  Users may override this behavior in enviroments where\n- * they have proper support.\n- */\n-static int check_config_allowremote(struct repository *r)\n-{\n-\tint allow;\n-\n-\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n-\t\treturn allow;\n-\n-\treturn -1; /* fsmonitor.allowremote not set */\n-}\n-\n-/*\n- * Check remote working directory protocol.\n- *\n- * Error if client machine cannot get remote protocol information.\n- */\n-static int check_remote_protocol(wchar_t *wpath)\n-{\n-\tHANDLE h;\n-\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n-\n-\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n-\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n-\n-\tif (h == INVALID_HANDLE_VALUE) {\n-\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\treturn -1;\n-\t}\n-\n-\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n-\t\t&proto_info, sizeof(proto_info))) {\n-\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\tCloseHandle(h);\n-\t\treturn -1;\n-\t}\n-\n-\tCloseHandle(h);\n-\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n-\t\t\t\twpath, proto_info.Protocol);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type dictates whether notification events are available at\n- * all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- * Notes for testing:\n- *\n- * (a) Windows allows a network share to be mapped to a drive letter.\n- *     (This is the normal method to access it.)\n- *\n- *     $ NET USE Z: \\\\server\\share\n- *     $ git -C Z:/repo status\n- *\n- * (b) Windows allows a network share to be referenced WITHOUT mapping\n- *     it to drive letter.\n- *\n- *     $ NET USE \\\\server\\share\\dir\n- *     $ git -C //server/share/repo status\n- *\n- * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n- *     arbitrary path (which may be remote)\n- *\n- *     $ SUBST Q: Z:\\repo\n- *     $ git -C Q:/ status\n- *\n- * (d) Windows allows a directory symlink to be created on a local\n- *     file system that points to a remote repo.\n- *\n- *     $ mklink /d ./link //server/share/repo\n- *     $ git -C ./link status\n- */\n-static enum fsmonitor_reason check_remote(struct repository *r)\n-{\n-\tint ret;\n-\twchar_t wpath[MAX_PATH];\n-\twchar_t wfullpath[MAX_PATH];\n-\tsize_t wlen;\n-\tUINT driveType;\n-\n-\t/*\n-\t * Do everything in wide chars because the drive letter might be\n-\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n-\t */\n-\tif (xutftowcs_path(wpath, r->worktree) < 0)\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t/*\n-\t * GetDriveTypeW() requires a final slash.  We assume that the\n-\t * worktree pathname points to an actual directory.\n-\t */\n-\twlen = wcslen(wpath);\n-\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n-\t\twpath[wlen++] = L'\\\\';\n-\t\twpath[wlen] = 0;\n-\t}\n-\n-\t/*\n-\t * Normalize the path.  If nothing else, this converts forward\n-\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n-\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n-\t */\n-\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\tdriveType = GetDriveTypeW(wfullpath);\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n-\t\t\t r->worktree, wfullpath, driveType);\n-\n-\tif (driveType == DRIVE_REMOTE) {\n-\t\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t \"check_remote('%s') true\",\n-\t\t\t\t r->worktree);\n-\n-\t\tret = check_remote_protocol(wfullpath);\n-\t\tif (ret < 0)\n-\t\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t\tswitch (check_config_allowremote(r)) {\n-\t\tcase 0: /* config overrides and disables */\n-\t\t\treturn FSMONITOR_REASON_REMOTE;\n-\t\tcase 1: /* config overrides and enables */\n-\t\t\treturn FSMONITOR_REASON_OK;\n-\t\tdefault:\n-\t\t\tbreak; /* config has no opinion */\n-\t\t}\n-\n-\t\treturn FSMONITOR_REASON_REMOTE;\n-\t}\n-\n-\treturn FSMONITOR_REASON_OK;\n-}\n-\n enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n@@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n-\treason = check_remote(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n-\n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex ea2a531be87..5482a04b3ce 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)\n@@ -315,6 +316,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nnew file mode 100644\nindex 00000000000..e48592887e7\n--- /dev/null\n+++ b/fsmonitor-path-utils.h\n@@ -0,0 +1,23 @@\n+#ifndef FSM_PATH_UTILS_H\n+#define FSM_PATH_UTILS_H\n+\n+struct fs_info {\n+\tint is_remote;\n+\tchar *typename;\n+};\n+\n+/*\n+ * Get some basic filesystem informtion for the given path\n+ *\n+ * Returns -1 on error, zero otherwise.\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n+\n+/*\n+ * Determines if the filesystem that path resides on is remote.\n+ *\n+ * Returns -1 on error, 0 if not remote, 1 if remote.\n+ */\n+int fsmonitor__is_fs_remote(const char *path);\n+\n+#endif\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..d288cbad479 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * We keep this structure defintion private and have getters\n@@ -13,6 +14,52 @@ struct fsmonitor_settings {\n \tchar *hook_path;\n };\n \n+/*\n+ * Remote working directories are problematic for FSMonitor.\n+ *\n+ * The underlying file system on the server machine and/or the remote\n+ * mount type dictates whether notification events are available at\n+ * all to remote client machines.\n+ *\n+ * Kernel differences between the server and client machines also\n+ * dictate the how (buffering, frequency, de-dup) the events are\n+ * delivered to client machine processes.\n+ *\n+ * A client machine (such as a laptop) may choose to suspend/resume\n+ * and it is unclear (without lots of testing) whether the watcher can\n+ * resync after a resume.  We might be able to treat this as a normal\n+ * \"events were dropped by the kernel\" event and do our normal \"flush\n+ * and resync\" --or-- we might need to close the existing (zombie?)\n+ * notification fd and create a new one.\n+ *\n+ * In theory, the above issues need to be addressed whether we are\n+ * using the Hook or IPC API.\n+ *\n+ * So (for now at least), mark remote working directories as\n+ * incompatible unless 'fsmonitor.allowRemote' is true.\n+ *\n+ */\n+#ifdef HAVE_FSMONITOR_OS_SETTINGS\n+static enum fsmonitor_reason check_remote(struct repository *r)\n+{\n+\tint allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */\n+\tint is_remote = fsmonitor__is_fs_remote(r->worktree);\n+\n+\tswitch (is_remote) {\n+\t\tcase 0:\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tcase 1:\n+\t\t\trepo_config_get_bool(r, \"fsmonitor.allowremote\", &allow_remote);\n+\t\t\tif (allow_remote < 1)\n+\t\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\t\telse\n+\t\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+}\n+#endif\n+\n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n {\n \tif (!r->worktree) {\n@@ -27,6 +74,9 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t{\n \t\tenum fsmonitor_reason reason;\n \n+\t\treason = check_remote(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n \t\treason = fsm_os__incompatible(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-- \ngitgitgadget\n\n"},{"id":"463578","messageId":"20220b08edb61f717b07c41298e2ad4c05b47874.1664048782.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v12.git.1664048782.gitgitgadget@gmail.com","subject":"[PATCH v12 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-24T19:46:18Z","receivedAt":"2022-09-24T19:46:41Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf the .git directory is on a remote file system, create the socket\nfile in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                               |  1 +\n builtin/fsmonitor--daemon.c            |  3 +-\n compat/fsmonitor/fsm-ipc-darwin.c      | 52 ++++++++++++++++++++++++++\n compat/fsmonitor/fsm-ipc-win32.c       |  9 +++++\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n contrib/buildsystems/CMakeLists.txt    |  2 +\n fsmonitor-ipc.c                        | 18 ++++-----\n fsmonitor-ipc.h                        |  4 +-\n 8 files changed, 78 insertions(+), 13 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n\ndiff --git a/Makefile b/Makefile\nindex ffab427ea5b..feb675a6959 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2039,6 +2039,7 @@ ifdef FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n endif\n \n ifdef FSMONITOR_OS_SETTINGS\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 2c109cf8b37..0123fc33ed2 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -1343,7 +1343,8 @@ static int fsmonitor_run_daemon(void)\n \t * directory.)\n \t */\n \tstrbuf_init(&state.path_ipc, 0);\n-\tstrbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));\n+\tstrbuf_addstr(&state.path_ipc,\n+\t\tabsolute_path(fsmonitor_ipc__get_path(the_repository)));\n \n \t/*\n \t * Confirm that we can create platform-specific resources for the\ndiff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\nnew file mode 100644\nindex 00000000000..ce843d63348\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-darwin.c\n@@ -0,0 +1,52 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"strbuf.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n+{\n+\tstatic const char *ipc_path = NULL;\n+\tSHA_CTX sha1ctx;\n+\tchar *sock_dir = NULL;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (!r)\n+\t\tBUG(\"No repository passed into fsmonitor_ipc__get_path\");\n+\n+\tif (ipc_path)\n+\t\treturn ipc_path;\n+\n+\n+\t/* By default the socket file is created in the .git directory */\n+\tif (fsmonitor__is_fs_remote(r->gitdir) < 1) {\n+\t\tipc_path = fsmonitor_ipc__get_default_path();\n+\t\treturn ipc_path;\n+\t}\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\trepo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n+\n+\t/* Create the socket file in either socketDir or $HOME */\n+\tif (sock_dir && *sock_dir) {\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\t} else {\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\t}\n+\tfree(sock_dir);\n+\n+\tipc_path = interpolate_path(ipc_file.buf, 1);\n+\tif (!ipc_path)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\n+\tstrbuf_release(&ipc_file);\n+\treturn ipc_path;\n+}\ndiff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\nnew file mode 100644\nindex 00000000000..e08c505c148\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-win32.c\n@@ -0,0 +1,9 @@\n+#include \"config.h\"\n+#include \"fsmonitor-ipc.h\"\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r) {\n+\tstatic char *ret;\n+\tif (!ret)\n+\t\tret = git_pathdup(\"fsmonitor--daemon.ipc\");\n+\treturn ret;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex dba3ced6bb7..681d8bf963e 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -26,7 +26,7 @@\n static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n \tstruct fs_info fs;\n-\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tconst char *ipc_path = fsmonitor_ipc__get_path(r);\n \tstruct strbuf path = STRBUF_INIT;\n \tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 5482a04b3ce..787738e6fa3 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n@@ -316,6 +317,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..c0f42301c84 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -18,7 +18,7 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 0;\n }\n \n-const char *fsmonitor_ipc__get_path(void)\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n {\n \treturn NULL;\n }\n@@ -47,11 +47,9 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n-\n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\n-\treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n+\treturn ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));\n }\n \n static int spawn_daemon(void)\n@@ -81,8 +79,8 @@ int fsmonitor_ipc__send_query(const char *since_token,\n \ttrace2_data_string(\"fsm_client\", NULL, \"query/command\", tok);\n \n try_again:\n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \n \tswitch (state) {\n \tcase IPC_STATE__LISTENING:\n@@ -117,13 +115,13 @@ try_again:\n \n \tcase IPC_STATE__INVALID_PATH:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: invalid path '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \n \tcase IPC_STATE__OTHER_ERROR:\n \tdefault:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: unspecified error on '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \t}\n \n@@ -149,8 +147,8 @@ int fsmonitor_ipc__send_command(const char *command,\n \toptions.wait_if_busy = 1;\n \toptions.wait_if_not_found = 0;\n \n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \tif (state != IPC_STATE__LISTENING) {\n \t\tdie(_(\"fsmonitor--daemon is not running\"));\n \t\treturn -1;\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..8b489da762b 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -3,6 +3,8 @@\n \n #include \"simple-ipc.h\"\n \n+struct repository;\n+\n /*\n  * Returns true if built-in file system monitor daemon is defined\n  * for this platform.\n@@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);\n  *\n  * Returns NULL if the daemon is not supported on this platform.\n  */\n-const char *fsmonitor_ipc__get_path(void);\n+const char *fsmonitor_ipc__get_path(struct repository *r);\n \n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n-- \ngitgitgadget\n\n"},{"id":"463579","messageId":"e9921550a671cf3bf12180373fba7f51c4fe7460.1664048782.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v12.git.1664048782.gitgitgadget@gmail.com","subject":"[PATCH v12 3/6] fsmonitor: avoid socket location check if using hook","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-24T19:46:19Z","receivedAt":"2022-09-24T19:46:42Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf monitoring is done via fsmonitor hook rather than IPC there is no\nneed to check if the location of the Unix Domain socket (UDS) file is\non a remote filesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 10 ++++++----\n compat/fsmonitor/fsm-settings-win32.c  |  2 +-\n fsmonitor-settings.c                   |  8 ++++----\n fsmonitor-settings.h                   |  2 +-\n 4 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 681d8bf963e..40da2d3b533 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -49,13 +49,15 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_uds_volume(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n+\tif (ipc) {\n+\t\treason = check_uds_volume(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n+\t}\n \n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex d88b06ae610..a8af31b71de 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex d288cbad479..531a1b6f956 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -60,7 +60,7 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n }\n #endif\n \n-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n+static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n {\n \tif (!r->worktree) {\n \t\t/*\n@@ -77,7 +77,7 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t\treason = check_remote(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-\t\treason = fsm_os__incompatible(r);\n+\t\treason = fsm_os__incompatible(r, ipc);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n \t}\n@@ -162,7 +162,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -185,7 +185,7 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..0721617b95a 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -48,7 +48,7 @@ struct fsmonitor_settings;\n  * fsm_os__* routines should considered private to fsm_settings__\n  * routines.\n  */\n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n \n #endif /* FSMONITOR_SETTINGS_H */\n-- \ngitgitgadget\n\n"},{"id":"463580","messageId":"6efdc6ed74ec9224d93a1b88ff8be85d533cb30f.1664048782.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v12.git.1664048782.gitgitgadget@gmail.com","subject":"[PATCH v12 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-24T19:46:20Z","receivedAt":"2022-09-24T19:46:43Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n builtin/fsmonitor--daemon.c              |  8 +++\n compat/fsmonitor/fsm-listen-darwin.c     | 14 +++-\n compat/fsmonitor/fsm-path-utils-darwin.c | 92 ++++++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 17 +++++\n fsmonitor--daemon.h                      |  3 +\n fsmonitor-path-utils.h                   | 36 ++++++++++\n 6 files changed, 168 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 0123fc33ed2..56fcd1c2baa 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"fsmonitor.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n #include \"compat/fsmonitor/fsm-health.h\"\n #include \"compat/fsmonitor/fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n@@ -1282,6 +1283,13 @@ static int fsmonitor_run_daemon(void)\n \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n \tstate.nr_paths_watching = 1;\n \n+\tstate.alias.alias = NULL;\n+\tstate.alias.points_to = NULL;\n+\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n+\t\terr = error(_(\"could not get worktree alias\"));\n+\t\tgoto done;\n+\t}\n+\n \t/*\n \t * We create and delete cookie files somewhere inside the .git\n \t * directory to help us keep sync with the file system.  If\ndiff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\nindex 8e208e8289e..daeee4e465c 100644\n--- a/compat/fsmonitor/fsm-listen-darwin.c\n+++ b/compat/fsmonitor/fsm-listen-darwin.c\n@@ -26,6 +26,7 @@\n #include \"fsmonitor.h\"\n #include \"fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsm_listen_data\n {\n@@ -198,8 +199,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \tstruct string_list cookie_list = STRING_LIST_INIT_DUP;\n \tconst char *path_k;\n \tconst char *slash;\n-\tint k;\n+\tchar *resolved = NULL;\n \tstruct strbuf tmp = STRBUF_INIT;\n+\tint k;\n \n \t/*\n \t * Build a list of all filesystem changes into a private/local\n@@ -209,7 +211,12 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t/*\n \t\t * On Mac, we receive an array of absolute paths.\n \t\t */\n-\t\tpath_k = paths[k];\n+\t\tfree(resolved);\n+\t\tresolved = fsmonitor__resolve_alias(paths[k], &state->alias);\n+\t\tif (resolved)\n+\t\t\tpath_k = resolved;\n+\t\telse\n+\t\t\tpath_k = paths[k];\n \n \t\t/*\n \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n@@ -238,6 +245,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t\tfsmonitor_force_resync(state);\n \t\t\tfsmonitor_batch__free_list(batch);\n \t\t\tstring_list_clear(&cookie_list, 0);\n+\t\t\tbatch = NULL;\n \n \t\t\t/*\n \t\t\t * We assume that any events that we received\n@@ -360,12 +368,14 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t}\n \t}\n \n+\tfree(resolved);\n \tfsmonitor_publish(state, batch, &cookie_list);\n \tstring_list_clear(&cookie_list, 0);\n \tstrbuf_release(&tmp);\n \treturn;\n \n force_shutdown:\n+\tfree(resolved);\n \tfsmonitor_batch__free_list(batch);\n \tstring_list_clear(&cookie_list, 0);\n \ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nindex 067cbe6990a..13807f58e95 100644\n--- a/compat/fsmonitor/fsm-path-utils-darwin.c\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -1,5 +1,8 @@\n #include \"fsmonitor.h\"\n #include \"fsmonitor-path-utils.h\"\n+#include <dirent.h>\n+#include <errno.h>\n+#include <fcntl.h>\n #include <sys/param.h>\n #include <sys/mount.h>\n \n@@ -38,3 +41,92 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * Scan the root directory for synthetic firmlinks that when resolved\n+ * are a prefix of the path, stopping at the first one found.\n+ *\n+ * Some information about firmlinks and synthetic firmlinks:\n+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n+ *\n+ * macOS no longer allows symlinks in the root directory; any link found\n+ * there is therefore a synthetic firmlink.\n+ *\n+ * If this function gets called often, will want to cache all the firmlink\n+ * information, but for now there is only one caller of this function.\n+ *\n+ * If there is more than one alias for the path, that is another\n+ * matter altogether.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\tDIR * dir;\n+\tint read;\n+\tint retval;\n+\tstruct dirent *de;\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+\n+\tretval = 0;\n+\tdir = opendir(\"/\");\n+\tif (!dir)\n+\t\treturn -1;\n+\n+\tstrbuf_init(&alias, 256);\n+\tstrbuf_init(&points_to, MAXPATHLEN);\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstrbuf_reset(&alias);\n+\t\tstrbuf_addch(&alias, '/');\n+\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n+\n+\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n+\t\tif (read > 0) {\n+\t\t\tstrbuf_setlen(&points_to, read);\n+\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n+\t\t\t\t&& path[points_to.len] == '/') {\n+\t\t\t\tinfo->alias = strbuf_detach(&alias, NULL);\n+\t\t\t\tinfo->points_to = strbuf_detach(&points_to, NULL);\n+\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n+\t\t\t\t\tpath, info->alias, info->points_to);\n+\t\t\t\tretval = 0;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n+\t\t\ttrace_printf_key(&trace_fsmonitor, \"Error %s\", strerror(errno));\n+\t\t\tretval = -1;\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\n+\tdone:\n+\tclosedir(dir);\n+\tstrbuf_release(&alias);\n+\tstrbuf_release(&points_to);\n+\treturn retval;\n+}\n+\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\tint len = info->alias ? strlen(info->alias) : 0;\n+\n+\tif (!len)\n+\t\treturn NULL;\n+\n+\tif ((strncmp(info->alias, path, len) == 0)\n+\t\t&& path[len] == '/') {\n+\t\tstruct strbuf tmp;\n+\t\tconst char *remainder = path + len;\n+\t\tint ptr_len = strlen(info->points_to);\n+\t\tint rem_len = strlen(remainder);\n+\n+\t\tstrbuf_init(&tmp, ptr_len + rem_len);\n+\t\tstrbuf_add(&tmp, info->points_to, ptr_len);\n+\t\tstrbuf_add(&tmp, remainder, rem_len);\n+\t\treturn strbuf_detach(&tmp, NULL);\n+\t}\n+\n+\treturn NULL;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nindex a90b8f7925b..0d95bbb416f 100644\n--- a/compat/fsmonitor/fsm-path-utils-win32.c\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -126,3 +126,20 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * No-op for now.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\treturn 0;\n+}\n+\n+/*\n+ * No-op for now.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\treturn NULL;\n+}\ndiff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\nindex 2102a5c9ff5..e24838f9a86 100644\n--- a/fsmonitor--daemon.h\n+++ b/fsmonitor--daemon.h\n@@ -8,6 +8,7 @@\n #include \"run-command.h\"\n #include \"simple-ipc.h\"\n #include \"thread-utils.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsmonitor_batch;\n struct fsmonitor_token_data;\n@@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct strbuf path_worktree_watch;\n \tstruct strbuf path_gitdir_watch;\n+\tstruct alias_info alias;\n \tint nr_paths_watching;\n \n \tstruct fsmonitor_token_data *current_token_data;\n@@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct ipc_server_data *ipc_server_data;\n \tstruct strbuf path_ipc;\n+\n };\n \n /*\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nindex e48592887e7..50ef37e57bb 100644\n--- a/fsmonitor-path-utils.h\n+++ b/fsmonitor-path-utils.h\n@@ -1,6 +1,14 @@\n #ifndef FSM_PATH_UTILS_H\n #define FSM_PATH_UTILS_H\n \n+#include \"strbuf.h\"\n+\n+struct alias_info\n+{\n+\tchar *alias;\n+\tchar *points_to;\n+};\n+\n struct fs_info {\n \tint is_remote;\n \tchar *typename;\n@@ -20,4 +28,32 @@ int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n  */\n int fsmonitor__is_fs_remote(const char *path);\n \n+/*\n+ * Get the alias in given path, if any.\n+ *\n+ * Sets alias to the first alias that matches any part of the path.\n+ *\n+ * If an alias is found, info.alias and info.points_to are set to the\n+ * found mapping.\n+ *\n+ * Returns -1 on error, 0 otherwise.\n+ *\n+ * The caller owns the storage that is occupied by set info.alias and\n+ * info.points_to and is responsible for releasing it with `free(3)`\n+ * when done.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info);\n+\n+/*\n+ * Resolve the path against the given alias.\n+ *\n+ * Returns the resolved path if there is one, NULL otherwise.\n+ *\n+ * The caller owns the storage that the returned string occupies and\n+ * is responsible for releasing it with `free(3)` when done.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info);\n+\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"463581","messageId":"421d77775dc24e52ab26336a1a82ed0e7b15ff5a.1664048782.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v12.git.1664048782.gitgitgadget@gmail.com","subject":"[PATCH v12 5/6] fsmonitor: check for compatability before communicating with fsmonitor","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-24T19:46:21Z","receivedAt":"2022-09-24T19:46:45Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf fsmonitor is not in a compatible state, die with an appropriate error\nmessge.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n fsmonitor-settings.c                   | 10 +++++++---\n fsmonitor-settings.h                   |  2 +-\n fsmonitor.c                            |  7 +++++++\n 4 files changed, 16 insertions(+), 5 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 40da2d3b533..44233125df8 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -38,7 +38,7 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \tstrbuf_release(&path);\n \n \tif (fs.is_remote)\n-\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 531a1b6f956..8592a4d9bad 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n+#include \"fsmonitor-ipc.h\"\n #include \"fsmonitor-settings.h\"\n #include \"fsmonitor-path-utils.h\"\n \n@@ -242,10 +243,11 @@ enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)\n \treturn r->settings.fsmonitor->reason;\n }\n \n-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n+char *fsm_settings__get_incompatible_msg(struct repository *r,\n \t\t\t\t\t enum fsmonitor_reason reason)\n {\n \tstruct strbuf msg = STRBUF_INIT;\n+\tconst char *socket_dir;\n \n \tswitch (reason) {\n \tcase FSMONITOR_REASON_UNTESTED:\n@@ -281,9 +283,11 @@ char *fsm_settings__get_incompatible_msg(const struct repository *r,\n \t\tgoto done;\n \n \tcase FSMONITOR_REASON_NOSOCKETS:\n+\t\tsocket_dir = dirname((char *)fsmonitor_ipc__get_path(r));\n \t\tstrbuf_addf(&msg,\n-\t\t\t    _(\"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets\"),\n-\t\t\t    r->worktree);\n+\t\t\t    _(\"socket directory '%s' is incompatible with fsmonitor due\"\n+\t\t\t\t  \" to lack of Unix sockets support\"),\n+\t\t\t    socket_dir);\n \t\tgoto done;\n \t}\n \ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex 0721617b95a..ab02e3995ee 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -33,7 +33,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n const char *fsm_settings__get_hook_path(struct repository *r);\n \n enum fsmonitor_reason fsm_settings__get_reason(struct repository *r);\n-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n+char *fsm_settings__get_incompatible_msg(struct repository *r,\n \t\t\t\t\t enum fsmonitor_reason reason);\n \n struct fsmonitor_settings;\ndiff --git a/fsmonitor.c b/fsmonitor.c\nindex 57d6a483bee..540736b39fd 100644\n--- a/fsmonitor.c\n+++ b/fsmonitor.c\n@@ -295,6 +295,7 @@ static int fsmonitor_force_update_threshold = 100;\n \n void refresh_fsmonitor(struct index_state *istate)\n {\n+\tstatic int warn_once = 0;\n \tstruct strbuf query_result = STRBUF_INIT;\n \tint query_success = 0, hook_version = -1;\n \tsize_t bol = 0; /* beginning of line */\n@@ -305,6 +306,12 @@ void refresh_fsmonitor(struct index_state *istate)\n \tint is_trivial = 0;\n \tstruct repository *r = istate->repo ? istate->repo : the_repository;\n \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);\n+\tenum fsmonitor_reason reason = fsm_settings__get_reason(r);\n+\n+\tif (!warn_once && reason > FSMONITOR_REASON_OK) {\n+\t\twarn_once = 1;\n+\t\twarning(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n+\t}\n \n \tif (fsm_mode <= FSMONITOR_MODE_DISABLED ||\n \t    istate->fsmonitor_has_run_once)\n-- \ngitgitgadget\n\n"},{"id":"463582","messageId":"b375b0ac798a673301a09903dcdfb22bedda7393.1664048782.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v12.git.1664048782.gitgitgadget@gmail.com","subject":"[PATCH v12 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-24T19:46:22Z","receivedAt":"2022-09-24T19:46:59Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nAdd documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\nCall-out experimental nature of 'fsmonitor.allowRemote' and limited file\nsystem support for 'fsmonitor.socketDir'.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Documentation/git-fsmonitor--daemon.txt | 48 +++++++++++++++++++++++--\n 1 file changed, 45 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt\nindex cc142fb8612..6ad3e518ae0 100644\n--- a/Documentation/git-fsmonitor--daemon.txt\n+++ b/Documentation/git-fsmonitor--daemon.txt\n@@ -3,7 +3,7 @@ git-fsmonitor{litdd}daemon(1)\n \n NAME\n ----\n-git-fsmonitor--daemon - A Built-in File System Monitor\n+git-fsmonitor--daemon - A Built-in Filesystem Monitor\n \n SYNOPSIS\n --------\n@@ -17,7 +17,7 @@ DESCRIPTION\n -----------\n \n A daemon to watch the working directory for file and directory\n-changes using platform-specific file system notification facilities.\n+changes using platform-specific filesystem notification facilities.\n \n This daemon communicates directly with commands like `git status`\n using the link:technical/api-simple-ipc.html[simple IPC] interface\n@@ -63,13 +63,55 @@ CAVEATS\n -------\n \n The fsmonitor daemon does not currently know about submodules and does\n-not know to filter out file system events that happen within a\n+not know to filter out filesystem events that happen within a\n submodule.  If fsmonitor daemon is watching a super repo and a file is\n modified within the working directory of a submodule, it will report\n the change (as happening against the super repo).  However, the client\n will properly ignore these extra events, so performance may be affected\n but it will not cause an incorrect result.\n \n+By default, the fsmonitor daemon refuses to work against network-mounted\n+repositories; this may be overridden by setting `fsmonitor.allowRemote` to\n+`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n+correctly with all network-mounted repositories and such use is considered\n+experimental.\n+\n+On Mac OS, the inter-process communication (IPC) between various Git\n+commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a\n+special type of file -- which is supported by native Mac OS filesystems,\n+but not on network-mounted filesystems, NTFS, or FAT32.  Other filesystems\n+may or may not have the needed support; the fsmonitor daemon is not guaranteed\n+to work with these filesystems and such use is considered experimental.\n+\n+By default, the socket is created in the `.git` directory, however, if the\n+`.git` directory is on a network-mounted filesystem, it will be instead be\n+created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a\n+network-mounted filesystem in which case you must set the configuration\n+variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native\n+filesystem in which to create the socket file.\n+\n+If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)\n+is on a native Mac OS file filesystem the fsmonitor daemon will report an\n+error that will cause the daemon and the currently running command to exit.\n+\n+CONFIGURATION\n+-------------\n+\n+When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])\n+the fsmonitor daemon will pay attention to the following configuration\n+variables:\n+\n+`fsmonitor.allowRemote`::\n+\tBy default, the daemon refuses to work against network-mounted\n+\trepositories. Setting `fsmonitor.allowRemote` to `true` overrides\n+\tthis behavior.\n+\n+`fsmonitor.socketDir`::\n+    This Mac OS-specific option, if set, specifies the directory in\n+    which to create the Unix domain socket used for communication\n+    between fsmonitor and various Git commands. The directory must\n+    reside on a native Mac OS filesystem as discussed above.\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \ngitgitgadget\n"},{"id":"463601","messageId":"BL0PR05MB5571F2A2BA548C5D8F2F5E99D9539@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"421d77775dc24e52ab26336a1a82ed0e7b15ff5a.1664048782.git.gitgitgadget@gmail.com","subject":"RE: [PATCH v12 5/6] fsmonitor: check for compatability before communicating with fsmonitor","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-25T14:00:27Z","receivedAt":"2022-09-25T14:00:37Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>\n> Sent: Saturday, September 24, 2022 3:46 PM\n> To: git@vger.kernel.org\n> Cc: Jeff Hostetler <git@jeffhostetler.com>; Eric Sunshine\n> <sunshine@sunshineco.com>; Torsten Bögershausen <tboegi@web.de>;\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com>; Ramsay Jones\n> <ramsay@ramsayjones.plus.com>; Johannes Schindelin\n> <Johannes.Schindelin@gmx.de>; Eric DeCosta <edecosta@mathworks.com>;\n> Eric DeCosta <edecosta@mathworks.com>\n> Subject: [PATCH v12 5/6] fsmonitor: check for compatability before\n> communicating with fsmonitor\n> \n> From: Eric DeCosta <edecosta@mathworks.com>\n> \n> If fsmonitor is not in a compatible state, die with an appropriate error\n> messge.\n> \n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n\nGrr. Should be \"warn with an appropriate error message\".\n\n-Eric\n\n>  compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n>  fsmonitor-settings.c                   | 10 +++++++---\n>  fsmonitor-settings.h                   |  2 +-\n>  fsmonitor.c                            |  7 +++++++\n>  4 files changed, 16 insertions(+), 5 deletions(-)\n> \n> diff --git a/compat/fsmonitor/fsm-settings-darwin.c\n> b/compat/fsmonitor/fsm-settings-darwin.c\n> index 40da2d3b533..44233125df8 100644\n> --- a/compat/fsmonitor/fsm-settings-darwin.c\n> +++ b/compat/fsmonitor/fsm-settings-darwin.c\n> @@ -38,7 +38,7 @@ static enum fsmonitor_reason\n> check_uds_volume(struct repository *r)\n>  \tstrbuf_release(&path);\n> \n>  \tif (fs.is_remote)\n> -\t\treturn FSMONITOR_REASON_REMOTE;\n> +\t\treturn FSMONITOR_REASON_NOSOCKETS;\n> \n>  \tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n>  \t\treturn FSMONITOR_REASON_NOSOCKETS;\n> diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c index\n> 531a1b6f956..8592a4d9bad 100644\n> --- a/fsmonitor-settings.c\n> +++ b/fsmonitor-settings.c\n> @@ -1,6 +1,7 @@\n>  #include \"cache.h\"\n>  #include \"config.h\"\n>  #include \"repository.h\"\n> +#include \"fsmonitor-ipc.h\"\n>  #include \"fsmonitor-settings.h\"\n>  #include \"fsmonitor-path-utils.h\"\n> \n> @@ -242,10 +243,11 @@ enum fsmonitor_reason\n> fsm_settings__get_reason(struct repository *r)\n>  \treturn r->settings.fsmonitor->reason;\n>  }\n> \n> -char *fsm_settings__get_incompatible_msg(const struct repository *r,\n> +char *fsm_settings__get_incompatible_msg(struct repository *r,\n>  \t\t\t\t\t enum fsmonitor_reason reason)\n>  {\n>  \tstruct strbuf msg = STRBUF_INIT;\n> +\tconst char *socket_dir;\n> \n>  \tswitch (reason) {\n>  \tcase FSMONITOR_REASON_UNTESTED:\n> @@ -281,9 +283,11 @@ char *fsm_settings__get_incompatible_msg(const\n> struct repository *r,\n>  \t\tgoto done;\n> \n>  \tcase FSMONITOR_REASON_NOSOCKETS:\n> +\t\tsocket_dir = dirname((char *)fsmonitor_ipc__get_path(r));\n>  \t\tstrbuf_addf(&msg,\n> -\t\t\t    _(\"repository '%s' is incompatible with fsmonitor\n> due to lack of Unix sockets\"),\n> -\t\t\t    r->worktree);\n> +\t\t\t    _(\"socket directory '%s' is incompatible with\n> fsmonitor due\"\n> +\t\t\t\t  \" to lack of Unix sockets support\"),\n> +\t\t\t    socket_dir);\n>  \t\tgoto done;\n>  \t}\n> \n> diff --git a/fsmonitor-settings.h b/fsmonitor-settings.h index\n> 0721617b95a..ab02e3995ee 100644\n> --- a/fsmonitor-settings.h\n> +++ b/fsmonitor-settings.h\n> @@ -33,7 +33,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct\n> repository *r);  const char *fsm_settings__get_hook_path(struct repository\n> *r);\n> \n>  enum fsmonitor_reason fsm_settings__get_reason(struct repository *r); -\n> char *fsm_settings__get_incompatible_msg(const struct repository *r,\n> +char *fsm_settings__get_incompatible_msg(struct repository *r,\n>  \t\t\t\t\t enum fsmonitor_reason reason);\n> \n>  struct fsmonitor_settings;\n> diff --git a/fsmonitor.c b/fsmonitor.c\n> index 57d6a483bee..540736b39fd 100644\n> --- a/fsmonitor.c\n> +++ b/fsmonitor.c\n> @@ -295,6 +295,7 @@ static int fsmonitor_force_update_threshold = 100;\n> \n>  void refresh_fsmonitor(struct index_state *istate)  {\n> +\tstatic int warn_once = 0;\n>  \tstruct strbuf query_result = STRBUF_INIT;\n>  \tint query_success = 0, hook_version = -1;\n>  \tsize_t bol = 0; /* beginning of line */ @@ -305,6 +306,12 @@ void\n> refresh_fsmonitor(struct index_state *istate)\n>  \tint is_trivial = 0;\n>  \tstruct repository *r = istate->repo ? istate->repo : the_repository;\n>  \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);\n> +\tenum fsmonitor_reason reason = fsm_settings__get_reason(r);\n> +\n> +\tif (!warn_once && reason > FSMONITOR_REASON_OK) {\n> +\t\twarn_once = 1;\n> +\t\twarning(\"%s\", fsm_settings__get_incompatible_msg(r,\n> reason));\n> +\t}\n> \n>  \tif (fsm_mode <= FSMONITOR_MODE_DISABLED ||\n>  \t    istate->fsmonitor_has_run_once)\n> --\n> gitgitgadget\n\n"},{"id":"463602","messageId":"BL0PR05MB5571E918B9EFD632C30E83C3D9539@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"pull.1326.v12.git.1664048782.gitgitgadget@gmail.com","subject":"RE: [PATCH v12 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-25T14:18:22Z","receivedAt":"2022-09-25T14:18:34Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>\n> Sent: Saturday, September 24, 2022 3:46 PM\n> To: git@vger.kernel.org\n> Cc: Jeff Hostetler <git@jeffhostetler.com>; Eric Sunshine\n> <sunshine@sunshineco.com>; Torsten Bögershausen <tboegi@web.de>;\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com>; Ramsay Jones\n> <ramsay@ramsayjones.plus.com>; Johannes Schindelin\n> <Johannes.Schindelin@gmx.de>; Eric DeCosta <edecosta@mathworks.com>\n> Subject: [PATCH v12 0/6] fsmonitor: option to allow fsmonitor to run against\n> network-mounted repos\n> \n> Follow-on to the work done to allow Windows to work against network-\n> mounted repos for macOS.\n> \n> Have macOS take advantage of the same configuration option,\n> 'fsmonitor.allowRemote' that was introduced for Windows. Setting this\n> option to true will override the default behavior (erroring-out) when a\n> network-mounted repo is detected by fsmonitor.\n> \n> The added wrinkle being that the Unix domain socket (UDS) file used for IPC\n> cannot be created in a network location; instead $HOME is used if the default\n> location is on the network. The user may, optionally, set the\n> 'fsmonitor.socketDir' configuration option to a valid, local directory if $HOME\n> itself is on the network or is simply not the desired location for the UDS file.\n> \n> An additional issue is that for mount points in the root directory, FSEvents\n> does not report a path that matches the worktree directory due to the\n> introduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\n> to the worktree directory by interrogating the root filesystem for synthetic\n> firmlinks and using that information to translate the path.\n> \n> v12 differs from v11:\n> \n> * bug fixes\n> \n> v11 differs from v10:\n> \n> * incorporates code review feedback\n> * fix memory leak in fsm-listen-darwin.c\n> \n> v10 differs from v9:\n> \n> * incorporates code review feedback\n> * improves error messaging for incompatible socket directory\n> \n> v9 differs from v8:\n> \n> * incorporates code review feedback\n> * check for incompatibility before communicating with fsmonitor\n> \n> v8 differs from v7:\n> \n> * incorporates code review feedback\n> * gets the rebase right\n> \n> v7 differs from v6:\n> \n> * incorporates code review feedback\n> \n> v6 differs from v5:\n> \n> * incorporates earlier, Windows-specific changes that have not made it back\n> yet to the master branch\n> * incorporates code review feedback\n> * adds documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n> \n> v5 differs significantly from earlier versions:\n> \n> * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n> such that these options are no longer added to the settings data structure\n> but are rather read from config at point of use\n> * refactoring of code for handling platform-specific file system checks via a\n> common interface to avoid platform #ifdef in IPC code and be in-model with\n> other platform-specific fsmonitor code\n> * dealing with 'synthetic firmlinks' on macOS\n> \n> Eric DeCosta (6):\n> fsmonitor: refactor filesystem checks to common interface\n> fsmonitor: relocate socket file if .git directory is remote\n> fsmonitor: avoid socket location check if using hook\n> fsmonitor: deal with synthetic firmlinks on macOS\n> fsmonitor: check for compatability before communicating with fsmonitor\n> fsmonitor: add documentation for allowRemote and socketDir options\n> \n\nAny further thoughts? If not I think this patch set is good to merge.\n\n-Eric\n\n> Documentation/git-fsmonitor--daemon.txt | 48 ++++++- Makefile | 2 +\n> builtin/fsmonitor--daemon.c | 11 +- compat/fsmonitor/fsm-ipc-darwin.c | 52\n> +++++++ compat/fsmonitor/fsm-ipc-win32.c | 9 ++ compat/fsmonitor/fsm-\n> listen-darwin.c | 14 +- compat/fsmonitor/fsm-path-utils-darwin.c | 132\n> +++++++++++++++++ compat/fsmonitor/fsm-path-utils-win32.c | 145\n> +++++++++++++++++++ compat/fsmonitor/fsm-settings-darwin.c | 72 +++-----\n> -- compat/fsmonitor/fsm-settings-win32.c | 174 +----------------------\n> contrib/buildsystems/CMakeLists.txt | 4 + fsmonitor--daemon.h | 3 +\n> fsmonitor-ipc.c | 18 ++- fsmonitor-ipc.h | 4 +- fsmonitor-path-utils.h | 59\n> ++++++++ fsmonitor-settings.c | 68 ++++++++- fsmonitor-settings.h | 4 +-\n> fsmonitor.c | 7 +\n> 18 files changed, 580 insertions(+), 246 deletions(-) create mode 100644\n> compat/fsmonitor/fsm-ipc-darwin.c create mode 100644\n> compat/fsmonitor/fsm-ipc-win32.c create mode 100644\n> compat/fsmonitor/fsm-path-utils-darwin.c\n> create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n> create mode 100644 fsmonitor-path-utils.h\n> \n> \n> base-commit: 4b79ee4b0cd1130ba8907029cdc5f6a1632aca26\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-\n> 1326%2Fedecosta-mw%2Ffsmonitor_macos-v12 <https://protect-\n> us.mimecast.com/s/8B-_CDkx7zSvvOYvhZsW8c?domain=github.com>\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git <https://protect-\n> us.mimecast.com/s/G1p4CERy7Ah99649sZioql?domain=github.com>  pr-\n> 1326/edecosta-mw/fsmonitor_macos-v12\n> Pull-Request: https://github.com/gitgitgadget/git/pull/1326 <https://protect-\n> us.mimecast.com/s/tTa3CG6A7DIYYL3Ys0on8b?domain=github.com>\n> \n> Range-diff vs v11:\n> \n> 1: 155a6890806 = 1: 5958dab0163 fsmonitor: refactor filesystem checks to\n> common interface\n> 2: dbe113abb87 ! 2: 20220b08edb fsmonitor: relocate socket file if .git\n> directory is remote @@ compat/fsmonitor/fsm-ipc-darwin.c (new)\n> +\n> +const char *fsmonitor_ipc__get_path(struct repository *r) {\n> -+ static const char *ipc_path;\n> ++ static const char *ipc_path = NULL;\n> + SHA_CTX sha1ctx;\n> + char *sock_dir = NULL;\n> + struct strbuf ipc_file = STRBUF_INIT;\n> @@ compat/fsmonitor/fsm-ipc-darwin.c (new)\n> + if (ipc_path)\n> + return ipc_path;\n> +\n> -+ ipc_path = fsmonitor_ipc__get_default_path();\n> +\n> + /* By default the socket file is created in the .git directory */\n> -+ if (fsmonitor__is_fs_remote(ipc_path) < 1)\n> ++ if (fsmonitor__is_fs_remote(r->gitdir) < 1) { ipc_path =\n> ++ fsmonitor_ipc__get_default_path();\n> + return ipc_path;\n> ++ }\n> +\n> + SHA1_Init(&sha1ctx);\n> + SHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n> 3: 86c15299ae8 = 3: e9921550a67 fsmonitor: avoid socket location check if\n> using hook\n> 4: 1a516fd9214 = 4: 6efdc6ed74e fsmonitor: deal with synthetic firmlinks on\n> macOS\n> 5: e0fe05dabef ! 5: 421d77775dc fsmonitor: check for compatability before\n> communicating with fsmonitor @@ fsmonitor-settings.h: enum\n> fsmonitor_mode fsm_settings__get_mode(struct reposito struct\n> fsmonitor_settings;\n> \n> ## fsmonitor.c ##\n> +@@ fsmonitor.c: static int fsmonitor_force_update_threshold = 100;\n> +\n> + void refresh_fsmonitor(struct index_state *istate) {\n> ++ static int warn_once = 0;\n> + struct strbuf query_result = STRBUF_INIT; int query_success = 0,\n> + hook_version = -1; size_t bol = 0; /* beginning of line */\n> @@ fsmonitor.c: void refresh_fsmonitor(struct index_state *istate) int\n> is_trivial = 0; struct repository *r = istate->repo ? istate->repo :\n> the_repository; enum fsmonitor_mode fsm_mode =\n> fsm_settings__get_mode(r);\n> + enum fsmonitor_reason reason = fsm_settings__get_reason(r);\n> +\n> -+ if (reason > FSMONITOR_REASON_OK)\n> ++ if (!warn_once && reason > FSMONITOR_REASON_OK) { warn_once = 1;\n> + warning(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n> ++ }\n> \n> if (fsm_mode <= FSMONITOR_MODE_DISABLED ||\n> istate->fsmonitor_has_run_once)\n> 6: 3200b505988 = 6: b375b0ac798 fsmonitor: add documentation for\n> allowRemote and socketDir options\n> \n> --\n> gitgitgadget\n\n"},{"id":"463633","messageId":"220926.864jwu181g.gmgdl@evledraar.gmail.com","threadId":"58324","inReplyTo":"b375b0ac798a673301a09903dcdfb22bedda7393.1664048782.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v12 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-09-26T15:11:03Z","receivedAt":"2022-09-26T16:27:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Sep 24 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n>\n> Add documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\n> Call-out experimental nature of 'fsmonitor.allowRemote' and limited file\n> system support for 'fsmonitor.socketDir'.\n>\n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n>  Documentation/git-fsmonitor--daemon.txt | 48 +++++++++++++++++++++++--\n>  1 file changed, 45 insertions(+), 3 deletions(-)\n>\n> diff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt\n> index cc142fb8612..6ad3e518ae0 100644\n> --- a/Documentation/git-fsmonitor--daemon.txt\n> +++ b/Documentation/git-fsmonitor--daemon.txt\n> @@ -3,7 +3,7 @@ git-fsmonitor{litdd}daemon(1)\n>  \n>  NAME\n>  ----\n> -git-fsmonitor--daemon - A Built-in File System Monitor\n> +git-fsmonitor--daemon - A Built-in Filesystem Monitor\n\nWe have ~400 uses of \"filesystem\" in-tree, but ~100 for \"file system\". I\ndon't mind the change per-se, but this looks like an odd \"while at it\"\nchange.\n\nSkmming your series your 1/6 even uses \"file system\" in the commit message :)\n\n>  A daemon to watch the working directory for file and directory\n> -changes using platform-specific file system notification facilities.\n> +changes using platform-specific filesystem notification facilities.\n\nMore while-at-it...\n\n>  \n>  This daemon communicates directly with commands like `git status`\n>  using the link:technical/api-simple-ipc.html[simple IPC] interface\n> @@ -63,13 +63,55 @@ CAVEATS\n>  -------\n>  \n>  The fsmonitor daemon does not currently know about submodules and does\n> -not know to filter out file system events that happen within a\n> +not know to filter out filesystem events that happen within a\n\n...and here...\n\n>  submodule.  If fsmonitor daemon is watching a super repo and a file is\n>  modified within the working directory of a submodule, it will report\n>  the change (as happening against the super repo).  However, the client\n>  will properly ignore these extra events, so performance may be affected\n>  but it will not cause an incorrect result.\n\nBut here we get to the real meat of this change...\n\n> +By default, the fsmonitor daemon refuses to work against network-mounted\n> +repositories; this may be overridden by setting `fsmonitor.allowRemote` to\n> +`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n> +correctly with all network-mounted repositories and such use is considered\n> +experimental.\n> +\n> +On Mac OS, the inter-process communication (IPC) between various Git\n> +commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a\n> +special type of file -- which is supported by native Mac OS filesystems,\n> +but not on network-mounted filesystems, NTFS, or FAT32.  Other filesystems\n> +may or may not have the needed support; the fsmonitor daemon is not guaranteed\n> +to work with these filesystems and such use is considered experimental.\n> +\n> +By default, the socket is created in the `.git` directory, however, if the\n> +`.git` directory is on a network-mounted filesystem, it will be instead be\n> +created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a\n> +network-mounted filesystem in which case you must set the configuration\n> +variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native\n> +filesystem in which to create the socket file.\n> +\n> +If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)\n> +is on a native Mac OS file filesystem the fsmonitor daemon will report an\n> +error that will cause the daemon and the currently running command to exit.\n\nFrom skimming this looks reasonable.\n\n> +CONFIGURATION\n> +-------------\n> +\n> +When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])\n> +the fsmonitor daemon will pay attention to the following configuration\n> +variables:\n> +\n> +`fsmonitor.allowRemote`::\n> +\tBy default, the daemon refuses to work against network-mounted\n> +\trepositories. Setting `fsmonitor.allowRemote` to `true` overrides\n> +\tthis behavior.\n> +\n> +`fsmonitor.socketDir`::\n> +    This Mac OS-specific option, if set, specifies the directory in\n> +    which to create the Unix domain socket used for communication\n> +    between fsmonitor and various Git commands. The directory must\n> +    reside on a native Mac OS filesystem as discussed above.\n> +\n\nBut here we should instead create Documentation/config/fsmonitor.txt,\nand include it here. See my recent 7a54d740451 (Merge branch\n'ab/dedup-config-and-command-docs', 2022-09-14) which made it so for\nmany commands.\n\nBy doing it like this \"git config\" is no longer the canonical reference\nfor all config, which it mostly was pre-7a54d740451, then was again\npost-7a54d740451, and now we'd have this exception...\n"},{"id":"463634","messageId":"220926.86zgemyxbf.gmgdl@evledraar.gmail.com","threadId":"58324","inReplyTo":"6efdc6ed74ec9224d93a1b88ff8be85d533cb30f.1664048782.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v12 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-09-26T15:16:23Z","receivedAt":"2022-09-26T16:35:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Sep 24 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n> [...]\n> +\tstate.alias.alias = NULL;\n> +\tstate.alias.points_to = NULL;\n> +\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n> +\t\terr = error(_(\"could not get worktree alias\"));\n> +\t\tgoto done;\n> +\t}\n\nAs we can see here this is in the one-off setup code...\n\n> +int fsmonitor__get_alias(const char *path, struct alias_info *info)\n> +{\n> +\tDIR * dir;\n> +\tint read;\n> +\tint retval;\n> +\tstruct dirent *de;\n> +\tstruct strbuf alias;\n> +\tstruct strbuf points_to;\n\n...more of a code clarity comment than anything, else, but...\n\n> +\n> +\tretval = 0;\n> +\tdir = opendir(\"/\");\n> +\tif (!dir)\n> +\t\treturn -1;\n> +\n> +\tstrbuf_init(&alias, 256);\n> +\tstrbuf_init(&points_to, MAXPATHLEN);\n\n\n...can't we just use the STRBUF_INIT macro here instead? most paths are\nnowhere near MAXPATHLEN, but more importantly we try to avoid these\nsorts of memory micro-managements except for hot codepaths.\n\nIn this case it's just the one-off setup of fsmonitor, isn't it? So just\nusing the default allocation pattern seems worthwhile, and will save\ne.g. anyone grepping for MAXPATHLEN looking for bugs (the MAXPATHLEN is\nsometimes not the actual maximum pathlen).\n\n> +\n> +\twhile ((de = readdir(dir)) != NULL) {\n> +\t\tstrbuf_reset(&alias);\n> +\t\tstrbuf_addch(&alias, '/');\n> +\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n> +\n> +\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n> +\t\tif (read > 0) {\n> +\t\t\tstrbuf_setlen(&points_to, read);\n> +\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n\nWe usually do (!strcmp()), not strcmp() == 0, ditto strncmp. See\nCodingGuidelines.\n> +\tdone:\n\nNit: labels shouldn't be indented.\n\n> +\tclosedir(dir);\n\nWe checked the opendir() return value, why not closedir() too?\n\n> +\tif ((strncmp(info->alias, path, len) == 0)\n\nditto !foo() v.s. foo() == 0.\n\n> +\t\t&& path[len] == '/') {\n> +\t\tstruct strbuf tmp;\n> +\t\tconst char *remainder = path + len;\n> +\t\tint ptr_len = strlen(info->points_to);\n> +\t\tint rem_len = strlen(remainder);\n\nMake these s/int/size_t/.\n\n> +\n> +\t\tstrbuf_init(&tmp, ptr_len + rem_len);\n\nAnd use st_add() here instead of \" + \". I don't think it'll overflow,\nbut it's good to guard overflows out of habit...\n\n> +\t\tstrbuf_add(&tmp, info->points_to, ptr_len);\n\nEarlier you constructed a strbuf, and then strbuf_detached() it into\nthis new \"struct alias_info\" you made. And now we're having to strlen()\nthat to get the lenght that we knew earlier?\n\nCan't we just make the member a \"struct strbuf\" instead? Maybe not, I\nhave not reviewed that aspect carefully...\n"},{"id":"463635","messageId":"220926.86v8payx6p.gmgdl@evledraar.gmail.com","threadId":"58324","inReplyTo":"421d77775dc24e52ab26336a1a82ed0e7b15ff5a.1664048782.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v12 5/6] fsmonitor: check for compatability before communicating with fsmonitor","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-09-26T15:23:56Z","receivedAt":"2022-09-26T16:38:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Sep 24 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n> [...]\n> @@ -281,9 +283,11 @@ char *fsm_settings__get_incompatible_msg(const struct repository *r,\n>  \t\tgoto done;\n>  \n>  \tcase FSMONITOR_REASON_NOSOCKETS:\n> +\t\tsocket_dir = dirname((char *)fsmonitor_ipc__get_path(r));\n>  \t\tstrbuf_addf(&msg,\n> -\t\t\t    _(\"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets\"),\n> -\t\t\t    r->worktree);\n> +\t\t\t    _(\"socket directory '%s' is incompatible with fsmonitor due\"\n> +\t\t\t\t  \" to lack of Unix sockets support\"),\n> +\t\t\t    socket_dir);\n\nCould do with less \"while at it\" here. We are:\n\n * Wrapping the string, making the functional change(s) harder to spot.\n * replacing r->worktree with socket_dir\n * Adding \" support\" to the end of the string, and replacing \"repository\" with \"socket directory\" \n\nAFAICT the continuation of the string isn't indented in the way we\nusually do, i.e. to align with the opening \".\n\n"},{"id":"463637","messageId":"220926.86r0zyywp1.gmgdl@evledraar.gmail.com","threadId":"58324","inReplyTo":"6efdc6ed74ec9224d93a1b88ff8be85d533cb30f.1664048782.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v12 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-09-26T15:27:16Z","receivedAt":"2022-09-26T16:46:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Sep 24 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n\n..one aspect I missed...\n\n> +\tstate.alias.alias = NULL;\n> +\tstate.alias.points_to = NULL;\n> +\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n> +\t\terr = error(_(\"could not get worktree alias\"));\n> +\t\tgoto done;\n\nOkey, it errored and we call error() to say it didn't work, good so far,\nbut...\n\n> +int fsmonitor__get_alias(const char *path, struct alias_info *info)\n> +{\n> +\tDIR * dir;\n> +\tint read;\n> +\tint retval;\n\n...we could just...\n\n> +\tstruct dirent *de;\n> +\tstruct strbuf alias;\n> +\tstruct strbuf points_to;\n> +\n> +\tretval = 0;\n\n...have initialized that above if we do it unconditionally, but more on\nthis below...\n\n> +\tdir = opendir(\"/\");\n> +\tif (!dir)\n> +\t\treturn -1;\n\nHere in the actual implementation, which looking at the end-state we\n*only* end up calling from that one caller we could have called\nerror_errno() to get a better message, but didn't.\n\nI think much better would be to skip that above entirely, or keep it you\nwant two errors, but then just have the more meaningful error_errno()\nhere, where we're closer to the error, and can report a better one.\n\nOf course we might sometimes have a good error, and sometimes a bad one,\nbut...(continued below)\n\n> +\n> +\tstrbuf_init(&alias, 256);\n> +\tstrbuf_init(&points_to, MAXPATHLEN);\n> +\n> +\twhile ((de = readdir(dir)) != NULL) {\n> +\t\tstrbuf_reset(&alias);\n> +\t\tstrbuf_addch(&alias, '/');\n> +\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n> +\n> +\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n\nI think a:\n\n\tif (!read)\n\t\tBUG(\"got 0 from readlink?\");\n\nOr something would be a good paranoia addition, as you're technically\nrelying on...\n\n> +\t\tif (read > 0) {\n> +\t\t\tstrbuf_setlen(&points_to, read);\n> +\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n> +\t\t\t\t&& path[points_to.len] == '/') {\n> +\t\t\t\tinfo->alias = strbuf_detach(&alias, NULL);\n> +\t\t\t\tinfo->points_to = strbuf_detach(&points_to, NULL);\n> +\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n> +\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n> +\t\t\t\t\tpath, info->alias, info->points_to);\n> +\t\t\t\tretval = 0;\n> +\t\t\t\tgoto done;\n> +\t\t\t}\n> +\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n\n...the possibility that we return 0 but a stale errno happens to be set,\nI don't think it'll happen in practice and that it always returned -1 if\nwe get here, but being strict with calling syscalls is generally good.\n\n> +\t\t\ttrace_printf_key(&trace_fsmonitor, \"Error %s\", strerror(errno));\n\n(continued from above)..here we see the only codepath that sets retval\n!= 0,\n\n> +\t\t\tretval = -1;\n\nHere we could have just called error_errno() instead.\n\n> + * The caller owns the storage that the returned string occupies and\n> + * is responsible for releasing it with `free(3)` when done.\n\nnit: we could just put a full stop after \"it\" and skip the\nrest. I.e. trust that the reader knows that allocated memory is freed\nwith free().\n\n> + */\n> +char *fsmonitor__resolve_alias(const char *path,\n> +\tconst struct alias_info *info);\n> +\n> +\n\nnit: extra whitespace at end of file.\n>  #endif\n\n"},{"id":"463689","messageId":"BL0PR05MB5571BE9E38E1E972ED3077C6D9559@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"220926.86v8payx6p.gmgdl@evledraar.gmail.com","subject":"RE: [PATCH v12 5/6] fsmonitor: check for compatability before communicating with fsmonitor","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-27T01:25:35Z","receivedAt":"2022-09-27T01:25:46Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> Sent: Monday, September 26, 2022 11:24 AM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>\n> Cc: git@vger.kernel.org; Jeff Hostetler <git@jeffhostetler.com>; Eric Sunshine\n> <sunshine@sunshineco.com>; Torsten Bögershausen <tboegi@web.de>;\n> Ramsay Jones <ramsay@ramsayjones.plus.com>; Johannes Schindelin\n> <Johannes.Schindelin@gmx.de>; Eric DeCosta <edecosta@mathworks.com>\n> Subject: Re: [PATCH v12 5/6] fsmonitor: check for compatability before\n> communicating with fsmonitor\n> \n> \n> On Sat, Sep 24 2022, Eric DeCosta via GitGitGadget wrote:\n> \n> > From: Eric DeCosta <edecosta@mathworks.com> [...] @@ -281,9 +283,11\n> @@\n> > char *fsm_settings__get_incompatible_msg(const struct repository *r,\n> >  \t\tgoto done;\n> >\n> >  \tcase FSMONITOR_REASON_NOSOCKETS:\n> > +\t\tsocket_dir = dirname((char *)fsmonitor_ipc__get_path(r));\n> >  \t\tstrbuf_addf(&msg,\n> > -\t\t\t    _(\"repository '%s' is incompatible with fsmonitor\n> due to lack of Unix sockets\"),\n> > -\t\t\t    r->worktree);\n> > +\t\t\t    _(\"socket directory '%s' is incompatible with\n> fsmonitor due\"\n> > +\t\t\t\t  \" to lack of Unix sockets support\"),\n> > +\t\t\t    socket_dir);\n> \n> Could do with less \"while at it\" here. We are:\n> \n>  * Wrapping the string, making the functional change(s) harder to spot.\n>  * replacing r->worktree with socket_dir\n>  * Adding \" support\" to the end of the string, and replacing \"repository\" with\n> \"socket directory\"\n> \n> AFAICT the continuation of the string isn't indented in the way we usually do,\n> i.e. to align with the opening \".\n\nThe string, when properly indented, exceeds an 80 character line length. I'll fix the indentation, but I don't think there's a much better alternative to the wrapping.\n\nThe worktree could be in a perfectly fine location whereas the socket_dir may not . Crafting the error message the way I did reflects where the problem is rather than reporting a potentially misleading error about the repository.\n\n-Eric\n\n"},{"id":"463691","messageId":"BL0PR05MB5571D13B63CFC7AE2C3D6EDFD9559@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"220926.86zgemyxbf.gmgdl@evledraar.gmail.com","subject":"RE: [PATCH v12 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-27T01:53:47Z","receivedAt":"2022-09-27T01:53:57Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> Sent: Monday, September 26, 2022 11:16 AM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>\n> Cc: git@vger.kernel.org; Jeff Hostetler <git@jeffhostetler.com>; Eric Sunshine\n> <sunshine@sunshineco.com>; Torsten Bögershausen <tboegi@web.de>;\n> Ramsay Jones <ramsay@ramsayjones.plus.com>; Johannes Schindelin\n> <Johannes.Schindelin@gmx.de>; Eric DeCosta <edecosta@mathworks.com>\n> Subject: Re: [PATCH v12 4/6] fsmonitor: deal with synthetic firmlinks on\n> macOS\n> \n> \n> On Sat, Sep 24 2022, Eric DeCosta via GitGitGadget wrote:\n> \n> > From: Eric DeCosta <edecosta@mathworks.com> [...]\n> > +\tstate.alias.alias = NULL;\n> > +\tstate.alias.points_to = NULL;\n> > +\tif (fsmonitor__get_alias(state.path_worktree_watch.buf,\n> &state.alias)) {\n> > +\t\terr = error(_(\"could not get worktree alias\"));\n> > +\t\tgoto done;\n> > +\t}\n> \n> As we can see here this is in the one-off setup code...\n> \n> > +int fsmonitor__get_alias(const char *path, struct alias_info *info) {\n> > +\tDIR * dir;\n> > +\tint read;\n> > +\tint retval;\n> > +\tstruct dirent *de;\n> > +\tstruct strbuf alias;\n> > +\tstruct strbuf points_to;\n> \n> ...more of a code clarity comment than anything, else, but...\n> \n> > +\n> > +\tretval = 0;\n> > +\tdir = opendir(\"/\");\n> > +\tif (!dir)\n> > +\t\treturn -1;\n> > +\n> > +\tstrbuf_init(&alias, 256);\n> > +\tstrbuf_init(&points_to, MAXPATHLEN);\n> \n> \n> ...can't we just use the STRBUF_INIT macro here instead? most paths are\n> nowhere near MAXPATHLEN, but more importantly we try to avoid these\n> sorts of memory micro-managements except for hot codepaths.\n> \n> In this case it's just the one-off setup of fsmonitor, isn't it? So just using the\n> default allocation pattern seems worthwhile, and will save e.g. anyone\n> grepping for MAXPATHLEN looking for bugs (the MAXPATHLEN is sometimes\n> not the actual maximum pathlen).\n> \nOK, makes sense.\n\n> > +\n> > +\twhile ((de = readdir(dir)) != NULL) {\n> > +\t\tstrbuf_reset(&alias);\n> > +\t\tstrbuf_addch(&alias, '/');\n> > +\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n> > +\n> > +\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n> > +\t\tif (read > 0) {\n> > +\t\t\tstrbuf_setlen(&points_to, read);\n> > +\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n> \n> We usually do (!strcmp()), not strcmp() == 0, ditto strncmp. See\n> CodingGuidelines.\n> > +\tdone:\n> \nFixed.\n\n> Nit: labels shouldn't be indented.\n> \nFixed\n\n> > +\tclosedir(dir);\n> \n> We checked the opendir() return value, why not closedir() too?\n> \nOK, will do.\n\n> > +\tif ((strncmp(info->alias, path, len) == 0)\n> \n> ditto !foo() v.s. foo() == 0.\n> \n> > +\t\t&& path[len] == '/') {\n> > +\t\tstruct strbuf tmp;\n> > +\t\tconst char *remainder = path + len;\n> > +\t\tint ptr_len = strlen(info->points_to);\n> > +\t\tint rem_len = strlen(remainder);\n> \n> Make these s/int/size_t/.\n> \n> > +\n> > +\t\tstrbuf_init(&tmp, ptr_len + rem_len);\n> \n> And use st_add() here instead of \" + \". I don't think it'll overflow, but it's good\n> to guard overflows out of habit...\n> \nSure, just strbuf_add(). Then I don't need ptr_len or rem_len either.\n\n> > +\t\tstrbuf_add(&tmp, info->points_to, ptr_len);\n> \n> Earlier you constructed a strbuf, and then strbuf_detached() it into this new\n> \"struct alias_info\" you made. And now we're having to strlen() that to get the\n> lenght that we knew earlier?\n> \n> Can't we just make the member a \"struct strbuf\" instead? Maybe not, I have\n> not reviewed that aspect carefully...\n>\nCertainly could do that.\n\n-Eric\n\n"},{"id":"463692","messageId":"CAPig+cSsKu9N+vSx5gTuxksi6sDczszXntQnMinHgRZVfY9JGA@mail.gmail.com","threadId":"58324","inReplyTo":"220926.864jwu181g.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v12 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-09-27T02:16:19Z","receivedAt":"2022-09-27T02:16:37Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Sep 26, 2022 at 11:15 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> On Sat, Sep 24 2022, Eric DeCosta via GitGitGadget wrote:\n> > Add documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\n> > Call-out experimental nature of 'fsmonitor.allowRemote' and limited file\n> > system support for 'fsmonitor.socketDir'.\n> >\n> > Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> > ---\n> > -git-fsmonitor--daemon - A Built-in File System Monitor\n> > +git-fsmonitor--daemon - A Built-in Filesystem Monitor\n>\n> We have ~400 uses of \"filesystem\" in-tree, but ~100 for \"file system\". I\n> don't mind the change per-se, but this looks like an odd \"while at it\"\n> change.\n>\n> >  A daemon to watch the working directory for file and directory\n> > -changes using platform-specific file system notification facilities.\n> > +changes using platform-specific filesystem notification facilities.\n>\n> More while-at-it...\n\nThese changes may have been in response to my review[1], though I did\nsay that such a change was outside the scope of this series. Perhaps I\nneed to choose my wording more carefully?\n\n[1]: https://lore.kernel.org/git/CAPig+cQ5SMw+0Cwtw47LQM59-mQjJaOPe_LTybAC2j=3F9OywA@mail.gmail.com/\n"},{"id":"463694","messageId":"BL0PR05MB55710CE7C3AB27C3F4CF738DD9559@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"CAPig+cSsKu9N+vSx5gTuxksi6sDczszXntQnMinHgRZVfY9JGA@mail.gmail.com","subject":"RE: [PATCH v12 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-09-27T04:03:10Z","receivedAt":"2022-09-27T04:03:19Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Eric Sunshine <sunshine@sunshineco.com>\n> Sent: Monday, September 26, 2022 10:16 PM\n> To: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> Cc: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>; Git List\n> <git@vger.kernel.org>; Jeff Hostetler <git@jeffhostetler.com>; Torsten\n> Bögershausen <tboegi@web.de>; Ramsay Jones\n> <ramsay@ramsayjones.plus.com>; Johannes Schindelin\n> <Johannes.Schindelin@gmx.de>; Eric DeCosta <edecosta@mathworks.com>\n> Subject: Re: [PATCH v12 6/6] fsmonitor: add documentation for allowRemote\n> and socketDir options\n> \n> On Mon, Sep 26, 2022 at 11:15 AM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n> > On Sat, Sep 24 2022, Eric DeCosta via GitGitGadget wrote:\n> > > Add documentation for 'fsmonitor.allowRemote' and\n> 'fsmonitor.socketDir'.\n> > > Call-out experimental nature of 'fsmonitor.allowRemote' and limited\n> > > file system support for 'fsmonitor.socketDir'.\n> > >\n> > > Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> > > ---\n> > > -git-fsmonitor--daemon - A Built-in File System Monitor\n> > > +git-fsmonitor--daemon - A Built-in Filesystem Monitor\n> >\n> > We have ~400 uses of \"filesystem\" in-tree, but ~100 for \"file system\".\n> > I don't mind the change per-se, but this looks like an odd \"while at it\"\n> > change.\n> >\n> > > A daemon to watch the working directory for file and directory\n> > > -changes using platform-specific file system notification facilities.\n> > > +changes using platform-specific filesystem notification facilities.\n> >\n> > More while-at-it...\n> \n> These changes may have been in response to my review[1], though I did say\n> that such a change was outside the scope of this series. Perhaps I need to\n> choose my wording more carefully?\n> \n> [1]: https://lore.kernel.org/git/CAPig+cQ5SMw+0Cwtw47LQM59-\n> mQjJaOPe_LTybAC2j=3F9OywA@mail.gmail.com/ <https://protect-\n> us.mimecast.com/s/STwOCG6A7DIYgAqYc7Wdth?domain=lore.kernel.org>\n>\nYour wording was fine. Next time I'll know better and leave it for another patch set.\n\n-Eric\n\n"},{"id":"463761","messageId":"pull.1326.v13.git.1664312253.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v12.git.1664048782.gitgitgadget@gmail.com","subject":"[PATCH v13 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-27T20:57:27Z","receivedAt":"2022-09-27T20:57:45Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"Follow-on to the work done to allow Windows to work against network-mounted\nrepos for macOS.\n\nHave macOS take advantage of the same configuration option,\n'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\nto true will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for IPC\ncannot be created in a network location; instead $HOME is used if the\ndefault location is on the network. The user may, optionally, set the\n'fsmonitor.socketDir' configuration option to a valid, local directory if\n$HOME itself is on the network or is simply not the desired location for the\nUDS file.\n\nAn additional issue is that for mount points in the root directory, FSEvents\ndoes not report a path that matches the worktree directory due to the\nintroduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\nto the worktree directory by interrogating the root filesystem for synthetic\nfirmlinks and using that information to translate the path.\n\nv13 differs from v12:\n\n * code review feedback\n\nv12 differs from v11:\n\n * bug fixes\n\nv11 differs from v10:\n\n * incorporates code review feedback\n * fix memory leak in fsm-listen-darwin.c\n\nv10 differs from v9:\n\n * incorporates code review feedback\n * improves error messaging for incompatible socket directory\n\nv9 differs from v8:\n\n * incorporates code review feedback\n * check for incompatibility before communicating with fsmonitor\n\nv8 differs from v7:\n\n * incorporates code review feedback\n * gets the rebase right\n\nv7 differs from v6:\n\n * incorporates code review feedback\n\nv6 differs from v5:\n\n * incorporates earlier, Windows-specific changes that have not made it back\n   yet to the master branch\n * incorporates code review feedback\n * adds documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n\nv5 differs significantly from earlier versions:\n\n * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n   such that these options are no longer added to the settings data\n   structure but are rather read from config at point of use\n * refactoring of code for handling platform-specific file system checks via\n   a common interface to avoid platform #ifdef in IPC code and be in-model\n   with other platform-specific fsmonitor code\n * dealing with 'synthetic firmlinks' on macOS\n\nEric DeCosta (6):\n  fsmonitor: refactor filesystem checks to common interface\n  fsmonitor: relocate socket file if .git directory is remote\n  fsmonitor: avoid socket location check if using hook\n  fsmonitor: deal with synthetic firmlinks on macOS\n  fsmonitor: check for compatability before communicating with fsmonitor\n  fsmonitor: add documentation for allowRemote and socketDir options\n\n Documentation/config.txt                   |   2 +\n Documentation/config/fsmonitor--daemon.txt |  11 ++\n Documentation/git-fsmonitor--daemon.txt    |  37 ++++-\n Makefile                                   |   2 +\n builtin/fsmonitor--daemon.c                |  13 +-\n compat/fsmonitor/fsm-ipc-darwin.c          |  52 ++++++\n compat/fsmonitor/fsm-ipc-win32.c           |   9 ++\n compat/fsmonitor/fsm-listen-darwin.c       |  14 +-\n compat/fsmonitor/fsm-path-utils-darwin.c   | 133 ++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c    | 145 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c     |  72 +++------\n compat/fsmonitor/fsm-settings-win32.c      | 174 +--------------------\n contrib/buildsystems/CMakeLists.txt        |   4 +\n fsmonitor--daemon.h                        |   3 +\n fsmonitor-ipc.c                            |  18 +--\n fsmonitor-ipc.h                            |   4 +-\n fsmonitor-path-utils.h                     |  57 +++++++\n fsmonitor-settings.c                       |  68 +++++++-\n fsmonitor-settings.h                       |   4 +-\n fsmonitor.c                                |   7 +\n 20 files changed, 583 insertions(+), 246 deletions(-)\n create mode 100644 Documentation/config/fsmonitor--daemon.txt\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\n\nbase-commit: 2a7d63a2453e2c30353342a2c9385fa22a846987\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v13\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v13\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v12:\n\n 1:  5958dab0163 = 1:  0b9b64428c5 fsmonitor: refactor filesystem checks to common interface\n 2:  20220b08edb ! 2:  680d4c83f99 fsmonitor: relocate socket file if .git directory is remote\n     @@ Metadata\n       ## Commit message ##\n          fsmonitor: relocate socket file if .git directory is remote\n      \n     -    If the .git directory is on a remote file system, create the socket\n     +    If the .git directory is on a remote filesystem, create the socket\n          file in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n      \n          Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n 3:  e9921550a67 = 3:  7987d0c1f33 fsmonitor: avoid socket location check if using hook\n 4:  6efdc6ed74e ! 4:  324eb5acd85 fsmonitor: deal with synthetic firmlinks on macOS\n     @@ builtin/fsmonitor--daemon.c: static int fsmonitor_run_daemon(void)\n       \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n       \tstate.nr_paths_watching = 1;\n       \n     -+\tstate.alias.alias = NULL;\n     -+\tstate.alias.points_to = NULL;\n     ++\tstrbuf_init(&state.alias.alias, 0);\n     ++\tstrbuf_init(&state.alias.points_to, 0);\n      +\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n      +\t\terr = error(_(\"could not get worktree alias\"));\n      +\t\tgoto done;\n     @@ builtin/fsmonitor--daemon.c: static int fsmonitor_run_daemon(void)\n       \t/*\n       \t * We create and delete cookie files somewhere inside the .git\n       \t * directory to help us keep sync with the file system.  If\n     +@@ builtin/fsmonitor--daemon.c: done:\n     + \tstrbuf_release(&state.path_gitdir_watch);\n     + \tstrbuf_release(&state.path_cookie_prefix);\n     + \tstrbuf_release(&state.path_ipc);\n     ++\tstrbuf_release(&state.alias.alias);\n     ++\tstrbuf_release(&state.alias.points_to);\n     + \n     + \treturn err;\n     + }\n      \n       ## compat/fsmonitor/fsm-listen-darwin.c ##\n      @@\n     @@ compat/fsmonitor/fsm-path-utils-darwin.c: int fsmonitor__is_fs_remote(const char\n      + */\n      +int fsmonitor__get_alias(const char *path, struct alias_info *info)\n      +{\n     -+\tDIR * dir;\n     ++\tDIR *dir;\n      +\tint read;\n     -+\tint retval;\n     ++\tint retval = -1;\n      +\tstruct dirent *de;\n      +\tstruct strbuf alias;\n      +\tstruct strbuf points_to;\n      +\n     -+\tretval = 0;\n      +\tdir = opendir(\"/\");\n     -+\tif (!dir)\n     ++\tif (!dir) {\n     ++\t\terror_errno(\"opendir('/') failed\");\n      +\t\treturn -1;\n     ++\t}\n      +\n      +\tstrbuf_init(&alias, 256);\n     ++\n     ++\t/* no way of knowing what the link will resolve to, so MAXPATHLEN */\n      +\tstrbuf_init(&points_to, MAXPATHLEN);\n      +\n      +\twhile ((de = readdir(dir)) != NULL) {\n     @@ compat/fsmonitor/fsm-path-utils-darwin.c: int fsmonitor__is_fs_remote(const char\n      +\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n      +\t\tif (read > 0) {\n      +\t\t\tstrbuf_setlen(&points_to, read);\n     -+\t\t\tif ((strncmp(points_to.buf, path, points_to.len) == 0)\n     ++\t\t\tif ((!strncmp(points_to.buf, path, points_to.len))\n      +\t\t\t\t&& path[points_to.len] == '/') {\n     -+\t\t\t\tinfo->alias = strbuf_detach(&alias, NULL);\n     -+\t\t\t\tinfo->points_to = strbuf_detach(&points_to, NULL);\n     ++\t\t\t\tstrbuf_addbuf(&info->alias, &alias);\n     ++\t\t\t\tstrbuf_addbuf(&info->points_to, &points_to);\n      +\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n      +\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n     -+\t\t\t\t\tpath, info->alias, info->points_to);\n     ++\t\t\t\t\tpath, info->alias.buf, info->points_to.buf);\n      +\t\t\t\tretval = 0;\n      +\t\t\t\tgoto done;\n      +\t\t\t}\n     ++\t\t} else if (!read) {\n     ++\t\t\tBUG(\"readlink returned 0\");\n      +\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n     -+\t\t\ttrace_printf_key(&trace_fsmonitor, \"Error %s\", strerror(errno));\n     -+\t\t\tretval = -1;\n     ++\t\t\terror_errno(\"readlink('%s') failed\", alias.buf);\n      +\t\t\tgoto done;\n      +\t\t}\n      +\t}\n     ++\tretval = 0; /* no alias */\n      +\n     -+\tdone:\n     -+\tclosedir(dir);\n     ++done:\n     ++\tif (closedir(dir) < 0)\n     ++\t\twarning_errno(\"closedir('/') failed\");\n      +\tstrbuf_release(&alias);\n      +\tstrbuf_release(&points_to);\n      +\treturn retval;\n     @@ compat/fsmonitor/fsm-path-utils-darwin.c: int fsmonitor__is_fs_remote(const char\n      +char *fsmonitor__resolve_alias(const char *path,\n      +\tconst struct alias_info *info)\n      +{\n     -+\tint len = info->alias ? strlen(info->alias) : 0;\n     -+\n     -+\tif (!len)\n     ++\tif (!info->alias.len)\n      +\t\treturn NULL;\n      +\n     -+\tif ((strncmp(info->alias, path, len) == 0)\n     -+\t\t&& path[len] == '/') {\n     -+\t\tstruct strbuf tmp;\n     -+\t\tconst char *remainder = path + len;\n     -+\t\tint ptr_len = strlen(info->points_to);\n     -+\t\tint rem_len = strlen(remainder);\n     ++\tif ((!strncmp(info->alias.buf, path, info->alias.len))\n     ++\t\t&& path[info->alias.len] == '/') {\n     ++\t\tstruct strbuf tmp = STRBUF_INIT;\n     ++\t\tconst char *remainder = path + info->alias.len;\n      +\n     -+\t\tstrbuf_init(&tmp, ptr_len + rem_len);\n     -+\t\tstrbuf_add(&tmp, info->points_to, ptr_len);\n     -+\t\tstrbuf_add(&tmp, remainder, rem_len);\n     ++\t\tstrbuf_addbuf(&tmp, &info->points_to);\n     ++\t\tstrbuf_add(&tmp, remainder, strlen(remainder));\n      +\t\treturn strbuf_detach(&tmp, NULL);\n      +\t}\n      +\n     @@ fsmonitor-path-utils.h\n      +\n      +struct alias_info\n      +{\n     -+\tchar *alias;\n     -+\tchar *points_to;\n     ++\tstruct strbuf alias;\n     ++\tstruct strbuf points_to;\n      +};\n      +\n       struct fs_info {\n       \tint is_remote;\n       \tchar *typename;\n     + };\n     + \n     + /*\n     +- * Get some basic filesystem informtion for the given path\n     ++ * Get some basic filesystem information for the given path\n     +  *\n     +  * Returns -1 on error, zero otherwise.\n     +  */\n      @@ fsmonitor-path-utils.h: int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n        */\n       int fsmonitor__is_fs_remote(const char *path);\n     @@ fsmonitor-path-utils.h: int fsmonitor__get_fs_info(const char *path, struct fs_i\n      + *\n      + * Returns -1 on error, 0 otherwise.\n      + *\n     -+ * The caller owns the storage that is occupied by set info.alias and\n     -+ * info.points_to and is responsible for releasing it with `free(3)`\n     -+ * when done.\n     ++ * The caller owns the storage that is occupied by info.alias and\n     ++ * info.points_to and is responsible for releasing it.\n      + */\n      +int fsmonitor__get_alias(const char *path, struct alias_info *info);\n      +\n     @@ fsmonitor-path-utils.h: int fsmonitor__get_fs_info(const char *path, struct fs_i\n      + * Returns the resolved path if there is one, NULL otherwise.\n      + *\n      + * The caller owns the storage that the returned string occupies and\n     -+ * is responsible for releasing it with `free(3)` when done.\n     ++ * is responsible for releasing it.\n      + */\n      +char *fsmonitor__resolve_alias(const char *path,\n      +\tconst struct alias_info *info);\n     -+\n      +\n       #endif\n 5:  421d77775dc ! 5:  b1ea378dff7 fsmonitor: check for compatability before communicating with fsmonitor\n     @@ Metadata\n       ## Commit message ##\n          fsmonitor: check for compatability before communicating with fsmonitor\n      \n     -    If fsmonitor is not in a compatible state, die with an appropriate error\n     -    messge.\n     +    If fsmonitor is not in a compatible state, warn with an appropriate message.\n      \n          Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n      \n     @@ fsmonitor-settings.c: char *fsm_settings__get_incompatible_msg(const struct repo\n      -\t\t\t    _(\"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets\"),\n      -\t\t\t    r->worktree);\n      +\t\t\t    _(\"socket directory '%s' is incompatible with fsmonitor due\"\n     -+\t\t\t\t  \" to lack of Unix sockets support\"),\n     ++\t\t\t      \" to lack of Unix sockets support\"),\n      +\t\t\t    socket_dir);\n       \t\tgoto done;\n       \t}\n 6:  b375b0ac798 ! 6:  04f607b1f21 fsmonitor: add documentation for allowRemote and socketDir options\n     @@ Commit message\n          fsmonitor: add documentation for allowRemote and socketDir options\n      \n          Add documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\n     -    Call-out experimental nature of 'fsmonitor.allowRemote' and limited file\n     -    system support for 'fsmonitor.socketDir'.\n     +    Call-out experimental nature of 'fsmonitor.allowRemote' and limited\n     +    filesystem support for 'fsmonitor.socketDir'.\n      \n          Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n      \n     + ## Documentation/config.txt ##\n     +@@ Documentation/config.txt: include::config/filter.txt[]\n     + \n     + include::config/fsck.txt[]\n     + \n     ++include::config/fsmonitor--daemon.txt[]\n     ++\n     + include::config/gc.txt[]\n     + \n     + include::config/gitcvs.txt[]\n     +\n     + ## Documentation/config/fsmonitor--daemon.txt (new) ##\n     +@@\n     ++fsmonitor.allowRemote::\n     ++    By default, the fsmonitor daemon refuses to work against network-mounted\n     ++    repositories. Setting `fsmonitor.allowRemote` to `true` overrides this\n     ++    behavior.  Only respected when `core.fsmonitor` is set to `true`.\n     ++\n     ++fsmonitor.socketDir::\n     ++    This Mac OS-specific option, if set, specifies the directory in\n     ++    which to create the Unix domain socket used for communication\n     ++    between the fsmonitor daemon and various Git commands. The directory must\n     ++    reside on a native Mac OS filesystem.  Only respected when `core.fsmonitor`\n     ++    is set to `true`.\n     +\n       ## Documentation/git-fsmonitor--daemon.txt ##\n      @@ Documentation/git-fsmonitor--daemon.txt: git-fsmonitor{litdd}daemon(1)\n       \n     @@ Documentation/git-fsmonitor--daemon.txt: CAVEATS\n      +CONFIGURATION\n      +-------------\n      +\n     -+When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])\n     -+the fsmonitor daemon will pay attention to the following configuration\n     -+variables:\n     ++include::includes/cmd-config-section-all.txt[]\n      +\n     -+`fsmonitor.allowRemote`::\n     -+\tBy default, the daemon refuses to work against network-mounted\n     -+\trepositories. Setting `fsmonitor.allowRemote` to `true` overrides\n     -+\tthis behavior.\n     -+\n     -+`fsmonitor.socketDir`::\n     -+    This Mac OS-specific option, if set, specifies the directory in\n     -+    which to create the Unix domain socket used for communication\n     -+    between fsmonitor and various Git commands. The directory must\n     -+    reside on a native Mac OS filesystem as discussed above.\n     ++include::config/fsmonitor--daemon.txt[]\n      +\n       GIT\n       ---\n\n-- \ngitgitgadget\n"},{"id":"463762","messageId":"0b9b64428c5e096ada1f274639be9604e8c2f9ed.1664312254.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v13.git.1664312253.gitgitgadget@gmail.com","subject":"[PATCH v13 1/6] fsmonitor: refactor filesystem checks to common interface","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-27T20:57:28Z","receivedAt":"2022-09-27T20:57:47Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nProvide a common interface for getting basic filesystem information\nincluding filesystem type and whether the filesystem is remote.\n\nRefactor existing code for getting basic filesystem info and detecting\nremote file systems to the new interface.\n\nRefactor filesystem checks to leverage new interface. For macOS,\nerror-out if the Unix Domain socket (UDS) file is on a remote\nfilesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                                 |   1 +\n compat/fsmonitor/fsm-path-utils-darwin.c |  40 ++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 128 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  62 +++-----\n compat/fsmonitor/fsm-settings-win32.c    | 172 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   2 +\n fsmonitor-path-utils.h                   |  23 +++\n fsmonitor-settings.c                     |  50 +++++++\n 8 files changed, 263 insertions(+), 215 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\ndiff --git a/Makefile b/Makefile\nindex cac3452edb9..ffab427ea5b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2044,6 +2044,7 @@ endif\n ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n ifeq ($(TCLTK_PATH),)\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nnew file mode 100644\nindex 00000000000..067cbe6990a\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -0,0 +1,40 @@\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+#include <sys/param.h>\n+#include <sys/mount.h>\n+\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\tstruct statfs fs;\n+\tif (statfs(path, &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\treturn -1;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\n+\tif (!(fs.f_flags & MNT_LOCAL))\n+\t\tfs_info->is_remote = 1;\n+\telse\n+\t\tfs_info->is_remote = 0;\n+\n+\tfs_info->typename = fs.f_fstypename;\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nnew file mode 100644\nindex 00000000000..a90b8f7925b\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -0,0 +1,128 @@\n+#include \"cache.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+/*\n+ * Check remote working directory protocol.\n+ *\n+ * Return -1 if client machine cannot get remote protocol information.\n+ */\n+static int check_remote_protocol(wchar_t *wpath)\n+{\n+\tHANDLE h;\n+\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n+\n+\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n+\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\n+\tif (h == INVALID_HANDLE_VALUE) {\n+\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n+\t\t&proto_info, sizeof(proto_info))) {\n+\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\tCloseHandle(h);\n+\t\treturn -1;\n+\t}\n+\n+\tCloseHandle(h);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n+\t\t\t\twpath, proto_info.Protocol);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Notes for testing:\n+ *\n+ * (a) Windows allows a network share to be mapped to a drive letter.\n+ *     (This is the normal method to access it.)\n+ *\n+ *     $ NET USE Z: \\\\server\\share\n+ *     $ git -C Z:/repo status\n+ *\n+ * (b) Windows allows a network share to be referenced WITHOUT mapping\n+ *     it to drive letter.\n+ *\n+ *     $ NET USE \\\\server\\share\\dir\n+ *     $ git -C //server/share/repo status\n+ *\n+ * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n+ *     arbitrary path (which may be remote)\n+ *\n+ *     $ SUBST Q: Z:\\repo\n+ *     $ git -C Q:/ status\n+ *\n+ * (d) Windows allows a directory symlink to be created on a local\n+ *     file system that points to a remote repo.\n+ *\n+ *     $ mklink /d ./link //server/share/repo\n+ *     $ git -C ./link status\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\twchar_t wpath[MAX_PATH];\n+\twchar_t wfullpath[MAX_PATH];\n+\tsize_t wlen;\n+\tUINT driveType;\n+\n+\t/*\n+\t * Do everything in wide chars because the drive letter might be\n+\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n+\t */\n+\tif (xutftowcs_path(wpath, path) < 0) {\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * GetDriveTypeW() requires a final slash.  We assume that the\n+\t * worktree pathname points to an actual directory.\n+\t */\n+\twlen = wcslen(wpath);\n+\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n+\t\twpath[wlen++] = L'\\\\';\n+\t\twpath[wlen] = 0;\n+\t}\n+\n+\t/*\n+\t * Normalize the path.  If nothing else, this converts forward\n+\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n+\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n+\t */\n+\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {\n+\t\treturn -1;\n+\t}\n+\n+\tdriveType = GetDriveTypeW(wfullpath);\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n+\t\t\t path, wfullpath, driveType);\n+\n+\tif (driveType == DRIVE_REMOTE) {\n+\t\tfs_info->is_remote = 1;\n+\t\tif (check_remote_protocol(wfullpath) < 0)\n+\t\t\treturn -1;\n+\t} else {\n+\t\tfs_info->is_remote = 0;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dba3ced6bb7 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -1,32 +1,10 @@\n-#include \"cache.h\"\n #include \"config.h\"\n-#include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n-#include <sys/param.h>\n-#include <sys/mount.h>\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n-/*\n- * [1] Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type (NFS, SAMBA, etc.) dictates whether notification events\n- * are available at all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n+ /*\n  * For the builtin FSMonitor, we create the Unix domain socket for the\n  * IPC in the .git directory.  If the working directory is remote,\n  * then the socket will be created on the remote file system.  This\n@@ -38,40 +16,34 @@\n  * be taken to ensure that $HOME is actually local and not a managed\n  * file share.)\n  *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n+ * FAT32 and NTFS working directories are problematic too.\n  *\n  * The builtin FSMonitor uses a Unix domain socket in the .git\n  * directory for IPC.  These Windows drive formats do not support\n  * Unix domain sockets, so mark them as incompatible for the daemon.\n  *\n  */\n-static enum fsmonitor_reason check_volume(struct repository *r)\n+static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n-\tstruct statfs fs;\n+\tstruct fs_info fs;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \n-\tif (statfs(r->worktree, &fs) == -1) {\n-\t\tint saved_errno = errno;\n-\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n-\t\t\t\t r->worktree, strerror(saved_errno));\n-\t\terrno = saved_errno;\n+\tif (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {\n+\t\tstrbuf_release(&path);\n \t\treturn FSMONITOR_REASON_ERROR;\n \t}\n \n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n-\t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n \n-\tif (!(fs.f_flags & MNT_LOCAL))\n+\tif (fs.is_remote)\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n-\tif (!strcmp(fs.f_fstypename, \"msdos\")) /* aka FAT32 */\n+\tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n-\tif (!strcmp(fs.f_fstypename, \"ntfs\"))\n+\tif (!strcmp(fs.typename, \"ntfs\"))\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \treturn FSMONITOR_REASON_OK;\n@@ -81,7 +53,7 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_volume(r);\n+\treason = check_uds_volume(r);\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex e5ec5b0a9f7..d88b06ae610 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -1,8 +1,9 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * VFS for Git is incompatible with FSMonitor.\n@@ -24,171 +25,6 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-/*\n- * Check if monitoring remote working directories is allowed.\n- *\n- * By default, monitoring remote working directories is\n- * disabled.  Users may override this behavior in enviroments where\n- * they have proper support.\n- */\n-static int check_config_allowremote(struct repository *r)\n-{\n-\tint allow;\n-\n-\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n-\t\treturn allow;\n-\n-\treturn -1; /* fsmonitor.allowremote not set */\n-}\n-\n-/*\n- * Check remote working directory protocol.\n- *\n- * Error if client machine cannot get remote protocol information.\n- */\n-static int check_remote_protocol(wchar_t *wpath)\n-{\n-\tHANDLE h;\n-\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n-\n-\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n-\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n-\n-\tif (h == INVALID_HANDLE_VALUE) {\n-\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\treturn -1;\n-\t}\n-\n-\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n-\t\t&proto_info, sizeof(proto_info))) {\n-\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\tCloseHandle(h);\n-\t\treturn -1;\n-\t}\n-\n-\tCloseHandle(h);\n-\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n-\t\t\t\twpath, proto_info.Protocol);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type dictates whether notification events are available at\n- * all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- * Notes for testing:\n- *\n- * (a) Windows allows a network share to be mapped to a drive letter.\n- *     (This is the normal method to access it.)\n- *\n- *     $ NET USE Z: \\\\server\\share\n- *     $ git -C Z:/repo status\n- *\n- * (b) Windows allows a network share to be referenced WITHOUT mapping\n- *     it to drive letter.\n- *\n- *     $ NET USE \\\\server\\share\\dir\n- *     $ git -C //server/share/repo status\n- *\n- * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n- *     arbitrary path (which may be remote)\n- *\n- *     $ SUBST Q: Z:\\repo\n- *     $ git -C Q:/ status\n- *\n- * (d) Windows allows a directory symlink to be created on a local\n- *     file system that points to a remote repo.\n- *\n- *     $ mklink /d ./link //server/share/repo\n- *     $ git -C ./link status\n- */\n-static enum fsmonitor_reason check_remote(struct repository *r)\n-{\n-\tint ret;\n-\twchar_t wpath[MAX_PATH];\n-\twchar_t wfullpath[MAX_PATH];\n-\tsize_t wlen;\n-\tUINT driveType;\n-\n-\t/*\n-\t * Do everything in wide chars because the drive letter might be\n-\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n-\t */\n-\tif (xutftowcs_path(wpath, r->worktree) < 0)\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t/*\n-\t * GetDriveTypeW() requires a final slash.  We assume that the\n-\t * worktree pathname points to an actual directory.\n-\t */\n-\twlen = wcslen(wpath);\n-\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n-\t\twpath[wlen++] = L'\\\\';\n-\t\twpath[wlen] = 0;\n-\t}\n-\n-\t/*\n-\t * Normalize the path.  If nothing else, this converts forward\n-\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n-\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n-\t */\n-\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\tdriveType = GetDriveTypeW(wfullpath);\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n-\t\t\t r->worktree, wfullpath, driveType);\n-\n-\tif (driveType == DRIVE_REMOTE) {\n-\t\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t \"check_remote('%s') true\",\n-\t\t\t\t r->worktree);\n-\n-\t\tret = check_remote_protocol(wfullpath);\n-\t\tif (ret < 0)\n-\t\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t\tswitch (check_config_allowremote(r)) {\n-\t\tcase 0: /* config overrides and disables */\n-\t\t\treturn FSMONITOR_REASON_REMOTE;\n-\t\tcase 1: /* config overrides and enables */\n-\t\t\treturn FSMONITOR_REASON_OK;\n-\t\tdefault:\n-\t\t\tbreak; /* config has no opinion */\n-\t\t}\n-\n-\t\treturn FSMONITOR_REASON_REMOTE;\n-\t}\n-\n-\treturn FSMONITOR_REASON_OK;\n-}\n-\n enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n@@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n-\treason = check_remote(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n-\n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex ea2a531be87..5482a04b3ce 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)\n@@ -315,6 +316,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nnew file mode 100644\nindex 00000000000..e48592887e7\n--- /dev/null\n+++ b/fsmonitor-path-utils.h\n@@ -0,0 +1,23 @@\n+#ifndef FSM_PATH_UTILS_H\n+#define FSM_PATH_UTILS_H\n+\n+struct fs_info {\n+\tint is_remote;\n+\tchar *typename;\n+};\n+\n+/*\n+ * Get some basic filesystem informtion for the given path\n+ *\n+ * Returns -1 on error, zero otherwise.\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n+\n+/*\n+ * Determines if the filesystem that path resides on is remote.\n+ *\n+ * Returns -1 on error, 0 if not remote, 1 if remote.\n+ */\n+int fsmonitor__is_fs_remote(const char *path);\n+\n+#endif\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..d288cbad479 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * We keep this structure defintion private and have getters\n@@ -13,6 +14,52 @@ struct fsmonitor_settings {\n \tchar *hook_path;\n };\n \n+/*\n+ * Remote working directories are problematic for FSMonitor.\n+ *\n+ * The underlying file system on the server machine and/or the remote\n+ * mount type dictates whether notification events are available at\n+ * all to remote client machines.\n+ *\n+ * Kernel differences between the server and client machines also\n+ * dictate the how (buffering, frequency, de-dup) the events are\n+ * delivered to client machine processes.\n+ *\n+ * A client machine (such as a laptop) may choose to suspend/resume\n+ * and it is unclear (without lots of testing) whether the watcher can\n+ * resync after a resume.  We might be able to treat this as a normal\n+ * \"events were dropped by the kernel\" event and do our normal \"flush\n+ * and resync\" --or-- we might need to close the existing (zombie?)\n+ * notification fd and create a new one.\n+ *\n+ * In theory, the above issues need to be addressed whether we are\n+ * using the Hook or IPC API.\n+ *\n+ * So (for now at least), mark remote working directories as\n+ * incompatible unless 'fsmonitor.allowRemote' is true.\n+ *\n+ */\n+#ifdef HAVE_FSMONITOR_OS_SETTINGS\n+static enum fsmonitor_reason check_remote(struct repository *r)\n+{\n+\tint allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */\n+\tint is_remote = fsmonitor__is_fs_remote(r->worktree);\n+\n+\tswitch (is_remote) {\n+\t\tcase 0:\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tcase 1:\n+\t\t\trepo_config_get_bool(r, \"fsmonitor.allowremote\", &allow_remote);\n+\t\t\tif (allow_remote < 1)\n+\t\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\t\telse\n+\t\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+}\n+#endif\n+\n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n {\n \tif (!r->worktree) {\n@@ -27,6 +74,9 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t{\n \t\tenum fsmonitor_reason reason;\n \n+\t\treason = check_remote(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n \t\treason = fsm_os__incompatible(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-- \ngitgitgadget\n\n"},{"id":"463763","messageId":"680d4c83f99dacb1cac90f96bc2f02d9614b880c.1664312254.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v13.git.1664312253.gitgitgadget@gmail.com","subject":"[PATCH v13 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-27T20:57:29Z","receivedAt":"2022-09-27T20:57:49Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf the .git directory is on a remote filesystem, create the socket\nfile in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                               |  1 +\n builtin/fsmonitor--daemon.c            |  3 +-\n compat/fsmonitor/fsm-ipc-darwin.c      | 52 ++++++++++++++++++++++++++\n compat/fsmonitor/fsm-ipc-win32.c       |  9 +++++\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n contrib/buildsystems/CMakeLists.txt    |  2 +\n fsmonitor-ipc.c                        | 18 ++++-----\n fsmonitor-ipc.h                        |  4 +-\n 8 files changed, 78 insertions(+), 13 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n\ndiff --git a/Makefile b/Makefile\nindex ffab427ea5b..feb675a6959 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2039,6 +2039,7 @@ ifdef FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n endif\n \n ifdef FSMONITOR_OS_SETTINGS\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 2c109cf8b37..0123fc33ed2 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -1343,7 +1343,8 @@ static int fsmonitor_run_daemon(void)\n \t * directory.)\n \t */\n \tstrbuf_init(&state.path_ipc, 0);\n-\tstrbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));\n+\tstrbuf_addstr(&state.path_ipc,\n+\t\tabsolute_path(fsmonitor_ipc__get_path(the_repository)));\n \n \t/*\n \t * Confirm that we can create platform-specific resources for the\ndiff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\nnew file mode 100644\nindex 00000000000..ce843d63348\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-darwin.c\n@@ -0,0 +1,52 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"strbuf.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n+{\n+\tstatic const char *ipc_path = NULL;\n+\tSHA_CTX sha1ctx;\n+\tchar *sock_dir = NULL;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (!r)\n+\t\tBUG(\"No repository passed into fsmonitor_ipc__get_path\");\n+\n+\tif (ipc_path)\n+\t\treturn ipc_path;\n+\n+\n+\t/* By default the socket file is created in the .git directory */\n+\tif (fsmonitor__is_fs_remote(r->gitdir) < 1) {\n+\t\tipc_path = fsmonitor_ipc__get_default_path();\n+\t\treturn ipc_path;\n+\t}\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\trepo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n+\n+\t/* Create the socket file in either socketDir or $HOME */\n+\tif (sock_dir && *sock_dir) {\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\t} else {\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\t}\n+\tfree(sock_dir);\n+\n+\tipc_path = interpolate_path(ipc_file.buf, 1);\n+\tif (!ipc_path)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\n+\tstrbuf_release(&ipc_file);\n+\treturn ipc_path;\n+}\ndiff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\nnew file mode 100644\nindex 00000000000..e08c505c148\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-win32.c\n@@ -0,0 +1,9 @@\n+#include \"config.h\"\n+#include \"fsmonitor-ipc.h\"\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r) {\n+\tstatic char *ret;\n+\tif (!ret)\n+\t\tret = git_pathdup(\"fsmonitor--daemon.ipc\");\n+\treturn ret;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex dba3ced6bb7..681d8bf963e 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -26,7 +26,7 @@\n static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n \tstruct fs_info fs;\n-\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tconst char *ipc_path = fsmonitor_ipc__get_path(r);\n \tstruct strbuf path = STRBUF_INIT;\n \tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 5482a04b3ce..787738e6fa3 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n@@ -316,6 +317,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..c0f42301c84 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -18,7 +18,7 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 0;\n }\n \n-const char *fsmonitor_ipc__get_path(void)\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n {\n \treturn NULL;\n }\n@@ -47,11 +47,9 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n-\n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\n-\treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n+\treturn ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));\n }\n \n static int spawn_daemon(void)\n@@ -81,8 +79,8 @@ int fsmonitor_ipc__send_query(const char *since_token,\n \ttrace2_data_string(\"fsm_client\", NULL, \"query/command\", tok);\n \n try_again:\n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \n \tswitch (state) {\n \tcase IPC_STATE__LISTENING:\n@@ -117,13 +115,13 @@ try_again:\n \n \tcase IPC_STATE__INVALID_PATH:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: invalid path '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \n \tcase IPC_STATE__OTHER_ERROR:\n \tdefault:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: unspecified error on '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \t}\n \n@@ -149,8 +147,8 @@ int fsmonitor_ipc__send_command(const char *command,\n \toptions.wait_if_busy = 1;\n \toptions.wait_if_not_found = 0;\n \n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \tif (state != IPC_STATE__LISTENING) {\n \t\tdie(_(\"fsmonitor--daemon is not running\"));\n \t\treturn -1;\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..8b489da762b 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -3,6 +3,8 @@\n \n #include \"simple-ipc.h\"\n \n+struct repository;\n+\n /*\n  * Returns true if built-in file system monitor daemon is defined\n  * for this platform.\n@@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);\n  *\n  * Returns NULL if the daemon is not supported on this platform.\n  */\n-const char *fsmonitor_ipc__get_path(void);\n+const char *fsmonitor_ipc__get_path(struct repository *r);\n \n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n-- \ngitgitgadget\n\n"},{"id":"463764","messageId":"7987d0c1f33da7cdd62f2ffbaccd1cda281a7305.1664312254.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v13.git.1664312253.gitgitgadget@gmail.com","subject":"[PATCH v13 3/6] fsmonitor: avoid socket location check if using hook","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-27T20:57:30Z","receivedAt":"2022-09-27T20:57:52Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf monitoring is done via fsmonitor hook rather than IPC there is no\nneed to check if the location of the Unix Domain socket (UDS) file is\non a remote filesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 10 ++++++----\n compat/fsmonitor/fsm-settings-win32.c  |  2 +-\n fsmonitor-settings.c                   |  8 ++++----\n fsmonitor-settings.h                   |  2 +-\n 4 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 681d8bf963e..40da2d3b533 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -49,13 +49,15 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_uds_volume(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n+\tif (ipc) {\n+\t\treason = check_uds_volume(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n+\t}\n \n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex d88b06ae610..a8af31b71de 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex d288cbad479..531a1b6f956 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -60,7 +60,7 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n }\n #endif\n \n-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n+static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n {\n \tif (!r->worktree) {\n \t\t/*\n@@ -77,7 +77,7 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t\treason = check_remote(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-\t\treason = fsm_os__incompatible(r);\n+\t\treason = fsm_os__incompatible(r, ipc);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n \t}\n@@ -162,7 +162,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -185,7 +185,7 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..0721617b95a 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -48,7 +48,7 @@ struct fsmonitor_settings;\n  * fsm_os__* routines should considered private to fsm_settings__\n  * routines.\n  */\n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n \n #endif /* FSMONITOR_SETTINGS_H */\n-- \ngitgitgadget\n\n"},{"id":"463765","messageId":"324eb5acd856f6a8122a5239f9fa1a9a8ecc9ee3.1664312254.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v13.git.1664312253.gitgitgadget@gmail.com","subject":"[PATCH v13 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-27T20:57:31Z","receivedAt":"2022-09-27T20:57:56Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n builtin/fsmonitor--daemon.c              | 10 +++\n compat/fsmonitor/fsm-listen-darwin.c     | 14 +++-\n compat/fsmonitor/fsm-path-utils-darwin.c | 93 ++++++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 17 +++++\n fsmonitor--daemon.h                      |  3 +\n fsmonitor-path-utils.h                   | 36 ++++++++-\n 6 files changed, 170 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 0123fc33ed2..d5056a97a06 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"fsmonitor.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n #include \"compat/fsmonitor/fsm-health.h\"\n #include \"compat/fsmonitor/fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n@@ -1282,6 +1283,13 @@ static int fsmonitor_run_daemon(void)\n \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n \tstate.nr_paths_watching = 1;\n \n+\tstrbuf_init(&state.alias.alias, 0);\n+\tstrbuf_init(&state.alias.points_to, 0);\n+\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n+\t\terr = error(_(\"could not get worktree alias\"));\n+\t\tgoto done;\n+\t}\n+\n \t/*\n \t * We create and delete cookie files somewhere inside the .git\n \t * directory to help us keep sync with the file system.  If\n@@ -1391,6 +1399,8 @@ done:\n \tstrbuf_release(&state.path_gitdir_watch);\n \tstrbuf_release(&state.path_cookie_prefix);\n \tstrbuf_release(&state.path_ipc);\n+\tstrbuf_release(&state.alias.alias);\n+\tstrbuf_release(&state.alias.points_to);\n \n \treturn err;\n }\ndiff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\nindex 8e208e8289e..daeee4e465c 100644\n--- a/compat/fsmonitor/fsm-listen-darwin.c\n+++ b/compat/fsmonitor/fsm-listen-darwin.c\n@@ -26,6 +26,7 @@\n #include \"fsmonitor.h\"\n #include \"fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsm_listen_data\n {\n@@ -198,8 +199,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \tstruct string_list cookie_list = STRING_LIST_INIT_DUP;\n \tconst char *path_k;\n \tconst char *slash;\n-\tint k;\n+\tchar *resolved = NULL;\n \tstruct strbuf tmp = STRBUF_INIT;\n+\tint k;\n \n \t/*\n \t * Build a list of all filesystem changes into a private/local\n@@ -209,7 +211,12 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t/*\n \t\t * On Mac, we receive an array of absolute paths.\n \t\t */\n-\t\tpath_k = paths[k];\n+\t\tfree(resolved);\n+\t\tresolved = fsmonitor__resolve_alias(paths[k], &state->alias);\n+\t\tif (resolved)\n+\t\t\tpath_k = resolved;\n+\t\telse\n+\t\t\tpath_k = paths[k];\n \n \t\t/*\n \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n@@ -238,6 +245,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t\tfsmonitor_force_resync(state);\n \t\t\tfsmonitor_batch__free_list(batch);\n \t\t\tstring_list_clear(&cookie_list, 0);\n+\t\t\tbatch = NULL;\n \n \t\t\t/*\n \t\t\t * We assume that any events that we received\n@@ -360,12 +368,14 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t}\n \t}\n \n+\tfree(resolved);\n \tfsmonitor_publish(state, batch, &cookie_list);\n \tstring_list_clear(&cookie_list, 0);\n \tstrbuf_release(&tmp);\n \treturn;\n \n force_shutdown:\n+\tfree(resolved);\n \tfsmonitor_batch__free_list(batch);\n \tstring_list_clear(&cookie_list, 0);\n \ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nindex 067cbe6990a..931c9318572 100644\n--- a/compat/fsmonitor/fsm-path-utils-darwin.c\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -1,5 +1,8 @@\n #include \"fsmonitor.h\"\n #include \"fsmonitor-path-utils.h\"\n+#include <dirent.h>\n+#include <errno.h>\n+#include <fcntl.h>\n #include <sys/param.h>\n #include <sys/mount.h>\n \n@@ -38,3 +41,93 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * Scan the root directory for synthetic firmlinks that when resolved\n+ * are a prefix of the path, stopping at the first one found.\n+ *\n+ * Some information about firmlinks and synthetic firmlinks:\n+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n+ *\n+ * macOS no longer allows symlinks in the root directory; any link found\n+ * there is therefore a synthetic firmlink.\n+ *\n+ * If this function gets called often, will want to cache all the firmlink\n+ * information, but for now there is only one caller of this function.\n+ *\n+ * If there is more than one alias for the path, that is another\n+ * matter altogether.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\tDIR *dir;\n+\tint read;\n+\tint retval = -1;\n+\tstruct dirent *de;\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+\n+\tdir = opendir(\"/\");\n+\tif (!dir) {\n+\t\terror_errno(\"opendir('/') failed\");\n+\t\treturn -1;\n+\t}\n+\n+\tstrbuf_init(&alias, 256);\n+\n+\t/* no way of knowing what the link will resolve to, so MAXPATHLEN */\n+\tstrbuf_init(&points_to, MAXPATHLEN);\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstrbuf_reset(&alias);\n+\t\tstrbuf_addch(&alias, '/');\n+\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n+\n+\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n+\t\tif (read > 0) {\n+\t\t\tstrbuf_setlen(&points_to, read);\n+\t\t\tif ((!strncmp(points_to.buf, path, points_to.len))\n+\t\t\t\t&& path[points_to.len] == '/') {\n+\t\t\t\tstrbuf_addbuf(&info->alias, &alias);\n+\t\t\t\tstrbuf_addbuf(&info->points_to, &points_to);\n+\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n+\t\t\t\t\tpath, info->alias.buf, info->points_to.buf);\n+\t\t\t\tretval = 0;\n+\t\t\t\tgoto done;\n+\t\t\t}\n+\t\t} else if (!read) {\n+\t\t\tBUG(\"readlink returned 0\");\n+\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n+\t\t\terror_errno(\"readlink('%s') failed\", alias.buf);\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\tretval = 0; /* no alias */\n+\n+done:\n+\tif (closedir(dir) < 0)\n+\t\twarning_errno(\"closedir('/') failed\");\n+\tstrbuf_release(&alias);\n+\tstrbuf_release(&points_to);\n+\treturn retval;\n+}\n+\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\tif (!info->alias.len)\n+\t\treturn NULL;\n+\n+\tif ((!strncmp(info->alias.buf, path, info->alias.len))\n+\t\t&& path[info->alias.len] == '/') {\n+\t\tstruct strbuf tmp = STRBUF_INIT;\n+\t\tconst char *remainder = path + info->alias.len;\n+\n+\t\tstrbuf_addbuf(&tmp, &info->points_to);\n+\t\tstrbuf_add(&tmp, remainder, strlen(remainder));\n+\t\treturn strbuf_detach(&tmp, NULL);\n+\t}\n+\n+\treturn NULL;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nindex a90b8f7925b..0d95bbb416f 100644\n--- a/compat/fsmonitor/fsm-path-utils-win32.c\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -126,3 +126,20 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * No-op for now.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\treturn 0;\n+}\n+\n+/*\n+ * No-op for now.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\treturn NULL;\n+}\ndiff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\nindex 2102a5c9ff5..e24838f9a86 100644\n--- a/fsmonitor--daemon.h\n+++ b/fsmonitor--daemon.h\n@@ -8,6 +8,7 @@\n #include \"run-command.h\"\n #include \"simple-ipc.h\"\n #include \"thread-utils.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsmonitor_batch;\n struct fsmonitor_token_data;\n@@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct strbuf path_worktree_watch;\n \tstruct strbuf path_gitdir_watch;\n+\tstruct alias_info alias;\n \tint nr_paths_watching;\n \n \tstruct fsmonitor_token_data *current_token_data;\n@@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct ipc_server_data *ipc_server_data;\n \tstruct strbuf path_ipc;\n+\n };\n \n /*\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nindex e48592887e7..2624798d2c7 100644\n--- a/fsmonitor-path-utils.h\n+++ b/fsmonitor-path-utils.h\n@@ -1,13 +1,21 @@\n #ifndef FSM_PATH_UTILS_H\n #define FSM_PATH_UTILS_H\n \n+#include \"strbuf.h\"\n+\n+struct alias_info\n+{\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+};\n+\n struct fs_info {\n \tint is_remote;\n \tchar *typename;\n };\n \n /*\n- * Get some basic filesystem informtion for the given path\n+ * Get some basic filesystem information for the given path\n  *\n  * Returns -1 on error, zero otherwise.\n  */\n@@ -20,4 +28,30 @@ int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n  */\n int fsmonitor__is_fs_remote(const char *path);\n \n+/*\n+ * Get the alias in given path, if any.\n+ *\n+ * Sets alias to the first alias that matches any part of the path.\n+ *\n+ * If an alias is found, info.alias and info.points_to are set to the\n+ * found mapping.\n+ *\n+ * Returns -1 on error, 0 otherwise.\n+ *\n+ * The caller owns the storage that is occupied by info.alias and\n+ * info.points_to and is responsible for releasing it.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info);\n+\n+/*\n+ * Resolve the path against the given alias.\n+ *\n+ * Returns the resolved path if there is one, NULL otherwise.\n+ *\n+ * The caller owns the storage that the returned string occupies and\n+ * is responsible for releasing it.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info);\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"463766","messageId":"b1ea378dff7848a4e654e1e10f8a1f9681d42dec.1664312254.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v13.git.1664312253.gitgitgadget@gmail.com","subject":"[PATCH v13 5/6] fsmonitor: check for compatability before communicating with fsmonitor","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-27T20:57:32Z","receivedAt":"2022-09-27T20:57:57Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf fsmonitor is not in a compatible state, warn with an appropriate message.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n fsmonitor-settings.c                   | 10 +++++++---\n fsmonitor-settings.h                   |  2 +-\n fsmonitor.c                            |  7 +++++++\n 4 files changed, 16 insertions(+), 5 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 40da2d3b533..44233125df8 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -38,7 +38,7 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \tstrbuf_release(&path);\n \n \tif (fs.is_remote)\n-\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 531a1b6f956..ee63a97dc51 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n+#include \"fsmonitor-ipc.h\"\n #include \"fsmonitor-settings.h\"\n #include \"fsmonitor-path-utils.h\"\n \n@@ -242,10 +243,11 @@ enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)\n \treturn r->settings.fsmonitor->reason;\n }\n \n-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n+char *fsm_settings__get_incompatible_msg(struct repository *r,\n \t\t\t\t\t enum fsmonitor_reason reason)\n {\n \tstruct strbuf msg = STRBUF_INIT;\n+\tconst char *socket_dir;\n \n \tswitch (reason) {\n \tcase FSMONITOR_REASON_UNTESTED:\n@@ -281,9 +283,11 @@ char *fsm_settings__get_incompatible_msg(const struct repository *r,\n \t\tgoto done;\n \n \tcase FSMONITOR_REASON_NOSOCKETS:\n+\t\tsocket_dir = dirname((char *)fsmonitor_ipc__get_path(r));\n \t\tstrbuf_addf(&msg,\n-\t\t\t    _(\"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets\"),\n-\t\t\t    r->worktree);\n+\t\t\t    _(\"socket directory '%s' is incompatible with fsmonitor due\"\n+\t\t\t      \" to lack of Unix sockets support\"),\n+\t\t\t    socket_dir);\n \t\tgoto done;\n \t}\n \ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex 0721617b95a..ab02e3995ee 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -33,7 +33,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n const char *fsm_settings__get_hook_path(struct repository *r);\n \n enum fsmonitor_reason fsm_settings__get_reason(struct repository *r);\n-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n+char *fsm_settings__get_incompatible_msg(struct repository *r,\n \t\t\t\t\t enum fsmonitor_reason reason);\n \n struct fsmonitor_settings;\ndiff --git a/fsmonitor.c b/fsmonitor.c\nindex 57d6a483bee..540736b39fd 100644\n--- a/fsmonitor.c\n+++ b/fsmonitor.c\n@@ -295,6 +295,7 @@ static int fsmonitor_force_update_threshold = 100;\n \n void refresh_fsmonitor(struct index_state *istate)\n {\n+\tstatic int warn_once = 0;\n \tstruct strbuf query_result = STRBUF_INIT;\n \tint query_success = 0, hook_version = -1;\n \tsize_t bol = 0; /* beginning of line */\n@@ -305,6 +306,12 @@ void refresh_fsmonitor(struct index_state *istate)\n \tint is_trivial = 0;\n \tstruct repository *r = istate->repo ? istate->repo : the_repository;\n \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);\n+\tenum fsmonitor_reason reason = fsm_settings__get_reason(r);\n+\n+\tif (!warn_once && reason > FSMONITOR_REASON_OK) {\n+\t\twarn_once = 1;\n+\t\twarning(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n+\t}\n \n \tif (fsm_mode <= FSMONITOR_MODE_DISABLED ||\n \t    istate->fsmonitor_has_run_once)\n-- \ngitgitgadget\n\n"},{"id":"463767","messageId":"04f607b1f21ede3ef35af05846995e4f80906301.1664312254.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v13.git.1664312253.gitgitgadget@gmail.com","subject":"[PATCH v13 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-27T20:57:33Z","receivedAt":"2022-09-27T20:58:00Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nAdd documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\nCall-out experimental nature of 'fsmonitor.allowRemote' and limited\nfilesystem support for 'fsmonitor.socketDir'.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Documentation/config.txt                   |  2 ++\n Documentation/config/fsmonitor--daemon.txt | 11 +++++++\n Documentation/git-fsmonitor--daemon.txt    | 37 ++++++++++++++++++++--\n 3 files changed, 47 insertions(+), 3 deletions(-)\n create mode 100644 Documentation/config/fsmonitor--daemon.txt\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 5b5b9765699..1e205831656 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -423,6 +423,8 @@ include::config/filter.txt[]\n \n include::config/fsck.txt[]\n \n+include::config/fsmonitor--daemon.txt[]\n+\n include::config/gc.txt[]\n \n include::config/gitcvs.txt[]\ndiff --git a/Documentation/config/fsmonitor--daemon.txt b/Documentation/config/fsmonitor--daemon.txt\nnew file mode 100644\nindex 00000000000..c225c6c9e74\n--- /dev/null\n+++ b/Documentation/config/fsmonitor--daemon.txt\n@@ -0,0 +1,11 @@\n+fsmonitor.allowRemote::\n+    By default, the fsmonitor daemon refuses to work against network-mounted\n+    repositories. Setting `fsmonitor.allowRemote` to `true` overrides this\n+    behavior.  Only respected when `core.fsmonitor` is set to `true`.\n+\n+fsmonitor.socketDir::\n+    This Mac OS-specific option, if set, specifies the directory in\n+    which to create the Unix domain socket used for communication\n+    between the fsmonitor daemon and various Git commands. The directory must\n+    reside on a native Mac OS filesystem.  Only respected when `core.fsmonitor`\n+    is set to `true`.\ndiff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt\nindex cc142fb8612..8238eadb0e1 100644\n--- a/Documentation/git-fsmonitor--daemon.txt\n+++ b/Documentation/git-fsmonitor--daemon.txt\n@@ -3,7 +3,7 @@ git-fsmonitor{litdd}daemon(1)\n \n NAME\n ----\n-git-fsmonitor--daemon - A Built-in File System Monitor\n+git-fsmonitor--daemon - A Built-in Filesystem Monitor\n \n SYNOPSIS\n --------\n@@ -17,7 +17,7 @@ DESCRIPTION\n -----------\n \n A daemon to watch the working directory for file and directory\n-changes using platform-specific file system notification facilities.\n+changes using platform-specific filesystem notification facilities.\n \n This daemon communicates directly with commands like `git status`\n using the link:technical/api-simple-ipc.html[simple IPC] interface\n@@ -63,13 +63,44 @@ CAVEATS\n -------\n \n The fsmonitor daemon does not currently know about submodules and does\n-not know to filter out file system events that happen within a\n+not know to filter out filesystem events that happen within a\n submodule.  If fsmonitor daemon is watching a super repo and a file is\n modified within the working directory of a submodule, it will report\n the change (as happening against the super repo).  However, the client\n will properly ignore these extra events, so performance may be affected\n but it will not cause an incorrect result.\n \n+By default, the fsmonitor daemon refuses to work against network-mounted\n+repositories; this may be overridden by setting `fsmonitor.allowRemote` to\n+`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n+correctly with all network-mounted repositories and such use is considered\n+experimental.\n+\n+On Mac OS, the inter-process communication (IPC) between various Git\n+commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a\n+special type of file -- which is supported by native Mac OS filesystems,\n+but not on network-mounted filesystems, NTFS, or FAT32.  Other filesystems\n+may or may not have the needed support; the fsmonitor daemon is not guaranteed\n+to work with these filesystems and such use is considered experimental.\n+\n+By default, the socket is created in the `.git` directory, however, if the\n+`.git` directory is on a network-mounted filesystem, it will be instead be\n+created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a\n+network-mounted filesystem in which case you must set the configuration\n+variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native\n+filesystem in which to create the socket file.\n+\n+If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)\n+is on a native Mac OS file filesystem the fsmonitor daemon will report an\n+error that will cause the daemon and the currently running command to exit.\n+\n+CONFIGURATION\n+-------------\n+\n+include::includes/cmd-config-section-all.txt[]\n+\n+include::config/fsmonitor--daemon.txt[]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \ngitgitgadget\n"},{"id":"463792","messageId":"220928.867d1oxckc.gmgdl@evledraar.gmail.com","threadId":"58324","inReplyTo":"324eb5acd856f6a8122a5239f9fa1a9a8ecc9ee3.1664312254.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v13 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-09-28T05:55:56Z","receivedAt":"2022-09-28T06:01:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Sep 27 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n>\n\n\n> +\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n> +\t\terr = error(_(\"could not get worktree alias\"));\n\nWe could keep this, but if we'd error() in the function itself we'd just\nemit two error() lines when one would do.\n\n\n\n> +\tdir = opendir(\"/\");\n\nWe should have a:\n\n\tconst char *const dir = \"/\";\n\nSo we can pass it to this..\n\n\n> +\tif (!dir) {\n> +\t\terror_errno(\"opendir('/') failed\");\n\n...and as a parameter to a _()-translated string. That way the next\ntranslation doesn't need to translate opendir('/home') or whatever.\n\n> +\t\treturn -1;\n\nJust skip the braces and do \"return error_errno()\", these functions return -1 for thath reason.\n\n> +\tstrbuf_init(&alias, 256);\n>\n> +\t/* no way of knowing what the link will resolve to, so MAXPATHLEN */\n> +\tstrbuf_init(&points_to, MAXPATHLEN);\n\nStill need manual memory juggling? Ok.\n\n> +\n> +\twhile ((de = readdir(dir)) != NULL) {\n> +\t\tstrbuf_reset(&alias);\n> +\t\tstrbuf_addch(&alias, '/');\n> +\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n\n\tstrbuf_addf(&alias, \"/%s\", de->d_name);\n\nOr rather:\n\n\tstrbuf_addf(&alias, \"%s%s\", root_dir, de->d_name);\n\nIf you split that \"/\" into a variable?\n\n> +\n> +\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n> +\t\tif (read > 0) {\n> +\t\t\tstrbuf_setlen(&points_to, read);\n> +\t\t\tif ((!strncmp(points_to.buf, path, points_to.len))\n> +\t\t\t\t&& path[points_to.len] == '/') {\n> +\t\t\t\tstrbuf_addbuf(&info->alias, &alias);\n> +\t\t\t\tstrbuf_addbuf(&info->points_to, &points_to);\n> +\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n> +\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n> +\t\t\t\t\tpath, info->alias.buf, info->points_to.buf);\n> +\t\t\t\tretval = 0;\n> +\t\t\t\tgoto done;\n> +\t\t\t}\n> +\t\t} else if (!read) {\n> +\t\t\tBUG(\"readlink returned 0\");\n> +\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n> +\t\t\terror_errno(\"readlink('%s') failed\", alias.buf);\n> +\t\t\tgoto done;\n> +\t\t}\n> +\t}\n> +\tretval = 0; /* no alias */\n> +\n> +done:\n> +\tif (closedir(dir) < 0)\n> +\t\twarning_errno(\"closedir('/') failed\");\n\nWhy not return an error for this? If you can't close the dir that\nusually means the write or similar didn't work.\n"},{"id":"463875","messageId":"pull.1326.v14.git.1664395931.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v13.git.1664312253.gitgitgadget@gmail.com","subject":"[PATCH v14 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-28T20:12:05Z","receivedAt":"2022-09-28T20:12:21Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"Follow-on to the work done to allow Windows to work against network-mounted\nrepos for macOS.\n\nHave macOS take advantage of the same configuration option,\n'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\nto true will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for IPC\ncannot be created in a network location; instead $HOME is used if the\ndefault location is on the network. The user may, optionally, set the\n'fsmonitor.socketDir' configuration option to a valid, local directory if\n$HOME itself is on the network or is simply not the desired location for the\nUDS file.\n\nAn additional issue is that for mount points in the root directory, FSEvents\ndoes not report a path that matches the worktree directory due to the\nintroduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\nto the worktree directory by interrogating the root filesystem for synthetic\nfirmlinks and using that information to translate the path.\n\nv14 differs from v13:\n\n * code review feedback\n\nv13 differs from v12:\n\n * code review feedback\n\nv12 differs from v11:\n\n * bug fixes\n\nv11 differs from v10:\n\n * incorporates code review feedback\n * fix memory leak in fsm-listen-darwin.c\n\nv10 differs from v9:\n\n * incorporates code review feedback\n * improves error messaging for incompatible socket directory\n\nv9 differs from v8:\n\n * incorporates code review feedback\n * check for incompatibility before communicating with fsmonitor\n\nv8 differs from v7:\n\n * incorporates code review feedback\n * gets the rebase right\n\nv7 differs from v6:\n\n * incorporates code review feedback\n\nv6 differs from v5:\n\n * incorporates earlier, Windows-specific changes that have not made it back\n   yet to the master branch\n * incorporates code review feedback\n * adds documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n\nv5 differs significantly from earlier versions:\n\n * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n   such that these options are no longer added to the settings data\n   structure but are rather read from config at point of use\n * refactoring of code for handling platform-specific file system checks via\n   a common interface to avoid platform #ifdef in IPC code and be in-model\n   with other platform-specific fsmonitor code\n * dealing with 'synthetic firmlinks' on macOS\n\nEric DeCosta (6):\n  fsmonitor: refactor filesystem checks to common interface\n  fsmonitor: relocate socket file if .git directory is remote\n  fsmonitor: avoid socket location check if using hook\n  fsmonitor: deal with synthetic firmlinks on macOS\n  fsmonitor: check for compatability before communicating with fsmonitor\n  fsmonitor: add documentation for allowRemote and socketDir options\n\n Documentation/config.txt                   |   2 +\n Documentation/config/fsmonitor--daemon.txt |  11 ++\n Documentation/git-fsmonitor--daemon.txt    |  37 ++++-\n Makefile                                   |   2 +\n builtin/fsmonitor--daemon.c                |  11 +-\n compat/fsmonitor/fsm-ipc-darwin.c          |  52 ++++++\n compat/fsmonitor/fsm-ipc-win32.c           |   9 ++\n compat/fsmonitor/fsm-listen-darwin.c       |  14 +-\n compat/fsmonitor/fsm-path-utils-darwin.c   | 132 ++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c    | 145 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c     |  72 +++------\n compat/fsmonitor/fsm-settings-win32.c      | 174 +--------------------\n contrib/buildsystems/CMakeLists.txt        |   4 +\n fsmonitor--daemon.h                        |   3 +\n fsmonitor-ipc.c                            |  18 +--\n fsmonitor-ipc.h                            |   4 +-\n fsmonitor-path-utils.h                     |  57 +++++++\n fsmonitor-settings.c                       |  68 +++++++-\n fsmonitor-settings.h                       |   4 +-\n fsmonitor.c                                |   7 +\n 20 files changed, 580 insertions(+), 246 deletions(-)\n create mode 100644 Documentation/config/fsmonitor--daemon.txt\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\n\nbase-commit: 2a7d63a2453e2c30353342a2c9385fa22a846987\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v14\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v14\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v13:\n\n 1:  0b9b64428c5 = 1:  0b9b64428c5 fsmonitor: refactor filesystem checks to common interface\n 2:  680d4c83f99 = 2:  680d4c83f99 fsmonitor: relocate socket file if .git directory is remote\n 3:  7987d0c1f33 = 3:  7987d0c1f33 fsmonitor: avoid socket location check if using hook\n 4:  324eb5acd85 ! 4:  241043b7c15 fsmonitor: deal with synthetic firmlinks on macOS\n     @@ builtin/fsmonitor--daemon.c: static int fsmonitor_run_daemon(void)\n       \n      +\tstrbuf_init(&state.alias.alias, 0);\n      +\tstrbuf_init(&state.alias.points_to, 0);\n     -+\tif (fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)) {\n     -+\t\terr = error(_(\"could not get worktree alias\"));\n     ++\tif ((err = fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)))\n      +\t\tgoto done;\n     -+\t}\n      +\n       \t/*\n       \t * We create and delete cookie files somewhere inside the .git\n     @@ compat/fsmonitor/fsm-path-utils-darwin.c: int fsmonitor__is_fs_remote(const char\n      +int fsmonitor__get_alias(const char *path, struct alias_info *info)\n      +{\n      +\tDIR *dir;\n     -+\tint read;\n      +\tint retval = -1;\n     ++\tconst char *const root = \"/\";\n     ++\tstruct stat st;\n      +\tstruct dirent *de;\n      +\tstruct strbuf alias;\n     -+\tstruct strbuf points_to;\n     ++\tstruct strbuf points_to = STRBUF_INIT;\n      +\n     -+\tdir = opendir(\"/\");\n     -+\tif (!dir) {\n     -+\t\terror_errno(\"opendir('/') failed\");\n     -+\t\treturn -1;\n     -+\t}\n     ++\tdir = opendir(root);\n     ++\tif (!dir)\n     ++\t\treturn error_errno(_(\"opendir('%s') failed\"), root);\n      +\n      +\tstrbuf_init(&alias, 256);\n      +\n     -+\t/* no way of knowing what the link will resolve to, so MAXPATHLEN */\n     -+\tstrbuf_init(&points_to, MAXPATHLEN);\n     -+\n      +\twhile ((de = readdir(dir)) != NULL) {\n      +\t\tstrbuf_reset(&alias);\n     -+\t\tstrbuf_addch(&alias, '/');\n     -+\t\tstrbuf_add(&alias, de->d_name, strlen(de->d_name));\n     ++\t\tstrbuf_addf(&alias, \"%s%s\", root, de->d_name);\n      +\n     -+\t\tread = readlink(alias.buf, points_to.buf, MAXPATHLEN);\n     -+\t\tif (read > 0) {\n     -+\t\t\tstrbuf_setlen(&points_to, read);\n     -+\t\t\tif ((!strncmp(points_to.buf, path, points_to.len))\n     -+\t\t\t\t&& path[points_to.len] == '/') {\n     -+\t\t\t\tstrbuf_addbuf(&info->alias, &alias);\n     -+\t\t\t\tstrbuf_addbuf(&info->points_to, &points_to);\n     -+\t\t\t\ttrace_printf_key(&trace_fsmonitor,\n     -+\t\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n     -+\t\t\t\t\tpath, info->alias.buf, info->points_to.buf);\n     -+\t\t\t\tretval = 0;\n     -+\t\t\t\tgoto done;\n     -+\t\t\t}\n     -+\t\t} else if (!read) {\n     -+\t\t\tBUG(\"readlink returned 0\");\n     -+\t\t} else if (errno != EINVAL) { /* Something other than not a link */\n     -+\t\t\terror_errno(\"readlink('%s') failed\", alias.buf);\n     ++\t\tif (lstat(alias.buf, &st) < 0) {\n     ++\t\t\terror_errno(_(\"lstat('%s') failed\"), alias.buf);\n     ++\t\t\tgoto done;\n     ++\t\t}\n     ++\n     ++\t\tif (!S_ISLNK(st.st_mode))\n     ++\t\t\tcontinue;\n     ++\n     ++\t\tif (strbuf_readlink(&points_to, alias.buf, st.st_size) < 0) {\n     ++\t\t\terror_errno(_(\"strbuf_readlink('%s') failed\"), alias.buf);\n     ++\t\t\tgoto done;\n     ++\t\t}\n     ++\n     ++\t\tif (!strncmp(points_to.buf, path, points_to.len) &&\n     ++\t\t\t(path[points_to.len] == '/')) {\n     ++\t\t\tstrbuf_addbuf(&info->alias, &alias);\n     ++\t\t\tstrbuf_addbuf(&info->points_to, &points_to);\n     ++\t\t\ttrace_printf_key(&trace_fsmonitor,\n     ++\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n     ++\t\t\t\tpath, info->alias.buf, info->points_to.buf);\n     ++\t\t\tretval = 0;\n      +\t\t\tgoto done;\n      +\t\t}\n      +\t}\n      +\tretval = 0; /* no alias */\n      +\n      +done:\n     -+\tif (closedir(dir) < 0)\n     -+\t\twarning_errno(\"closedir('/') failed\");\n      +\tstrbuf_release(&alias);\n      +\tstrbuf_release(&points_to);\n     ++\tif (closedir(dir) < 0)\n     ++\t\treturn error_errno(_(\"closedir('%s') failed\"), root);\n      +\treturn retval;\n      +}\n      +\n 5:  b1ea378dff7 = 5:  d906debba5e fsmonitor: check for compatability before communicating with fsmonitor\n 6:  04f607b1f21 = 6:  ed14fbd009e fsmonitor: add documentation for allowRemote and socketDir options\n\n-- \ngitgitgadget\n"},{"id":"463876","messageId":"0b9b64428c5e096ada1f274639be9604e8c2f9ed.1664395931.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v14.git.1664395931.gitgitgadget@gmail.com","subject":"[PATCH v14 1/6] fsmonitor: refactor filesystem checks to common interface","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-28T20:12:06Z","receivedAt":"2022-09-28T20:12:24Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nProvide a common interface for getting basic filesystem information\nincluding filesystem type and whether the filesystem is remote.\n\nRefactor existing code for getting basic filesystem info and detecting\nremote file systems to the new interface.\n\nRefactor filesystem checks to leverage new interface. For macOS,\nerror-out if the Unix Domain socket (UDS) file is on a remote\nfilesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                                 |   1 +\n compat/fsmonitor/fsm-path-utils-darwin.c |  40 ++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 128 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  62 +++-----\n compat/fsmonitor/fsm-settings-win32.c    | 172 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   2 +\n fsmonitor-path-utils.h                   |  23 +++\n fsmonitor-settings.c                     |  50 +++++++\n 8 files changed, 263 insertions(+), 215 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\ndiff --git a/Makefile b/Makefile\nindex cac3452edb9..ffab427ea5b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2044,6 +2044,7 @@ endif\n ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n ifeq ($(TCLTK_PATH),)\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nnew file mode 100644\nindex 00000000000..067cbe6990a\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -0,0 +1,40 @@\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+#include <sys/param.h>\n+#include <sys/mount.h>\n+\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\tstruct statfs fs;\n+\tif (statfs(path, &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\treturn -1;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\n+\tif (!(fs.f_flags & MNT_LOCAL))\n+\t\tfs_info->is_remote = 1;\n+\telse\n+\t\tfs_info->is_remote = 0;\n+\n+\tfs_info->typename = fs.f_fstypename;\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nnew file mode 100644\nindex 00000000000..a90b8f7925b\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -0,0 +1,128 @@\n+#include \"cache.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+/*\n+ * Check remote working directory protocol.\n+ *\n+ * Return -1 if client machine cannot get remote protocol information.\n+ */\n+static int check_remote_protocol(wchar_t *wpath)\n+{\n+\tHANDLE h;\n+\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n+\n+\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n+\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\n+\tif (h == INVALID_HANDLE_VALUE) {\n+\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n+\t\t&proto_info, sizeof(proto_info))) {\n+\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\tCloseHandle(h);\n+\t\treturn -1;\n+\t}\n+\n+\tCloseHandle(h);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n+\t\t\t\twpath, proto_info.Protocol);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Notes for testing:\n+ *\n+ * (a) Windows allows a network share to be mapped to a drive letter.\n+ *     (This is the normal method to access it.)\n+ *\n+ *     $ NET USE Z: \\\\server\\share\n+ *     $ git -C Z:/repo status\n+ *\n+ * (b) Windows allows a network share to be referenced WITHOUT mapping\n+ *     it to drive letter.\n+ *\n+ *     $ NET USE \\\\server\\share\\dir\n+ *     $ git -C //server/share/repo status\n+ *\n+ * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n+ *     arbitrary path (which may be remote)\n+ *\n+ *     $ SUBST Q: Z:\\repo\n+ *     $ git -C Q:/ status\n+ *\n+ * (d) Windows allows a directory symlink to be created on a local\n+ *     file system that points to a remote repo.\n+ *\n+ *     $ mklink /d ./link //server/share/repo\n+ *     $ git -C ./link status\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\twchar_t wpath[MAX_PATH];\n+\twchar_t wfullpath[MAX_PATH];\n+\tsize_t wlen;\n+\tUINT driveType;\n+\n+\t/*\n+\t * Do everything in wide chars because the drive letter might be\n+\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n+\t */\n+\tif (xutftowcs_path(wpath, path) < 0) {\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * GetDriveTypeW() requires a final slash.  We assume that the\n+\t * worktree pathname points to an actual directory.\n+\t */\n+\twlen = wcslen(wpath);\n+\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n+\t\twpath[wlen++] = L'\\\\';\n+\t\twpath[wlen] = 0;\n+\t}\n+\n+\t/*\n+\t * Normalize the path.  If nothing else, this converts forward\n+\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n+\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n+\t */\n+\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {\n+\t\treturn -1;\n+\t}\n+\n+\tdriveType = GetDriveTypeW(wfullpath);\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n+\t\t\t path, wfullpath, driveType);\n+\n+\tif (driveType == DRIVE_REMOTE) {\n+\t\tfs_info->is_remote = 1;\n+\t\tif (check_remote_protocol(wfullpath) < 0)\n+\t\t\treturn -1;\n+\t} else {\n+\t\tfs_info->is_remote = 0;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..dba3ced6bb7 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -1,32 +1,10 @@\n-#include \"cache.h\"\n #include \"config.h\"\n-#include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n-#include <sys/param.h>\n-#include <sys/mount.h>\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n-/*\n- * [1] Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type (NFS, SAMBA, etc.) dictates whether notification events\n- * are available at all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n+ /*\n  * For the builtin FSMonitor, we create the Unix domain socket for the\n  * IPC in the .git directory.  If the working directory is remote,\n  * then the socket will be created on the remote file system.  This\n@@ -38,40 +16,34 @@\n  * be taken to ensure that $HOME is actually local and not a managed\n  * file share.)\n  *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n+ * FAT32 and NTFS working directories are problematic too.\n  *\n  * The builtin FSMonitor uses a Unix domain socket in the .git\n  * directory for IPC.  These Windows drive formats do not support\n  * Unix domain sockets, so mark them as incompatible for the daemon.\n  *\n  */\n-static enum fsmonitor_reason check_volume(struct repository *r)\n+static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n-\tstruct statfs fs;\n+\tstruct fs_info fs;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \n-\tif (statfs(r->worktree, &fs) == -1) {\n-\t\tint saved_errno = errno;\n-\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n-\t\t\t\t r->worktree, strerror(saved_errno));\n-\t\terrno = saved_errno;\n+\tif (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {\n+\t\tstrbuf_release(&path);\n \t\treturn FSMONITOR_REASON_ERROR;\n \t}\n \n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n-\t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\tstrbuf_release(&path);\n \n-\tif (!(fs.f_flags & MNT_LOCAL))\n+\tif (fs.is_remote)\n \t\treturn FSMONITOR_REASON_REMOTE;\n \n-\tif (!strcmp(fs.f_fstypename, \"msdos\")) /* aka FAT32 */\n+\tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n-\tif (!strcmp(fs.f_fstypename, \"ntfs\"))\n+\tif (!strcmp(fs.typename, \"ntfs\"))\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \treturn FSMONITOR_REASON_OK;\n@@ -81,7 +53,7 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_volume(r);\n+\treason = check_uds_volume(r);\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex e5ec5b0a9f7..d88b06ae610 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -1,8 +1,9 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * VFS for Git is incompatible with FSMonitor.\n@@ -24,171 +25,6 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-/*\n- * Check if monitoring remote working directories is allowed.\n- *\n- * By default, monitoring remote working directories is\n- * disabled.  Users may override this behavior in enviroments where\n- * they have proper support.\n- */\n-static int check_config_allowremote(struct repository *r)\n-{\n-\tint allow;\n-\n-\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n-\t\treturn allow;\n-\n-\treturn -1; /* fsmonitor.allowremote not set */\n-}\n-\n-/*\n- * Check remote working directory protocol.\n- *\n- * Error if client machine cannot get remote protocol information.\n- */\n-static int check_remote_protocol(wchar_t *wpath)\n-{\n-\tHANDLE h;\n-\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n-\n-\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n-\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n-\n-\tif (h == INVALID_HANDLE_VALUE) {\n-\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\treturn -1;\n-\t}\n-\n-\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n-\t\t&proto_info, sizeof(proto_info))) {\n-\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\tCloseHandle(h);\n-\t\treturn -1;\n-\t}\n-\n-\tCloseHandle(h);\n-\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n-\t\t\t\twpath, proto_info.Protocol);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type dictates whether notification events are available at\n- * all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- * Notes for testing:\n- *\n- * (a) Windows allows a network share to be mapped to a drive letter.\n- *     (This is the normal method to access it.)\n- *\n- *     $ NET USE Z: \\\\server\\share\n- *     $ git -C Z:/repo status\n- *\n- * (b) Windows allows a network share to be referenced WITHOUT mapping\n- *     it to drive letter.\n- *\n- *     $ NET USE \\\\server\\share\\dir\n- *     $ git -C //server/share/repo status\n- *\n- * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n- *     arbitrary path (which may be remote)\n- *\n- *     $ SUBST Q: Z:\\repo\n- *     $ git -C Q:/ status\n- *\n- * (d) Windows allows a directory symlink to be created on a local\n- *     file system that points to a remote repo.\n- *\n- *     $ mklink /d ./link //server/share/repo\n- *     $ git -C ./link status\n- */\n-static enum fsmonitor_reason check_remote(struct repository *r)\n-{\n-\tint ret;\n-\twchar_t wpath[MAX_PATH];\n-\twchar_t wfullpath[MAX_PATH];\n-\tsize_t wlen;\n-\tUINT driveType;\n-\n-\t/*\n-\t * Do everything in wide chars because the drive letter might be\n-\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n-\t */\n-\tif (xutftowcs_path(wpath, r->worktree) < 0)\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t/*\n-\t * GetDriveTypeW() requires a final slash.  We assume that the\n-\t * worktree pathname points to an actual directory.\n-\t */\n-\twlen = wcslen(wpath);\n-\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n-\t\twpath[wlen++] = L'\\\\';\n-\t\twpath[wlen] = 0;\n-\t}\n-\n-\t/*\n-\t * Normalize the path.  If nothing else, this converts forward\n-\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n-\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n-\t */\n-\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\tdriveType = GetDriveTypeW(wfullpath);\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n-\t\t\t r->worktree, wfullpath, driveType);\n-\n-\tif (driveType == DRIVE_REMOTE) {\n-\t\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t \"check_remote('%s') true\",\n-\t\t\t\t r->worktree);\n-\n-\t\tret = check_remote_protocol(wfullpath);\n-\t\tif (ret < 0)\n-\t\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t\tswitch (check_config_allowremote(r)) {\n-\t\tcase 0: /* config overrides and disables */\n-\t\t\treturn FSMONITOR_REASON_REMOTE;\n-\t\tcase 1: /* config overrides and enables */\n-\t\t\treturn FSMONITOR_REASON_OK;\n-\t\tdefault:\n-\t\t\tbreak; /* config has no opinion */\n-\t\t}\n-\n-\t\treturn FSMONITOR_REASON_REMOTE;\n-\t}\n-\n-\treturn FSMONITOR_REASON_OK;\n-}\n-\n enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n@@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n-\treason = check_remote(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n-\n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex ea2a531be87..5482a04b3ce 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)\n@@ -315,6 +316,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nnew file mode 100644\nindex 00000000000..e48592887e7\n--- /dev/null\n+++ b/fsmonitor-path-utils.h\n@@ -0,0 +1,23 @@\n+#ifndef FSM_PATH_UTILS_H\n+#define FSM_PATH_UTILS_H\n+\n+struct fs_info {\n+\tint is_remote;\n+\tchar *typename;\n+};\n+\n+/*\n+ * Get some basic filesystem informtion for the given path\n+ *\n+ * Returns -1 on error, zero otherwise.\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n+\n+/*\n+ * Determines if the filesystem that path resides on is remote.\n+ *\n+ * Returns -1 on error, 0 if not remote, 1 if remote.\n+ */\n+int fsmonitor__is_fs_remote(const char *path);\n+\n+#endif\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..d288cbad479 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * We keep this structure defintion private and have getters\n@@ -13,6 +14,52 @@ struct fsmonitor_settings {\n \tchar *hook_path;\n };\n \n+/*\n+ * Remote working directories are problematic for FSMonitor.\n+ *\n+ * The underlying file system on the server machine and/or the remote\n+ * mount type dictates whether notification events are available at\n+ * all to remote client machines.\n+ *\n+ * Kernel differences between the server and client machines also\n+ * dictate the how (buffering, frequency, de-dup) the events are\n+ * delivered to client machine processes.\n+ *\n+ * A client machine (such as a laptop) may choose to suspend/resume\n+ * and it is unclear (without lots of testing) whether the watcher can\n+ * resync after a resume.  We might be able to treat this as a normal\n+ * \"events were dropped by the kernel\" event and do our normal \"flush\n+ * and resync\" --or-- we might need to close the existing (zombie?)\n+ * notification fd and create a new one.\n+ *\n+ * In theory, the above issues need to be addressed whether we are\n+ * using the Hook or IPC API.\n+ *\n+ * So (for now at least), mark remote working directories as\n+ * incompatible unless 'fsmonitor.allowRemote' is true.\n+ *\n+ */\n+#ifdef HAVE_FSMONITOR_OS_SETTINGS\n+static enum fsmonitor_reason check_remote(struct repository *r)\n+{\n+\tint allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */\n+\tint is_remote = fsmonitor__is_fs_remote(r->worktree);\n+\n+\tswitch (is_remote) {\n+\t\tcase 0:\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tcase 1:\n+\t\t\trepo_config_get_bool(r, \"fsmonitor.allowremote\", &allow_remote);\n+\t\t\tif (allow_remote < 1)\n+\t\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\t\telse\n+\t\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+}\n+#endif\n+\n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n {\n \tif (!r->worktree) {\n@@ -27,6 +74,9 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t{\n \t\tenum fsmonitor_reason reason;\n \n+\t\treason = check_remote(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n \t\treason = fsm_os__incompatible(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-- \ngitgitgadget\n\n"},{"id":"463877","messageId":"680d4c83f99dacb1cac90f96bc2f02d9614b880c.1664395931.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v14.git.1664395931.gitgitgadget@gmail.com","subject":"[PATCH v14 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-28T20:12:07Z","receivedAt":"2022-09-28T20:12:27Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf the .git directory is on a remote filesystem, create the socket\nfile in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                               |  1 +\n builtin/fsmonitor--daemon.c            |  3 +-\n compat/fsmonitor/fsm-ipc-darwin.c      | 52 ++++++++++++++++++++++++++\n compat/fsmonitor/fsm-ipc-win32.c       |  9 +++++\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n contrib/buildsystems/CMakeLists.txt    |  2 +\n fsmonitor-ipc.c                        | 18 ++++-----\n fsmonitor-ipc.h                        |  4 +-\n 8 files changed, 78 insertions(+), 13 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n\ndiff --git a/Makefile b/Makefile\nindex ffab427ea5b..feb675a6959 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2039,6 +2039,7 @@ ifdef FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n endif\n \n ifdef FSMONITOR_OS_SETTINGS\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 2c109cf8b37..0123fc33ed2 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -1343,7 +1343,8 @@ static int fsmonitor_run_daemon(void)\n \t * directory.)\n \t */\n \tstrbuf_init(&state.path_ipc, 0);\n-\tstrbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));\n+\tstrbuf_addstr(&state.path_ipc,\n+\t\tabsolute_path(fsmonitor_ipc__get_path(the_repository)));\n \n \t/*\n \t * Confirm that we can create platform-specific resources for the\ndiff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\nnew file mode 100644\nindex 00000000000..ce843d63348\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-darwin.c\n@@ -0,0 +1,52 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"strbuf.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n+{\n+\tstatic const char *ipc_path = NULL;\n+\tSHA_CTX sha1ctx;\n+\tchar *sock_dir = NULL;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (!r)\n+\t\tBUG(\"No repository passed into fsmonitor_ipc__get_path\");\n+\n+\tif (ipc_path)\n+\t\treturn ipc_path;\n+\n+\n+\t/* By default the socket file is created in the .git directory */\n+\tif (fsmonitor__is_fs_remote(r->gitdir) < 1) {\n+\t\tipc_path = fsmonitor_ipc__get_default_path();\n+\t\treturn ipc_path;\n+\t}\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\trepo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n+\n+\t/* Create the socket file in either socketDir or $HOME */\n+\tif (sock_dir && *sock_dir) {\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\t} else {\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\t}\n+\tfree(sock_dir);\n+\n+\tipc_path = interpolate_path(ipc_file.buf, 1);\n+\tif (!ipc_path)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\n+\tstrbuf_release(&ipc_file);\n+\treturn ipc_path;\n+}\ndiff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\nnew file mode 100644\nindex 00000000000..e08c505c148\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-win32.c\n@@ -0,0 +1,9 @@\n+#include \"config.h\"\n+#include \"fsmonitor-ipc.h\"\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r) {\n+\tstatic char *ret;\n+\tif (!ret)\n+\t\tret = git_pathdup(\"fsmonitor--daemon.ipc\");\n+\treturn ret;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex dba3ced6bb7..681d8bf963e 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -26,7 +26,7 @@\n static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n \tstruct fs_info fs;\n-\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tconst char *ipc_path = fsmonitor_ipc__get_path(r);\n \tstruct strbuf path = STRBUF_INIT;\n \tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 5482a04b3ce..787738e6fa3 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n@@ -316,6 +317,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..c0f42301c84 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -18,7 +18,7 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 0;\n }\n \n-const char *fsmonitor_ipc__get_path(void)\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n {\n \treturn NULL;\n }\n@@ -47,11 +47,9 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n-\n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\n-\treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n+\treturn ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));\n }\n \n static int spawn_daemon(void)\n@@ -81,8 +79,8 @@ int fsmonitor_ipc__send_query(const char *since_token,\n \ttrace2_data_string(\"fsm_client\", NULL, \"query/command\", tok);\n \n try_again:\n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \n \tswitch (state) {\n \tcase IPC_STATE__LISTENING:\n@@ -117,13 +115,13 @@ try_again:\n \n \tcase IPC_STATE__INVALID_PATH:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: invalid path '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \n \tcase IPC_STATE__OTHER_ERROR:\n \tdefault:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: unspecified error on '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \t}\n \n@@ -149,8 +147,8 @@ int fsmonitor_ipc__send_command(const char *command,\n \toptions.wait_if_busy = 1;\n \toptions.wait_if_not_found = 0;\n \n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \tif (state != IPC_STATE__LISTENING) {\n \t\tdie(_(\"fsmonitor--daemon is not running\"));\n \t\treturn -1;\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..8b489da762b 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -3,6 +3,8 @@\n \n #include \"simple-ipc.h\"\n \n+struct repository;\n+\n /*\n  * Returns true if built-in file system monitor daemon is defined\n  * for this platform.\n@@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);\n  *\n  * Returns NULL if the daemon is not supported on this platform.\n  */\n-const char *fsmonitor_ipc__get_path(void);\n+const char *fsmonitor_ipc__get_path(struct repository *r);\n \n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n-- \ngitgitgadget\n\n"},{"id":"463878","messageId":"7987d0c1f33da7cdd62f2ffbaccd1cda281a7305.1664395931.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v14.git.1664395931.gitgitgadget@gmail.com","subject":"[PATCH v14 3/6] fsmonitor: avoid socket location check if using hook","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-28T20:12:08Z","receivedAt":"2022-09-28T20:12:29Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf monitoring is done via fsmonitor hook rather than IPC there is no\nneed to check if the location of the Unix Domain socket (UDS) file is\non a remote filesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 10 ++++++----\n compat/fsmonitor/fsm-settings-win32.c  |  2 +-\n fsmonitor-settings.c                   |  8 ++++----\n fsmonitor-settings.h                   |  2 +-\n 4 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 681d8bf963e..40da2d3b533 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -49,13 +49,15 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_uds_volume(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n+\tif (ipc) {\n+\t\treason = check_uds_volume(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n+\t}\n \n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex d88b06ae610..a8af31b71de 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex d288cbad479..531a1b6f956 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -60,7 +60,7 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n }\n #endif\n \n-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n+static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n {\n \tif (!r->worktree) {\n \t\t/*\n@@ -77,7 +77,7 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t\treason = check_remote(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-\t\treason = fsm_os__incompatible(r);\n+\t\treason = fsm_os__incompatible(r, ipc);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n \t}\n@@ -162,7 +162,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -185,7 +185,7 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..0721617b95a 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -48,7 +48,7 @@ struct fsmonitor_settings;\n  * fsm_os__* routines should considered private to fsm_settings__\n  * routines.\n  */\n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n \n #endif /* FSMONITOR_SETTINGS_H */\n-- \ngitgitgadget\n\n"},{"id":"463879","messageId":"d906debba5e22d4cef0bc9d63d049ced24710882.1664395931.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v14.git.1664395931.gitgitgadget@gmail.com","subject":"[PATCH v14 5/6] fsmonitor: check for compatability before communicating with fsmonitor","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-28T20:12:10Z","receivedAt":"2022-09-28T20:12:31Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf fsmonitor is not in a compatible state, warn with an appropriate message.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n fsmonitor-settings.c                   | 10 +++++++---\n fsmonitor-settings.h                   |  2 +-\n fsmonitor.c                            |  7 +++++++\n 4 files changed, 16 insertions(+), 5 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 40da2d3b533..44233125df8 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -38,7 +38,7 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \tstrbuf_release(&path);\n \n \tif (fs.is_remote)\n-\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\treturn FSMONITOR_REASON_NOSOCKETS;\n \n \tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 531a1b6f956..ee63a97dc51 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n+#include \"fsmonitor-ipc.h\"\n #include \"fsmonitor-settings.h\"\n #include \"fsmonitor-path-utils.h\"\n \n@@ -242,10 +243,11 @@ enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)\n \treturn r->settings.fsmonitor->reason;\n }\n \n-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n+char *fsm_settings__get_incompatible_msg(struct repository *r,\n \t\t\t\t\t enum fsmonitor_reason reason)\n {\n \tstruct strbuf msg = STRBUF_INIT;\n+\tconst char *socket_dir;\n \n \tswitch (reason) {\n \tcase FSMONITOR_REASON_UNTESTED:\n@@ -281,9 +283,11 @@ char *fsm_settings__get_incompatible_msg(const struct repository *r,\n \t\tgoto done;\n \n \tcase FSMONITOR_REASON_NOSOCKETS:\n+\t\tsocket_dir = dirname((char *)fsmonitor_ipc__get_path(r));\n \t\tstrbuf_addf(&msg,\n-\t\t\t    _(\"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets\"),\n-\t\t\t    r->worktree);\n+\t\t\t    _(\"socket directory '%s' is incompatible with fsmonitor due\"\n+\t\t\t      \" to lack of Unix sockets support\"),\n+\t\t\t    socket_dir);\n \t\tgoto done;\n \t}\n \ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex 0721617b95a..ab02e3995ee 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -33,7 +33,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n const char *fsm_settings__get_hook_path(struct repository *r);\n \n enum fsmonitor_reason fsm_settings__get_reason(struct repository *r);\n-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n+char *fsm_settings__get_incompatible_msg(struct repository *r,\n \t\t\t\t\t enum fsmonitor_reason reason);\n \n struct fsmonitor_settings;\ndiff --git a/fsmonitor.c b/fsmonitor.c\nindex 57d6a483bee..540736b39fd 100644\n--- a/fsmonitor.c\n+++ b/fsmonitor.c\n@@ -295,6 +295,7 @@ static int fsmonitor_force_update_threshold = 100;\n \n void refresh_fsmonitor(struct index_state *istate)\n {\n+\tstatic int warn_once = 0;\n \tstruct strbuf query_result = STRBUF_INIT;\n \tint query_success = 0, hook_version = -1;\n \tsize_t bol = 0; /* beginning of line */\n@@ -305,6 +306,12 @@ void refresh_fsmonitor(struct index_state *istate)\n \tint is_trivial = 0;\n \tstruct repository *r = istate->repo ? istate->repo : the_repository;\n \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);\n+\tenum fsmonitor_reason reason = fsm_settings__get_reason(r);\n+\n+\tif (!warn_once && reason > FSMONITOR_REASON_OK) {\n+\t\twarn_once = 1;\n+\t\twarning(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n+\t}\n \n \tif (fsm_mode <= FSMONITOR_MODE_DISABLED ||\n \t    istate->fsmonitor_has_run_once)\n-- \ngitgitgadget\n\n"},{"id":"463880","messageId":"241043b7c151b8c85b37dd3aceb3a00f58a14aac.1664395931.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v14.git.1664395931.gitgitgadget@gmail.com","subject":"[PATCH v14 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-28T20:12:09Z","receivedAt":"2022-09-28T20:12:33Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n builtin/fsmonitor--daemon.c              |  8 +++\n compat/fsmonitor/fsm-listen-darwin.c     | 14 +++-\n compat/fsmonitor/fsm-path-utils-darwin.c | 92 ++++++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 17 +++++\n fsmonitor--daemon.h                      |  3 +\n fsmonitor-path-utils.h                   | 36 +++++++++-\n 6 files changed, 167 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 0123fc33ed2..7a4cb78c7dd 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"fsmonitor.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n #include \"compat/fsmonitor/fsm-health.h\"\n #include \"compat/fsmonitor/fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n@@ -1282,6 +1283,11 @@ static int fsmonitor_run_daemon(void)\n \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n \tstate.nr_paths_watching = 1;\n \n+\tstrbuf_init(&state.alias.alias, 0);\n+\tstrbuf_init(&state.alias.points_to, 0);\n+\tif ((err = fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)))\n+\t\tgoto done;\n+\n \t/*\n \t * We create and delete cookie files somewhere inside the .git\n \t * directory to help us keep sync with the file system.  If\n@@ -1391,6 +1397,8 @@ done:\n \tstrbuf_release(&state.path_gitdir_watch);\n \tstrbuf_release(&state.path_cookie_prefix);\n \tstrbuf_release(&state.path_ipc);\n+\tstrbuf_release(&state.alias.alias);\n+\tstrbuf_release(&state.alias.points_to);\n \n \treturn err;\n }\ndiff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\nindex 8e208e8289e..daeee4e465c 100644\n--- a/compat/fsmonitor/fsm-listen-darwin.c\n+++ b/compat/fsmonitor/fsm-listen-darwin.c\n@@ -26,6 +26,7 @@\n #include \"fsmonitor.h\"\n #include \"fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsm_listen_data\n {\n@@ -198,8 +199,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \tstruct string_list cookie_list = STRING_LIST_INIT_DUP;\n \tconst char *path_k;\n \tconst char *slash;\n-\tint k;\n+\tchar *resolved = NULL;\n \tstruct strbuf tmp = STRBUF_INIT;\n+\tint k;\n \n \t/*\n \t * Build a list of all filesystem changes into a private/local\n@@ -209,7 +211,12 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t/*\n \t\t * On Mac, we receive an array of absolute paths.\n \t\t */\n-\t\tpath_k = paths[k];\n+\t\tfree(resolved);\n+\t\tresolved = fsmonitor__resolve_alias(paths[k], &state->alias);\n+\t\tif (resolved)\n+\t\t\tpath_k = resolved;\n+\t\telse\n+\t\t\tpath_k = paths[k];\n \n \t\t/*\n \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n@@ -238,6 +245,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t\tfsmonitor_force_resync(state);\n \t\t\tfsmonitor_batch__free_list(batch);\n \t\t\tstring_list_clear(&cookie_list, 0);\n+\t\t\tbatch = NULL;\n \n \t\t\t/*\n \t\t\t * We assume that any events that we received\n@@ -360,12 +368,14 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t}\n \t}\n \n+\tfree(resolved);\n \tfsmonitor_publish(state, batch, &cookie_list);\n \tstring_list_clear(&cookie_list, 0);\n \tstrbuf_release(&tmp);\n \treturn;\n \n force_shutdown:\n+\tfree(resolved);\n \tfsmonitor_batch__free_list(batch);\n \tstring_list_clear(&cookie_list, 0);\n \ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nindex 067cbe6990a..fc12fd831ec 100644\n--- a/compat/fsmonitor/fsm-path-utils-darwin.c\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -1,5 +1,8 @@\n #include \"fsmonitor.h\"\n #include \"fsmonitor-path-utils.h\"\n+#include <dirent.h>\n+#include <errno.h>\n+#include <fcntl.h>\n #include <sys/param.h>\n #include <sys/mount.h>\n \n@@ -38,3 +41,92 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * Scan the root directory for synthetic firmlinks that when resolved\n+ * are a prefix of the path, stopping at the first one found.\n+ *\n+ * Some information about firmlinks and synthetic firmlinks:\n+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n+ *\n+ * macOS no longer allows symlinks in the root directory; any link found\n+ * there is therefore a synthetic firmlink.\n+ *\n+ * If this function gets called often, will want to cache all the firmlink\n+ * information, but for now there is only one caller of this function.\n+ *\n+ * If there is more than one alias for the path, that is another\n+ * matter altogether.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\tDIR *dir;\n+\tint retval = -1;\n+\tconst char *const root = \"/\";\n+\tstruct stat st;\n+\tstruct dirent *de;\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to = STRBUF_INIT;\n+\n+\tdir = opendir(root);\n+\tif (!dir)\n+\t\treturn error_errno(_(\"opendir('%s') failed\"), root);\n+\n+\tstrbuf_init(&alias, 256);\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstrbuf_reset(&alias);\n+\t\tstrbuf_addf(&alias, \"%s%s\", root, de->d_name);\n+\n+\t\tif (lstat(alias.buf, &st) < 0) {\n+\t\t\terror_errno(_(\"lstat('%s') failed\"), alias.buf);\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\tif (!S_ISLNK(st.st_mode))\n+\t\t\tcontinue;\n+\n+\t\tif (strbuf_readlink(&points_to, alias.buf, st.st_size) < 0) {\n+\t\t\terror_errno(_(\"strbuf_readlink('%s') failed\"), alias.buf);\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\tif (!strncmp(points_to.buf, path, points_to.len) &&\n+\t\t\t(path[points_to.len] == '/')) {\n+\t\t\tstrbuf_addbuf(&info->alias, &alias);\n+\t\t\tstrbuf_addbuf(&info->points_to, &points_to);\n+\t\t\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n+\t\t\t\tpath, info->alias.buf, info->points_to.buf);\n+\t\t\tretval = 0;\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\tretval = 0; /* no alias */\n+\n+done:\n+\tstrbuf_release(&alias);\n+\tstrbuf_release(&points_to);\n+\tif (closedir(dir) < 0)\n+\t\treturn error_errno(_(\"closedir('%s') failed\"), root);\n+\treturn retval;\n+}\n+\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\tif (!info->alias.len)\n+\t\treturn NULL;\n+\n+\tif ((!strncmp(info->alias.buf, path, info->alias.len))\n+\t\t&& path[info->alias.len] == '/') {\n+\t\tstruct strbuf tmp = STRBUF_INIT;\n+\t\tconst char *remainder = path + info->alias.len;\n+\n+\t\tstrbuf_addbuf(&tmp, &info->points_to);\n+\t\tstrbuf_add(&tmp, remainder, strlen(remainder));\n+\t\treturn strbuf_detach(&tmp, NULL);\n+\t}\n+\n+\treturn NULL;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nindex a90b8f7925b..0d95bbb416f 100644\n--- a/compat/fsmonitor/fsm-path-utils-win32.c\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -126,3 +126,20 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * No-op for now.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\treturn 0;\n+}\n+\n+/*\n+ * No-op for now.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\treturn NULL;\n+}\ndiff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\nindex 2102a5c9ff5..e24838f9a86 100644\n--- a/fsmonitor--daemon.h\n+++ b/fsmonitor--daemon.h\n@@ -8,6 +8,7 @@\n #include \"run-command.h\"\n #include \"simple-ipc.h\"\n #include \"thread-utils.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsmonitor_batch;\n struct fsmonitor_token_data;\n@@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct strbuf path_worktree_watch;\n \tstruct strbuf path_gitdir_watch;\n+\tstruct alias_info alias;\n \tint nr_paths_watching;\n \n \tstruct fsmonitor_token_data *current_token_data;\n@@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct ipc_server_data *ipc_server_data;\n \tstruct strbuf path_ipc;\n+\n };\n \n /*\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nindex e48592887e7..2624798d2c7 100644\n--- a/fsmonitor-path-utils.h\n+++ b/fsmonitor-path-utils.h\n@@ -1,13 +1,21 @@\n #ifndef FSM_PATH_UTILS_H\n #define FSM_PATH_UTILS_H\n \n+#include \"strbuf.h\"\n+\n+struct alias_info\n+{\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+};\n+\n struct fs_info {\n \tint is_remote;\n \tchar *typename;\n };\n \n /*\n- * Get some basic filesystem informtion for the given path\n+ * Get some basic filesystem information for the given path\n  *\n  * Returns -1 on error, zero otherwise.\n  */\n@@ -20,4 +28,30 @@ int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n  */\n int fsmonitor__is_fs_remote(const char *path);\n \n+/*\n+ * Get the alias in given path, if any.\n+ *\n+ * Sets alias to the first alias that matches any part of the path.\n+ *\n+ * If an alias is found, info.alias and info.points_to are set to the\n+ * found mapping.\n+ *\n+ * Returns -1 on error, 0 otherwise.\n+ *\n+ * The caller owns the storage that is occupied by info.alias and\n+ * info.points_to and is responsible for releasing it.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info);\n+\n+/*\n+ * Resolve the path against the given alias.\n+ *\n+ * Returns the resolved path if there is one, NULL otherwise.\n+ *\n+ * The caller owns the storage that the returned string occupies and\n+ * is responsible for releasing it.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info);\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"463881","messageId":"ed14fbd009eae5f67524e5aba99d0a917f29ae7f.1664395931.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v14.git.1664395931.gitgitgadget@gmail.com","subject":"[PATCH v14 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-28T20:12:11Z","receivedAt":"2022-09-28T20:12:47Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nAdd documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\nCall-out experimental nature of 'fsmonitor.allowRemote' and limited\nfilesystem support for 'fsmonitor.socketDir'.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Documentation/config.txt                   |  2 ++\n Documentation/config/fsmonitor--daemon.txt | 11 +++++++\n Documentation/git-fsmonitor--daemon.txt    | 37 ++++++++++++++++++++--\n 3 files changed, 47 insertions(+), 3 deletions(-)\n create mode 100644 Documentation/config/fsmonitor--daemon.txt\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 5b5b9765699..1e205831656 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -423,6 +423,8 @@ include::config/filter.txt[]\n \n include::config/fsck.txt[]\n \n+include::config/fsmonitor--daemon.txt[]\n+\n include::config/gc.txt[]\n \n include::config/gitcvs.txt[]\ndiff --git a/Documentation/config/fsmonitor--daemon.txt b/Documentation/config/fsmonitor--daemon.txt\nnew file mode 100644\nindex 00000000000..c225c6c9e74\n--- /dev/null\n+++ b/Documentation/config/fsmonitor--daemon.txt\n@@ -0,0 +1,11 @@\n+fsmonitor.allowRemote::\n+    By default, the fsmonitor daemon refuses to work against network-mounted\n+    repositories. Setting `fsmonitor.allowRemote` to `true` overrides this\n+    behavior.  Only respected when `core.fsmonitor` is set to `true`.\n+\n+fsmonitor.socketDir::\n+    This Mac OS-specific option, if set, specifies the directory in\n+    which to create the Unix domain socket used for communication\n+    between the fsmonitor daemon and various Git commands. The directory must\n+    reside on a native Mac OS filesystem.  Only respected when `core.fsmonitor`\n+    is set to `true`.\ndiff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt\nindex cc142fb8612..8238eadb0e1 100644\n--- a/Documentation/git-fsmonitor--daemon.txt\n+++ b/Documentation/git-fsmonitor--daemon.txt\n@@ -3,7 +3,7 @@ git-fsmonitor{litdd}daemon(1)\n \n NAME\n ----\n-git-fsmonitor--daemon - A Built-in File System Monitor\n+git-fsmonitor--daemon - A Built-in Filesystem Monitor\n \n SYNOPSIS\n --------\n@@ -17,7 +17,7 @@ DESCRIPTION\n -----------\n \n A daemon to watch the working directory for file and directory\n-changes using platform-specific file system notification facilities.\n+changes using platform-specific filesystem notification facilities.\n \n This daemon communicates directly with commands like `git status`\n using the link:technical/api-simple-ipc.html[simple IPC] interface\n@@ -63,13 +63,44 @@ CAVEATS\n -------\n \n The fsmonitor daemon does not currently know about submodules and does\n-not know to filter out file system events that happen within a\n+not know to filter out filesystem events that happen within a\n submodule.  If fsmonitor daemon is watching a super repo and a file is\n modified within the working directory of a submodule, it will report\n the change (as happening against the super repo).  However, the client\n will properly ignore these extra events, so performance may be affected\n but it will not cause an incorrect result.\n \n+By default, the fsmonitor daemon refuses to work against network-mounted\n+repositories; this may be overridden by setting `fsmonitor.allowRemote` to\n+`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n+correctly with all network-mounted repositories and such use is considered\n+experimental.\n+\n+On Mac OS, the inter-process communication (IPC) between various Git\n+commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a\n+special type of file -- which is supported by native Mac OS filesystems,\n+but not on network-mounted filesystems, NTFS, or FAT32.  Other filesystems\n+may or may not have the needed support; the fsmonitor daemon is not guaranteed\n+to work with these filesystems and such use is considered experimental.\n+\n+By default, the socket is created in the `.git` directory, however, if the\n+`.git` directory is on a network-mounted filesystem, it will be instead be\n+created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a\n+network-mounted filesystem in which case you must set the configuration\n+variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native\n+filesystem in which to create the socket file.\n+\n+If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)\n+is on a native Mac OS file filesystem the fsmonitor daemon will report an\n+error that will cause the daemon and the currently running command to exit.\n+\n+CONFIGURATION\n+-------------\n+\n+include::includes/cmd-config-section-all.txt[]\n+\n+include::config/fsmonitor--daemon.txt[]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \ngitgitgadget\n"},{"id":"464196","messageId":"pull.1326.v15.git.1664904751.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v14.git.1664395931.gitgitgadget@gmail.com","subject":"[PATCH v15 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-10-04T17:32:25Z","receivedAt":"2022-10-04T17:32:40Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"Follow-on to the work done to allow Windows to work against network-mounted\nrepos for macOS.\n\nHave macOS take advantage of the same configuration option,\n'fsmonitor.allowRemote' that was introduced for Windows. Setting this option\nto true will override the default behavior (erroring-out) when a\nnetwork-mounted repo is detected by fsmonitor.\n\nThe added wrinkle being that the Unix domain socket (UDS) file used for IPC\ncannot be created in a network location; instead $HOME is used if the\ndefault location is on the network. The user may, optionally, set the\n'fsmonitor.socketDir' configuration option to a valid, local directory if\n$HOME itself is on the network or is simply not the desired location for the\nUDS file.\n\nAn additional issue is that for mount points in the root directory, FSEvents\ndoes not report a path that matches the worktree directory due to the\nintroduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\nto the worktree directory by interrogating the root filesystem for synthetic\nfirmlinks and using that information to translate the path.\n\nv15 differs from v14:\n\n * fix memory leak\n\nv14 differs from v13:\n\n * code review feedback\n\nv13 differs from v12:\n\n * code review feedback\n\nv12 differs from v11:\n\n * bug fixes\n\nv11 differs from v10:\n\n * incorporates code review feedback\n * fix memory leak in fsm-listen-darwin.c\n\nv10 differs from v9:\n\n * incorporates code review feedback\n * improves error messaging for incompatible socket directory\n\nv9 differs from v8:\n\n * incorporates code review feedback\n * check for incompatibility before communicating with fsmonitor\n\nv8 differs from v7:\n\n * incorporates code review feedback\n * gets the rebase right\n\nv7 differs from v6:\n\n * incorporates code review feedback\n\nv6 differs from v5:\n\n * incorporates earlier, Windows-specific changes that have not made it back\n   yet to the master branch\n * incorporates code review feedback\n * adds documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n\nv5 differs significantly from earlier versions:\n\n * redesign of handling 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'\n   such that these options are no longer added to the settings data\n   structure but are rather read from config at point of use\n * refactoring of code for handling platform-specific file system checks via\n   a common interface to avoid platform #ifdef in IPC code and be in-model\n   with other platform-specific fsmonitor code\n * dealing with 'synthetic firmlinks' on macOS\n\nEric DeCosta (6):\n  fsmonitor: refactor filesystem checks to common interface\n  fsmonitor: relocate socket file if .git directory is remote\n  fsmonitor: avoid socket location check if using hook\n  fsmonitor: deal with synthetic firmlinks on macOS\n  fsmonitor: check for compatability before communicating with fsmonitor\n  fsmonitor: add documentation for allowRemote and socketDir options\n\n Documentation/config.txt                   |   2 +\n Documentation/config/fsmonitor--daemon.txt |  11 ++\n Documentation/git-fsmonitor--daemon.txt    |  37 ++++-\n Makefile                                   |   2 +\n builtin/fsmonitor--daemon.c                |  11 +-\n compat/fsmonitor/fsm-ipc-darwin.c          |  52 ++++++\n compat/fsmonitor/fsm-ipc-win32.c           |   9 ++\n compat/fsmonitor/fsm-listen-darwin.c       |  14 +-\n compat/fsmonitor/fsm-path-utils-darwin.c   | 135 ++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c    | 145 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c     |  77 +++------\n compat/fsmonitor/fsm-settings-win32.c      | 174 +--------------------\n contrib/buildsystems/CMakeLists.txt        |   4 +\n fsmonitor--daemon.h                        |   3 +\n fsmonitor-ipc.c                            |  18 +--\n fsmonitor-ipc.h                            |   4 +-\n fsmonitor-path-utils.h                     |  60 +++++++\n fsmonitor-settings.c                       |  68 +++++++-\n fsmonitor-settings.h                       |   4 +-\n fsmonitor.c                                |   7 +\n 20 files changed, 588 insertions(+), 249 deletions(-)\n create mode 100644 Documentation/config/fsmonitor--daemon.txt\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\n\nbase-commit: 3dcec76d9df911ed8321007b1d197c1a206dc164\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1326%2Fedecosta-mw%2Ffsmonitor_macos-v15\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1326/edecosta-mw/fsmonitor_macos-v15\nPull-Request: https://github.com/gitgitgadget/git/pull/1326\n\nRange-diff vs v14:\n\n 1:  0b9b64428c5 ! 1:  ec49a74086d fsmonitor: refactor filesystem checks to common interface\n     @@ compat/fsmonitor/fsm-path-utils-darwin.c (new)\n      +\telse\n      +\t\tfs_info->is_remote = 0;\n      +\n     -+\tfs_info->typename = fs.f_fstypename;\n     ++\tfs_info->typename = xstrdup(fs.f_fstypename);\n      +\n      +\ttrace_printf_key(&trace_fsmonitor,\n      +\t\t\t\t\"'%s' is_remote: %d\",\n     @@ compat/fsmonitor/fsm-path-utils-darwin.c (new)\n      +\tstruct fs_info fs;\n      +\tif (fsmonitor__get_fs_info(path, &fs))\n      +\t\treturn -1;\n     ++\n     ++\tfree(fs.typename);\n     ++\n      +\treturn fs.is_remote;\n      +}\n      \n     @@ compat/fsmonitor/fsm-settings-darwin.c\n      -\ttrace_printf_key(&trace_fsmonitor,\n      -\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n      -\t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n     -+\tstrbuf_release(&path);\n     - \n     +-\n      -\tif (!(fs.f_flags & MNT_LOCAL))\n     -+\tif (fs.is_remote)\n     - \t\treturn FSMONITOR_REASON_REMOTE;\n     +-\t\treturn FSMONITOR_REASON_REMOTE;\n     ++\tstrbuf_release(&path);\n       \n      -\tif (!strcmp(fs.f_fstypename, \"msdos\")) /* aka FAT32 */\n     -+\tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n     - \t\treturn FSMONITOR_REASON_NOSOCKETS;\n     - \n     +-\t\treturn FSMONITOR_REASON_NOSOCKETS;\n     +-\n      -\tif (!strcmp(fs.f_fstypename, \"ntfs\"))\n     -+\tif (!strcmp(fs.typename, \"ntfs\"))\n     ++\tif (fs.is_remote ||\n     ++\t\t!strcmp(fs.typename, \"msdos\") ||\n     ++\t\t!strcmp(fs.typename, \"ntfs\")) {\n     ++\t\tfree(fs.typename);\n       \t\treturn FSMONITOR_REASON_NOSOCKETS;\n     ++\t}\n       \n     ++\tfree(fs.typename);\n       \treturn FSMONITOR_REASON_OK;\n     + }\n     + \n      @@ compat/fsmonitor/fsm-settings-darwin.c: enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n       {\n       \tenum fsmonitor_reason reason;\n     @@ fsmonitor-path-utils.h (new)\n      +/*\n      + * Get some basic filesystem informtion for the given path\n      + *\n     ++ * The caller owns the storage that is occupied by fs_info and\n     ++ * is responsible for releasing it.\n     ++ *\n      + * Returns -1 on error, zero otherwise.\n      + */\n      +int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n 2:  680d4c83f99 = 2:  7bf1cdfe3b2 fsmonitor: relocate socket file if .git directory is remote\n 3:  7987d0c1f33 = 3:  c5e8b6cfe5d fsmonitor: avoid socket location check if using hook\n 4:  241043b7c15 ! 4:  863063aefee fsmonitor: deal with synthetic firmlinks on macOS\n     @@ compat/fsmonitor/fsm-path-utils-darwin.c\n       #include <sys/mount.h>\n       \n      @@ compat/fsmonitor/fsm-path-utils-darwin.c: int fsmonitor__is_fs_remote(const char *path)\n     - \t\treturn -1;\n     + \n       \treturn fs.is_remote;\n       }\n      +\n     @@ fsmonitor-path-utils.h\n      - * Get some basic filesystem informtion for the given path\n      + * Get some basic filesystem information for the given path\n        *\n     -  * Returns -1 on error, zero otherwise.\n     -  */\n     +  * The caller owns the storage that is occupied by fs_info and\n     +  * is responsible for releasing it.\n      @@ fsmonitor-path-utils.h: int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n        */\n       int fsmonitor__is_fs_remote(const char *path);\n 5:  d906debba5e ! 5:  fa974bfd5ef fsmonitor: check for compatability before communicating with fsmonitor\n     @@ Commit message\n      \n          Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n      \n     - ## compat/fsmonitor/fsm-settings-darwin.c ##\n     -@@ compat/fsmonitor/fsm-settings-darwin.c: static enum fsmonitor_reason check_uds_volume(struct repository *r)\n     - \tstrbuf_release(&path);\n     - \n     - \tif (fs.is_remote)\n     --\t\treturn FSMONITOR_REASON_REMOTE;\n     -+\t\treturn FSMONITOR_REASON_NOSOCKETS;\n     - \n     - \tif (!strcmp(fs.typename, \"msdos\")) /* aka FAT32 */\n     - \t\treturn FSMONITOR_REASON_NOSOCKETS;\n     -\n       ## fsmonitor-settings.c ##\n      @@\n       #include \"cache.h\"\n 6:  ed14fbd009e = 6:  af7309745f7 fsmonitor: add documentation for allowRemote and socketDir options\n\n-- \ngitgitgadget\n"},{"id":"464197","messageId":"ec49a74086dee4545a3bb7b24c8da22e77aef579.1664904751.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v15.git.1664904751.gitgitgadget@gmail.com","subject":"[PATCH v15 1/6] fsmonitor: refactor filesystem checks to common interface","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-10-04T17:32:26Z","receivedAt":"2022-10-04T17:32:43Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nProvide a common interface for getting basic filesystem information\nincluding filesystem type and whether the filesystem is remote.\n\nRefactor existing code for getting basic filesystem info and detecting\nremote file systems to the new interface.\n\nRefactor filesystem checks to leverage new interface. For macOS,\nerror-out if the Unix Domain socket (UDS) file is on a remote\nfilesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                                 |   1 +\n compat/fsmonitor/fsm-path-utils-darwin.c |  43 ++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 128 +++++++++++++++++\n compat/fsmonitor/fsm-settings-darwin.c   |  69 +++------\n compat/fsmonitor/fsm-settings-win32.c    | 172 +----------------------\n contrib/buildsystems/CMakeLists.txt      |   2 +\n fsmonitor-path-utils.h                   |  26 ++++\n fsmonitor-settings.c                     |  50 +++++++\n 8 files changed, 272 insertions(+), 219 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n create mode 100644 fsmonitor-path-utils.h\n\ndiff --git a/Makefile b/Makefile\nindex cac3452edb9..ffab427ea5b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2044,6 +2044,7 @@ endif\n ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n ifeq ($(TCLTK_PATH),)\ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nnew file mode 100644\nindex 00000000000..d46d7f13538\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -0,0 +1,43 @@\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+#include <sys/param.h>\n+#include <sys/mount.h>\n+\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\tstruct statfs fs;\n+\tif (statfs(path, &fs) == -1) {\n+\t\tint saved_errno = errno;\n+\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n+\t\t\t\t path, strerror(saved_errno));\n+\t\terrno = saved_errno;\n+\t\treturn -1;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n+\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n+\n+\tif (!(fs.f_flags & MNT_LOCAL))\n+\t\tfs_info->is_remote = 1;\n+\telse\n+\t\tfs_info->is_remote = 0;\n+\n+\tfs_info->typename = xstrdup(fs.f_fstypename);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\n+\tfree(fs.typename);\n+\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nnew file mode 100644\nindex 00000000000..a90b8f7925b\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -0,0 +1,128 @@\n+#include \"cache.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+/*\n+ * Check remote working directory protocol.\n+ *\n+ * Return -1 if client machine cannot get remote protocol information.\n+ */\n+static int check_remote_protocol(wchar_t *wpath)\n+{\n+\tHANDLE h;\n+\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n+\n+\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n+\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\n+\tif (h == INVALID_HANDLE_VALUE) {\n+\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\treturn -1;\n+\t}\n+\n+\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n+\t\t&proto_info, sizeof(proto_info))) {\n+\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n+\t\t      GetLastError(), wpath);\n+\t\tCloseHandle(h);\n+\t\treturn -1;\n+\t}\n+\n+\tCloseHandle(h);\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n+\t\t\t\twpath, proto_info.Protocol);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Notes for testing:\n+ *\n+ * (a) Windows allows a network share to be mapped to a drive letter.\n+ *     (This is the normal method to access it.)\n+ *\n+ *     $ NET USE Z: \\\\server\\share\n+ *     $ git -C Z:/repo status\n+ *\n+ * (b) Windows allows a network share to be referenced WITHOUT mapping\n+ *     it to drive letter.\n+ *\n+ *     $ NET USE \\\\server\\share\\dir\n+ *     $ git -C //server/share/repo status\n+ *\n+ * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n+ *     arbitrary path (which may be remote)\n+ *\n+ *     $ SUBST Q: Z:\\repo\n+ *     $ git -C Q:/ status\n+ *\n+ * (d) Windows allows a directory symlink to be created on a local\n+ *     file system that points to a remote repo.\n+ *\n+ *     $ mklink /d ./link //server/share/repo\n+ *     $ git -C ./link status\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n+{\n+\twchar_t wpath[MAX_PATH];\n+\twchar_t wfullpath[MAX_PATH];\n+\tsize_t wlen;\n+\tUINT driveType;\n+\n+\t/*\n+\t * Do everything in wide chars because the drive letter might be\n+\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n+\t */\n+\tif (xutftowcs_path(wpath, path) < 0) {\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * GetDriveTypeW() requires a final slash.  We assume that the\n+\t * worktree pathname points to an actual directory.\n+\t */\n+\twlen = wcslen(wpath);\n+\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n+\t\twpath[wlen++] = L'\\\\';\n+\t\twpath[wlen] = 0;\n+\t}\n+\n+\t/*\n+\t * Normalize the path.  If nothing else, this converts forward\n+\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n+\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n+\t */\n+\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {\n+\t\treturn -1;\n+\t}\n+\n+\tdriveType = GetDriveTypeW(wfullpath);\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n+\t\t\t path, wfullpath, driveType);\n+\n+\tif (driveType == DRIVE_REMOTE) {\n+\t\tfs_info->is_remote = 1;\n+\t\tif (check_remote_protocol(wfullpath) < 0)\n+\t\t\treturn -1;\n+\t} else {\n+\t\tfs_info->is_remote = 0;\n+\t}\n+\n+\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"'%s' is_remote: %d\",\n+\t\t\t\tpath, fs_info->is_remote);\n+\n+\treturn 0;\n+}\n+\n+int fsmonitor__is_fs_remote(const char *path)\n+{\n+\tstruct fs_info fs;\n+\tif (fsmonitor__get_fs_info(path, &fs))\n+\t\treturn -1;\n+\treturn fs.is_remote;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex efc732c0f31..699f0b272e6 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -1,32 +1,10 @@\n-#include \"cache.h\"\n #include \"config.h\"\n-#include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n-#include <sys/param.h>\n-#include <sys/mount.h>\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n-/*\n- * [1] Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type (NFS, SAMBA, etc.) dictates whether notification events\n- * are available at all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n+ /*\n  * For the builtin FSMonitor, we create the Unix domain socket for the\n  * IPC in the .git directory.  If the working directory is remote,\n  * then the socket will be created on the remote file system.  This\n@@ -38,42 +16,35 @@\n  * be taken to ensure that $HOME is actually local and not a managed\n  * file share.)\n  *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- *\n- * [2] FAT32 and NTFS working directories are problematic too.\n+ * FAT32 and NTFS working directories are problematic too.\n  *\n  * The builtin FSMonitor uses a Unix domain socket in the .git\n  * directory for IPC.  These Windows drive formats do not support\n  * Unix domain sockets, so mark them as incompatible for the daemon.\n  *\n  */\n-static enum fsmonitor_reason check_volume(struct repository *r)\n+static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n-\tstruct statfs fs;\n+\tstruct fs_info fs;\n+\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tstruct strbuf path = STRBUF_INIT;\n+\tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \n-\tif (statfs(r->worktree, &fs) == -1) {\n-\t\tint saved_errno = errno;\n-\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n-\t\t\t\t r->worktree, strerror(saved_errno));\n-\t\terrno = saved_errno;\n+\tif (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {\n+\t\tstrbuf_release(&path);\n \t\treturn FSMONITOR_REASON_ERROR;\n \t}\n \n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n-\t\t\t r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);\n-\n-\tif (!(fs.f_flags & MNT_LOCAL))\n-\t\treturn FSMONITOR_REASON_REMOTE;\n+\tstrbuf_release(&path);\n \n-\tif (!strcmp(fs.f_fstypename, \"msdos\")) /* aka FAT32 */\n-\t\treturn FSMONITOR_REASON_NOSOCKETS;\n-\n-\tif (!strcmp(fs.f_fstypename, \"ntfs\"))\n+\tif (fs.is_remote ||\n+\t\t!strcmp(fs.typename, \"msdos\") ||\n+\t\t!strcmp(fs.typename, \"ntfs\")) {\n+\t\tfree(fs.typename);\n \t\treturn FSMONITOR_REASON_NOSOCKETS;\n+\t}\n \n+\tfree(fs.typename);\n \treturn FSMONITOR_REASON_OK;\n }\n \n@@ -81,7 +52,7 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_volume(r);\n+\treason = check_uds_volume(r);\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex e5ec5b0a9f7..d88b06ae610 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -1,8 +1,9 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n-#include \"fsmonitor-settings.h\"\n #include \"fsmonitor.h\"\n+#include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * VFS for Git is incompatible with FSMonitor.\n@@ -24,171 +25,6 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-/*\n- * Check if monitoring remote working directories is allowed.\n- *\n- * By default, monitoring remote working directories is\n- * disabled.  Users may override this behavior in enviroments where\n- * they have proper support.\n- */\n-static int check_config_allowremote(struct repository *r)\n-{\n-\tint allow;\n-\n-\tif (!repo_config_get_bool(r, \"fsmonitor.allowremote\", &allow))\n-\t\treturn allow;\n-\n-\treturn -1; /* fsmonitor.allowremote not set */\n-}\n-\n-/*\n- * Check remote working directory protocol.\n- *\n- * Error if client machine cannot get remote protocol information.\n- */\n-static int check_remote_protocol(wchar_t *wpath)\n-{\n-\tHANDLE h;\n-\tFILE_REMOTE_PROTOCOL_INFO proto_info;\n-\n-\th = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,\n-\t\t\tFILE_FLAG_BACKUP_SEMANTICS, NULL);\n-\n-\tif (h == INVALID_HANDLE_VALUE) {\n-\t\terror(_(\"[GLE %ld] unable to open for read '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\treturn -1;\n-\t}\n-\n-\tif (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,\n-\t\t&proto_info, sizeof(proto_info))) {\n-\t\terror(_(\"[GLE %ld] unable to get protocol information for '%ls'\"),\n-\t\t      GetLastError(), wpath);\n-\t\tCloseHandle(h);\n-\t\treturn -1;\n-\t}\n-\n-\tCloseHandle(h);\n-\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t\"check_remote_protocol('%ls') remote protocol %#8.8lx\",\n-\t\t\t\twpath, proto_info.Protocol);\n-\n-\treturn 0;\n-}\n-\n-/*\n- * Remote working directories are problematic for FSMonitor.\n- *\n- * The underlying file system on the server machine and/or the remote\n- * mount type dictates whether notification events are available at\n- * all to remote client machines.\n- *\n- * Kernel differences between the server and client machines also\n- * dictate the how (buffering, frequency, de-dup) the events are\n- * delivered to client machine processes.\n- *\n- * A client machine (such as a laptop) may choose to suspend/resume\n- * and it is unclear (without lots of testing) whether the watcher can\n- * resync after a resume.  We might be able to treat this as a normal\n- * \"events were dropped by the kernel\" event and do our normal \"flush\n- * and resync\" --or-- we might need to close the existing (zombie?)\n- * notification fd and create a new one.\n- *\n- * In theory, the above issues need to be addressed whether we are\n- * using the Hook or IPC API.\n- *\n- * So (for now at least), mark remote working directories as\n- * incompatible.\n- *\n- * Notes for testing:\n- *\n- * (a) Windows allows a network share to be mapped to a drive letter.\n- *     (This is the normal method to access it.)\n- *\n- *     $ NET USE Z: \\\\server\\share\n- *     $ git -C Z:/repo status\n- *\n- * (b) Windows allows a network share to be referenced WITHOUT mapping\n- *     it to drive letter.\n- *\n- *     $ NET USE \\\\server\\share\\dir\n- *     $ git -C //server/share/repo status\n- *\n- * (c) Windows allows \"SUBST\" to create a fake drive mapping to an\n- *     arbitrary path (which may be remote)\n- *\n- *     $ SUBST Q: Z:\\repo\n- *     $ git -C Q:/ status\n- *\n- * (d) Windows allows a directory symlink to be created on a local\n- *     file system that points to a remote repo.\n- *\n- *     $ mklink /d ./link //server/share/repo\n- *     $ git -C ./link status\n- */\n-static enum fsmonitor_reason check_remote(struct repository *r)\n-{\n-\tint ret;\n-\twchar_t wpath[MAX_PATH];\n-\twchar_t wfullpath[MAX_PATH];\n-\tsize_t wlen;\n-\tUINT driveType;\n-\n-\t/*\n-\t * Do everything in wide chars because the drive letter might be\n-\t * a multi-byte sequence.  See win32_has_dos_drive_prefix().\n-\t */\n-\tif (xutftowcs_path(wpath, r->worktree) < 0)\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t/*\n-\t * GetDriveTypeW() requires a final slash.  We assume that the\n-\t * worktree pathname points to an actual directory.\n-\t */\n-\twlen = wcslen(wpath);\n-\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n-\t\twpath[wlen++] = L'\\\\';\n-\t\twpath[wlen] = 0;\n-\t}\n-\n-\t/*\n-\t * Normalize the path.  If nothing else, this converts forward\n-\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n-\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n-\t */\n-\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))\n-\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\tdriveType = GetDriveTypeW(wfullpath);\n-\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n-\t\t\t r->worktree, wfullpath, driveType);\n-\n-\tif (driveType == DRIVE_REMOTE) {\n-\t\ttrace_printf_key(&trace_fsmonitor,\n-\t\t\t\t \"check_remote('%s') true\",\n-\t\t\t\t r->worktree);\n-\n-\t\tret = check_remote_protocol(wfullpath);\n-\t\tif (ret < 0)\n-\t\t\treturn FSMONITOR_REASON_ERROR;\n-\n-\t\tswitch (check_config_allowremote(r)) {\n-\t\tcase 0: /* config overrides and disables */\n-\t\t\treturn FSMONITOR_REASON_REMOTE;\n-\t\tcase 1: /* config overrides and enables */\n-\t\t\treturn FSMONITOR_REASON_OK;\n-\t\tdefault:\n-\t\t\tbreak; /* config has no opinion */\n-\t\t}\n-\n-\t\treturn FSMONITOR_REASON_REMOTE;\n-\t}\n-\n-\treturn FSMONITOR_REASON_OK;\n-}\n-\n enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n {\n \tenum fsmonitor_reason reason;\n@@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n \tif (reason != FSMONITOR_REASON_OK)\n \t\treturn reason;\n \n-\treason = check_remote(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n-\n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex ea2a531be87..5482a04b3ce 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)\n@@ -315,6 +316,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nnew file mode 100644\nindex 00000000000..41edf5b934f\n--- /dev/null\n+++ b/fsmonitor-path-utils.h\n@@ -0,0 +1,26 @@\n+#ifndef FSM_PATH_UTILS_H\n+#define FSM_PATH_UTILS_H\n+\n+struct fs_info {\n+\tint is_remote;\n+\tchar *typename;\n+};\n+\n+/*\n+ * Get some basic filesystem informtion for the given path\n+ *\n+ * The caller owns the storage that is occupied by fs_info and\n+ * is responsible for releasing it.\n+ *\n+ * Returns -1 on error, zero otherwise.\n+ */\n+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n+\n+/*\n+ * Determines if the filesystem that path resides on is remote.\n+ *\n+ * Returns -1 on error, 0 if not remote, 1 if remote.\n+ */\n+int fsmonitor__is_fs_remote(const char *path);\n+\n+#endif\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 464424a1e92..d288cbad479 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"fsmonitor-settings.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n /*\n  * We keep this structure defintion private and have getters\n@@ -13,6 +14,52 @@ struct fsmonitor_settings {\n \tchar *hook_path;\n };\n \n+/*\n+ * Remote working directories are problematic for FSMonitor.\n+ *\n+ * The underlying file system on the server machine and/or the remote\n+ * mount type dictates whether notification events are available at\n+ * all to remote client machines.\n+ *\n+ * Kernel differences between the server and client machines also\n+ * dictate the how (buffering, frequency, de-dup) the events are\n+ * delivered to client machine processes.\n+ *\n+ * A client machine (such as a laptop) may choose to suspend/resume\n+ * and it is unclear (without lots of testing) whether the watcher can\n+ * resync after a resume.  We might be able to treat this as a normal\n+ * \"events were dropped by the kernel\" event and do our normal \"flush\n+ * and resync\" --or-- we might need to close the existing (zombie?)\n+ * notification fd and create a new one.\n+ *\n+ * In theory, the above issues need to be addressed whether we are\n+ * using the Hook or IPC API.\n+ *\n+ * So (for now at least), mark remote working directories as\n+ * incompatible unless 'fsmonitor.allowRemote' is true.\n+ *\n+ */\n+#ifdef HAVE_FSMONITOR_OS_SETTINGS\n+static enum fsmonitor_reason check_remote(struct repository *r)\n+{\n+\tint allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */\n+\tint is_remote = fsmonitor__is_fs_remote(r->worktree);\n+\n+\tswitch (is_remote) {\n+\t\tcase 0:\n+\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tcase 1:\n+\t\t\trepo_config_get_bool(r, \"fsmonitor.allowremote\", &allow_remote);\n+\t\t\tif (allow_remote < 1)\n+\t\t\t\treturn FSMONITOR_REASON_REMOTE;\n+\t\t\telse\n+\t\t\t\treturn FSMONITOR_REASON_OK;\n+\t\tdefault:\n+\t\t\treturn FSMONITOR_REASON_ERROR;\n+\t}\n+}\n+#endif\n+\n static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n {\n \tif (!r->worktree) {\n@@ -27,6 +74,9 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t{\n \t\tenum fsmonitor_reason reason;\n \n+\t\treason = check_remote(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n \t\treason = fsm_os__incompatible(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-- \ngitgitgadget\n\n"},{"id":"464198","messageId":"7bf1cdfe3b259f7135f3a50dcdd653563d5e19f6.1664904751.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v15.git.1664904751.gitgitgadget@gmail.com","subject":"[PATCH v15 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-10-04T17:32:27Z","receivedAt":"2022-10-04T17:32:44Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf the .git directory is on a remote filesystem, create the socket\nfile in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Makefile                               |  1 +\n builtin/fsmonitor--daemon.c            |  3 +-\n compat/fsmonitor/fsm-ipc-darwin.c      | 52 ++++++++++++++++++++++++++\n compat/fsmonitor/fsm-ipc-win32.c       |  9 +++++\n compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n contrib/buildsystems/CMakeLists.txt    |  2 +\n fsmonitor-ipc.c                        | 18 ++++-----\n fsmonitor-ipc.h                        |  4 +-\n 8 files changed, 78 insertions(+), 13 deletions(-)\n create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n\ndiff --git a/Makefile b/Makefile\nindex ffab427ea5b..feb675a6959 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2039,6 +2039,7 @@ ifdef FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n+\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n endif\n \n ifdef FSMONITOR_OS_SETTINGS\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 2c109cf8b37..0123fc33ed2 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -1343,7 +1343,8 @@ static int fsmonitor_run_daemon(void)\n \t * directory.)\n \t */\n \tstrbuf_init(&state.path_ipc, 0);\n-\tstrbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));\n+\tstrbuf_addstr(&state.path_ipc,\n+\t\tabsolute_path(fsmonitor_ipc__get_path(the_repository)));\n \n \t/*\n \t * Confirm that we can create platform-specific resources for the\ndiff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\nnew file mode 100644\nindex 00000000000..ce843d63348\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-darwin.c\n@@ -0,0 +1,52 @@\n+#include \"cache.h\"\n+#include \"config.h\"\n+#include \"strbuf.h\"\n+#include \"fsmonitor.h\"\n+#include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n+\n+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n+{\n+\tstatic const char *ipc_path = NULL;\n+\tSHA_CTX sha1ctx;\n+\tchar *sock_dir = NULL;\n+\tstruct strbuf ipc_file = STRBUF_INIT;\n+\tunsigned char hash[SHA_DIGEST_LENGTH];\n+\n+\tif (!r)\n+\t\tBUG(\"No repository passed into fsmonitor_ipc__get_path\");\n+\n+\tif (ipc_path)\n+\t\treturn ipc_path;\n+\n+\n+\t/* By default the socket file is created in the .git directory */\n+\tif (fsmonitor__is_fs_remote(r->gitdir) < 1) {\n+\t\tipc_path = fsmonitor_ipc__get_default_path();\n+\t\treturn ipc_path;\n+\t}\n+\n+\tSHA1_Init(&sha1ctx);\n+\tSHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n+\tSHA1_Final(hash, &sha1ctx);\n+\n+\trepo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n+\n+\t/* Create the socket file in either socketDir or $HOME */\n+\tif (sock_dir && *sock_dir) {\n+\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n+\t\t\t\t\tsock_dir, hash_to_hex(hash));\n+\t} else {\n+\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n+\t}\n+\tfree(sock_dir);\n+\n+\tipc_path = interpolate_path(ipc_file.buf, 1);\n+\tif (!ipc_path)\n+\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n+\n+\tstrbuf_release(&ipc_file);\n+\treturn ipc_path;\n+}\ndiff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\nnew file mode 100644\nindex 00000000000..e08c505c148\n--- /dev/null\n+++ b/compat/fsmonitor/fsm-ipc-win32.c\n@@ -0,0 +1,9 @@\n+#include \"config.h\"\n+#include \"fsmonitor-ipc.h\"\n+\n+const char *fsmonitor_ipc__get_path(struct repository *r) {\n+\tstatic char *ret;\n+\tif (!ret)\n+\t\tret = git_pathdup(\"fsmonitor--daemon.ipc\");\n+\treturn ret;\n+}\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 699f0b272e6..7241c4c22c9 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -26,7 +26,7 @@\n static enum fsmonitor_reason check_uds_volume(struct repository *r)\n {\n \tstruct fs_info fs;\n-\tconst char *ipc_path = fsmonitor_ipc__get_path();\n+\tconst char *ipc_path = fsmonitor_ipc__get_path(r);\n \tstruct strbuf path = STRBUF_INIT;\n \tstrbuf_add(&path, ipc_path, strlen(ipc_path));\n \ndiff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt\nindex 5482a04b3ce..787738e6fa3 100644\n--- a/contrib/buildsystems/CMakeLists.txt\n+++ b/contrib/buildsystems/CMakeLists.txt\n@@ -308,6 +308,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\n@@ -316,6 +317,7 @@ if(SUPPORTS_SIMPLE_IPC)\n \t\tadd_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)\n+\t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)\n \t\tlist(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)\n \n \t\tadd_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)\ndiff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c\nindex 789e7397baa..c0f42301c84 100644\n--- a/fsmonitor-ipc.c\n+++ b/fsmonitor-ipc.c\n@@ -18,7 +18,7 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 0;\n }\n \n-const char *fsmonitor_ipc__get_path(void)\n+const char *fsmonitor_ipc__get_path(struct repository *r)\n {\n \treturn NULL;\n }\n@@ -47,11 +47,9 @@ int fsmonitor_ipc__is_supported(void)\n \treturn 1;\n }\n \n-GIT_PATH_FUNC(fsmonitor_ipc__get_path, \"fsmonitor--daemon.ipc\")\n-\n enum ipc_active_state fsmonitor_ipc__get_state(void)\n {\n-\treturn ipc_get_active_state(fsmonitor_ipc__get_path());\n+\treturn ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));\n }\n \n static int spawn_daemon(void)\n@@ -81,8 +79,8 @@ int fsmonitor_ipc__send_query(const char *since_token,\n \ttrace2_data_string(\"fsm_client\", NULL, \"query/command\", tok);\n \n try_again:\n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \n \tswitch (state) {\n \tcase IPC_STATE__LISTENING:\n@@ -117,13 +115,13 @@ try_again:\n \n \tcase IPC_STATE__INVALID_PATH:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: invalid path '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \n \tcase IPC_STATE__OTHER_ERROR:\n \tdefault:\n \t\tret = error(_(\"fsmonitor_ipc__send_query: unspecified error on '%s'\"),\n-\t\t\t    fsmonitor_ipc__get_path());\n+\t\t\t    fsmonitor_ipc__get_path(the_repository));\n \t\tgoto done;\n \t}\n \n@@ -149,8 +147,8 @@ int fsmonitor_ipc__send_command(const char *command,\n \toptions.wait_if_busy = 1;\n \toptions.wait_if_not_found = 0;\n \n-\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n-\t\t\t\t       &connection);\n+\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n+\t\t\t\t\t\t&options, &connection);\n \tif (state != IPC_STATE__LISTENING) {\n \t\tdie(_(\"fsmonitor--daemon is not running\"));\n \t\treturn -1;\ndiff --git a/fsmonitor-ipc.h b/fsmonitor-ipc.h\nindex b6a7067c3af..8b489da762b 100644\n--- a/fsmonitor-ipc.h\n+++ b/fsmonitor-ipc.h\n@@ -3,6 +3,8 @@\n \n #include \"simple-ipc.h\"\n \n+struct repository;\n+\n /*\n  * Returns true if built-in file system monitor daemon is defined\n  * for this platform.\n@@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);\n  *\n  * Returns NULL if the daemon is not supported on this platform.\n  */\n-const char *fsmonitor_ipc__get_path(void);\n+const char *fsmonitor_ipc__get_path(struct repository *r);\n \n /*\n  * Try to determine whether there is a `git-fsmonitor--daemon` process\n-- \ngitgitgadget\n\n"},{"id":"464199","messageId":"c5e8b6cfe5da269d27d3f171ccedd119a6db4840.1664904752.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v15.git.1664904751.gitgitgadget@gmail.com","subject":"[PATCH v15 3/6] fsmonitor: avoid socket location check if using hook","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-10-04T17:32:28Z","receivedAt":"2022-10-04T17:32:46Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf monitoring is done via fsmonitor hook rather than IPC there is no\nneed to check if the location of the Unix Domain socket (UDS) file is\non a remote filesystem.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n compat/fsmonitor/fsm-settings-darwin.c | 10 ++++++----\n compat/fsmonitor/fsm-settings-win32.c  |  2 +-\n fsmonitor-settings.c                   |  8 ++++----\n fsmonitor-settings.h                   |  2 +-\n 4 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c\nindex 7241c4c22c9..6abbc7af3ab 100644\n--- a/compat/fsmonitor/fsm-settings-darwin.c\n+++ b/compat/fsmonitor/fsm-settings-darwin.c\n@@ -48,13 +48,15 @@ static enum fsmonitor_reason check_uds_volume(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \n-\treason = check_uds_volume(r);\n-\tif (reason != FSMONITOR_REASON_OK)\n-\t\treturn reason;\n+\tif (ipc) {\n+\t\treason = check_uds_volume(r);\n+\t\tif (reason != FSMONITOR_REASON_OK)\n+\t\t\treturn reason;\n+\t}\n \n \treturn FSMONITOR_REASON_OK;\n }\ndiff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c\nindex d88b06ae610..a8af31b71de 100644\n--- a/compat/fsmonitor/fsm-settings-win32.c\n+++ b/compat/fsmonitor/fsm-settings-win32.c\n@@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)\n \treturn FSMONITOR_REASON_OK;\n }\n \n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)\n {\n \tenum fsmonitor_reason reason;\n \ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex d288cbad479..531a1b6f956 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -60,7 +60,7 @@ static enum fsmonitor_reason check_remote(struct repository *r)\n }\n #endif\n \n-static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n+static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)\n {\n \tif (!r->worktree) {\n \t\t/*\n@@ -77,7 +77,7 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)\n \t\treason = check_remote(r);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n-\t\treason = fsm_os__incompatible(r);\n+\t\treason = fsm_os__incompatible(r, ipc);\n \t\tif (reason != FSMONITOR_REASON_OK)\n \t\t\treturn reason;\n \t}\n@@ -162,7 +162,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)\n \n void fsm_settings__set_ipc(struct repository *r)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 1);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\n@@ -185,7 +185,7 @@ void fsm_settings__set_ipc(struct repository *r)\n \n void fsm_settings__set_hook(struct repository *r, const char *path)\n {\n-\tenum fsmonitor_reason reason = check_for_incompatible(r);\n+\tenum fsmonitor_reason reason = check_for_incompatible(r, 0);\n \n \tif (reason != FSMONITOR_REASON_OK) {\n \t\tfsm_settings__set_incompatible(r, reason);\ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex d9c2605197f..0721617b95a 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -48,7 +48,7 @@ struct fsmonitor_settings;\n  * fsm_os__* routines should considered private to fsm_settings__\n  * routines.\n  */\n-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);\n+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);\n #endif /* HAVE_FSMONITOR_OS_SETTINGS */\n \n #endif /* FSMONITOR_SETTINGS_H */\n-- \ngitgitgadget\n\n"},{"id":"464200","messageId":"fa974bfd5ef8f24d259ec31663ee18b65d96d83c.1664904752.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v15.git.1664904751.gitgitgadget@gmail.com","subject":"[PATCH v15 5/6] fsmonitor: check for compatability before communicating with fsmonitor","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-10-04T17:32:30Z","receivedAt":"2022-10-04T17:33:00Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nIf fsmonitor is not in a compatible state, warn with an appropriate message.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n fsmonitor-settings.c | 10 +++++++---\n fsmonitor-settings.h |  2 +-\n fsmonitor.c          |  7 +++++++\n 3 files changed, 15 insertions(+), 4 deletions(-)\n\ndiff --git a/fsmonitor-settings.c b/fsmonitor-settings.c\nindex 531a1b6f956..ee63a97dc51 100644\n--- a/fsmonitor-settings.c\n+++ b/fsmonitor-settings.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"repository.h\"\n+#include \"fsmonitor-ipc.h\"\n #include \"fsmonitor-settings.h\"\n #include \"fsmonitor-path-utils.h\"\n \n@@ -242,10 +243,11 @@ enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)\n \treturn r->settings.fsmonitor->reason;\n }\n \n-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n+char *fsm_settings__get_incompatible_msg(struct repository *r,\n \t\t\t\t\t enum fsmonitor_reason reason)\n {\n \tstruct strbuf msg = STRBUF_INIT;\n+\tconst char *socket_dir;\n \n \tswitch (reason) {\n \tcase FSMONITOR_REASON_UNTESTED:\n@@ -281,9 +283,11 @@ char *fsm_settings__get_incompatible_msg(const struct repository *r,\n \t\tgoto done;\n \n \tcase FSMONITOR_REASON_NOSOCKETS:\n+\t\tsocket_dir = dirname((char *)fsmonitor_ipc__get_path(r));\n \t\tstrbuf_addf(&msg,\n-\t\t\t    _(\"repository '%s' is incompatible with fsmonitor due to lack of Unix sockets\"),\n-\t\t\t    r->worktree);\n+\t\t\t    _(\"socket directory '%s' is incompatible with fsmonitor due\"\n+\t\t\t      \" to lack of Unix sockets support\"),\n+\t\t\t    socket_dir);\n \t\tgoto done;\n \t}\n \ndiff --git a/fsmonitor-settings.h b/fsmonitor-settings.h\nindex 0721617b95a..ab02e3995ee 100644\n--- a/fsmonitor-settings.h\n+++ b/fsmonitor-settings.h\n@@ -33,7 +33,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);\n const char *fsm_settings__get_hook_path(struct repository *r);\n \n enum fsmonitor_reason fsm_settings__get_reason(struct repository *r);\n-char *fsm_settings__get_incompatible_msg(const struct repository *r,\n+char *fsm_settings__get_incompatible_msg(struct repository *r,\n \t\t\t\t\t enum fsmonitor_reason reason);\n \n struct fsmonitor_settings;\ndiff --git a/fsmonitor.c b/fsmonitor.c\nindex 57d6a483bee..540736b39fd 100644\n--- a/fsmonitor.c\n+++ b/fsmonitor.c\n@@ -295,6 +295,7 @@ static int fsmonitor_force_update_threshold = 100;\n \n void refresh_fsmonitor(struct index_state *istate)\n {\n+\tstatic int warn_once = 0;\n \tstruct strbuf query_result = STRBUF_INIT;\n \tint query_success = 0, hook_version = -1;\n \tsize_t bol = 0; /* beginning of line */\n@@ -305,6 +306,12 @@ void refresh_fsmonitor(struct index_state *istate)\n \tint is_trivial = 0;\n \tstruct repository *r = istate->repo ? istate->repo : the_repository;\n \tenum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);\n+\tenum fsmonitor_reason reason = fsm_settings__get_reason(r);\n+\n+\tif (!warn_once && reason > FSMONITOR_REASON_OK) {\n+\t\twarn_once = 1;\n+\t\twarning(\"%s\", fsm_settings__get_incompatible_msg(r, reason));\n+\t}\n \n \tif (fsm_mode <= FSMONITOR_MODE_DISABLED ||\n \t    istate->fsmonitor_has_run_once)\n-- \ngitgitgadget\n\n"},{"id":"464201","messageId":"863063aefeeecfd23bb50eb111fcfbf5879a8ee3.1664904752.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v15.git.1664904751.gitgitgadget@gmail.com","subject":"[PATCH v15 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-10-04T17:32:29Z","receivedAt":"2022-10-04T17:33:03Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nStarting with macOS 10.15 (Catalina), Apple introduced a new feature\ncalled 'firmlinks' in order to separate the boot volume into two\nvolumes, one read-only and one writable but still present them to the\nuser as a single volume. Along with this change, Apple removed the\nability to create symlinks in the root directory and replaced them with\n'synthetic firmlinks'. See 'man synthetic.conf'\n\nWhen FSEevents reports the path of changed files, if the path involves\na synthetic firmlink, the path is reported from the point of the\nsynthetic firmlink and not the real path. For example:\n\nReal path:\n/System/Volumes/Data/network/working/directory/foo.txt\n\nSynthetic firmlink:\n/network -> /System/Volumes/Data/network\n\nFSEvents path:\n/network/working/directory/foo.txt\n\nThis causes the FSEvents path to not match against the worktree\ndirectory.\n\nThere are several ways in which synthetic firmlinks can be created:\nthey can be defined in /etc/synthetic.conf, the automounter can create\nthem, and there may be other means. Simply reading /etc/synthetic.conf\nis insufficient. No matter what process creates synthetic firmlinks,\nthey all get created in the root directory.\n\nTherefore, in order to deal with synthetic firmlinks, the root directory\nis scanned and the first possible synthetic firmink that, when resolved,\nis a prefix of the worktree is used to map FSEvents paths to worktree\npaths.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n builtin/fsmonitor--daemon.c              |  8 +++\n compat/fsmonitor/fsm-listen-darwin.c     | 14 +++-\n compat/fsmonitor/fsm-path-utils-darwin.c | 92 ++++++++++++++++++++++++\n compat/fsmonitor/fsm-path-utils-win32.c  | 17 +++++\n fsmonitor--daemon.h                      |  3 +\n fsmonitor-path-utils.h                   | 36 +++++++++-\n 6 files changed, 167 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\nindex 0123fc33ed2..7a4cb78c7dd 100644\n--- a/builtin/fsmonitor--daemon.c\n+++ b/builtin/fsmonitor--daemon.c\n@@ -3,6 +3,7 @@\n #include \"parse-options.h\"\n #include \"fsmonitor.h\"\n #include \"fsmonitor-ipc.h\"\n+#include \"fsmonitor-path-utils.h\"\n #include \"compat/fsmonitor/fsm-health.h\"\n #include \"compat/fsmonitor/fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n@@ -1282,6 +1283,11 @@ static int fsmonitor_run_daemon(void)\n \tstrbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));\n \tstate.nr_paths_watching = 1;\n \n+\tstrbuf_init(&state.alias.alias, 0);\n+\tstrbuf_init(&state.alias.points_to, 0);\n+\tif ((err = fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)))\n+\t\tgoto done;\n+\n \t/*\n \t * We create and delete cookie files somewhere inside the .git\n \t * directory to help us keep sync with the file system.  If\n@@ -1391,6 +1397,8 @@ done:\n \tstrbuf_release(&state.path_gitdir_watch);\n \tstrbuf_release(&state.path_cookie_prefix);\n \tstrbuf_release(&state.path_ipc);\n+\tstrbuf_release(&state.alias.alias);\n+\tstrbuf_release(&state.alias.points_to);\n \n \treturn err;\n }\ndiff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c\nindex 8e208e8289e..daeee4e465c 100644\n--- a/compat/fsmonitor/fsm-listen-darwin.c\n+++ b/compat/fsmonitor/fsm-listen-darwin.c\n@@ -26,6 +26,7 @@\n #include \"fsmonitor.h\"\n #include \"fsm-listen.h\"\n #include \"fsmonitor--daemon.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsm_listen_data\n {\n@@ -198,8 +199,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \tstruct string_list cookie_list = STRING_LIST_INIT_DUP;\n \tconst char *path_k;\n \tconst char *slash;\n-\tint k;\n+\tchar *resolved = NULL;\n \tstruct strbuf tmp = STRBUF_INIT;\n+\tint k;\n \n \t/*\n \t * Build a list of all filesystem changes into a private/local\n@@ -209,7 +211,12 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t/*\n \t\t * On Mac, we receive an array of absolute paths.\n \t\t */\n-\t\tpath_k = paths[k];\n+\t\tfree(resolved);\n+\t\tresolved = fsmonitor__resolve_alias(paths[k], &state->alias);\n+\t\tif (resolved)\n+\t\t\tpath_k = resolved;\n+\t\telse\n+\t\t\tpath_k = paths[k];\n \n \t\t/*\n \t\t * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.\n@@ -238,6 +245,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t\tfsmonitor_force_resync(state);\n \t\t\tfsmonitor_batch__free_list(batch);\n \t\t\tstring_list_clear(&cookie_list, 0);\n+\t\t\tbatch = NULL;\n \n \t\t\t/*\n \t\t\t * We assume that any events that we received\n@@ -360,12 +368,14 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,\n \t\t}\n \t}\n \n+\tfree(resolved);\n \tfsmonitor_publish(state, batch, &cookie_list);\n \tstring_list_clear(&cookie_list, 0);\n \tstrbuf_release(&tmp);\n \treturn;\n \n force_shutdown:\n+\tfree(resolved);\n \tfsmonitor_batch__free_list(batch);\n \tstring_list_clear(&cookie_list, 0);\n \ndiff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\nindex d46d7f13538..ce5a8febe09 100644\n--- a/compat/fsmonitor/fsm-path-utils-darwin.c\n+++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n@@ -1,5 +1,8 @@\n #include \"fsmonitor.h\"\n #include \"fsmonitor-path-utils.h\"\n+#include <dirent.h>\n+#include <errno.h>\n+#include <fcntl.h>\n #include <sys/param.h>\n #include <sys/mount.h>\n \n@@ -41,3 +44,92 @@ int fsmonitor__is_fs_remote(const char *path)\n \n \treturn fs.is_remote;\n }\n+\n+/*\n+ * Scan the root directory for synthetic firmlinks that when resolved\n+ * are a prefix of the path, stopping at the first one found.\n+ *\n+ * Some information about firmlinks and synthetic firmlinks:\n+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/\n+ *\n+ * macOS no longer allows symlinks in the root directory; any link found\n+ * there is therefore a synthetic firmlink.\n+ *\n+ * If this function gets called often, will want to cache all the firmlink\n+ * information, but for now there is only one caller of this function.\n+ *\n+ * If there is more than one alias for the path, that is another\n+ * matter altogether.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\tDIR *dir;\n+\tint retval = -1;\n+\tconst char *const root = \"/\";\n+\tstruct stat st;\n+\tstruct dirent *de;\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to = STRBUF_INIT;\n+\n+\tdir = opendir(root);\n+\tif (!dir)\n+\t\treturn error_errno(_(\"opendir('%s') failed\"), root);\n+\n+\tstrbuf_init(&alias, 256);\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstrbuf_reset(&alias);\n+\t\tstrbuf_addf(&alias, \"%s%s\", root, de->d_name);\n+\n+\t\tif (lstat(alias.buf, &st) < 0) {\n+\t\t\terror_errno(_(\"lstat('%s') failed\"), alias.buf);\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\tif (!S_ISLNK(st.st_mode))\n+\t\t\tcontinue;\n+\n+\t\tif (strbuf_readlink(&points_to, alias.buf, st.st_size) < 0) {\n+\t\t\terror_errno(_(\"strbuf_readlink('%s') failed\"), alias.buf);\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\tif (!strncmp(points_to.buf, path, points_to.len) &&\n+\t\t\t(path[points_to.len] == '/')) {\n+\t\t\tstrbuf_addbuf(&info->alias, &alias);\n+\t\t\tstrbuf_addbuf(&info->points_to, &points_to);\n+\t\t\ttrace_printf_key(&trace_fsmonitor,\n+\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n+\t\t\t\tpath, info->alias.buf, info->points_to.buf);\n+\t\t\tretval = 0;\n+\t\t\tgoto done;\n+\t\t}\n+\t}\n+\tretval = 0; /* no alias */\n+\n+done:\n+\tstrbuf_release(&alias);\n+\tstrbuf_release(&points_to);\n+\tif (closedir(dir) < 0)\n+\t\treturn error_errno(_(\"closedir('%s') failed\"), root);\n+\treturn retval;\n+}\n+\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\tif (!info->alias.len)\n+\t\treturn NULL;\n+\n+\tif ((!strncmp(info->alias.buf, path, info->alias.len))\n+\t\t&& path[info->alias.len] == '/') {\n+\t\tstruct strbuf tmp = STRBUF_INIT;\n+\t\tconst char *remainder = path + info->alias.len;\n+\n+\t\tstrbuf_addbuf(&tmp, &info->points_to);\n+\t\tstrbuf_add(&tmp, remainder, strlen(remainder));\n+\t\treturn strbuf_detach(&tmp, NULL);\n+\t}\n+\n+\treturn NULL;\n+}\ndiff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c\nindex a90b8f7925b..0d95bbb416f 100644\n--- a/compat/fsmonitor/fsm-path-utils-win32.c\n+++ b/compat/fsmonitor/fsm-path-utils-win32.c\n@@ -126,3 +126,20 @@ int fsmonitor__is_fs_remote(const char *path)\n \t\treturn -1;\n \treturn fs.is_remote;\n }\n+\n+/*\n+ * No-op for now.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info)\n+{\n+\treturn 0;\n+}\n+\n+/*\n+ * No-op for now.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info)\n+{\n+\treturn NULL;\n+}\ndiff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h\nindex 2102a5c9ff5..e24838f9a86 100644\n--- a/fsmonitor--daemon.h\n+++ b/fsmonitor--daemon.h\n@@ -8,6 +8,7 @@\n #include \"run-command.h\"\n #include \"simple-ipc.h\"\n #include \"thread-utils.h\"\n+#include \"fsmonitor-path-utils.h\"\n \n struct fsmonitor_batch;\n struct fsmonitor_token_data;\n@@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct strbuf path_worktree_watch;\n \tstruct strbuf path_gitdir_watch;\n+\tstruct alias_info alias;\n \tint nr_paths_watching;\n \n \tstruct fsmonitor_token_data *current_token_data;\n@@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {\n \n \tstruct ipc_server_data *ipc_server_data;\n \tstruct strbuf path_ipc;\n+\n };\n \n /*\ndiff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h\nindex 41edf5b934f..5bfdfb81c14 100644\n--- a/fsmonitor-path-utils.h\n+++ b/fsmonitor-path-utils.h\n@@ -1,13 +1,21 @@\n #ifndef FSM_PATH_UTILS_H\n #define FSM_PATH_UTILS_H\n \n+#include \"strbuf.h\"\n+\n+struct alias_info\n+{\n+\tstruct strbuf alias;\n+\tstruct strbuf points_to;\n+};\n+\n struct fs_info {\n \tint is_remote;\n \tchar *typename;\n };\n \n /*\n- * Get some basic filesystem informtion for the given path\n+ * Get some basic filesystem information for the given path\n  *\n  * The caller owns the storage that is occupied by fs_info and\n  * is responsible for releasing it.\n@@ -23,4 +31,30 @@ int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);\n  */\n int fsmonitor__is_fs_remote(const char *path);\n \n+/*\n+ * Get the alias in given path, if any.\n+ *\n+ * Sets alias to the first alias that matches any part of the path.\n+ *\n+ * If an alias is found, info.alias and info.points_to are set to the\n+ * found mapping.\n+ *\n+ * Returns -1 on error, 0 otherwise.\n+ *\n+ * The caller owns the storage that is occupied by info.alias and\n+ * info.points_to and is responsible for releasing it.\n+ */\n+int fsmonitor__get_alias(const char *path, struct alias_info *info);\n+\n+/*\n+ * Resolve the path against the given alias.\n+ *\n+ * Returns the resolved path if there is one, NULL otherwise.\n+ *\n+ * The caller owns the storage that the returned string occupies and\n+ * is responsible for releasing it.\n+ */\n+char *fsmonitor__resolve_alias(const char *path,\n+\tconst struct alias_info *info);\n+\n #endif\n-- \ngitgitgadget\n\n"},{"id":"464202","messageId":"af7309745f759532fdb79794289d9e02de0e035c.1664904752.git.gitgitgadget@gmail.com","threadId":"58324","inReplyTo":"pull.1326.v15.git.1664904751.gitgitgadget@gmail.com","subject":"[PATCH v15 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Eric DeCosta via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-10-04T17:32:31Z","receivedAt":"2022-10-04T17:33:06Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"From: Eric DeCosta <edecosta@mathworks.com>\n\nAdd documentation for 'fsmonitor.allowRemote' and 'fsmonitor.socketDir'.\nCall-out experimental nature of 'fsmonitor.allowRemote' and limited\nfilesystem support for 'fsmonitor.socketDir'.\n\nSigned-off-by: Eric DeCosta <edecosta@mathworks.com>\n---\n Documentation/config.txt                   |  2 ++\n Documentation/config/fsmonitor--daemon.txt | 11 +++++++\n Documentation/git-fsmonitor--daemon.txt    | 37 ++++++++++++++++++++--\n 3 files changed, 47 insertions(+), 3 deletions(-)\n create mode 100644 Documentation/config/fsmonitor--daemon.txt\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 5b5b9765699..1e205831656 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -423,6 +423,8 @@ include::config/filter.txt[]\n \n include::config/fsck.txt[]\n \n+include::config/fsmonitor--daemon.txt[]\n+\n include::config/gc.txt[]\n \n include::config/gitcvs.txt[]\ndiff --git a/Documentation/config/fsmonitor--daemon.txt b/Documentation/config/fsmonitor--daemon.txt\nnew file mode 100644\nindex 00000000000..c225c6c9e74\n--- /dev/null\n+++ b/Documentation/config/fsmonitor--daemon.txt\n@@ -0,0 +1,11 @@\n+fsmonitor.allowRemote::\n+    By default, the fsmonitor daemon refuses to work against network-mounted\n+    repositories. Setting `fsmonitor.allowRemote` to `true` overrides this\n+    behavior.  Only respected when `core.fsmonitor` is set to `true`.\n+\n+fsmonitor.socketDir::\n+    This Mac OS-specific option, if set, specifies the directory in\n+    which to create the Unix domain socket used for communication\n+    between the fsmonitor daemon and various Git commands. The directory must\n+    reside on a native Mac OS filesystem.  Only respected when `core.fsmonitor`\n+    is set to `true`.\ndiff --git a/Documentation/git-fsmonitor--daemon.txt b/Documentation/git-fsmonitor--daemon.txt\nindex cc142fb8612..8238eadb0e1 100644\n--- a/Documentation/git-fsmonitor--daemon.txt\n+++ b/Documentation/git-fsmonitor--daemon.txt\n@@ -3,7 +3,7 @@ git-fsmonitor{litdd}daemon(1)\n \n NAME\n ----\n-git-fsmonitor--daemon - A Built-in File System Monitor\n+git-fsmonitor--daemon - A Built-in Filesystem Monitor\n \n SYNOPSIS\n --------\n@@ -17,7 +17,7 @@ DESCRIPTION\n -----------\n \n A daemon to watch the working directory for file and directory\n-changes using platform-specific file system notification facilities.\n+changes using platform-specific filesystem notification facilities.\n \n This daemon communicates directly with commands like `git status`\n using the link:technical/api-simple-ipc.html[simple IPC] interface\n@@ -63,13 +63,44 @@ CAVEATS\n -------\n \n The fsmonitor daemon does not currently know about submodules and does\n-not know to filter out file system events that happen within a\n+not know to filter out filesystem events that happen within a\n submodule.  If fsmonitor daemon is watching a super repo and a file is\n modified within the working directory of a submodule, it will report\n the change (as happening against the super repo).  However, the client\n will properly ignore these extra events, so performance may be affected\n but it will not cause an incorrect result.\n \n+By default, the fsmonitor daemon refuses to work against network-mounted\n+repositories; this may be overridden by setting `fsmonitor.allowRemote` to\n+`true`. Note, however, that the fsmonitor daemon is not guaranteed to work\n+correctly with all network-mounted repositories and such use is considered\n+experimental.\n+\n+On Mac OS, the inter-process communication (IPC) between various Git\n+commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a\n+special type of file -- which is supported by native Mac OS filesystems,\n+but not on network-mounted filesystems, NTFS, or FAT32.  Other filesystems\n+may or may not have the needed support; the fsmonitor daemon is not guaranteed\n+to work with these filesystems and such use is considered experimental.\n+\n+By default, the socket is created in the `.git` directory, however, if the\n+`.git` directory is on a network-mounted filesystem, it will be instead be\n+created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a\n+network-mounted filesystem in which case you must set the configuration\n+variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native\n+filesystem in which to create the socket file.\n+\n+If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)\n+is on a native Mac OS file filesystem the fsmonitor daemon will report an\n+error that will cause the daemon and the currently running command to exit.\n+\n+CONFIGURATION\n+-------------\n+\n+include::includes/cmd-config-section-all.txt[]\n+\n+include::config/fsmonitor--daemon.txt[]\n+\n GIT\n ---\n Part of the linkgit:git[1] suite\n-- \ngitgitgadget\n"},{"id":"464249","messageId":"xmqq35c2qh7u.fsf@gitster.g","threadId":"58324","inReplyTo":"pull.1326.v15.git.1664904751.gitgitgadget@gmail.com","subject":"Re: [PATCH v15 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-10-05T18:05:09Z","receivedAt":"2022-10-05T18:06:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> An additional issue is that for mount points in the root directory, FSEvents\n> does not report a path that matches the worktree directory due to the\n> introduction of 'synthetic firmlinks'. fsmonitor must map the FSEvents paths\n> to the worktree directory by interrogating the root filesystem for synthetic\n> firmlinks and using that information to translate the path.\n>\n> v15 differs from v14:\n>\n>  * fix memory leak\n\nThanks.  I thought \"what, another iteration?  didn't we see this\nenough times already?\" and a note like this does help reminding\nreviewers why we need one.\n\nVery much appreciated.\n\n"},{"id":"464260","messageId":"BL0PR05MB557152E4728FBF3DB4B27CFAD95D9@BL0PR05MB5571.namprd05.prod.outlook.com","threadId":"58324","inReplyTo":"xmqq35c2qh7u.fsf@gitster.g","subject":"RE: [PATCH v15 0/6] fsmonitor: option to allow fsmonitor to run against network-mounted repos","fromName":"Eric DeCosta","fromEmail":"edecosta@mathworks.com","sentAt":"2022-10-05T21:14:15Z","receivedAt":"2022-10-05T21:14:34Z","isPatch":true,"sender":{"key":"edecosta@mathworks.com","avatar":"https://avatars.githubusercontent.com/u/67609563?v=4"},"body":"\n\n> -----Original Message-----\n> From: Junio C Hamano <jch2355@gmail.com> On Behalf Of Junio C Hamano\n> Sent: Wednesday, October 5, 2022 2:05 PM\n> To: Eric DeCosta via GitGitGadget <gitgitgadget@gmail.com>\n> Cc: git@vger.kernel.org; Jeff Hostetler <git@jeffhostetler.com>; Eric Sunshine\n> <sunshine@sunshineco.com>; Torsten Bögershausen <tboegi@web.de>;\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com>; Ramsay Jones\n> <ramsay@ramsayjones.plus.com>; Johannes Schindelin\n> <Johannes.Schindelin@gmx.de>; Eric DeCosta <edecosta@mathworks.com>\n> Subject: Re: [PATCH v15 0/6] fsmonitor: option to allow fsmonitor to run\n> against network-mounted repos\n> \n> \"Eric DeCosta via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n> > An additional issue is that for mount points in the root directory,\n> > FSEvents does not report a path that matches the worktree directory\n> > due to the introduction of 'synthetic firmlinks'. fsmonitor must map\n> > the FSEvents paths to the worktree directory by interrogating the root\n> > filesystem for synthetic firmlinks and using that information to translate the\n> path.\n> >\n> > v15 differs from v14:\n> >\n> >  * fix memory leak\n> \n> Thanks.  I thought \"what, another iteration?  didn't we see this enough times\n> already?\" and a note like this does help reminding reviewers why we need\n> one.\n> \n> Very much appreciated.\n\nThanks.  Just getting better at doing the right thing as I climb up the learning curve :)\n\nHopefully it is in a state where it is now GTM.\n\n-Eric\n\n"},{"id":"471132","messageId":"230130.86sffss5s5.gmgdl@evledraar.gmail.com","threadId":"58324","inReplyTo":"ec49a74086dee4545a3bb7b24c8da22e77aef579.1664904751.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v15 1/6] fsmonitor: refactor filesystem checks to common interface","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2023-01-30T09:37:58Z","receivedAt":"2023-01-30T09:59:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Oct 04 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n>\n> Provide a common interface for getting basic filesystem information\n> including filesystem type and whether the filesystem is remote.\n>\n> Refactor existing code for getting basic filesystem info and detecting\n> remote file systems to the new interface.\n>\n> Refactor filesystem checks to leverage new interface. For macOS,\n> error-out if the Unix Domain socket (UDS) file is on a remote\n> filesystem.\n>\n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n>  Makefile                                 |   1 +\n>  compat/fsmonitor/fsm-path-utils-darwin.c |  43 ++++++\n>  compat/fsmonitor/fsm-path-utils-win32.c  | 128 +++++++++++++++++\n>  compat/fsmonitor/fsm-settings-darwin.c   |  69 +++------\n>  compat/fsmonitor/fsm-settings-win32.c    | 172 +----------------------\n>  contrib/buildsystems/CMakeLists.txt      |   2 +\n>  fsmonitor-path-utils.h                   |  26 ++++\n>  fsmonitor-settings.c                     |  50 +++++++\n>  8 files changed, 272 insertions(+), 219 deletions(-)\n>  create mode 100644 compat/fsmonitor/fsm-path-utils-darwin.c\n>  create mode 100644 compat/fsmonitor/fsm-path-utils-win32.c\n>  create mode 100644 fsmonitor-path-utils.h\n>\n> diff --git a/Makefile b/Makefile\n> index cac3452edb9..ffab427ea5b 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -2044,6 +2044,7 @@ endif\n>  ifdef FSMONITOR_OS_SETTINGS\n>  \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS\n>  \tCOMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o\n> +\tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n>  endif\n>  \n>  ifeq ($(TCLTK_PATH),)\n> diff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c\n> new file mode 100644\n> index 00000000000..d46d7f13538\n> --- /dev/null\n> +++ b/compat/fsmonitor/fsm-path-utils-darwin.c\n> @@ -0,0 +1,43 @@\n> +#include \"fsmonitor.h\"\n> +#include \"fsmonitor-path-utils.h\"\n> +#include <sys/param.h>\n> +#include <sys/mount.h>\n> +\n> +int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)\n> +{\n> +\tstruct statfs fs;\n\nShould have a \\n here after variable decls.\n\n> +\tif (statfs(path, &fs) == -1) {\n> +\t\tint saved_errno = errno;\n> +\t\ttrace_printf_key(&trace_fsmonitor, \"statfs('%s') failed: %s\",\n> +\t\t\t\t path, strerror(saved_errno));\n> +\t\terrno = saved_errno;\n> +\t\treturn -1;\n> +\t}\n> +\n> +\ttrace_printf_key(&trace_fsmonitor,\n> +\t\t\t \"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'\",\n> +\t\t\t path, fs.f_type, fs.f_flags, fs.f_fstypename);\n> +\n> +\tif (!(fs.f_flags & MNT_LOCAL))\n> +\t\tfs_info->is_remote = 1;\n> +\telse\n> +\t\tfs_info->is_remote = 0;\n\nInstead:\n\n\tfs_info->is_remote = !(fs.f_flags & MNT_LOCAL)\n\n?\n\n> +int fsmonitor__is_fs_remote(const char *path)\n> +{\n> +\tstruct fs_info fs;\n\nSame style issue as above.\n\n> +\tif (fsmonitor__get_fs_info(path, &fs))\n> +\t\treturn -1;\n> +\n> +\tfree(fs.typename);\n\nCan we get a \"free_fs_info()\" function or something instead, this is one\nof N codepaths where we now peek into that struct. If we ever add\nanother field that needs malloc'ing altering all callers will be\npainful.\n\n\n> +\tif (xutftowcs_path(wpath, path) < 0) {\n> +\t\treturn -1;\n> +\t}\n\nMaybe drop the braces here as this code is being modified-while-moved\nanyway?\n\n> +\n> +\t/*\n> +\t * GetDriveTypeW() requires a final slash.  We assume that the\n> +\t * worktree pathname points to an actual directory.\n> +\t */\n> +\twlen = wcslen(wpath);\n> +\tif (wpath[wlen - 1] != L'\\\\' && wpath[wlen - 1] != L'/') {\n> +\t\twpath[wlen++] = L'\\\\';\n> +\t\twpath[wlen] = 0;\n> +\t}\n> +\n> +\t/*\n> +\t * Normalize the path.  If nothing else, this converts forward\n> +\t * slashes to backslashes.  This is essential to get GetDriveTypeW()\n> +\t * correctly handle some UNC \"\\\\server\\share\\...\" paths.\n> +\t */\n> +\tif (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {\n> +\t\treturn -1;\n> +\t}\n\nHere you have added needless braces while moving this, let's not do\nthat.\n\n> +\n> +\tdriveType = GetDriveTypeW(wfullpath);\n> +\ttrace_printf_key(&trace_fsmonitor,\n> +\t\t\t \"DriveType '%s' L'%ls' (%u)\",\n> +\t\t\t path, wfullpath, driveType);\n> +\n> +\tif (driveType == DRIVE_REMOTE) {\n> +\t\tfs_info->is_remote = 1;\n> +\t\tif (check_remote_protocol(wfullpath) < 0)\n> +\t\t\treturn -1;\n\nMaybe this code should be more careful not to modify the passed-in\nstruct if we're returning an error?\n\nI.e. do this \"return -1\" before assigning \"is_remote = 1\"?\n\n> +\t} else {\n> +\t\tfs_info->is_remote = 0;\n> +\t}\n\nMaybe just at the start: \"struct fs_info = { 0 }\" and skip this \"else\" ?\n\n> +\n> +\ttrace_printf_key(&trace_fsmonitor,\n> +\t\t\t\t\"'%s' is_remote: %d\",\n> +\t\t\t\tpath, fs_info->is_remote);\n> +\n> +\treturn 0;\n> +}\n> +\n> +int fsmonitor__is_fs_remote(const char *path)\n> +{\n> +\tstruct fs_info fs;\n> +\tif (fsmonitor__get_fs_info(path, &fs))\n> +\t\treturn -1;\n> +\treturn fs.is_remote;\n\nI find this and the resulting \"switch/case\" caller rather un-idiomatic,\ni.e. you end up checking 1/0/default.\n\nWhy not in your check_remote() instead:\n\n\tint is_remote;\n\n\tif (fsmonitor__check_fs_remote(r->worktree, &is_remote))\n\t\treturn FSMONITOR_REASON_ERROR;\n\telse if (!is_remote)\n\t\treturn FSMONITOR_REASON_OK;\n\t...\n\nWhere the \"...\" is the \"repo_config_get_bool()\" etc. code I suggest\nbelow.\n\nI.e. having an \"is\" function return non-boolean is somewhat confusing,\nbetter to write to a variable (which the config API you're using does).\n\n> +#ifdef HAVE_FSMONITOR_OS_SETTINGS\n> +static enum fsmonitor_reason check_remote(struct repository *r)\n> +{\n> +\tint allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */\n\nLet's not init this to -1, and instead...\n\n> +\tint is_remote = fsmonitor__is_fs_remote(r->worktree);\n> +\n> +\tswitch (is_remote) {\n> +\t\tcase 0:\n\nThe usual coding style is to not indent the \"case\" further than the\n\"switch\".\n\n> +\t\t\treturn FSMONITOR_REASON_OK;\n> +\t\tcase 1:\n> +\t\t\trepo_config_get_bool(r, \"fsmonitor.allowremote\", &allow_remote);\n> +\t\t\tif (allow_remote < 1)\n\n...continued from above: We can scope this variable to this case\nstatement, as \"case 1: { int v; ... }\", but furthermore you don't need\nto init it to -1 and check this -1 case if you just check the return\nvalue of repo_config_get_bool(), which IMO is also more obvious:\n\n\tint v;\n\n\tif (!repo...(..., &v))\n\t\treturn v ? FSMONITOR_REASON_OK : FSMONITOR_REASON_REMOTE:\n\telse\n\t\treturn FSMONITOR_REASON_REMOTE;\n\nI.e. you clearly separate the cases where it's un-init'd by checking the\nreturn value.\n\nMaybe better (but would result in more churn later if you ever want to\nchange the default):\n\n\tint v;\n\n\treturn !repo...(..., &v) && v ? FSMONITOR_REASON_OK :\n\t\tFSMONITOR_REASON_REMOTE:\n"},{"id":"471133","messageId":"230130.86o7qgs5j6.gmgdl@evledraar.gmail.com","threadId":"58324","inReplyTo":"7bf1cdfe3b259f7135f3a50dcdd653563d5e19f6.1664904751.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v15 2/6] fsmonitor: relocate socket file if .git directory is remote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2023-01-30T09:58:49Z","receivedAt":"2023-01-30T10:03:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Oct 04 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n>\n> If the .git directory is on a remote filesystem, create the socket\n> file in 'fsmonitor.socketDir' if it is defined, else create it in $HOME.\n>\n> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>\n> ---\n>  Makefile                               |  1 +\n>  builtin/fsmonitor--daemon.c            |  3 +-\n>  compat/fsmonitor/fsm-ipc-darwin.c      | 52 ++++++++++++++++++++++++++\n>  compat/fsmonitor/fsm-ipc-win32.c       |  9 +++++\n>  compat/fsmonitor/fsm-settings-darwin.c |  2 +-\n>  contrib/buildsystems/CMakeLists.txt    |  2 +\n>  fsmonitor-ipc.c                        | 18 ++++-----\n>  fsmonitor-ipc.h                        |  4 +-\n>  8 files changed, 78 insertions(+), 13 deletions(-)\n>  create mode 100644 compat/fsmonitor/fsm-ipc-darwin.c\n>  create mode 100644 compat/fsmonitor/fsm-ipc-win32.c\n>\n> diff --git a/Makefile b/Makefile\n> index ffab427ea5b..feb675a6959 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -2039,6 +2039,7 @@ ifdef FSMONITOR_DAEMON_BACKEND\n>  \tCOMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND\n>  \tCOMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o\n>  \tCOMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o\n> +\tCOMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o\n>  endif\n>  \n>  ifdef FSMONITOR_OS_SETTINGS\n> diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c\n> index 2c109cf8b37..0123fc33ed2 100644\n> --- a/builtin/fsmonitor--daemon.c\n> +++ b/builtin/fsmonitor--daemon.c\n> @@ -1343,7 +1343,8 @@ static int fsmonitor_run_daemon(void)\n>  \t * directory.)\n>  \t */\n>  \tstrbuf_init(&state.path_ipc, 0);\n> -\tstrbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));\n> +\tstrbuf_addstr(&state.path_ipc,\n> +\t\tabsolute_path(fsmonitor_ipc__get_path(the_repository)));\n>  \n>  \t/*\n>  \t * Confirm that we can create platform-specific resources for the\n> diff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c\n> new file mode 100644\n> index 00000000000..ce843d63348\n> --- /dev/null\n> +++ b/compat/fsmonitor/fsm-ipc-darwin.c\n> @@ -0,0 +1,52 @@\n> +#include \"cache.h\"\n> +#include \"config.h\"\n> +#include \"strbuf.h\"\n> +#include \"fsmonitor.h\"\n> +#include \"fsmonitor-ipc.h\"\n> +#include \"fsmonitor-path-utils.h\"\n> +\n> +static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, \"fsmonitor--daemon.ipc\")\n> +\n> +const char *fsmonitor_ipc__get_path(struct repository *r)\n> +{\n> +\tstatic const char *ipc_path = NULL;\n> +\tSHA_CTX sha1ctx;\n> +\tchar *sock_dir = NULL;\n\n...don't init this to NULL...\n\n> +\tstruct strbuf ipc_file = STRBUF_INIT;\n> +\tunsigned char hash[SHA_DIGEST_LENGTH];\n> +\n> +\tif (!r)\n> +\t\tBUG(\"No repository passed into fsmonitor_ipc__get_path\");\n> +\n> +\tif (ipc_path)\n> +\t\treturn ipc_path;\n> +\n> +\n> +\t/* By default the socket file is created in the .git directory */\n> +\tif (fsmonitor__is_fs_remote(r->gitdir) < 1) {\n> +\t\tipc_path = fsmonitor_ipc__get_default_path();\n> +\t\treturn ipc_path;\n> +\t}\n> +\n> +\tSHA1_Init(&sha1ctx);\n> +\tSHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));\n> +\tSHA1_Final(hash, &sha1ctx);\n> +\n> +\trepo_config_get_string(r, \"fsmonitor.socketdir\", &sock_dir);\n\n...instead check the return value here. So:\n\n\tif (!repo_config_get_string(..., &sock_dir))\n        \t...\n\n> +\t/* Create the socket file in either socketDir or $HOME */\n> +\tif (sock_dir && *sock_dir) {\n> +\t\tstrbuf_addf(&ipc_file, \"%s/.git-fsmonitor-%s\",\n> +\t\t\t\t\tsock_dir, hash_to_hex(hash));\n\n^ Add the body of this branch to the \"...\" above.\n\n> +\t} else {\n> +\t\tstrbuf_addf(&ipc_file, \"~/.git-fsmonitor-%s\", hash_to_hex(hash));\n\n...and keep this in the \"else\".\n\n> +\t}\n> +\tfree(sock_dir);\n\nYou'd then add this free() to the first branch, but better yet in this\ncase avoid this allocation, use \"const char *\" and use\nrepo_config_get_string_tmp(). It's made for exactly this sort of use-case.\n\n> +\n> +\tipc_path = interpolate_path(ipc_file.buf, 1);\n> +\tif (!ipc_path)\n> +\t\tdie(_(\"Invalid path: %s\"), ipc_file.buf);\n> +\n> +\tstrbuf_release(&ipc_file);\n> +\treturn ipc_path;\n> +}\n> diff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c\n> new file mode 100644\n> index 00000000000..e08c505c148\n> --- /dev/null\n> +++ b/compat/fsmonitor/fsm-ipc-win32.c\n> @@ -0,0 +1,9 @@\n> +#include \"config.h\"\n> +#include \"fsmonitor-ipc.h\"\n> +\n> +const char *fsmonitor_ipc__get_path(struct repository *r) {\n> +\tstatic char *ret;\n\nMissing \\n here.\n\n>  try_again:\n> -\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,\n> -\t\t\t\t       &connection);\n> +\tstate = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),\n> +\t\t\t\t\t\t&options, &connection);\n\nThis post-image is mis-indented.\n\n>  #include \"simple-ipc.h\"\n>  \n> +struct repository;\n> +\n\nI think we'd usually forard-declare such things, if needed...\n\n>  /*\n>   * Returns true if built-in file system monitor daemon is defined\n>   * for this platform.\n> @@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);\n>   *\n>   * Returns NULL if the daemon is not supported on this platform.\n>   */\n> -const char *fsmonitor_ipc__get_path(void);\n\n..right before they're needed, so before this line?\n\n> +const char *fsmonitor_ipc__get_path(struct repository *r);\n>  \n>  /*\n>   * Try to determine whether there is a `git-fsmonitor--daemon` process\n\n"},{"id":"471134","messageId":"230130.86k014s5eq.gmgdl@evledraar.gmail.com","threadId":"58324","inReplyTo":"af7309745f759532fdb79794289d9e02de0e035c.1664904752.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v15 6/6] fsmonitor: add documentation for allowRemote and socketDir options","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2023-01-30T10:04:46Z","receivedAt":"2023-01-30T10:06:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Oct 04 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n> [...]\n> +include::config/fsmonitor--daemon.txt[]\n\nWe tend to name these files after the config namespace, not the name of\nthe built-in that's (mostly?) using it.\n> +\n>  include::config/gc.txt[]\n>  \n>  include::config/gitcvs.txt[]\n> diff --git a/Documentation/config/fsmonitor--daemon.txt b/Documentation/config/fsmonitor--daemon.txt\n> new file mode 100644\n> index 00000000000..c225c6c9e74\n> --- /dev/null\n> +++ b/Documentation/config/fsmonitor--daemon.txt\n> @@ -0,0 +1,11 @@\n\nSo this should be just .../config/fsmonitor.txt\n\n"},{"id":"471135","messageId":"230130.86fsbss4h8.gmgdl@evledraar.gmail.com","threadId":"58324","inReplyTo":"863063aefeeecfd23bb50eb111fcfbf5879a8ee3.1664904752.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v15 4/6] fsmonitor: deal with synthetic firmlinks on macOS","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2023-01-30T10:08:06Z","receivedAt":"2023-01-30T10:26:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Oct 04 2022, Eric DeCosta via GitGitGadget wrote:\n\n> From: Eric DeCosta <edecosta@mathworks.com>\n\n> +\tstruct strbuf alias;\n\nRather than init-ing here...\n> +\tstruct strbuf points_to = STRBUF_INIT;\n> +\n> +\tdir = opendir(root);\n> +\tif (!dir)\n> +\t\treturn error_errno(_(\"opendir('%s') failed\"), root);\n> +\n> +\tstrbuf_init(&alias, 256);\n\n...we pre-grow here...\n\n> +\twhile ((de = readdir(dir)) != NULL) {\n\n...but as shown here, we may not even use this at all, but even then is\nthis micro-optimization worth it? If it is a reader would be helped with\nan explanation of what the 256 is, is this meant to be some OSX-specific\nPATH_MAX, but hardcoded?\n\n> +\t\tstrbuf_reset(&alias);\n> +\t\tstrbuf_addf(&alias, \"%s%s\", root, de->d_name);\n> +\n> +\t\tif (lstat(alias.buf, &st) < 0) {\n> +\t\t\terror_errno(_(\"lstat('%s') failed\"), alias.buf);\n> +\t\t\tgoto done;\n> +\t\t}\n> +\n> +\t\tif (!S_ISLNK(st.st_mode))\n> +\t\t\tcontinue;\n> +\n> +\t\tif (strbuf_readlink(&points_to, alias.buf, st.st_size) < 0) {\n> +\t\t\terror_errno(_(\"strbuf_readlink('%s') failed\"), alias.buf);\n> +\t\t\tgoto done;\n> +\t\t}\n> +\n\nMaybe this code would be simpler if you split it into a trivial static\nfunction, so you could pass in the \"alias\" and \"points_to\", and just do\n\"return error...(...)\" here and in the other places.\n\n> +\t\tif (!strncmp(points_to.buf, path, points_to.len) &&\n> +\t\t\t(path[points_to.len] == '/')) {\n> +\t\t\tstrbuf_addbuf(&info->alias, &alias);\n> +\t\t\tstrbuf_addbuf(&info->points_to, &points_to);\n\nEarlier you use strbuf_addf() for a \"append two strings\", maybe we could\nsave ourselves a line and do the same here...\n\n> +\t\t\ttrace_printf_key(&trace_fsmonitor,\n> +\t\t\t\t\"Found alias for '%s' : '%s' -> '%s'\",\n> +\t\t\t\tpath, info->alias.buf, info->points_to.buf);\n\n...except we're only doing this to emit this, and then we'll free() it?\nCan't we just use %s%s here instead of %s, and e.g. pass\n\"info->alias.buf, alias\" instead of the now-appende-to\n\"info->alias.buf\"?\n\n> +char *fsmonitor__resolve_alias(const char *path,\n> +\tconst struct alias_info *info)\n\nI commented on this in a few other places, and I'll stop noting these\nnow, but you're mis-indenting function decls consistently, also in a *.h\nchange later in this commit.\n\nPlease look through those for this series.\n\n> +{\n> +\tif (!info->alias.len)\n> +\t\treturn NULL;\n\nMaybe \"check if we have a zero-length string\" should belong in the\ncaller, as \"resolve it as an alias\" for \"\\0\" is nonsense?\n\n> +\n> +\tif ((!strncmp(info->alias.buf, path, info->alias.len))\n> +\t\t&& path[info->alias.len] == '/') {\n> +\t\tstruct strbuf tmp = STRBUF_INIT;\n> +\t\tconst char *remainder = path + info->alias.len;\n> +\n> +\t\tstrbuf_addbuf(&tmp, &info->points_to);\n> +\t\tstrbuf_add(&tmp, remainder, strlen(remainder));\n\nThere's no point in strbuf_add() if you have to dynamically use strlen()\nover just using strbuf_addstr() (which inline resolves to the same),\nlet's just use that.\n\nOr just a single strbuf_addf()...\n\n> +\t\treturn strbuf_detach(&tmp, NULL);\n\n...Or actually, these last 3 lines can be replaced by a mere:\n\n\treturn xstrfmt(\"%s%s\", info->points_to.buf, remainder);\n\nPlease just use that. It's not exactly the same due to the pre-sizing\nwith \"addbuf\", but again (I commented on a similar case in an earlier\ncommit), is such micro-optimization worth it here over brevity?\n\n> +/*\n> + * No-op for now.\n> + */\n\nPlease just drop this comment, it doesn't add any information. It would\nbe useful to say why we're seemingly faking \"no aliase on win32\", or to\nsay that it does have them, but implementing them is a \"TODO\".\n\nBut we can see it's a \"no-op for now\" from the code....\n\n> +int fsmonitor__get_alias(const char *path, struct alias_info *info)\n> +{\n> +\treturn 0;\n> +}\n> +\n> +/*\n> + * No-op for now.\n> + */\n\n...ditto.\n\n> +/*\n> + * Get the alias in given path, if any.\n> + *\n> + * Sets alias to the first alias that matches any part of the path.\n> + *\n> + * If an alias is found, info.alias and info.points_to are set to the\n> + * found mapping.\n> + *\n> + * Returns -1 on error, 0 otherwise.\n> + *\n> + * The caller owns the storage that is occupied by info.alias and\n> + * info.points_to and is responsible for releasing it.\n> + */\n> +int fsmonitor__get_alias(const char *path, struct alias_info *info);\n\nI have not looked carefully at this interface, but instead of all of\nthis explanation & the caller needing to carefully reason about what\nparts of this struct it can and can't peek into couldn't we just make it\ntake two \"char **\" arguments, one for \"alias\" and another for\n\"points_to\".\n\nIt would then be obvious what the semantics are, and who owns the\nmemory.\n\nBut maybe retaining the strbuf-ness is important (but even then, we\ncould pass a \"struct strbuf *\" to populate.\n> +\n> +/*\n> + * Resolve the path against the given alias.\n> + *\n> + * Returns the resolved path if there is one, NULL otherwise.\n> + *\n> + * The caller owns the storage that the returned string occupies and\n> + * is responsible for releasing it.\n> + */\n> +char *fsmonitor__resolve_alias(const char *path,\n> +\tconst struct alias_info *info);\n> +\n\nHere we don't say anything about the ownership & freeing of \"info\", does\nthe same apply? But the API design comment above also applies.\n\n"}]}