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