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