Re: [PATCH v4 2/6] fsmonitor: determine if filesystem is local or remote
flat view
From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2022-12-12 10:46:26
On Wed, Nov 23 2022, Eric DeCosta via GitGitGadget wrote:
quoted hunk ↗ jump to hunk
From: Eric DeCosta <redacted> Compare the given path to the mounted filesystems. Find the mount that is the longest prefix of the path (if any) and determine if that mount is on a local or remote filesystem. Signed-off-by: Eric DeCosta <redacted> --- compat/fsmonitor/fsm-path-utils-linux.c | 186 ++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 compat/fsmonitor/fsm-path-utils-linux.cdiff --git a/compat/fsmonitor/fsm-path-utils-linux.c b/compat/fsmonitor/fsm-path-utils-linux.c new file mode 100644 index 00000000000..d3281422ebc --- /dev/null +++ b/compat/fsmonitor/fsm-path-utils-linux.c@@ -0,0 +1,186 @@ +#include "fsmonitor.h" +#include "fsmonitor-path-utils.h" +#include <errno.h> +#include <mntent.h> +#include <sys/mount.h> +#include <sys/vfs.h> +#include <sys/statvfs.h> + +static int is_remote_fs(const char* path) { + struct statfs fs; + + if (statfs(path, &fs)) { + error_errno(_("statfs('%s') failed"), path); + return -1; + }
Nit: Drop the braces and do: if (statfs(...) == -1) return error_errno(...)
quoted hunk ↗ jump to hunk
+ switch (fs.f_type) { + case 0x61636673: /* ACFS */ + case 0x5346414F: /* AFS */ + case 0x00C36400: /* CEPH */ + case 0xFF534D42: /* CIFS */ + case 0x73757245: /* CODA */ + case 0x19830326: /* FHGFS */ + case 0x1161970: /* GFS */ + case 0x47504653: /* GPFS */ + case 0x013111A8: /* IBRIX */ + case 0x6B414653: /* KAFS */ + case 0x0BD00BD0: /* LUSTRE */ + case 0x564C: /* NCP */ + case 0x6969: /* NFS */ + case 0x6E667364: /* NFSD */ + case 0x7461636f: /* OCFS2 */ + case 0xAAD7AAEA: /* PANFS */ + case 0x517B: /* SMB */ + case 0xBEEFDEAD: /* SNFS */ + case 0xFE534D42: /* SMB2 */ + case 0xBACBACBC: /* VMHGFS */ + case 0xA501FCF5: /* VXFS */
So, before we'd compare against the name, but to avoid the GPLv3 copy/pasting we're now comparing against the fs.f_type. If we are hardcoding them, our usual convention is to lower-case hexdigits, so 0xbacbacbc not 0xBACBACBC. But at least my statfs() manpage documents the named defines in linux/magic.h for most of these. Why not use those?
quoted hunk ↗ jump to hunk
+ return 1; + default: + break;
You could just "return 0" here, and...
quoted hunk ↗ jump to hunk
+ } + + return 0;
...drop this "return 0".
quoted hunk ↗ jump to hunk
+} + +static int find_mount(const char *path, const struct statvfs *fs, + struct mntent *ent)
Misindentation.
quoted hunk ↗ jump to hunk
+{ + const char *const mounts = "/proc/mounts"; + const char *rp = real_pathdup(path, 1); + struct mntent *ment = NULL; + struct statvfs mntfs; + FILE *fp; + int found = 0; + int dlen, plen, flen = 0; + + ent->mnt_fsname = NULL; + ent->mnt_dir = NULL; + ent->mnt_type = NULL; + + fp = setmntent(mounts, "r"); + if (!fp) { + error_errno(_("setmntent('%s') failed"), mounts); + return -1;
Ditto "return error_errno()"
quoted hunk ↗ jump to hunk
+ } + + plen = strlen(rp);
Let's make "plen", "dlen" and "flen" a "size_t", not "int"
quoted hunk ↗ jump to hunk
+ + /* read all the mount information and compare to path */ + while ((ment = getmntent(fp)) != NULL) {
Drop the "!= NULL"
quoted hunk ↗ jump to hunk
+ if (statvfs(ment->mnt_dir, &mntfs)) { + switch (errno) { + case EPERM: + case ESRCH: + case EACCES: + continue; + default: + error_errno(_("statvfs('%s') failed"), ment->mnt_dir); + endmntent(fp);
Shouldn't we check the endmntent() error too? Now, from the manpage the
interface is funny, and always returns 1.
But since this is linux-specific code it seems safe enough to go with it
& glibc assumptions and:
errno = 0;
endmntent(fp);
if (errno)
return error_errno(....);
I.e. it'll just call fclose(), which might set errno() on failure.
Maybe it's not worth it...
+ if (statvfs(path, &fs))
+ return error_errno(_("statvfs('%s') failed"), path);Here you do use that "return error_errno(...)" pattern...", yay!
quoted hunk ↗ jump to hunk
+ + + if (find_mount(path, &fs, &ment) < 0) { + free(ment.mnt_fsname); + free(ment.mnt_dir); + free(ment.mnt_type); + return -1; + } + + trace_printf_key(&trace_fsmonitor, + "statvfs('%s') [flags 0x%08lx] '%s' '%s'", + path, fs.f_flag, ment.mnt_type, ment.mnt_fsname); + + fs_info->is_remote = is_remote_fs(ment.mnt_dir); + fs_info->typename = ment.mnt_fsname; + free(ment.mnt_dir); + free(ment.mnt_type);
If you're going to \n\n-seperate this and the trace_printf_key() above I think moving the second free() here to that "block" would make sense, sinec here is the last time we use mnt_dir, but the last time we used mnt_type was in the trace_printf_key(). But...
quoted hunk ↗ jump to hunk
+ + if (fs_info->is_remote < 0) { + free(ment.mnt_fsname);
...aren't you NULL init-ing these, why not just for all of these: goto error; Then....
quoted hunk ↗ jump to hunk
+ return -1; + } + + trace_printf_key(&trace_fsmonitor, + "'%s' is_remote: %d", + path, fs_info->is_remote); + + return 0;
Have this be: int ret = -1; /* earlier */ ret = 0; cleanup: free(...); free(...); return ret;
quoted hunk ↗ jump to hunk
+} + +int fsmonitor__is_fs_remote(const char *path) +{ + struct fs_info fs; + + if (fsmonitor__get_fs_info(path, &fs)) + return -1; + + free(fs.typename);
This will segfault if you take the part through fsmonitor__get_fs_info() where we don't have the fs.typename yet, i.e. if statfs() fails. There's the trivial NULL-init way to work around it, but I think this suggests a leaky abstraction. If we fail to get the fs info, then the function itself should have free'd that, shouldn't it?
quoted hunk ↗ jump to hunk
+/* + * No-op for now. + */ +char *fsmonitor__resolve_alias(const char *path, + const struct alias_info *info)
Ditto misindentatione