git
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
24struct one_watch25{
26char buffer[MAX_RDCW_BUF];27DWORD buf_len;28DWORD count;29
30struct strbuf path;31wchar_t wpath_longname[MAX_PATH + 1];32DWORD wpath_longname_len;33
34HANDLE hDir;35HANDLE hEvent;36OVERLAPPED overlapped;37
38/*39* Is there an active ReadDirectoryChangesW() call pending. If so, we
40* need to later call GetOverlappedResult() and possibly CancelIoEx().
41*/
42BOOL 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*/
55BOOL has_shortnames;56BOOL has_tilde;57wchar_t dotgit_shortname[16]; /* for 8.3 name */58};59
60struct fsm_listen_data61{
62struct one_watch *watch_worktree;63struct one_watch *watch_gitdir;64
65HANDLE hEventShutdown;66
67HANDLE hListener[3]; /* we don't own these handles */68#define LISTENER_SHUTDOWN 069#define LISTENER_HAVE_DATA_WORKTREE 170#define LISTENER_HAVE_DATA_GITDIR 271int 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*/
79static int normalize_path_in_utf8(wchar_t *wpath, DWORD wpath_len,80struct strbuf *normalized_path)81{
82int reserve;83int len = 0;84
85strbuf_reset(normalized_path);86if (!wpath_len)87goto 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*/
95reserve = 2 * wpath_len + 1;96strbuf_grow(normalized_path, reserve);97
98for (;;) {99len = WideCharToMultiByte(CP_UTF8, 0,100wpath, wpath_len,101normalized_path->buf,102strbuf_avail(normalized_path) - 1,103NULL, NULL);104if (len > 0)105goto normalize;106if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {107error(_("[GLE %ld] could not convert path to UTF-8: '%.*ls'"),108GetLastError(), (int)wpath_len, wpath);109return -1;110}111
112strbuf_grow(normalized_path,113strbuf_avail(normalized_path) + reserve);114}115
116normalize:117strbuf_setlen(normalized_path, len);118return 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*/
132static void check_for_shortnames(struct one_watch *watch)133{
134wchar_t buf_in[MAX_PATH + 1];135wchar_t buf_out[MAX_PATH + 1];136wchar_t *last;137wchar_t *p;138
139/* build L"<wt-root-path>/.git" */140swprintf(buf_in, ARRAY_SIZE(buf_in) - 1, L"%ls.git",141watch->wpath_longname);142
143if (!GetShortPathNameW(buf_in, buf_out, ARRAY_SIZE(buf_out)))144return;145
146/*147* Get the final filename component of the shortpath.
148* We know that the path does not have a final slash.
149*/
150for (last = p = buf_out; *p; p++)151if (*p == L'/' || *p == '\\')152last = p + 1;153
154if (!wcscmp(last, L".git"))155return;156
157watch->has_shortnames = 1;158wcsncpy(watch->dotgit_shortname, last,159ARRAY_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*/
173if (wcschr(watch->dotgit_shortname, L'~'))174watch->has_tilde = 1;175}
176
177enum get_relative_result {178GRR_NO_CONVERSION_NEEDED,179GRR_HAVE_CONVERSION,180GRR_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*/
195static enum get_relative_result get_relative_longname(196struct one_watch *watch,197const wchar_t *wpath, DWORD wpath_len,198wchar_t *wpath_longname, size_t bufsize_wpath_longname)199{
200wchar_t buf_in[2 * MAX_PATH + 1];201wchar_t buf_out[MAX_PATH + 1];202DWORD root_len;203DWORD 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*/
210root_len = watch->wpath_longname_len;211if (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*/
217return GRR_SHUTDOWN;218}219wcsncpy(buf_in, watch->wpath_longname, root_len);220wcsncpy(buf_in + root_len, wpath, wpath_len);221buf_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*/
228out_len = GetLongPathNameW(buf_in, buf_out, ARRAY_SIZE(buf_out));229if (!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*/
249return GRR_NO_CONVERSION_NEEDED;250}251
252if (!wcscmp(buf_in, buf_out)) {253/*254* The path does not have a shortname alias.
255*/
256return GRR_NO_CONVERSION_NEEDED;257}258
259if (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*/
270return GRR_SHUTDOWN;271}272
273if (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*/
279return GRR_SHUTDOWN;280}281
282/* Return the worktree root-relative portion of the longname. */283
284wcscpy(wpath_longname, buf_out + root_len);285return GRR_HAVE_CONVERSION;286}
287
288void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)289{
290SetEvent(state->listen_data->hListener[LISTENER_SHUTDOWN]);291}
292
293static struct one_watch *create_watch(const char *path)294{
295struct one_watch *watch = NULL;296DWORD desired_access = FILE_LIST_DIRECTORY;297DWORD share_mode =298FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;299HANDLE hDir;300DWORD len_longname;301wchar_t wpath[MAX_PATH + 1];302wchar_t wpath_longname[MAX_PATH + 1];303
304if (xutftowcs_path(wpath, path) < 0) {305error(_("could not convert to wide characters: '%s'"), path);306return NULL;307}308
309hDir = CreateFileW(wpath,310desired_access, share_mode, NULL, OPEN_EXISTING,311FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,312NULL);313if (hDir == INVALID_HANDLE_VALUE) {314error(_("[GLE %ld] could not watch '%s'"),315GetLastError(), path);316return NULL;317}318
319len_longname = GetLongPathNameW(wpath, wpath_longname,320ARRAY_SIZE(wpath_longname));321if (!len_longname) {322error(_("[GLE %ld] could not get longname of '%s'"),323GetLastError(), path);324CloseHandle(hDir);325return NULL;326}327
328if (wpath_longname[len_longname - 1] != L'/' &&329wpath_longname[len_longname - 1] != L'\\') {330wpath_longname[len_longname++] = L'/';331wpath_longname[len_longname] = 0;332}333
334CALLOC_ARRAY(watch, 1);335
336watch->buf_len = sizeof(watch->buffer); /* assume full MAX_RDCW_BUF */337
338strbuf_init(&watch->path, 0);339strbuf_addstr(&watch->path, path);340
341wcscpy(watch->wpath_longname, wpath_longname);342watch->wpath_longname_len = len_longname;343
344watch->hDir = hDir;345watch->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);346
347return watch;348}
349
350static void destroy_watch(struct one_watch *watch)351{
352if (!watch)353return;354
355strbuf_release(&watch->path);356if (watch->hDir != INVALID_HANDLE_VALUE)357CloseHandle(watch->hDir);358if (watch->hEvent != INVALID_HANDLE_VALUE)359CloseHandle(watch->hEvent);360
361free(watch);362}
363
364static int start_rdcw_watch(struct one_watch *watch)365{
366DWORD dwNotifyFilter =367FILE_NOTIFY_CHANGE_FILE_NAME |368FILE_NOTIFY_CHANGE_DIR_NAME |369FILE_NOTIFY_CHANGE_ATTRIBUTES |370FILE_NOTIFY_CHANGE_SIZE |371FILE_NOTIFY_CHANGE_LAST_WRITE |372FILE_NOTIFY_CHANGE_CREATION;373
374ResetEvent(watch->hEvent);375
376memset(&watch->overlapped, 0, sizeof(watch->overlapped));377watch->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*/
386watch->is_active = ReadDirectoryChangesW(387watch->hDir, watch->buffer, watch->buf_len, TRUE,388dwNotifyFilter, &watch->count, &watch->overlapped, NULL);389
390if (watch->is_active)391return 0;392
393error(_("ReadDirectoryChangedW failed on '%s' [GLE %ld]"),394watch->path.buf, GetLastError());395return -1;396}
397
398static int recv_rdcw_watch(struct one_watch *watch)399{
400DWORD gle;401
402watch->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*/
408if (GetOverlappedResult(watch->hDir, &watch->overlapped, &watch->count,409TRUE))410return 0;411
412gle = GetLastError();413if (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*/
422watch->buf_len > MAX_RDCW_BUF_FALLBACK) {423watch->buf_len = MAX_RDCW_BUF_FALLBACK;424return -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
446error(_("GetOverlappedResult failed on '%s' [GLE %ld]"),447watch->path.buf, gle);448return -1;449}
450
451static void cancel_rdcw_watch(struct one_watch *watch)452{
453DWORD count;454
455if (!watch || !watch->is_active)456return;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*/
471CancelIoEx(watch->hDir, &watch->overlapped);472GetOverlappedResult(watch->hDir, &watch->overlapped, &count, TRUE);473watch->is_active = FALSE;474}
475
476/*
477* Process a single relative pathname event.
478* Return 1 if we should shutdown.
479*/
480static int process_1_worktree_event(481struct string_list *cookie_list,482struct fsmonitor_batch **batch,483const struct strbuf *path,484enum fsmonitor_path_type t,485DWORD info_action)486{
487const char *slash;488
489switch (t) {490case IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX:491/* special case cookie files within .git */492
493/* Use just the filename of the cookie file. */494slash = find_last_dir_sep(path->buf);495string_list_append(cookie_list,496slash ? slash + 1 : path->buf);497break;498
499case IS_INSIDE_DOT_GIT:500/* ignore everything inside of "<worktree>/.git/" */501break;502
503case IS_DOT_GIT:504/* "<worktree>/.git" was deleted (or renamed away) */505if ((info_action == FILE_ACTION_REMOVED) ||506(info_action == FILE_ACTION_RENAMED_OLD_NAME)) {507trace2_data_string("fsmonitor", NULL,508"fsm-listen/dotgit",509"removed");510return 1;511}512break;513
514case IS_WORKDIR_PATH:515/* queue normal pathname */516if (!*batch)517*batch = fsmonitor_batch__new();518fsmonitor_batch__add_path(*batch, path->buf);519break;520
521case IS_GITDIR:522case IS_INSIDE_GITDIR:523case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX:524default:525BUG("unexpected path classification '%d' for '%s'",526t, path->buf);527}528
529return 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*/
540static int process_worktree_events(struct fsmonitor_daemon_state *state)541{
542struct fsm_listen_data *data = state->listen_data;543struct one_watch *watch = data->watch_worktree;544struct strbuf path = STRBUF_INIT;545struct string_list cookie_list = STRING_LIST_INIT_DUP;546struct fsmonitor_batch *batch = NULL;547const char *p = watch->buffer;548wchar_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*/
567if (!watch->count) {568trace2_data_string("fsmonitor", NULL, "fsm-listen/kernel",569"overflow");570fsmonitor_force_resync(state);571return 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*/
579for (;;) {580FILE_NOTIFY_INFORMATION *info = (void *)p;581wchar_t *wpath = info->FileName;582DWORD wpath_len = info->FileNameLength / sizeof(WCHAR);583enum fsmonitor_path_type t;584enum get_relative_result grr;585
586if (watch->has_shortnames) {587if (!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*/
598strbuf_reset(&path);599strbuf_addstr(&path, ".git");600t = IS_DOT_GIT;601goto process_it;602}603
604if (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*/
610goto normalize_it;611}612
613grr = get_relative_longname(watch, wpath, wpath_len,614wpath_longname,615ARRAY_SIZE(wpath_longname));616switch (grr) {617case GRR_NO_CONVERSION_NEEDED: /* use info buffer as is */618break;619case GRR_HAVE_CONVERSION:620wpath = wpath_longname;621wpath_len = wcslen(wpath);622break;623default:624case GRR_SHUTDOWN:625goto force_shutdown;626}627}628
629normalize_it:630if (normalize_path_in_utf8(wpath, wpath_len, &path) == -1)631goto skip_this_path;632
633t = fsmonitor_classify_path_workdir_relative(path.buf);634
635process_it:636if (process_1_worktree_event(&cookie_list, &batch, &path, t,637info->Action))638goto force_shutdown;639
640skip_this_path:641if (!info->NextEntryOffset)642break;643p += info->NextEntryOffset;644}645
646fsmonitor_publish(state, batch, &cookie_list);647batch = NULL;648string_list_clear(&cookie_list, 0);649strbuf_release(&path);650return LISTENER_HAVE_DATA_WORKTREE;651
652force_shutdown:653fsmonitor_batch__free_list(batch);654string_list_clear(&cookie_list, 0);655strbuf_release(&path);656return 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*/
671static int process_gitdir_events(struct fsmonitor_daemon_state *state)672{
673struct fsm_listen_data *data = state->listen_data;674struct one_watch *watch = data->watch_gitdir;675struct strbuf path = STRBUF_INIT;676struct string_list cookie_list = STRING_LIST_INIT_DUP;677const char *p = watch->buffer;678
679if (!watch->count) {680trace2_data_string("fsmonitor", NULL, "fsm-listen/kernel",681"overflow");682fsmonitor_force_resync(state);683return LISTENER_HAVE_DATA_GITDIR;684}685
686for (;;) {687FILE_NOTIFY_INFORMATION *info = (void *)p;688const char *slash;689enum fsmonitor_path_type t;690
691if (normalize_path_in_utf8(692info->FileName,693info->FileNameLength / sizeof(WCHAR),694&path) == -1)695goto skip_this_path;696
697t = fsmonitor_classify_path_gitdir_relative(path.buf);698
699switch (t) {700case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX:701/* special case cookie files within gitdir */702
703/* Use just the filename of the cookie file. */704slash = find_last_dir_sep(path.buf);705string_list_append(&cookie_list,706slash ? slash + 1 : path.buf);707break;708
709case IS_INSIDE_GITDIR:710goto skip_this_path;711
712default:713BUG("unexpected path classification '%d' for '%s'",714t, path.buf);715}716
717skip_this_path:718if (!info->NextEntryOffset)719break;720p += info->NextEntryOffset;721}722
723fsmonitor_publish(state, NULL, &cookie_list);724string_list_clear(&cookie_list, 0);725strbuf_release(&path);726return LISTENER_HAVE_DATA_GITDIR;727}
728
729void fsm_listen__loop(struct fsmonitor_daemon_state *state)730{
731struct fsm_listen_data *data = state->listen_data;732DWORD dwWait;733int result;734
735state->listen_error_code = 0;736
737if (start_rdcw_watch(data->watch_worktree) == -1)738goto force_error_stop;739
740if (data->watch_gitdir &&741start_rdcw_watch(data->watch_gitdir) == -1)742goto force_error_stop;743
744for (;;) {745dwWait = WaitForMultipleObjects(data->nr_listener_handles,746data->hListener,747FALSE, INFINITE);748
749if (dwWait == WAIT_OBJECT_0 + LISTENER_HAVE_DATA_WORKTREE) {750result = recv_rdcw_watch(data->watch_worktree);751if (result == -1) {752/* hard error */753goto force_error_stop;754}755if (result == -2) {756/* retryable error */757if (start_rdcw_watch(data->watch_worktree) == -1)758goto force_error_stop;759continue;760}761
762/* have data */763if (process_worktree_events(state) == LISTENER_SHUTDOWN)764goto force_shutdown;765if (start_rdcw_watch(data->watch_worktree) == -1)766goto force_error_stop;767continue;768}769
770if (dwWait == WAIT_OBJECT_0 + LISTENER_HAVE_DATA_GITDIR) {771result = recv_rdcw_watch(data->watch_gitdir);772if (result == -1) {773/* hard error */774goto force_error_stop;775}776if (result == -2) {777/* retryable error */778if (start_rdcw_watch(data->watch_gitdir) == -1)779goto force_error_stop;780continue;781}782
783/* have data */784if (process_gitdir_events(state) == LISTENER_SHUTDOWN)785goto force_shutdown;786if (start_rdcw_watch(data->watch_gitdir) == -1)787goto force_error_stop;788continue;789}790
791if (dwWait == WAIT_OBJECT_0 + LISTENER_SHUTDOWN)792goto clean_shutdown;793
794error(_("could not read directory changes [GLE %ld]"),795GetLastError());796goto force_error_stop;797}798
799force_error_stop:800state->listen_error_code = -1;801
802force_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*/
808ipc_server_stop_async(state->ipc_server_data);809
810clean_shutdown:811cancel_rdcw_watch(data->watch_worktree);812cancel_rdcw_watch(data->watch_gitdir);813}
814
815int fsm_listen__ctor(struct fsmonitor_daemon_state *state)816{
817struct fsm_listen_data *data;818
819CALLOC_ARRAY(data, 1);820
821data->hEventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);822
823data->watch_worktree = create_watch(state->path_worktree_watch.buf);824if (!data->watch_worktree)825goto failed;826
827check_for_shortnames(data->watch_worktree);828
829if (state->nr_paths_watching > 1) {830data->watch_gitdir = create_watch(state->path_gitdir_watch.buf);831if (!data->watch_gitdir)832goto failed;833}834
835data->hListener[LISTENER_SHUTDOWN] = data->hEventShutdown;836data->nr_listener_handles++;837
838data->hListener[LISTENER_HAVE_DATA_WORKTREE] =839data->watch_worktree->hEvent;840data->nr_listener_handles++;841
842if (data->watch_gitdir) {843data->hListener[LISTENER_HAVE_DATA_GITDIR] =844data->watch_gitdir->hEvent;845data->nr_listener_handles++;846}847
848state->listen_data = data;849return 0;850
851failed:852CloseHandle(data->hEventShutdown);853destroy_watch(data->watch_worktree);854destroy_watch(data->watch_gitdir);855
856return -1;857}
858
859void fsm_listen__dtor(struct fsmonitor_daemon_state *state)860{
861struct fsm_listen_data *data;862
863if (!state || !state->listen_data)864return;865
866data = state->listen_data;867
868CloseHandle(data->hEventShutdown);869destroy_watch(data->watch_worktree);870destroy_watch(data->watch_gitdir);871
872FREE_AND_NULL(state->listen_data);873}
874