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