Coverage Report

Created: 2026-10-17 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}