diff options
| author | Karel Zak <kzak@redhat.com> | 2025-11-19 13:01:02 +0100 |
|---|---|---|
| committer | Karel Zak <kzak@redhat.com> | 2025-11-20 11:25:22 +0100 |
| commit | 5fc8401d09d791d132badd3029426407a8733dea (patch) | |
| tree | 451d16a491a24770ae6e8dbcc1b4cb7caf2113f0 | |
| parent | 142eedfb247710db65f127e9738d5ffab7e27cae (diff) | |
| download | util-linux-5fc8401d09d791d132badd3029426407a8733dea.tar.gz | |
mountpoint: use statmount() syscall on modern kernels
Improve mountpoint(1) to use the modern statmount() system call
(available since Linux 6.8) instead of parsing /proc/self/mountinfo.
- Works without /proc mounted on modern kernels
- More efficient than parsing /proc/self/mountinfo
- Better detection of bind mounts via statmount()
- Graceful fallback maintains compatibility
Addresses: https://github.com/util-linux/util-linux/issues/3806
Signed-off-by: Karel Zak <kzak@redhat.com>
| -rw-r--r-- | sys-utils/mountpoint.1.adoc | 2 | ||||
| -rw-r--r-- | sys-utils/mountpoint.c | 108 |
2 files changed, 98 insertions, 12 deletions
diff --git a/sys-utils/mountpoint.1.adoc b/sys-utils/mountpoint.1.adoc index bc8a2e9ed6..0e1252c72c 100644 --- a/sys-utils/mountpoint.1.adoc +++ b/sys-utils/mountpoint.1.adoc @@ -18,7 +18,7 @@ mountpoint - see if a directory or file is a mountpoint == DESCRIPTION -*mountpoint* checks whether the given _directory_ or _file_ is mentioned in the _/proc/self/mountinfo_ file. +*mountpoint* checks whether the given _directory_ or _file_ is a mountpoint. On kernels that support the *statmount*(2) system call (Linux 6.8 and newer), it uses that interface. On older kernels, it falls back to reading _/proc/self/mountinfo_. == OPTIONS diff --git a/sys-utils/mountpoint.c b/sys-utils/mountpoint.c index 6294a4c07a..8a114b5bb0 100644 --- a/sys-utils/mountpoint.c +++ b/sys-utils/mountpoint.c @@ -31,6 +31,7 @@ #include "c.h" #include "closestream.h" #include "pathnames.h" +#include "mount-api-utils.h" #define MOUNTPOINT_EXIT_NOMNT 32 @@ -44,13 +45,80 @@ struct mountpoint_control { quiet; }; +#ifdef HAVE_STATMOUNT_API +/* + * dir_to_device_statmount - check if path is a mountpoint using statmount() + * @ctl: mountpoint control structure + * + * Returns: <0 on error, 0 if mountpoint, 1 if not a mountpoint + */ +static int dir_to_device_statmount(struct mountpoint_control *ctl) +{ + struct libmnt_fs *fs = NULL; + const char *mnt_target; + char *cn = NULL; + uint64_t id = 0; + int rc; + + cn = mnt_resolve_path(ctl->path, NULL); + + rc = mnt_id_from_path(cn ? cn : ctl->path, &id, NULL); + if (rc) + goto done; + + fs = mnt_new_fs(); + if (!fs) { + rc = -ENOMEM; + goto done; + } + + mnt_fs_set_uniq_id(fs, id); + + rc = mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_POINT | STATMOUNT_SB_BASIC); + if (rc) + goto done; + + mnt_target = mnt_fs_get_target(fs); + if (!mnt_target) { + rc = -EINVAL; + goto done; + } + + if (strcmp(mnt_target, cn ? cn : ctl->path) != 0) + rc = 1; /* not a mountpoint */ + else { + ctl->dev = mnt_fs_get_devno(fs); + rc = 0; /* is a mountpoint */ + } +done: + free(cn); + mnt_unref_fs(fs); + return rc; +} +#endif /* STATMOUNT_MNT_POINT */ + +/* + * dir_to_device - check if path is a mountpoint + * @ctl: mountpoint control structure + * + * Returns: <0 on error, 0 if mountpoint, 1 if not a mountpoint + */ static int dir_to_device(struct mountpoint_control *ctl) { - struct libmnt_table *tb = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO); + struct libmnt_table *tb; struct libmnt_fs *fs; struct libmnt_cache *cache; - int rc = -1; + int rc; +#ifdef HAVE_STATMOUNT_API + rc = dir_to_device_statmount(ctl); + if (rc >= 0) + return rc; +#endif + /* + * Fallback for older kernels without statmount() support + */ + tb = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO); if (!tb) { /* * Fallback. Traditional way to detect mountpoints. This way @@ -66,16 +134,18 @@ static int dir_to_device(struct mountpoint_control *ctl) free(cn); if (len < 0 || (size_t) len >= sizeof(buf)) - return -1; - if (stat(buf, &pst) !=0) - return -1; + return -EINVAL; + + rc = stat(buf, &pst); + if (rc) + return -errno; if (ctl->st.st_dev != pst.st_dev || ctl->st.st_ino == pst.st_ino) { ctl->dev = ctl->st.st_dev; - return 0; + return 0; /* is a mountpoint */ } - return -1; + return 1; /* not a mountpoint */ } /* to canonicalize all necessary paths */ @@ -86,8 +156,9 @@ static int dir_to_device(struct mountpoint_control *ctl) fs = mnt_table_find_target(tb, ctl->path, MNT_ITER_BACKWARD); if (fs && mnt_fs_get_target(fs)) { ctl->dev = mnt_fs_get_devno(fs); - rc = 0; - } + rc = 0; /* is a mountpoint */ + } else + rc = 1; /* not a mountpoint */ mnt_unref_table(tb); return rc; @@ -129,7 +200,7 @@ static void __attribute__((__noreturn__)) usage(void) int main(int argc, char **argv) { - int c; + int c, rc; struct mountpoint_control ctl = { NULL }; enum { @@ -196,11 +267,26 @@ int main(int argc, char **argv) if (ctl.dev_devno) return print_devno(&ctl) ? MOUNTPOINT_EXIT_NOMNT : EXIT_SUCCESS; - if ((ctl.nofollow && S_ISLNK(ctl.st.st_mode)) || dir_to_device(&ctl)) { + if (ctl.nofollow && S_ISLNK(ctl.st.st_mode)) { + if (!ctl.quiet) + printf(_("%s is not a mountpoint\n"), ctl.path); + return MOUNTPOINT_EXIT_NOMNT; + } + + rc = dir_to_device(&ctl); + if (rc < 0) { + if (!ctl.quiet) { + errno = -rc; + warn("%s", ctl.path); + } + return EXIT_FAILURE; + } + if (rc == 1) { if (!ctl.quiet) printf(_("%s is not a mountpoint\n"), ctl.path); return MOUNTPOINT_EXIT_NOMNT; } + if (ctl.fs_devno) printf("%u:%u\n", major(ctl.dev), minor(ctl.dev)); else if (!ctl.quiet) |
