/src/git/compat/fsmonitor/fsm-path-utils-linux.c
Line | Count | Source |
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 | | |
7 | | #include <sys/statfs.h> |
8 | | |
9 | | #ifdef HAVE_LINUX_MAGIC_H |
10 | | #include <linux/magic.h> |
11 | | #endif |
12 | | |
13 | | /* |
14 | | * Filesystem magic numbers for remote filesystems. |
15 | | * Defined here if not available in linux/magic.h. |
16 | | */ |
17 | | #ifndef CIFS_SUPER_MAGIC |
18 | 0 | #define CIFS_SUPER_MAGIC 0xff534d42 |
19 | | #endif |
20 | | #ifndef SMB_SUPER_MAGIC |
21 | | #define SMB_SUPER_MAGIC 0x517b |
22 | | #endif |
23 | | #ifndef SMB2_SUPER_MAGIC |
24 | 0 | #define SMB2_SUPER_MAGIC 0xfe534d42 |
25 | | #endif |
26 | | #ifndef NFS_SUPER_MAGIC |
27 | | #define NFS_SUPER_MAGIC 0x6969 |
28 | | #endif |
29 | | #ifndef AFS_SUPER_MAGIC |
30 | | #define AFS_SUPER_MAGIC 0x5346414f |
31 | | #endif |
32 | | #ifndef CODA_SUPER_MAGIC |
33 | | #define CODA_SUPER_MAGIC 0x73757245 |
34 | | #endif |
35 | | #ifndef FUSE_SUPER_MAGIC |
36 | 0 | #define FUSE_SUPER_MAGIC 0x65735546 |
37 | | #endif |
38 | | |
39 | | /* |
40 | | * Check if filesystem type is a remote filesystem. |
41 | | */ |
42 | | static int is_remote_fs(unsigned long f_type) |
43 | 0 | { |
44 | 0 | switch (f_type) { |
45 | 0 | case CIFS_SUPER_MAGIC: |
46 | 0 | case SMB_SUPER_MAGIC: |
47 | 0 | case SMB2_SUPER_MAGIC: |
48 | 0 | case NFS_SUPER_MAGIC: |
49 | 0 | case AFS_SUPER_MAGIC: |
50 | 0 | case CODA_SUPER_MAGIC: |
51 | 0 | case FUSE_SUPER_MAGIC: |
52 | 0 | return 1; |
53 | 0 | default: |
54 | 0 | return 0; |
55 | 0 | } |
56 | 0 | } |
57 | | |
58 | | /* |
59 | | * Map filesystem magic numbers to human-readable names as a fallback |
60 | | * when /proc/mounts is unavailable. This only covers the remote and |
61 | | * special filesystems in is_remote_fs() above; local filesystems are |
62 | | * never flagged as incompatible, so we do not need their names here. |
63 | | */ |
64 | | static const char *get_fs_typename(unsigned long f_type) |
65 | 0 | { |
66 | 0 | switch (f_type) { |
67 | 0 | case CIFS_SUPER_MAGIC: |
68 | 0 | return "cifs"; |
69 | 0 | case SMB_SUPER_MAGIC: |
70 | 0 | return "smb"; |
71 | 0 | case SMB2_SUPER_MAGIC: |
72 | 0 | return "smb2"; |
73 | 0 | case NFS_SUPER_MAGIC: |
74 | 0 | return "nfs"; |
75 | 0 | case AFS_SUPER_MAGIC: |
76 | 0 | return "afs"; |
77 | 0 | case CODA_SUPER_MAGIC: |
78 | 0 | return "coda"; |
79 | 0 | case FUSE_SUPER_MAGIC: |
80 | 0 | return "fuse"; |
81 | 0 | default: |
82 | 0 | return "unknown"; |
83 | 0 | } |
84 | 0 | } |
85 | | |
86 | | /* |
87 | | * Find the mount point for a given path by reading /proc/mounts. |
88 | | * |
89 | | * statfs(2) gives us f_type (the magic number) but not the human-readable |
90 | | * filesystem type string. We scan /proc/mounts to find the mount entry |
91 | | * whose path is the longest prefix of ours and whose f_fsid matches, |
92 | | * which gives us the fstype string (e.g. "nfs", "ext4") for logging. |
93 | | */ |
94 | | static char *find_mount(const char *path, const struct statfs *path_fs) |
95 | 0 | { |
96 | 0 | FILE *fp; |
97 | 0 | struct strbuf line = STRBUF_INIT; |
98 | 0 | struct strbuf match = STRBUF_INIT; |
99 | 0 | struct strbuf fstype = STRBUF_INIT; |
100 | 0 | char *result = NULL; |
101 | |
|
102 | 0 | fp = fopen("/proc/mounts", "r"); |
103 | 0 | if (!fp) |
104 | 0 | return NULL; |
105 | | |
106 | 0 | while (strbuf_getline(&line, fp) != EOF) { |
107 | 0 | char *fields[6]; |
108 | 0 | char *p = line.buf; |
109 | 0 | int i; |
110 | | |
111 | | /* Parse mount entry: device mountpoint fstype options dump pass */ |
112 | 0 | for (i = 0; i < 6 && p; i++) { |
113 | 0 | fields[i] = p; |
114 | 0 | p = strchr(p, ' '); |
115 | 0 | if (p) |
116 | 0 | *p++ = '\0'; |
117 | 0 | } |
118 | |
|
119 | 0 | if (i >= 3) { |
120 | 0 | const char *mountpoint = fields[1]; |
121 | 0 | const char *type = fields[2]; |
122 | 0 | struct statfs mount_fs; |
123 | | |
124 | | /* Check if this mount point is a prefix of our path */ |
125 | 0 | if (starts_with(path, mountpoint) && |
126 | 0 | (path[strlen(mountpoint)] == '/' || |
127 | 0 | path[strlen(mountpoint)] == '\0')) { |
128 | | /* Check if filesystem ID matches */ |
129 | 0 | if (statfs(mountpoint, &mount_fs) == 0 && |
130 | 0 | !memcmp(&mount_fs.f_fsid, &path_fs->f_fsid, |
131 | 0 | sizeof(mount_fs.f_fsid))) { |
132 | | /* Keep the longest matching mount point */ |
133 | 0 | if (strlen(mountpoint) > match.len) { |
134 | 0 | strbuf_reset(&match); |
135 | 0 | strbuf_addstr(&match, mountpoint); |
136 | 0 | strbuf_reset(&fstype); |
137 | 0 | strbuf_addstr(&fstype, type); |
138 | 0 | } |
139 | 0 | } |
140 | 0 | } |
141 | 0 | } |
142 | 0 | } |
143 | |
|
144 | 0 | fclose(fp); |
145 | 0 | strbuf_release(&line); |
146 | 0 | strbuf_release(&match); |
147 | |
|
148 | 0 | if (fstype.len) |
149 | 0 | result = strbuf_detach(&fstype, NULL); |
150 | 0 | else |
151 | 0 | strbuf_release(&fstype); |
152 | |
|
153 | 0 | return result; |
154 | 0 | } |
155 | | |
156 | | int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info) |
157 | 0 | { |
158 | 0 | struct statfs fs; |
159 | |
|
160 | 0 | if (statfs(path, &fs) == -1) { |
161 | 0 | int saved_errno = errno; |
162 | 0 | trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s", |
163 | 0 | path, strerror(saved_errno)); |
164 | 0 | errno = saved_errno; |
165 | 0 | return -1; |
166 | 0 | } |
167 | | |
168 | 0 | trace_printf_key(&trace_fsmonitor, |
169 | 0 | "statfs('%s') [type 0x%08lx]", |
170 | 0 | path, (unsigned long)fs.f_type); |
171 | |
|
172 | 0 | fs_info->is_remote = is_remote_fs(fs.f_type); |
173 | | |
174 | | /* |
175 | | * Try to get filesystem type from /proc/mounts for a more |
176 | | * descriptive name. |
177 | | */ |
178 | 0 | fs_info->typename = find_mount(path, &fs); |
179 | 0 | if (!fs_info->typename) |
180 | 0 | fs_info->typename = xstrdup(get_fs_typename(fs.f_type)); |
181 | |
|
182 | 0 | trace_printf_key(&trace_fsmonitor, |
183 | 0 | "'%s' is_remote: %d, typename: %s", |
184 | 0 | path, fs_info->is_remote, fs_info->typename); |
185 | |
|
186 | 0 | return 0; |
187 | 0 | } |
188 | | |
189 | | int fsmonitor__is_fs_remote(const char *path) |
190 | 0 | { |
191 | 0 | struct fs_info fs; |
192 | |
|
193 | 0 | if (fsmonitor__get_fs_info(path, &fs)) |
194 | 0 | return -1; |
195 | | |
196 | 0 | free(fs.typename); |
197 | |
|
198 | 0 | return fs.is_remote; |
199 | 0 | } |
200 | | |
201 | | /* |
202 | | * No-op for Linux - we don't have firmlinks like macOS. |
203 | | */ |
204 | | int fsmonitor__get_alias(const char *path UNUSED, |
205 | | struct alias_info *info UNUSED) |
206 | 0 | { |
207 | 0 | return 0; |
208 | 0 | } |
209 | | |
210 | | /* |
211 | | * No-op for Linux - we don't have firmlinks like macOS. |
212 | | */ |
213 | | char *fsmonitor__resolve_alias(const char *path UNUSED, |
214 | | const struct alias_info *info UNUSED) |
215 | 0 | { |
216 | | return NULL; |
217 | 0 | } |