#include "git-compat-util.h" #include "fsmonitor-ll.h" #include "fsmonitor-path-utils.h" #include "gettext.h" #include "trace.h" #include #ifdef HAVE_LINUX_MAGIC_H #include #endif /* * Filesystem magic numbers for remote filesystems. * Defined here if not available in linux/magic.h. */ #ifndef CIFS_SUPER_MAGIC #define CIFS_SUPER_MAGIC 0xff534d42 #endif #ifndef SMB_SUPER_MAGIC #define SMB_SUPER_MAGIC 0x517b #endif #ifndef SMB2_SUPER_MAGIC #define SMB2_SUPER_MAGIC 0xfe534d42 #endif #ifndef NFS_SUPER_MAGIC #define NFS_SUPER_MAGIC 0x6969 #endif #ifndef AFS_SUPER_MAGIC #define AFS_SUPER_MAGIC 0x5346414f #endif #ifndef CODA_SUPER_MAGIC #define CODA_SUPER_MAGIC 0x73757245 #endif #ifndef FUSE_SUPER_MAGIC #define FUSE_SUPER_MAGIC 0x65735546 #endif /* * Check if filesystem type is a remote filesystem. */ static int is_remote_fs(unsigned long f_type) { switch (f_type) { case CIFS_SUPER_MAGIC: case SMB_SUPER_MAGIC: case SMB2_SUPER_MAGIC: case NFS_SUPER_MAGIC: case AFS_SUPER_MAGIC: case CODA_SUPER_MAGIC: case FUSE_SUPER_MAGIC: return 1; default: return 0; } } /* * Map filesystem magic numbers to human-readable names as a fallback * when /proc/mounts is unavailable. This only covers the remote and * special filesystems in is_remote_fs() above; local filesystems are * never flagged as incompatible, so we do not need their names here. */ static const char *get_fs_typename(unsigned long f_type) { switch (f_type) { case CIFS_SUPER_MAGIC: return "cifs"; case SMB_SUPER_MAGIC: return "smb"; case SMB2_SUPER_MAGIC: return "smb2"; case NFS_SUPER_MAGIC: return "nfs"; case AFS_SUPER_MAGIC: return "afs"; case CODA_SUPER_MAGIC: return "coda"; case FUSE_SUPER_MAGIC: return "fuse"; default: return "unknown"; } } /* * Find the mount point for a given path by reading /proc/mounts. * * statfs(2) gives us f_type (the magic number) but not the human-readable * filesystem type string. We scan /proc/mounts to find the mount entry * whose path is the longest prefix of ours and whose f_fsid matches, * which gives us the fstype string (e.g. "nfs", "ext4") for logging. */ static char *find_mount(const char *path, const struct statfs *path_fs) { FILE *fp; struct strbuf line = STRBUF_INIT; struct strbuf match = STRBUF_INIT; struct strbuf fstype = STRBUF_INIT; char *result = NULL; fp = fopen("/proc/mounts", "r"); if (!fp) return NULL; while (strbuf_getline(&line, fp) != EOF) { char *fields[6]; char *p = line.buf; int i; /* Parse mount entry: device mountpoint fstype options dump pass */ for (i = 0; i < 6 && p; i++) { fields[i] = p; p = strchr(p, ' '); if (p) *p++ = '\0'; } if (i >= 3) { const char *mountpoint = fields[1]; const char *type = fields[2]; struct statfs mount_fs; /* Check if this mount point is a prefix of our path */ if (starts_with(path, mountpoint) && (path[strlen(mountpoint)] == '/' || path[strlen(mountpoint)] == '\0')) { /* Check if filesystem ID matches */ if (statfs(mountpoint, &mount_fs) == 0 && !memcmp(&mount_fs.f_fsid, &path_fs->f_fsid, sizeof(mount_fs.f_fsid))) { /* Keep the longest matching mount point */ if (strlen(mountpoint) > match.len) { strbuf_reset(&match); strbuf_addstr(&match, mountpoint); strbuf_reset(&fstype); strbuf_addstr(&fstype, type); } } } } } fclose(fp); strbuf_release(&line); strbuf_release(&match); if (fstype.len) result = strbuf_detach(&fstype, NULL); else strbuf_release(&fstype); return result; } int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info) { struct statfs fs; if (statfs(path, &fs) == -1) { int saved_errno = errno; trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s", path, strerror(saved_errno)); errno = saved_errno; return -1; } trace_printf_key(&trace_fsmonitor, "statfs('%s') [type 0x%08lx]", path, (unsigned long)fs.f_type); fs_info->is_remote = is_remote_fs(fs.f_type); /* * Try to get filesystem type from /proc/mounts for a more * descriptive name. */ fs_info->typename = find_mount(path, &fs); if (!fs_info->typename) fs_info->typename = xstrdup(get_fs_typename(fs.f_type)); trace_printf_key(&trace_fsmonitor, "'%s' is_remote: %d, typename: %s", path, fs_info->is_remote, fs_info->typename); return 0; } int fsmonitor__is_fs_remote(const char *path) { struct fs_info fs; if (fsmonitor__get_fs_info(path, &fs)) return -1; free(fs.typename); return fs.is_remote; } /* * No-op for Linux - we don't have firmlinks like macOS. */ int fsmonitor__get_alias(const char *path UNUSED, struct alias_info *info UNUSED) { return 0; } /* * No-op for Linux - we don't have firmlinks like macOS. */ char *fsmonitor__resolve_alias(const char *path UNUSED, const struct alias_info *info UNUSED) { return NULL; }