218 lines
4.8 KiB
C
218 lines
4.8 KiB
C
#include "git-compat-util.h"
|
|
#include "fsmonitor-ll.h"
|
|
#include "fsmonitor-path-utils.h"
|
|
#include "gettext.h"
|
|
#include "trace.h"
|
|
|
|
#include <sys/statfs.h>
|
|
|
|
#ifdef HAVE_LINUX_MAGIC_H
|
|
#include <linux/magic.h>
|
|
#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;
|
|
}
|