Merge branch 'jk/fsmonitor-event-listener-race-fix' into maint-2.47
[git/gitster.git] / compat / fsmonitor / fsm-path-utils-darwin.c
blob049f97eaaf249a0206c934d8b7ae1493115246d4
1 #include "git-compat-util.h"
2 #include "fsmonitor-ll.h"
3 #include "fsmonitor-path-utils.h"
4 #include "gettext.h"
5 #include "trace.h"
6 #include <dirent.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <sys/param.h>
10 #include <sys/mount.h>
12 int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
14 struct statfs fs;
15 if (statfs(path, &fs) == -1) {
16 int saved_errno = errno;
17 trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s",
18 path, strerror(saved_errno));
19 errno = saved_errno;
20 return -1;
23 trace_printf_key(&trace_fsmonitor,
24 "statfs('%s') [type 0x%08x][flags 0x%08x] '%s'",
25 path, fs.f_type, fs.f_flags, fs.f_fstypename);
27 if (!(fs.f_flags & MNT_LOCAL))
28 fs_info->is_remote = 1;
29 else
30 fs_info->is_remote = 0;
32 fs_info->typename = xstrdup(fs.f_fstypename);
34 trace_printf_key(&trace_fsmonitor,
35 "'%s' is_remote: %d",
36 path, fs_info->is_remote);
37 return 0;
40 int fsmonitor__is_fs_remote(const char *path)
42 struct fs_info fs;
43 if (fsmonitor__get_fs_info(path, &fs))
44 return -1;
46 free(fs.typename);
48 return fs.is_remote;
52 * Scan the root directory for synthetic firmlinks that when resolved
53 * are a prefix of the path, stopping at the first one found.
55 * Some information about firmlinks and synthetic firmlinks:
56 * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/
58 * macOS no longer allows symlinks in the root directory; any link found
59 * there is therefore a synthetic firmlink.
61 * If this function gets called often, will want to cache all the firmlink
62 * information, but for now there is only one caller of this function.
64 * If there is more than one alias for the path, that is another
65 * matter altogether.
67 int fsmonitor__get_alias(const char *path, struct alias_info *info)
69 DIR *dir;
70 int retval = -1;
71 const char *const root = "/";
72 struct stat st;
73 struct dirent *de;
74 struct strbuf alias;
75 struct strbuf points_to = STRBUF_INIT;
77 dir = opendir(root);
78 if (!dir)
79 return error_errno(_("opendir('%s') failed"), root);
81 strbuf_init(&alias, 256);
83 while ((de = readdir(dir)) != NULL) {
84 strbuf_reset(&alias);
85 strbuf_addf(&alias, "%s%s", root, de->d_name);
87 if (lstat(alias.buf, &st) < 0) {
88 error_errno(_("lstat('%s') failed"), alias.buf);
89 goto done;
92 if (!S_ISLNK(st.st_mode))
93 continue;
95 if (strbuf_readlink(&points_to, alias.buf, st.st_size) < 0) {
96 error_errno(_("strbuf_readlink('%s') failed"), alias.buf);
97 goto done;
100 if (!strncmp(points_to.buf, path, points_to.len) &&
101 (path[points_to.len] == '/')) {
102 strbuf_addbuf(&info->alias, &alias);
103 strbuf_addbuf(&info->points_to, &points_to);
104 trace_printf_key(&trace_fsmonitor,
105 "Found alias for '%s' : '%s' -> '%s'",
106 path, info->alias.buf, info->points_to.buf);
107 retval = 0;
108 goto done;
111 retval = 0; /* no alias */
113 done:
114 strbuf_release(&alias);
115 strbuf_release(&points_to);
116 if (closedir(dir) < 0)
117 return error_errno(_("closedir('%s') failed"), root);
118 return retval;
121 char *fsmonitor__resolve_alias(const char *path,
122 const struct alias_info *info)
124 if (!info->alias.len)
125 return NULL;
127 if ((!strncmp(info->alias.buf, path, info->alias.len))
128 && path[info->alias.len] == '/') {
129 struct strbuf tmp = STRBUF_INIT;
130 const char *remainder = path + info->alias.len;
132 strbuf_addbuf(&tmp, &info->points_to);
133 strbuf_add(&tmp, remainder, strlen(remainder));
134 return strbuf_detach(&tmp, NULL);
137 return NULL;