Skip to content

Commit

Permalink
fsmonitor-settings: remote repos on Windows are incompatible with FSM…
Browse files Browse the repository at this point in the history
…onitor

Teach Git to detect remote working directories on Windows and mark them as
incompatible with FSMonitor.

With this `git fsmonitor--daemon run` will error out with a message like it
does for bare repos.

Client commands, such as `git status`, will not attempt to start the daemon.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
  • Loading branch information
jeffhostetler authored and dscho committed Aug 16, 2021
1 parent da396b6 commit a002e81
Showing 1 changed file with 102 additions and 0 deletions.
102 changes: 102 additions & 0 deletions compat/fsmonitor/fsm-settings-win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "config.h"
#include "repository.h"
#include "fsmonitor-settings.h"
#include "fsmonitor.h"

/*
* GVFS (aka VFS for Git) is incompatible with FSMonitor.
Expand All @@ -23,6 +24,103 @@ static enum fsmonitor_reason is_virtual(struct repository *r)
return FSMONITOR_REASON_ZERO;
}

/*
* Remote working directories are problematic for FSMonitor.
*
* The underlying file system on the server machine and/or the remote
* mount type dictates whether notification events are available at
* all to remote client machines.
*
* Kernel differences between the server and client machines also
* dictate the how (buffering, frequency, de-dup) the events are
* delivered to client machine processes.
*
* A client machine (such as a laptop) may choose to suspend/resume
* and it is unclear (without lots of testing) whether the watcher can
* resync after a resume. We might be able to treat this as a normal
* "events were dropped by the kernel" event and do our normal "flush
* and resync" --or-- we might need to close the existing (zombie?)
* notification fd and create a new one.
*
* In theory, the above issues need to be addressed whether we are
* using the Hook or IPC API.
*
* So (for now at least), mark remote working directories as
* incompatible.
*
* Notes for testing:
*
* (a) Windows allows a network share to be mapped to a drive letter.
* (This is the normal method to access it.)
*
* $ NET USE Z: \\server\share
* $ git -C Z:/repo status
*
* (b) Windows allows a network share to be referenced WITHOUT mapping
* it to drive letter.
*
* $ NET USE \\server\share\dir
* $ git -C //server/share/repo status
*
* (c) Windows allows "SUBST" to create a fake drive mapping to an
* arbitrary path (which may be remote)
*
* $ SUBST Q: Z:\repo
* $ git -C Q:/ status
*
* (d) Windows allows a directory symlink to be created on a local
* file system that points to a remote repo.
*
* $ mklink /d ./link //server/share/repo
* $ git -C ./link status
*/
static enum fsmonitor_reason is_remote(struct repository *r)
{
wchar_t wpath[MAX_PATH];
wchar_t wfullpath[MAX_PATH];
size_t wlen;
UINT driveType;

/*
* Do everything in wide chars because the drive letter might be
* a multi-byte sequence. See win32_has_dos_drive_prefix().
*/
if (xutftowcs_path(wpath, r->worktree) < 0)
return FSMONITOR_REASON_ZERO;

/*
* GetDriveTypeW() requires a final slash. We assume that the
* worktree pathname points to an actual directory.
*/
wlen = wcslen(wpath);
if (wpath[wlen - 1] != L'\\' && wpath[wlen - 1] != L'/') {
wpath[wlen++] = L'\\';
wpath[wlen] = 0;
}

/*
* Normalize the path. If nothing else, this converts forward
* slashes to backslashes. This is essential to get GetDriveTypeW()
* correctly handle some UNC "\\server\share\..." paths.
*/
if (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))
return FSMONITOR_REASON_ZERO;

driveType = GetDriveTypeW(wfullpath);
trace_printf_key(&trace_fsmonitor,
"DriveType '%s' L'%S' (%u)",
r->worktree, wfullpath, driveType);

if (driveType == DRIVE_REMOTE) {
trace_printf_key(&trace_fsmonitor,
"is_remote('%s') true",
r->worktree);
return FSMONITOR_REASON_REMOTE;
}

return FSMONITOR_REASON_ZERO;
}

enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
{
enum fsmonitor_reason reason;
Expand All @@ -31,5 +129,9 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
if (reason)
return reason;

reason = is_remote(r);
if (reason)
return reason;

return FSMONITOR_REASON_ZERO;
}

0 comments on commit a002e81

Please sign in to comment.