Skip to content

Commit 508c1a5

Browse files
edecosta-mwgitster
authored andcommitted
fsmonitor: refactor filesystem checks to common interface
Provide a common interface for getting basic filesystem information including filesystem type and whether the filesystem is remote. Refactor existing code for getting basic filesystem info and detecting remote file systems to the new interface. Refactor filesystem checks to leverage new interface. For macOS, error-out if the Unix Domain socket (UDS) file is on a remote filesystem. Signed-off-by: Eric DeCosta <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 85dc0da commit 508c1a5

File tree

8 files changed

+272
-219
lines changed

8 files changed

+272
-219
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,6 +2037,7 @@ endif
20372037
ifdef FSMONITOR_OS_SETTINGS
20382038
COMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS
20392039
COMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o
2040+
COMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o
20402041
endif
20412042

20422043
ifeq ($(TCLTK_PATH),)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include "fsmonitor.h"
2+
#include "fsmonitor-path-utils.h"
3+
#include <sys/param.h>
4+
#include <sys/mount.h>
5+
6+
int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
7+
{
8+
struct statfs fs;
9+
if (statfs(path, &fs) == -1) {
10+
int saved_errno = errno;
11+
trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s",
12+
path, strerror(saved_errno));
13+
errno = saved_errno;
14+
return -1;
15+
}
16+
17+
trace_printf_key(&trace_fsmonitor,
18+
"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'",
19+
path, fs.f_type, fs.f_flags, fs.f_fstypename);
20+
21+
if (!(fs.f_flags & MNT_LOCAL))
22+
fs_info->is_remote = 1;
23+
else
24+
fs_info->is_remote = 0;
25+
26+
fs_info->typename = xstrdup(fs.f_fstypename);
27+
28+
trace_printf_key(&trace_fsmonitor,
29+
"'%s' is_remote: %d",
30+
path, fs_info->is_remote);
31+
return 0;
32+
}
33+
34+
int fsmonitor__is_fs_remote(const char *path)
35+
{
36+
struct fs_info fs;
37+
if (fsmonitor__get_fs_info(path, &fs))
38+
return -1;
39+
40+
free(fs.typename);
41+
42+
return fs.is_remote;
43+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#include "cache.h"
2+
#include "fsmonitor.h"
3+
#include "fsmonitor-path-utils.h"
4+
5+
/*
6+
* Check remote working directory protocol.
7+
*
8+
* Return -1 if client machine cannot get remote protocol information.
9+
*/
10+
static int check_remote_protocol(wchar_t *wpath)
11+
{
12+
HANDLE h;
13+
FILE_REMOTE_PROTOCOL_INFO proto_info;
14+
15+
h = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
16+
FILE_FLAG_BACKUP_SEMANTICS, NULL);
17+
18+
if (h == INVALID_HANDLE_VALUE) {
19+
error(_("[GLE %ld] unable to open for read '%ls'"),
20+
GetLastError(), wpath);
21+
return -1;
22+
}
23+
24+
if (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,
25+
&proto_info, sizeof(proto_info))) {
26+
error(_("[GLE %ld] unable to get protocol information for '%ls'"),
27+
GetLastError(), wpath);
28+
CloseHandle(h);
29+
return -1;
30+
}
31+
32+
CloseHandle(h);
33+
34+
trace_printf_key(&trace_fsmonitor,
35+
"check_remote_protocol('%ls') remote protocol %#8.8lx",
36+
wpath, proto_info.Protocol);
37+
38+
return 0;
39+
}
40+
41+
/*
42+
* Notes for testing:
43+
*
44+
* (a) Windows allows a network share to be mapped to a drive letter.
45+
* (This is the normal method to access it.)
46+
*
47+
* $ NET USE Z: \\server\share
48+
* $ git -C Z:/repo status
49+
*
50+
* (b) Windows allows a network share to be referenced WITHOUT mapping
51+
* it to drive letter.
52+
*
53+
* $ NET USE \\server\share\dir
54+
* $ git -C //server/share/repo status
55+
*
56+
* (c) Windows allows "SUBST" to create a fake drive mapping to an
57+
* arbitrary path (which may be remote)
58+
*
59+
* $ SUBST Q: Z:\repo
60+
* $ git -C Q:/ status
61+
*
62+
* (d) Windows allows a directory symlink to be created on a local
63+
* file system that points to a remote repo.
64+
*
65+
* $ mklink /d ./link //server/share/repo
66+
* $ git -C ./link status
67+
*/
68+
int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
69+
{
70+
wchar_t wpath[MAX_PATH];
71+
wchar_t wfullpath[MAX_PATH];
72+
size_t wlen;
73+
UINT driveType;
74+
75+
/*
76+
* Do everything in wide chars because the drive letter might be
77+
* a multi-byte sequence. See win32_has_dos_drive_prefix().
78+
*/
79+
if (xutftowcs_path(wpath, path) < 0) {
80+
return -1;
81+
}
82+
83+
/*
84+
* GetDriveTypeW() requires a final slash. We assume that the
85+
* worktree pathname points to an actual directory.
86+
*/
87+
wlen = wcslen(wpath);
88+
if (wpath[wlen - 1] != L'\\' && wpath[wlen - 1] != L'/') {
89+
wpath[wlen++] = L'\\';
90+
wpath[wlen] = 0;
91+
}
92+
93+
/*
94+
* Normalize the path. If nothing else, this converts forward
95+
* slashes to backslashes. This is essential to get GetDriveTypeW()
96+
* correctly handle some UNC "\\server\share\..." paths.
97+
*/
98+
if (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {
99+
return -1;
100+
}
101+
102+
driveType = GetDriveTypeW(wfullpath);
103+
trace_printf_key(&trace_fsmonitor,
104+
"DriveType '%s' L'%ls' (%u)",
105+
path, wfullpath, driveType);
106+
107+
if (driveType == DRIVE_REMOTE) {
108+
fs_info->is_remote = 1;
109+
if (check_remote_protocol(wfullpath) < 0)
110+
return -1;
111+
} else {
112+
fs_info->is_remote = 0;
113+
}
114+
115+
trace_printf_key(&trace_fsmonitor,
116+
"'%s' is_remote: %d",
117+
path, fs_info->is_remote);
118+
119+
return 0;
120+
}
121+
122+
int fsmonitor__is_fs_remote(const char *path)
123+
{
124+
struct fs_info fs;
125+
if (fsmonitor__get_fs_info(path, &fs))
126+
return -1;
127+
return fs.is_remote;
128+
}

compat/fsmonitor/fsm-settings-darwin.c

Lines changed: 20 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,10 @@
1-
#include "cache.h"
21
#include "config.h"
3-
#include "repository.h"
4-
#include "fsmonitor-settings.h"
52
#include "fsmonitor.h"
6-
#include <sys/param.h>
7-
#include <sys/mount.h>
3+
#include "fsmonitor-ipc.h"
4+
#include "fsmonitor-settings.h"
5+
#include "fsmonitor-path-utils.h"
86

9-
/*
10-
* [1] Remote working directories are problematic for FSMonitor.
11-
*
12-
* The underlying file system on the server machine and/or the remote
13-
* mount type (NFS, SAMBA, etc.) dictates whether notification events
14-
* are available at all to remote client machines.
15-
*
16-
* Kernel differences between the server and client machines also
17-
* dictate the how (buffering, frequency, de-dup) the events are
18-
* delivered to client machine processes.
19-
*
20-
* A client machine (such as a laptop) may choose to suspend/resume
21-
* and it is unclear (without lots of testing) whether the watcher can
22-
* resync after a resume. We might be able to treat this as a normal
23-
* "events were dropped by the kernel" event and do our normal "flush
24-
* and resync" --or-- we might need to close the existing (zombie?)
25-
* notification fd and create a new one.
26-
*
27-
* In theory, the above issues need to be addressed whether we are
28-
* using the Hook or IPC API.
29-
*
7+
/*
308
* For the builtin FSMonitor, we create the Unix domain socket for the
319
* IPC in the .git directory. If the working directory is remote,
3210
* then the socket will be created on the remote file system. This
@@ -38,50 +16,43 @@
3816
* be taken to ensure that $HOME is actually local and not a managed
3917
* file share.)
4018
*
41-
* So (for now at least), mark remote working directories as
42-
* incompatible.
43-
*
44-
*
45-
* [2] FAT32 and NTFS working directories are problematic too.
19+
* FAT32 and NTFS working directories are problematic too.
4620
*
4721
* The builtin FSMonitor uses a Unix domain socket in the .git
4822
* directory for IPC. These Windows drive formats do not support
4923
* Unix domain sockets, so mark them as incompatible for the daemon.
5024
*
5125
*/
52-
static enum fsmonitor_reason check_volume(struct repository *r)
26+
static enum fsmonitor_reason check_uds_volume(struct repository *r)
5327
{
54-
struct statfs fs;
28+
struct fs_info fs;
29+
const char *ipc_path = fsmonitor_ipc__get_path();
30+
struct strbuf path = STRBUF_INIT;
31+
strbuf_add(&path, ipc_path, strlen(ipc_path));
5532

56-
if (statfs(r->worktree, &fs) == -1) {
57-
int saved_errno = errno;
58-
trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s",
59-
r->worktree, strerror(saved_errno));
60-
errno = saved_errno;
33+
if (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {
34+
strbuf_release(&path);
6135
return FSMONITOR_REASON_ERROR;
6236
}
6337

64-
trace_printf_key(&trace_fsmonitor,
65-
"statfs('%s') [type 0x%08x][flags 0x%08x] '%s'",
66-
r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);
67-
68-
if (!(fs.f_flags & MNT_LOCAL))
69-
return FSMONITOR_REASON_REMOTE;
38+
strbuf_release(&path);
7039

71-
if (!strcmp(fs.f_fstypename, "msdos")) /* aka FAT32 */
72-
return FSMONITOR_REASON_NOSOCKETS;
73-
74-
if (!strcmp(fs.f_fstypename, "ntfs"))
40+
if (fs.is_remote ||
41+
!strcmp(fs.typename, "msdos") ||
42+
!strcmp(fs.typename, "ntfs")) {
43+
free(fs.typename);
7544
return FSMONITOR_REASON_NOSOCKETS;
45+
}
7646

47+
free(fs.typename);
7748
return FSMONITOR_REASON_OK;
7849
}
7950

8051
enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
8152
{
8253
enum fsmonitor_reason reason;
8354

84-
reason = check_volume(r);
55+
reason = check_uds_volume(r);
8556
if (reason != FSMONITOR_REASON_OK)
8657
return reason;
8758

0 commit comments

Comments
 (0)