]> git.ipfire.org Git - thirdparty/git.git/blame - compat/fsmonitor/fsm-listen-win32.c
Merge branch 'mp/rebase-label-length-limit' into maint-2.42
[thirdparty/git.git] / compat / fsmonitor / fsm-listen-win32.c
CommitLineData
bc5c5ec0 1#include "git-compat-util.h"
62c73671 2#include "config.h"
68d68646 3#include "fsmonitor-ll.h"
62c73671 4#include "fsm-listen.h"
1448edfb 5#include "fsmonitor--daemon.h"
f394e093 6#include "gettext.h"
74ea5c95 7#include "trace2.h"
1448edfb
JH
8
9/*
10 * The documentation of ReadDirectoryChangesW() states that the maximum
11 * buffer size is 64K when the monitored directory is remote.
12 *
13 * Larger buffers may be used when the monitored directory is local and
14 * will help us receive events faster from the kernel and avoid dropped
15 * events.
16 *
17 * So we try to use a very large buffer and silently fallback to 64K if
18 * we get an error.
19 */
20#define MAX_RDCW_BUF_FALLBACK (65536)
21#define MAX_RDCW_BUF (65536 * 8)
22
23struct one_watch
24{
25 char buffer[MAX_RDCW_BUF];
26 DWORD buf_len;
27 DWORD count;
28
29 struct strbuf path;
40f865dc
JH
30 wchar_t wpath_longname[MAX_PATH + 1];
31 DWORD wpath_longname_len;
32
1448edfb
JH
33 HANDLE hDir;
34 HANDLE hEvent;
35 OVERLAPPED overlapped;
36
37 /*
38 * Is there an active ReadDirectoryChangesW() call pending. If so, we
39 * need to later call GetOverlappedResult() and possibly CancelIoEx().
40 */
41 BOOL is_active;
40f865dc
JH
42
43 /*
44 * Are shortnames enabled on the containing drive? This is
45 * always true for "C:/" drives and usually never true for
46 * other drives.
47 *
48 * We only set this for the worktree because we only need to
49 * convert shortname paths to longname paths for items we send
50 * to clients. (We don't care about shortname expansion for
51 * paths inside a GITDIR because we never send them to
52 * clients.)
53 */
54 BOOL has_shortnames;
55 BOOL has_tilde;
56 wchar_t dotgit_shortname[16]; /* for 8.3 name */
1448edfb
JH
57};
58
207534e4 59struct fsm_listen_data
1448edfb
JH
60{
61 struct one_watch *watch_worktree;
62 struct one_watch *watch_gitdir;
63
64 HANDLE hEventShutdown;
65
66 HANDLE hListener[3]; /* we don't own these handles */
67#define LISTENER_SHUTDOWN 0
68#define LISTENER_HAVE_DATA_WORKTREE 1
69#define LISTENER_HAVE_DATA_GITDIR 2
70 int nr_listener_handles;
71};
72
73/*
40f865dc
JH
74 * Convert the WCHAR path from the event into UTF8 and normalize it.
75 *
76 * `wpath_len` is in WCHARS not bytes.
1448edfb 77 */
40f865dc 78static int normalize_path_in_utf8(wchar_t *wpath, DWORD wpath_len,
1448edfb
JH
79 struct strbuf *normalized_path)
80{
81 int reserve;
82 int len = 0;
83
84 strbuf_reset(normalized_path);
40f865dc 85 if (!wpath_len)
1448edfb
JH
86 goto normalize;
87
88 /*
89 * Pre-reserve enough space in the UTF8 buffer for
90 * each Unicode WCHAR character to be mapped into a
91 * sequence of 2 UTF8 characters. That should let us
92 * avoid ERROR_INSUFFICIENT_BUFFER 99.9+% of the time.
93 */
40f865dc 94 reserve = 2 * wpath_len + 1;
1448edfb
JH
95 strbuf_grow(normalized_path, reserve);
96
97 for (;;) {
40f865dc
JH
98 len = WideCharToMultiByte(CP_UTF8, 0,
99 wpath, wpath_len,
1448edfb
JH
100 normalized_path->buf,
101 strbuf_avail(normalized_path) - 1,
102 NULL, NULL);
103 if (len > 0)
104 goto normalize;
105 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
106 error(_("[GLE %ld] could not convert path to UTF-8: '%.*ls'"),
40f865dc 107 GetLastError(), (int)wpath_len, wpath);
1448edfb
JH
108 return -1;
109 }
110
111 strbuf_grow(normalized_path,
112 strbuf_avail(normalized_path) + reserve);
113 }
114
115normalize:
116 strbuf_setlen(normalized_path, len);
117 return strbuf_normalize_path(normalized_path);
118}
62c73671 119
40f865dc
JH
120/*
121 * See if the worktree root directory has shortnames enabled.
122 * This will help us decide if we need to do an expensive shortname
123 * to longname conversion on every notification event.
124 *
125 * We do not want to create a file to test this, so we assume that the
126 * root directory contains a ".git" file or directory. (Our caller
127 * only calls us for the worktree root, so this should be fine.)
128 *
129 * Remember the spelling of the shortname for ".git" if it exists.
130 */
131static void check_for_shortnames(struct one_watch *watch)
132{
133 wchar_t buf_in[MAX_PATH + 1];
134 wchar_t buf_out[MAX_PATH + 1];
135 wchar_t *last;
136 wchar_t *p;
137
138 /* build L"<wt-root-path>/.git" */
139 swprintf(buf_in, ARRAY_SIZE(buf_in) - 1, L"%ls.git",
140 watch->wpath_longname);
141
142 if (!GetShortPathNameW(buf_in, buf_out, ARRAY_SIZE(buf_out)))
143 return;
144
145 /*
146 * Get the final filename component of the shortpath.
147 * We know that the path does not have a final slash.
148 */
149 for (last = p = buf_out; *p; p++)
150 if (*p == L'/' || *p == '\\')
151 last = p + 1;
152
153 if (!wcscmp(last, L".git"))
154 return;
155
156 watch->has_shortnames = 1;
157 wcsncpy(watch->dotgit_shortname, last,
158 ARRAY_SIZE(watch->dotgit_shortname));
159
160 /*
161 * The shortname for ".git" is usually of the form "GIT~1", so
162 * we should be able to avoid shortname to longname mapping on
163 * every notification event if the source string does not
164 * contain a "~".
165 *
166 * However, the documentation for GetLongPathNameW() says
167 * that there are filesystems that don't follow that pattern
168 * and warns against this optimization.
169 *
170 * Lets test this.
171 */
172 if (wcschr(watch->dotgit_shortname, L'~'))
173 watch->has_tilde = 1;
174}
175
176enum get_relative_result {
177 GRR_NO_CONVERSION_NEEDED,
178 GRR_HAVE_CONVERSION,
179 GRR_SHUTDOWN,
180};
181
182/*
183 * Info notification paths are relative to the root of the watch.
184 * If our CWD is still at the root, then we can use relative paths
185 * to convert from shortnames to longnames. If our process has a
186 * different CWD, then we need to construct an absolute path, do
187 * the conversion, and then return the root-relative portion.
188 *
189 * We use the longname form of the root as our basis and assume that
190 * it already has a trailing slash.
191 *
192 * `wpath_len` is in WCHARS not bytes.
193 */
194static enum get_relative_result get_relative_longname(
195 struct one_watch *watch,
196 const wchar_t *wpath, DWORD wpath_len,
197 wchar_t *wpath_longname, size_t bufsize_wpath_longname)
198{
199 wchar_t buf_in[2 * MAX_PATH + 1];
200 wchar_t buf_out[MAX_PATH + 1];
201 DWORD root_len;
202 DWORD out_len;
203
204 /*
205 * Build L"<wt-root-path>/<event-rel-path>"
206 * Note that the <event-rel-path> might not be null terminated
207 * so we avoid swprintf() constructions.
208 */
209 root_len = watch->wpath_longname_len;
210 if (root_len + wpath_len >= ARRAY_SIZE(buf_in)) {
211 /*
212 * This should not happen. We cannot append the observed
213 * relative path onto the end of the worktree root path
214 * without overflowing the buffer. Just give up.
215 */
216 return GRR_SHUTDOWN;
217 }
218 wcsncpy(buf_in, watch->wpath_longname, root_len);
219 wcsncpy(buf_in + root_len, wpath, wpath_len);
220 buf_in[root_len + wpath_len] = 0;
221
222 /*
223 * We don't actually know if the source pathname is a
224 * shortname or a longname. This Windows routine allows
225 * either to be given as input.
226 */
227 out_len = GetLongPathNameW(buf_in, buf_out, ARRAY_SIZE(buf_out));
228 if (!out_len) {
229 /*
230 * The shortname to longname conversion can fail for
231 * various reasons, for example if the file has been
232 * deleted. (That is, if we just received a
233 * delete-file notification event and the file is
234 * already gone, we can't ask the file system to
235 * lookup the longname for it. Likewise, for moves
236 * and renames where we are given the old name.)
237 *
238 * Since deleting or moving a file or directory by its
239 * shortname is rather obscure, I'm going ignore the
240 * failure and ask the caller to report the original
241 * relative path. This seems kinder than failing here
242 * and forcing a resync. Besides, forcing a resync on
243 * every file/directory delete would effectively
244 * cripple monitoring.
245 *
246 * We might revisit this in the future.
247 */
248 return GRR_NO_CONVERSION_NEEDED;
249 }
250
251 if (!wcscmp(buf_in, buf_out)) {
252 /*
253 * The path does not have a shortname alias.
254 */
255 return GRR_NO_CONVERSION_NEEDED;
256 }
257
258 if (wcsncmp(buf_in, buf_out, root_len)) {
259 /*
260 * The spelling of the root directory portion of the computed
261 * longname has changed. This should not happen. Basically,
262 * it means that we don't know where (without recomputing the
263 * longname of just the root directory) to split out the
264 * relative path. Since this should not happen, I'm just
265 * going to let this fail and force a shutdown (because all
266 * subsequent events are probably going to see the same
267 * mismatch).
268 */
269 return GRR_SHUTDOWN;
270 }
271
272 if (out_len - root_len >= bufsize_wpath_longname) {
273 /*
274 * This should not happen. We cannot copy the root-relative
275 * portion of the path into the provided buffer without an
276 * overrun. Just give up.
277 */
278 return GRR_SHUTDOWN;
279 }
280
281 /* Return the worktree root-relative portion of the longname. */
282
283 wcscpy(wpath_longname, buf_out + root_len);
284 return GRR_HAVE_CONVERSION;
285}
286
62c73671
JH
287void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
288{
207534e4 289 SetEvent(state->listen_data->hListener[LISTENER_SHUTDOWN]);
1448edfb
JH
290}
291
292static struct one_watch *create_watch(struct fsmonitor_daemon_state *state,
293 const char *path)
294{
295 struct one_watch *watch = NULL;
296 DWORD desired_access = FILE_LIST_DIRECTORY;
297 DWORD share_mode =
298 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
299 HANDLE hDir;
40f865dc
JH
300 DWORD len_longname;
301 wchar_t wpath[MAX_PATH + 1];
302 wchar_t wpath_longname[MAX_PATH + 1];
1448edfb
JH
303
304 if (xutftowcs_path(wpath, path) < 0) {
305 error(_("could not convert to wide characters: '%s'"), path);
306 return NULL;
307 }
308
309 hDir = CreateFileW(wpath,
310 desired_access, share_mode, NULL, OPEN_EXISTING,
311 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
312 NULL);
313 if (hDir == INVALID_HANDLE_VALUE) {
314 error(_("[GLE %ld] could not watch '%s'"),
315 GetLastError(), path);
316 return NULL;
317 }
318
40f865dc
JH
319 len_longname = GetLongPathNameW(wpath, wpath_longname,
320 ARRAY_SIZE(wpath_longname));
321 if (!len_longname) {
322 error(_("[GLE %ld] could not get longname of '%s'"),
323 GetLastError(), path);
324 CloseHandle(hDir);
325 return NULL;
326 }
327
328 if (wpath_longname[len_longname - 1] != L'/' &&
329 wpath_longname[len_longname - 1] != L'\\') {
330 wpath_longname[len_longname++] = L'/';
331 wpath_longname[len_longname] = 0;
332 }
333
1448edfb
JH
334 CALLOC_ARRAY(watch, 1);
335
336 watch->buf_len = sizeof(watch->buffer); /* assume full MAX_RDCW_BUF */
337
338 strbuf_init(&watch->path, 0);
339 strbuf_addstr(&watch->path, path);
340
40f865dc
JH
341 wcscpy(watch->wpath_longname, wpath_longname);
342 watch->wpath_longname_len = len_longname;
343
1448edfb
JH
344 watch->hDir = hDir;
345 watch->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
346
347 return watch;
348}
349
350static void destroy_watch(struct one_watch *watch)
351{
352 if (!watch)
353 return;
354
355 strbuf_release(&watch->path);
356 if (watch->hDir != INVALID_HANDLE_VALUE)
357 CloseHandle(watch->hDir);
358 if (watch->hEvent != INVALID_HANDLE_VALUE)
359 CloseHandle(watch->hEvent);
360
361 free(watch);
362}
363
207534e4 364static int start_rdcw_watch(struct fsm_listen_data *data,
1448edfb
JH
365 struct one_watch *watch)
366{
367 DWORD dwNotifyFilter =
368 FILE_NOTIFY_CHANGE_FILE_NAME |
369 FILE_NOTIFY_CHANGE_DIR_NAME |
370 FILE_NOTIFY_CHANGE_ATTRIBUTES |
371 FILE_NOTIFY_CHANGE_SIZE |
372 FILE_NOTIFY_CHANGE_LAST_WRITE |
373 FILE_NOTIFY_CHANGE_CREATION;
374
375 ResetEvent(watch->hEvent);
376
377 memset(&watch->overlapped, 0, sizeof(watch->overlapped));
378 watch->overlapped.hEvent = watch->hEvent;
379
380 /*
381 * Queue an async call using Overlapped IO. This returns immediately.
382 * Our event handle will be signalled when the real result is available.
383 *
384 * The return value here just means that we successfully queued it.
385 * We won't know if the Read...() actually produces data until later.
386 */
387 watch->is_active = ReadDirectoryChangesW(
388 watch->hDir, watch->buffer, watch->buf_len, TRUE,
389 dwNotifyFilter, &watch->count, &watch->overlapped, NULL);
390
391 if (watch->is_active)
392 return 0;
393
394 error(_("ReadDirectoryChangedW failed on '%s' [GLE %ld]"),
395 watch->path.buf, GetLastError());
396 return -1;
397}
398
399static int recv_rdcw_watch(struct one_watch *watch)
400{
401 DWORD gle;
402
403 watch->is_active = FALSE;
404
405 /*
406 * The overlapped result is ready. If the Read...() was successful
407 * we finally receive the actual result into our buffer.
408 */
409 if (GetOverlappedResult(watch->hDir, &watch->overlapped, &watch->count,
410 TRUE))
411 return 0;
412
413 gle = GetLastError();
414 if (gle == ERROR_INVALID_PARAMETER &&
415 /*
416 * The kernel throws an invalid parameter error when our
417 * buffer is too big and we are pointed at a remote
418 * directory (and possibly for other reasons). Quietly
419 * set it down and try again.
420 *
421 * See note about MAX_RDCW_BUF at the top.
422 */
423 watch->buf_len > MAX_RDCW_BUF_FALLBACK) {
424 watch->buf_len = MAX_RDCW_BUF_FALLBACK;
425 return -2;
426 }
427
428 /*
39664e93
JH
429 * GetOverlappedResult() fails if the watched directory is
430 * deleted while we were waiting for an overlapped IO to
431 * complete. The documentation did not list specific errors,
432 * but I observed ERROR_ACCESS_DENIED (0x05) errors during
433 * testing.
434 *
435 * Note that we only get notificaiton events for events
436 * *within* the directory, not *on* the directory itself.
437 * (These might be properies of the parent directory, for
438 * example).
439 *
440 * NEEDSWORK: We might try to check for the deleted directory
441 * case and return a better error message, but I'm not sure it
442 * is worth it.
443 *
444 * Shutdown if we get any error.
1448edfb
JH
445 */
446
447 error(_("GetOverlappedResult failed on '%s' [GLE %ld]"),
448 watch->path.buf, gle);
449 return -1;
450}
451
452static void cancel_rdcw_watch(struct one_watch *watch)
453{
454 DWORD count;
455
456 if (!watch || !watch->is_active)
457 return;
458
459 /*
460 * The calls to ReadDirectoryChangesW() and GetOverlappedResult()
461 * form a "pair" (my term) where we queue an IO and promise to
462 * hang around and wait for the kernel to give us the result.
463 *
464 * If for some reason after we queue the IO, we have to quit
465 * or otherwise not stick around for the second half, we must
466 * tell the kernel to abort the IO. This prevents the kernel
467 * from writing to our buffer and/or signalling our event
468 * after we free them.
469 *
470 * (Ask me how much fun it was to track that one down).
471 */
472 CancelIoEx(watch->hDir, &watch->overlapped);
473 GetOverlappedResult(watch->hDir, &watch->overlapped, &count, TRUE);
474 watch->is_active = FALSE;
475}
476
40f865dc
JH
477/*
478 * Process a single relative pathname event.
479 * Return 1 if we should shutdown.
480 */
481static int process_1_worktree_event(
482 struct string_list *cookie_list,
483 struct fsmonitor_batch **batch,
484 const struct strbuf *path,
485 enum fsmonitor_path_type t,
486 DWORD info_action)
487{
488 const char *slash;
489
490 switch (t) {
491 case IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX:
492 /* special case cookie files within .git */
493
494 /* Use just the filename of the cookie file. */
495 slash = find_last_dir_sep(path->buf);
496 string_list_append(cookie_list,
497 slash ? slash + 1 : path->buf);
498 break;
499
500 case IS_INSIDE_DOT_GIT:
501 /* ignore everything inside of "<worktree>/.git/" */
502 break;
503
504 case IS_DOT_GIT:
505 /* "<worktree>/.git" was deleted (or renamed away) */
506 if ((info_action == FILE_ACTION_REMOVED) ||
507 (info_action == FILE_ACTION_RENAMED_OLD_NAME)) {
508 trace2_data_string("fsmonitor", NULL,
509 "fsm-listen/dotgit",
510 "removed");
511 return 1;
512 }
513 break;
514
515 case IS_WORKDIR_PATH:
516 /* queue normal pathname */
517 if (!*batch)
518 *batch = fsmonitor_batch__new();
519 fsmonitor_batch__add_path(*batch, path->buf);
520 break;
521
522 case IS_GITDIR:
523 case IS_INSIDE_GITDIR:
524 case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX:
525 default:
526 BUG("unexpected path classification '%d' for '%s'",
527 t, path->buf);
528 }
529
530 return 0;
531}
532
1448edfb
JH
533/*
534 * Process filesystem events that happen anywhere (recursively) under the
535 * <worktree> root directory. For a normal working directory, this includes
536 * both version controlled files and the contents of the .git/ directory.
537 *
538 * If <worktree>/.git is a file, then we only see events for the file
539 * itself.
540 */
541static int process_worktree_events(struct fsmonitor_daemon_state *state)
542{
207534e4 543 struct fsm_listen_data *data = state->listen_data;
1448edfb
JH
544 struct one_watch *watch = data->watch_worktree;
545 struct strbuf path = STRBUF_INIT;
546 struct string_list cookie_list = STRING_LIST_INIT_DUP;
547 struct fsmonitor_batch *batch = NULL;
548 const char *p = watch->buffer;
40f865dc 549 wchar_t wpath_longname[MAX_PATH + 1];
1448edfb
JH
550
551 /*
552 * If the kernel gets more events than will fit in the kernel
553 * buffer associated with our RDCW handle, it drops them and
554 * returns a count of zero.
555 *
556 * Yes, the call returns WITHOUT error and with length zero.
557 * This is the documented behavior. (My testing has confirmed
558 * that it also sets the last error to ERROR_NOTIFY_ENUM_DIR,
559 * but we do not rely on that since the function did not
560 * return an error and it is not documented.)
561 *
562 * (The "overflow" case is not ambiguous with the "no data" case
563 * because we did an INFINITE wait.)
564 *
565 * This means we have a gap in coverage. Tell the daemon layer
566 * to resync.
567 */
568 if (!watch->count) {
569 trace2_data_string("fsmonitor", NULL, "fsm-listen/kernel",
570 "overflow");
571 fsmonitor_force_resync(state);
572 return LISTENER_HAVE_DATA_WORKTREE;
573 }
574
575 /*
576 * On Windows, `info` contains an "array" of paths that are
577 * relative to the root of whichever directory handle received
578 * the event.
579 */
580 for (;;) {
581 FILE_NOTIFY_INFORMATION *info = (void *)p;
40f865dc
JH
582 wchar_t *wpath = info->FileName;
583 DWORD wpath_len = info->FileNameLength / sizeof(WCHAR);
1448edfb 584 enum fsmonitor_path_type t;
40f865dc
JH
585 enum get_relative_result grr;
586
587 if (watch->has_shortnames) {
588 if (!wcscmp(wpath, watch->dotgit_shortname)) {
589 /*
590 * This event exactly matches the
591 * spelling of the shortname of
592 * ".git", so we can skip some steps.
593 *
594 * (This case is odd because the user
595 * can "rm -rf GIT~1" and we cannot
596 * use the filesystem to map it back
597 * to ".git".)
598 */
599 strbuf_reset(&path);
600 strbuf_addstr(&path, ".git");
601 t = IS_DOT_GIT;
602 goto process_it;
603 }
1448edfb 604
40f865dc
JH
605 if (watch->has_tilde && !wcschr(wpath, L'~')) {
606 /*
607 * Shortnames on this filesystem have tildes
608 * and the notification path does not have
609 * one, so we assume that it is a longname.
610 */
611 goto normalize_it;
612 }
1448edfb 613
40f865dc
JH
614 grr = get_relative_longname(watch, wpath, wpath_len,
615 wpath_longname,
616 ARRAY_SIZE(wpath_longname));
617 switch (grr) {
618 case GRR_NO_CONVERSION_NEEDED: /* use info buffer as is */
619 break;
620 case GRR_HAVE_CONVERSION:
621 wpath = wpath_longname;
622 wpath_len = wcslen(wpath);
623 break;
624 default:
625 case GRR_SHUTDOWN:
1448edfb
JH
626 goto force_shutdown;
627 }
40f865dc 628 }
1448edfb 629
40f865dc
JH
630normalize_it:
631 if (normalize_path_in_utf8(wpath, wpath_len, &path) == -1)
632 goto skip_this_path;
1448edfb 633
40f865dc
JH
634 t = fsmonitor_classify_path_workdir_relative(path.buf);
635
636process_it:
637 if (process_1_worktree_event(&cookie_list, &batch, &path, t,
638 info->Action))
639 goto force_shutdown;
1448edfb
JH
640
641skip_this_path:
642 if (!info->NextEntryOffset)
643 break;
644 p += info->NextEntryOffset;
645 }
646
647 fsmonitor_publish(state, batch, &cookie_list);
648 batch = NULL;
649 string_list_clear(&cookie_list, 0);
650 strbuf_release(&path);
651 return LISTENER_HAVE_DATA_WORKTREE;
652
653force_shutdown:
654 fsmonitor_batch__free_list(batch);
655 string_list_clear(&cookie_list, 0);
656 strbuf_release(&path);
657 return LISTENER_SHUTDOWN;
658}
659
660/*
661 * Process filesystem events that happened anywhere (recursively) under the
662 * external <gitdir> (such as non-primary worktrees or submodules).
663 * We only care about cookie files that our client threads created here.
664 *
665 * Note that we DO NOT get filesystem events on the external <gitdir>
666 * itself (it is not inside something that we are watching). In particular,
667 * we do not get an event if the external <gitdir> is deleted.
40f865dc
JH
668 *
669 * Also, we do not care about shortnames within the external <gitdir>, since
670 * we never send these paths to clients.
1448edfb
JH
671 */
672static int process_gitdir_events(struct fsmonitor_daemon_state *state)
673{
207534e4 674 struct fsm_listen_data *data = state->listen_data;
1448edfb
JH
675 struct one_watch *watch = data->watch_gitdir;
676 struct strbuf path = STRBUF_INIT;
677 struct string_list cookie_list = STRING_LIST_INIT_DUP;
678 const char *p = watch->buffer;
679
680 if (!watch->count) {
681 trace2_data_string("fsmonitor", NULL, "fsm-listen/kernel",
682 "overflow");
683 fsmonitor_force_resync(state);
684 return LISTENER_HAVE_DATA_GITDIR;
685 }
686
687 for (;;) {
688 FILE_NOTIFY_INFORMATION *info = (void *)p;
689 const char *slash;
690 enum fsmonitor_path_type t;
691
40f865dc
JH
692 if (normalize_path_in_utf8(
693 info->FileName,
694 info->FileNameLength / sizeof(WCHAR),
695 &path) == -1)
1448edfb
JH
696 goto skip_this_path;
697
698 t = fsmonitor_classify_path_gitdir_relative(path.buf);
699
700 switch (t) {
701 case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX:
702 /* special case cookie files within gitdir */
703
704 /* Use just the filename of the cookie file. */
705 slash = find_last_dir_sep(path.buf);
706 string_list_append(&cookie_list,
707 slash ? slash + 1 : path.buf);
708 break;
709
710 case IS_INSIDE_GITDIR:
711 goto skip_this_path;
712
713 default:
714 BUG("unexpected path classification '%d' for '%s'",
715 t, path.buf);
716 }
717
718skip_this_path:
719 if (!info->NextEntryOffset)
720 break;
721 p += info->NextEntryOffset;
722 }
723
724 fsmonitor_publish(state, NULL, &cookie_list);
725 string_list_clear(&cookie_list, 0);
726 strbuf_release(&path);
727 return LISTENER_HAVE_DATA_GITDIR;
62c73671
JH
728}
729
730void fsm_listen__loop(struct fsmonitor_daemon_state *state)
731{
207534e4 732 struct fsm_listen_data *data = state->listen_data;
1448edfb
JH
733 DWORD dwWait;
734 int result;
735
207534e4 736 state->listen_error_code = 0;
1448edfb
JH
737
738 if (start_rdcw_watch(data, data->watch_worktree) == -1)
739 goto force_error_stop;
740
741 if (data->watch_gitdir &&
742 start_rdcw_watch(data, data->watch_gitdir) == -1)
743 goto force_error_stop;
744
745 for (;;) {
746 dwWait = WaitForMultipleObjects(data->nr_listener_handles,
747 data->hListener,
748 FALSE, INFINITE);
749
750 if (dwWait == WAIT_OBJECT_0 + LISTENER_HAVE_DATA_WORKTREE) {
751 result = recv_rdcw_watch(data->watch_worktree);
752 if (result == -1) {
753 /* hard error */
754 goto force_error_stop;
755 }
756 if (result == -2) {
757 /* retryable error */
758 if (start_rdcw_watch(data, data->watch_worktree) == -1)
759 goto force_error_stop;
760 continue;
761 }
762
763 /* have data */
764 if (process_worktree_events(state) == LISTENER_SHUTDOWN)
765 goto force_shutdown;
766 if (start_rdcw_watch(data, data->watch_worktree) == -1)
767 goto force_error_stop;
768 continue;
769 }
770
771 if (dwWait == WAIT_OBJECT_0 + LISTENER_HAVE_DATA_GITDIR) {
772 result = recv_rdcw_watch(data->watch_gitdir);
773 if (result == -1) {
774 /* hard error */
775 goto force_error_stop;
776 }
777 if (result == -2) {
778 /* retryable error */
779 if (start_rdcw_watch(data, data->watch_gitdir) == -1)
780 goto force_error_stop;
781 continue;
782 }
783
784 /* have data */
785 if (process_gitdir_events(state) == LISTENER_SHUTDOWN)
786 goto force_shutdown;
787 if (start_rdcw_watch(data, data->watch_gitdir) == -1)
788 goto force_error_stop;
789 continue;
790 }
791
792 if (dwWait == WAIT_OBJECT_0 + LISTENER_SHUTDOWN)
793 goto clean_shutdown;
794
795 error(_("could not read directory changes [GLE %ld]"),
796 GetLastError());
797 goto force_error_stop;
798 }
799
800force_error_stop:
207534e4 801 state->listen_error_code = -1;
1448edfb
JH
802
803force_shutdown:
804 /*
805 * Tell the IPC thead pool to stop (which completes the await
806 * in the main thread (which will also signal this thread (if
807 * we are still alive))).
808 */
809 ipc_server_stop_async(state->ipc_server_data);
810
811clean_shutdown:
812 cancel_rdcw_watch(data->watch_worktree);
813 cancel_rdcw_watch(data->watch_gitdir);
62c73671
JH
814}
815
816int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
817{
207534e4 818 struct fsm_listen_data *data;
1448edfb
JH
819
820 CALLOC_ARRAY(data, 1);
821
822 data->hEventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
823
824 data->watch_worktree = create_watch(state,
825 state->path_worktree_watch.buf);
826 if (!data->watch_worktree)
827 goto failed;
828
40f865dc
JH
829 check_for_shortnames(data->watch_worktree);
830
1448edfb
JH
831 if (state->nr_paths_watching > 1) {
832 data->watch_gitdir = create_watch(state,
833 state->path_gitdir_watch.buf);
834 if (!data->watch_gitdir)
835 goto failed;
836 }
837
838 data->hListener[LISTENER_SHUTDOWN] = data->hEventShutdown;
839 data->nr_listener_handles++;
840
841 data->hListener[LISTENER_HAVE_DATA_WORKTREE] =
842 data->watch_worktree->hEvent;
843 data->nr_listener_handles++;
844
845 if (data->watch_gitdir) {
846 data->hListener[LISTENER_HAVE_DATA_GITDIR] =
847 data->watch_gitdir->hEvent;
848 data->nr_listener_handles++;
849 }
850
207534e4 851 state->listen_data = data;
1448edfb
JH
852 return 0;
853
854failed:
855 CloseHandle(data->hEventShutdown);
856 destroy_watch(data->watch_worktree);
857 destroy_watch(data->watch_gitdir);
858
62c73671
JH
859 return -1;
860}
861
862void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
863{
207534e4 864 struct fsm_listen_data *data;
1448edfb 865
207534e4 866 if (!state || !state->listen_data)
1448edfb
JH
867 return;
868
207534e4 869 data = state->listen_data;
1448edfb
JH
870
871 CloseHandle(data->hEventShutdown);
872 destroy_watch(data->watch_worktree);
873 destroy_watch(data->watch_gitdir);
874
207534e4 875 FREE_AND_NULL(state->listen_data);
62c73671 876}