]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: use AT_STATX_DONT_SYNC when touch mountpoints
authorKarel Zak <kzak@redhat.com>
Mon, 17 Apr 2023 12:23:18 +0000 (14:23 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 17 Apr 2023 12:23:18 +0000 (14:23 +0200)
* prefer statx() with AT_STATX_DONT_SYNC if available
* keep fstatat() and stat() as fallback
* add test to mnt_stat_mountpoint()

The goal is to minimize situations when we need classic stat() because
it triggers automount, and stat() syscall may hang on unreachable
network filesystems. The automount issue was resolved years ago by
AT_NO_AUTOMOUNT; now we can use statx() to fix also hangs on NFS.

Addresses: https://github.com/util-linux/util-linux/issues/2049
Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/src/utils.c

index e4347f3ea9af5e0cb7aab119a1155e089a1d9864..1fa67b85f4d4c90a37edf2338af6c72304055677 100644 (file)
@@ -100,22 +100,53 @@ static int fstype_cmp(const void *v1, const void *v2)
        return strcmp(s1, s2);
 }
 
-int mnt_stat_mountpoint(const char *target, struct stat *st)
+static int safe_stat(const char *target, struct stat *st, int nofollow)
 {
-#ifdef AT_NO_AUTOMOUNT
-       return fstatat(AT_FDCWD, target, st, AT_NO_AUTOMOUNT);
+       assert(target);
+       assert(st);
+
+       memset(st, 0, sizeof(struct stat));
+
+#ifdef AT_STATX_DONT_SYNC
+       {
+               int rc;
+               struct statx stx = { 0 };
+
+               rc = statx(-1,  target,
+                               /* flags */
+                               AT_STATX_DONT_SYNC
+                                       | AT_NO_AUTOMOUNT
+                                       | (nofollow ? AT_SYMLINK_NOFOLLOW : 0),
+                               /* mask */
+                               STATX_TYPE
+                                       | STATX_MODE
+                                       | STATX_INO,
+                               &stx);
+               if (rc == 0) {
+                       st->st_ino  = stx.stx_ino;
+                       st->st_dev  = makedev(stx.stx_dev_major, stx.stx_dev_minor);
+                       st->st_mode = stx.stx_mode;
+               }
+               return rc;
+       }
 #else
-       return stat(target, st);
+# ifdef AT_NO_AUTOMOUNT
+       return fstatat(AT_FDCWD, target, st,
+                       AT_NO_AUTOMOUNT | (nofollow ? AT_SYMLINK_NOFOLLOW : 0));
+# else
+       return nofollow ? lstat(target, st) : stat(target, st);
+# endif
 #endif
 }
 
+int mnt_stat_mountpoint(const char *target, struct stat *st)
+{
+       return safe_stat(target, st, 0);
+}
+
 int mnt_lstat_mountpoint(const char *target, struct stat *st)
 {
-#ifdef AT_NO_AUTOMOUNT
-       return fstatat(AT_FDCWD, target, st, AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW);
-#else
-       return lstat(target, st);
-#endif
+       return safe_stat(target, st, 1);
 }
 
 /* Don't use access() or stat() here, we need a way how to check the path
@@ -124,7 +155,7 @@ int mnt_is_path(const char *target)
 {
        struct stat st;
 
-       return mnt_stat_mountpoint(target, &st) == 0;
+       return safe_stat(target, &st, 0) == 0;
 }
 
 /*
@@ -1450,6 +1481,32 @@ static int tests_parse_mode(struct libmnt_test *ts, int argc, char *argv[])
        return rc;
 }
 
+static int tests_stat(struct libmnt_test *ts, int argc, char *argv[])
+{
+       char *path = argv[1];
+       struct stat st;
+       int rc;
+
+       if (strcmp(argv[0], "--lstat") == 0)
+               rc = mnt_lstat_mountpoint(path, &st);
+       else
+               rc = mnt_stat_mountpoint(path, &st);
+       if (rc)
+               printf("%s: failed: rc=%d: %m\n", path, rc);
+       else {
+               printf("%s: \n", path);
+               printf(" S_ISDIR: %s\n", S_ISDIR(st.st_mode) ? "y" : "n");
+               printf(" S_ISREG: %s\n", S_ISREG(st.st_mode) ? "y" : "n");
+               printf(" S_IFLNK: %s\n", S_ISLNK(st.st_mode) ? "y" : "n");
+
+               printf("   devno: %lu (%d:%d)\n", (unsigned long) st.st_dev,
+                                         major(st.st_dev), minor(st.st_dev));
+               printf("     ino: %lu\n", (unsigned long) st.st_ino);
+
+       }
+       return rc;
+}
+
 int main(int argc, char *argv[])
 {
        struct libmnt_test tss[] = {
@@ -1467,6 +1524,8 @@ int main(int argc, char *argv[])
        { "--parse-uid",     tests_parse_uid,      "<username|uid>" },
        { "--parse-gid",     tests_parse_gid,      "<groupname|gid>" },
        { "--parse-mode",    tests_parse_mode,     "<number>" },
+       { "--stat",          tests_stat,           "<path>" },
+       { "--lstat",         tests_stat,           "<path>" },
        { NULL }
        };