From: Karel Zak Date: Mon, 17 Apr 2023 12:23:18 +0000 (+0200) Subject: libmount: use AT_STATX_DONT_SYNC when touch mountpoints X-Git-Tag: v2.39-rc3~17 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=42e141d20505a0deb969c2e583a463c26aadc62f;p=thirdparty%2Futil-linux.git libmount: use AT_STATX_DONT_SYNC when touch mountpoints * 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 --- diff --git a/libmount/src/utils.c b/libmount/src/utils.c index e4347f3ea9..1fa67b85f4 100644 --- a/libmount/src/utils.c +++ b/libmount/src/utils.c @@ -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, "" }, { "--parse-gid", tests_parse_gid, "" }, { "--parse-mode", tests_parse_mode, "" }, + { "--stat", tests_stat, "" }, + { "--lstat", tests_stat, "" }, { NULL } };