Raw
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 #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 #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 #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 {
44 switch (f_type) {
45 case CIFS_SUPER_MAGIC:
46 case SMB_SUPER_MAGIC:
47 case SMB2_SUPER_MAGIC:
48 case NFS_SUPER_MAGIC:
49 case AFS_SUPER_MAGIC:
50 case CODA_SUPER_MAGIC:
51 case FUSE_SUPER_MAGIC:
52 return 1;
53 default:
54 return 0;
55 }
56 }
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 {
66 switch (f_type) {
67 case CIFS_SUPER_MAGIC:
68 return "cifs";
69 case SMB_SUPER_MAGIC:
70 return "smb";
71 case SMB2_SUPER_MAGIC:
72 return "smb2";
73 case NFS_SUPER_MAGIC:
74 return "nfs";
75 case AFS_SUPER_MAGIC:
76 return "afs";
77 case CODA_SUPER_MAGIC:
78 return "coda";
79 case FUSE_SUPER_MAGIC:
80 return "fuse";
81 default:
82 return "unknown";
83 }
84 }
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 {
96 FILE *fp;
97 struct strbuf line = STRBUF_INIT;
98 struct strbuf match = STRBUF_INIT;
99 struct strbuf fstype = STRBUF_INIT;
100 char *result = NULL;
101
102 fp = fopen("/proc/mounts", "r");
103 if (!fp)
104 return NULL;
105
106 while (strbuf_getline(&line, fp) != EOF) {
107 char *fields[6];
108 char *p = line.buf;
109 int i;
110
111 /* Parse mount entry: device mountpoint fstype options dump pass */
112 for (i = 0; i < 6 && p; i++) {
113 fields[i] = p;
114 p = strchr(p, ' ');
115 if (p)
116 *p++ = '\0';
117 }
118
119 if (i >= 3) {
120 const char *mountpoint = fields[1];
121 const char *type = fields[2];
122 struct statfs mount_fs;
123
124 /* Check if this mount point is a prefix of our path */
125 if (starts_with(path, mountpoint) &&
126 (path[strlen(mountpoint)] == '/' ||
127 path[strlen(mountpoint)] == '\0')) {
128 /* Check if filesystem ID matches */
129 if (statfs(mountpoint, &mount_fs) == 0 &&
130 !memcmp(&mount_fs.f_fsid, &path_fs->f_fsid,
131 sizeof(mount_fs.f_fsid))) {
132 /* Keep the longest matching mount point */
133 if (strlen(mountpoint) > match.len) {
134 strbuf_reset(&match);
135 strbuf_addstr(&match, mountpoint);
136 strbuf_reset(&fstype);
137 strbuf_addstr(&fstype, type);
138 }
139 }
140 }
141 }
142 }
143
144 fclose(fp);
145 strbuf_release(&line);
146 strbuf_release(&match);
147
148 if (fstype.len)
149 result = strbuf_detach(&fstype, NULL);
150 else
151 strbuf_release(&fstype);
152
153 return result;
154 }
155
156 int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
157 {
158 struct statfs fs;
159
160 if (statfs(path, &fs) == -1) {
161 int saved_errno = errno;
162 trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s",
163 path, strerror(saved_errno));
164 errno = saved_errno;
165 return -1;
166 }
167
168 trace_printf_key(&trace_fsmonitor,
169 "statfs('%s') [type 0x%08lx]",
170 path, (unsigned long)fs.f_type);
171
172 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 fs_info->typename = find_mount(path, &fs);
179 if (!fs_info->typename)
180 fs_info->typename = xstrdup(get_fs_typename(fs.f_type));
181
182 trace_printf_key(&trace_fsmonitor,
183 "'%s' is_remote: %d, typename: %s",
184 path, fs_info->is_remote, fs_info->typename);
185
186 return 0;
187 }
188
189 int fsmonitor__is_fs_remote(const char *path)
190 {
191 struct fs_info fs;
192
193 if (fsmonitor__get_fs_info(path, &fs))
194 return -1;
195
196 free(fs.typename);
197
198 return fs.is_remote;
199 }
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 {
207 return 0;
208 }
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 {
216 return NULL;
217 }