]>
Commit | Line | Data |
---|---|---|
d33c804d JH |
1 | #include "cache.h" |
2 | #include "config.h" | |
3 | #include "repository.h" | |
4 | #include "fsmonitor-settings.h" | |
d989b266 | 5 | #include "fsmonitor.h" |
d33c804d | 6 | |
5c58fbd2 JH |
7 | /* |
8 | * VFS for Git is incompatible with FSMonitor. | |
9 | * | |
10 | * Granted, core Git does not know anything about VFS for Git and we | |
11 | * shouldn't make assumptions about a downstream feature, but users | |
12 | * can install both versions. And this can lead to incorrect results | |
13 | * from core Git commands. So, without bringing in any of the VFS for | |
14 | * Git code, do a simple config test for a published config setting. | |
15 | * (We do not look at the various *_TEST_* environment variables.) | |
16 | */ | |
17 | static enum fsmonitor_reason check_vfs4git(struct repository *r) | |
18 | { | |
19 | const char *const_str; | |
20 | ||
21 | if (!repo_config_get_value(r, "core.virtualfilesystem", &const_str)) | |
22 | return FSMONITOR_REASON_VFS4GIT; | |
23 | ||
24 | return FSMONITOR_REASON_OK; | |
25 | } | |
26 | ||
85dc0da6 ED |
27 | /* |
28 | * Check if monitoring remote working directories is allowed. | |
29 | * | |
30 | * By default, monitoring remote working directories is | |
31 | * disabled. Users may override this behavior in enviroments where | |
32 | * they have proper support. | |
33 | */ | |
34 | static int check_config_allowremote(struct repository *r) | |
35 | { | |
36 | int allow; | |
37 | ||
38 | if (!repo_config_get_bool(r, "fsmonitor.allowremote", &allow)) | |
39 | return allow; | |
40 | ||
41 | return -1; /* fsmonitor.allowremote not set */ | |
42 | } | |
43 | ||
44 | /* | |
45 | * Check remote working directory protocol. | |
46 | * | |
47 | * Error if client machine cannot get remote protocol information. | |
48 | */ | |
49 | static int check_remote_protocol(wchar_t *wpath) | |
50 | { | |
51 | HANDLE h; | |
52 | FILE_REMOTE_PROTOCOL_INFO proto_info; | |
53 | ||
54 | h = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, | |
55 | FILE_FLAG_BACKUP_SEMANTICS, NULL); | |
56 | ||
57 | if (h == INVALID_HANDLE_VALUE) { | |
58 | error(_("[GLE %ld] unable to open for read '%ls'"), | |
59 | GetLastError(), wpath); | |
60 | return -1; | |
61 | } | |
62 | ||
63 | if (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo, | |
64 | &proto_info, sizeof(proto_info))) { | |
65 | error(_("[GLE %ld] unable to get protocol information for '%ls'"), | |
66 | GetLastError(), wpath); | |
67 | CloseHandle(h); | |
68 | return -1; | |
69 | } | |
70 | ||
71 | CloseHandle(h); | |
72 | ||
73 | trace_printf_key(&trace_fsmonitor, | |
74 | "check_remote_protocol('%ls') remote protocol %#8.8lx", | |
75 | wpath, proto_info.Protocol); | |
76 | ||
77 | return 0; | |
78 | } | |
79 | ||
d989b266 JH |
80 | /* |
81 | * Remote working directories are problematic for FSMonitor. | |
82 | * | |
83 | * The underlying file system on the server machine and/or the remote | |
84 | * mount type dictates whether notification events are available at | |
85 | * all to remote client machines. | |
86 | * | |
87 | * Kernel differences between the server and client machines also | |
88 | * dictate the how (buffering, frequency, de-dup) the events are | |
89 | * delivered to client machine processes. | |
90 | * | |
91 | * A client machine (such as a laptop) may choose to suspend/resume | |
92 | * and it is unclear (without lots of testing) whether the watcher can | |
93 | * resync after a resume. We might be able to treat this as a normal | |
94 | * "events were dropped by the kernel" event and do our normal "flush | |
95 | * and resync" --or-- we might need to close the existing (zombie?) | |
96 | * notification fd and create a new one. | |
97 | * | |
98 | * In theory, the above issues need to be addressed whether we are | |
99 | * using the Hook or IPC API. | |
100 | * | |
101 | * So (for now at least), mark remote working directories as | |
102 | * incompatible. | |
103 | * | |
104 | * Notes for testing: | |
105 | * | |
106 | * (a) Windows allows a network share to be mapped to a drive letter. | |
107 | * (This is the normal method to access it.) | |
108 | * | |
109 | * $ NET USE Z: \\server\share | |
110 | * $ git -C Z:/repo status | |
111 | * | |
112 | * (b) Windows allows a network share to be referenced WITHOUT mapping | |
113 | * it to drive letter. | |
114 | * | |
115 | * $ NET USE \\server\share\dir | |
116 | * $ git -C //server/share/repo status | |
117 | * | |
118 | * (c) Windows allows "SUBST" to create a fake drive mapping to an | |
119 | * arbitrary path (which may be remote) | |
120 | * | |
121 | * $ SUBST Q: Z:\repo | |
122 | * $ git -C Q:/ status | |
123 | * | |
124 | * (d) Windows allows a directory symlink to be created on a local | |
125 | * file system that points to a remote repo. | |
126 | * | |
127 | * $ mklink /d ./link //server/share/repo | |
128 | * $ git -C ./link status | |
129 | */ | |
130 | static enum fsmonitor_reason check_remote(struct repository *r) | |
131 | { | |
85dc0da6 | 132 | int ret; |
d989b266 JH |
133 | wchar_t wpath[MAX_PATH]; |
134 | wchar_t wfullpath[MAX_PATH]; | |
135 | size_t wlen; | |
136 | UINT driveType; | |
137 | ||
138 | /* | |
139 | * Do everything in wide chars because the drive letter might be | |
140 | * a multi-byte sequence. See win32_has_dos_drive_prefix(). | |
141 | */ | |
142 | if (xutftowcs_path(wpath, r->worktree) < 0) | |
143 | return FSMONITOR_REASON_ERROR; | |
144 | ||
145 | /* | |
146 | * GetDriveTypeW() requires a final slash. We assume that the | |
147 | * worktree pathname points to an actual directory. | |
148 | */ | |
149 | wlen = wcslen(wpath); | |
150 | if (wpath[wlen - 1] != L'\\' && wpath[wlen - 1] != L'/') { | |
151 | wpath[wlen++] = L'\\'; | |
152 | wpath[wlen] = 0; | |
153 | } | |
154 | ||
155 | /* | |
156 | * Normalize the path. If nothing else, this converts forward | |
157 | * slashes to backslashes. This is essential to get GetDriveTypeW() | |
158 | * correctly handle some UNC "\\server\share\..." paths. | |
159 | */ | |
160 | if (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) | |
161 | return FSMONITOR_REASON_ERROR; | |
162 | ||
163 | driveType = GetDriveTypeW(wfullpath); | |
164 | trace_printf_key(&trace_fsmonitor, | |
165 | "DriveType '%s' L'%ls' (%u)", | |
166 | r->worktree, wfullpath, driveType); | |
167 | ||
168 | if (driveType == DRIVE_REMOTE) { | |
169 | trace_printf_key(&trace_fsmonitor, | |
170 | "check_remote('%s') true", | |
171 | r->worktree); | |
85dc0da6 ED |
172 | |
173 | ret = check_remote_protocol(wfullpath); | |
174 | if (ret < 0) | |
175 | return FSMONITOR_REASON_ERROR; | |
176 | ||
177 | switch (check_config_allowremote(r)) { | |
178 | case 0: /* config overrides and disables */ | |
179 | return FSMONITOR_REASON_REMOTE; | |
180 | case 1: /* config overrides and enables */ | |
181 | return FSMONITOR_REASON_OK; | |
182 | default: | |
183 | break; /* config has no opinion */ | |
184 | } | |
185 | ||
d989b266 JH |
186 | return FSMONITOR_REASON_REMOTE; |
187 | } | |
188 | ||
189 | return FSMONITOR_REASON_OK; | |
190 | } | |
191 | ||
d33c804d JH |
192 | enum fsmonitor_reason fsm_os__incompatible(struct repository *r) |
193 | { | |
5c58fbd2 JH |
194 | enum fsmonitor_reason reason; |
195 | ||
196 | reason = check_vfs4git(r); | |
197 | if (reason != FSMONITOR_REASON_OK) | |
198 | return reason; | |
199 | ||
d989b266 JH |
200 | reason = check_remote(r); |
201 | if (reason != FSMONITOR_REASON_OK) | |
202 | return reason; | |
203 | ||
d33c804d JH |
204 | return FSMONITOR_REASON_OK; |
205 | } |