]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: add support for statmount()
authorKarel Zak <kzak@redhat.com>
Wed, 14 Aug 2024 15:10:45 +0000 (17:10 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 8 Jan 2025 12:57:42 +0000 (13:57 +0100)
* introduce libmnt_statmnt object to store statmount mask and buffer
  (etc.)

* add API for libmnt_fs to retrieve data from the kernel using
  statmount()

* support on-demand statmount() from old mnt_fs_get_...() functions

* allow libmnt_statmnt to be shared and reused between libmnt_fs
  instances

* allow libmnt_statmnt to be assigned and used for filesystems in the
  table (assign libmnt_statmnt to the table to make it usable for all
  filesystems).

* allow temporary disabling of fetching data from the kernel to avoid
  unwanted recursion in certain use-cases

* support namespaces for statmount() based on the libmnt_fs namespace
  ID setting

* allow user-defined statmount masks to overwrite the default

* add a sample program

Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/docs/libmount-sections.txt
libmount/meson.build
libmount/samples/Makemodule.am
libmount/samples/statmount.c [new file with mode: 0644]
libmount/src/Makemodule.am
libmount/src/fs.c
libmount/src/fs_statmount.c [new file with mode: 0644]
libmount/src/libmount.h.in
libmount/src/libmount.sym
libmount/src/mountP.h
libmount/src/tab.c

index b72229c585bd1f592a00b46eeeb619b3ee18e4ce..79c6055e5ea29565574ffc021786091466dcfff7 100644 (file)
@@ -281,10 +281,27 @@ mnt_fs_strdup_options
 mnt_fs_streq_srcpath
 mnt_fs_streq_target
 mnt_fs_to_mntent
+mnt_fs_fetch_statmount
+mnt_fs_get_ns
+mnt_fs_set_ns
 mnt_new_fs
 mnt_reset_fs
 </SECTION>
 
+<SECTION>
+<FILE>statmount</FILE>
+libmnt_statmnt
+mnt_new_statmnt
+mnt_ref_statmnt
+mnt_unref_statmnt
+mnt_statmnt_disable_fetching
+mnt_statmnt_set_mask
+mnt_fs_fetch_statmount
+mnt_fs_get_statmnt
+mnt_fs_refer_statmnt
+mnt_table_refer_statmnt
+</SECTION>
+
 <SECTION>
 <FILE>init</FILE>
 mnt_init_debug
index a7145c5b454bc211dbd447e596784655ff2a34cb..335523d26b5eb96bd353c15c751a38d0274ed898 100644 (file)
@@ -43,6 +43,7 @@ lib_mount_sources = '''
 
 if LINUX
   lib_mount_sources += '''
+    src/fs_statmount.c
     src/hooks.c
     src/monitor.c
     src/optlist.c
index b9201e925c52642a3c2270ab6867857184d602ef..dc9f641f4e1d480114782627d60e4ee41a578b27 100644 (file)
@@ -1,13 +1,14 @@
 
 if LINUX
 check_PROGRAMS += \
-       sample-mount-overwrite
+       sample-mount-overwrite \
+       sample-mount-statmount
 endif
 
-sample_mount_cflags = $(AM_CFLAGS) -I$(ul_libmount_incdir)
-sample_mount_ldadd = libmount.la $(LDADD)
-
-
 sample_mount_overwrite_SOURCES = libmount/samples/overwrite.c
-sample_mount_overwrite_LDADD =  $(sample_mount_ldadd)
-sample_mount_overwrite_CFLAGS = $(sample_mount_cflags)
+sample_mount_overwrite_LDADD =  libmount.la $(LDADD)
+sample_mount_overwrite_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir)
+
+sample_mount_statmount_SOURCES = libmount/samples/statmount.c
+sample_mount_statmount_LDADD =  libmount.la $(LDADD) libcommon.la
+sample_mount_statmount_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir)
diff --git a/libmount/samples/statmount.c b/libmount/samples/statmount.c
new file mode 100644 (file)
index 0000000..c480206
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdlib.h>
+
+#include "c.h"
+#include "strutils.h"
+#include "libmount.h"
+
+#include <linux/mount.h>       /* STATMOUNT_* masks */
+
+#include "mount-api-utils.h"   /* fallback for old linux/mount.h */
+
+int main(int argc, char *argv[])
+{
+        struct libmnt_fs *fs;
+       struct libmnt_statmnt *sm;
+       const char *mnt;
+       uint64_t id = 0;
+
+       if (argc != 2)
+               errx(EXIT_FAILURE, "usage: %s <mountpoint | id>",
+                               program_invocation_short_name);
+
+       mnt_init_debug(0);
+
+       fs = mnt_new_fs();
+       if (!fs)
+               err(EXIT_FAILURE, "failed to allocate fs handler");
+
+       /* define target (mountpoint) or mount ID */
+       mnt = argv[1];
+       if (isdigit_string(mnt)) {
+               id = strtou64_or_err(mnt, "cannot ID");
+               mnt_fs_set_uniq_id(fs, id);
+       } else
+               mnt_fs_set_target(fs, mnt);
+
+       /*
+        * A) fetch all data without reference to libmnt_statmnt
+        */
+       if (mnt_fs_fetch_statmount(fs, 0) != 0)
+               warn("failed to read data by statmount()");
+       mnt_fs_print_debug(fs, stdout);
+
+       /* reset */
+       id = mnt_fs_get_uniq_id(fs);
+       mnt_reset_fs(fs);
+       mnt_fs_set_uniq_id(fs, id);
+
+       /*
+        * B) fetch data by on-demand way
+        */
+       sm = mnt_new_statmnt();
+       if (!sm)
+               err(EXIT_FAILURE, "failed to allocate statmount handler");
+
+       mnt_fs_refer_statmnt(fs, sm);
+
+       /* read fs type, but nothing else */
+       mnt_fs_get_fstype(fs);
+       mnt_fs_print_debug(fs, stdout);
+
+       /* read fs root, but nothing else */
+        mnt_fs_get_root(fs);
+        mnt_fs_print_debug(fs, stdout);
+
+       /* read all mising data */
+       mnt_fs_fetch_statmount(fs, 0);
+       mnt_fs_print_debug(fs, stdout);
+
+       /* see debug, this is no-op for statmount() */
+       mnt_fs_get_fstype(fs);
+
+       mnt_unref_fs(fs);
+       mnt_unref_statmnt(sm);
+
+       return EXIT_SUCCESS;
+}
index 66963bb0943a08ebf842468e9c0b11c6d4e31dc7..9c87e0abe2656d624d252d2bb24d2005c585c106 100644 (file)
@@ -30,6 +30,7 @@ libmount_la_SOURCES += \
        libmount/src/context.c \
        libmount/src/context_mount.c \
        libmount/src/context_umount.c \
+       libmount/src/fs_statmount.c \
        libmount/src/hooks.c \
        libmount/src/hook_mount.c \
        libmount/src/hook_mount_legacy.c \
index d8886dc389751bb3d003b1c98cd294e7b7778b50..e466bca134d06d542cab5e011aeed92930a0200d 100644 (file)
@@ -102,6 +102,10 @@ void mnt_reset_fs(struct libmnt_fs *fs)
        fs->opts_age = 0;
        fs->propagation = 0;
 
+       mnt_unref_statmnt(fs->stmnt);
+       fs->stmnt = NULL;
+       fs->stmnt_done = 0;
+
        memset(fs, 0, sizeof(*fs));
        INIT_LIST_HEAD(&fs->ents);
        fs->refcount = ref;
@@ -611,7 +615,11 @@ int mnt_fs_get_tag(struct libmnt_fs *fs, const char **name, const char **value)
  */
 const char *mnt_fs_get_target(struct libmnt_fs *fs)
 {
-       return fs ? fs->target : NULL;
+       if (!fs)
+               return NULL;
+       if (!fs->target && mnt_fs_want_statmount(fs, STATMOUNT_MNT_POINT))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_POINT);
+       return fs->target;;
 }
 
 /**
@@ -657,6 +665,8 @@ int mnt_fs_get_propagation(struct libmnt_fs *fs, unsigned long *flags)
 {
        if (!fs || !flags)
                return -EINVAL;
+       if (!fs->propagation && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC);
 
        if (!fs->propagation && fs->opt_fields) {
                 /*
@@ -707,6 +717,11 @@ int mnt_fs_is_swaparea(struct libmnt_fs *fs)
  */
 int mnt_fs_is_pseudofs(struct libmnt_fs *fs)
 {
+       if (!fs)
+               return 0;
+       if (!fs->fstype && mnt_fs_want_statmount(fs, STATMOUNT_FS_TYPE))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_FS_TYPE);
+
        return mnt_fs_get_flags(fs) & MNT_FS_PSEUDO ? 1 : 0;
 }
 
@@ -718,6 +733,11 @@ int mnt_fs_is_pseudofs(struct libmnt_fs *fs)
  */
 int mnt_fs_is_netfs(struct libmnt_fs *fs)
 {
+       if (!fs)
+               return 0;
+       if (!fs->fstype && mnt_fs_want_statmount(fs, STATMOUNT_FS_TYPE))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_FS_TYPE);
+
        return mnt_fs_get_flags(fs) & MNT_FS_NET ? 1 : 0;
 }
 
@@ -744,7 +764,13 @@ int mnt_fs_is_regularfs(struct libmnt_fs *fs)
  */
 const char *mnt_fs_get_fstype(struct libmnt_fs *fs)
 {
-       return fs ? fs->fstype : NULL;
+       if (!fs)
+               return NULL;
+
+       if (!fs->fstype && mnt_fs_want_statmount(fs, STATMOUNT_FS_TYPE))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_FS_TYPE);
+
+       return fs->fstype;
 }
 
 /* Used by the struct libmnt_file parser only */
@@ -866,6 +892,8 @@ char *mnt_fs_strdup_options(struct libmnt_fs *fs)
                return NULL;
        if (fs->optlist)
                sync_opts_from_optlist(fs, fs->optlist);
+       else if (!fs->optstr && mnt_fs_want_statmount(fs, STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC);
 
        errno = 0;
        if (fs->optstr)
@@ -890,10 +918,14 @@ char *mnt_fs_strdup_options(struct libmnt_fs *fs)
  */
 const char *mnt_fs_get_options(struct libmnt_fs *fs)
 {
-       if (fs && fs->optlist)
+       if (!fs)
+              return NULL;
+       if (fs->optlist)
                sync_opts_from_optlist(fs, fs->optlist);
+       else if (!fs->optstr && mnt_fs_want_statmount(fs, STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC);
 
-       return fs ? fs->optstr : NULL;
+       return fs->optstr;
 }
 
 /**
@@ -1062,6 +1094,8 @@ const char *mnt_fs_get_fs_options(struct libmnt_fs *fs)
                return NULL;
        if (fs->optlist)
                sync_opts_from_optlist(fs, fs->optlist);
+       else if (!fs->fs_optstr && mnt_fs_want_statmount(fs, STATMOUNT_SB_BASIC))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_SB_BASIC);
 
        return fs->fs_optstr;
 }
@@ -1078,6 +1112,8 @@ const char *mnt_fs_get_vfs_options(struct libmnt_fs *fs)
                return NULL;
        if (fs->optlist)
                sync_opts_from_optlist(fs, fs->optlist);
+       else if (!fs->vfs_optstr && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC);
 
        return fs->vfs_optstr;
 }
@@ -1154,6 +1190,9 @@ const char *mnt_fs_get_attributes(struct libmnt_fs *fs)
  * that information stored in userspace will not be available for libmount
  * after CLONE_FS unshare. Be careful, and don't use attributes if possible.
  *
+ * Please note that the new mount kernel API calls some VFS flags "mount attributes" 
+ * (MOUNT_ATTR_*), but these flags are not related to the old libmount functionality.
+ *
  * Returns: 0 on success or negative number in case of error.
  */
 int mnt_fs_set_attributes(struct libmnt_fs *fs, const char *optstr)
@@ -1258,7 +1297,12 @@ int mnt_fs_set_passno(struct libmnt_fs *fs, int passno)
  */
 const char *mnt_fs_get_root(struct libmnt_fs *fs)
 {
-       return fs ? fs->root : NULL;
+       if (!fs)
+               return NULL;
+       if (!fs->root && mnt_fs_want_statmount(fs, STATMOUNT_MNT_ROOT))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_ROOT);
+
+       return fs->root;
 }
 
 /**
@@ -1368,7 +1412,12 @@ int mnt_fs_set_bindsrc(struct libmnt_fs *fs, const char *src)
  */
 int mnt_fs_get_id(struct libmnt_fs *fs)
 {
-       return fs ? fs->id : -EINVAL;
+       if (!fs)
+               return 0;
+       if (!fs->id && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC);
+
+       return fs->id;
 }
 
 /**
@@ -1384,7 +1433,12 @@ int mnt_fs_get_id(struct libmnt_fs *fs)
  */
 uint64_t mnt_fs_get_uniq_id(struct libmnt_fs *fs)
 {
-       return fs ? fs->uniq_id : 0;
+       if (!fs)
+               return 0;
+       if (!fs->uniq_id && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC);
+
+       return fs->uniq_id;
 }
 
 /**
@@ -1415,7 +1469,12 @@ int mnt_fs_set_uniq_id(struct libmnt_fs *fs, uint64_t id)
  */
 int mnt_fs_get_parent_id(struct libmnt_fs *fs)
 {
-       return fs ? fs->parent : -EINVAL;
+       if (!fs)
+               return 0;
+       if (!fs->parent && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC);
+
+       return fs->parent;
 }
 
 /**
@@ -1428,9 +1487,51 @@ int mnt_fs_get_parent_id(struct libmnt_fs *fs)
  */
 uint64_t mnt_fs_get_parent_uniq_id(struct libmnt_fs *fs)
 {
-       return fs ? fs->uniq_parent : 0;
+       if (!fs)
+               return 0;
+       if (!fs->uniq_parent && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC);
+
+       return fs->uniq_parent;
 }
 
+/**
+ * mnt_fs_get_ns:
+ * @fs: filesystem instance
+ *
+ * This ID is provided by statmount() since Linux kernel since v6.10
+ *
+ * Returns: parent namespace ID or 0 if not avalable.
+ *
+ * Since: 2.41
+ */
+uint64_t mnt_fs_get_ns(struct libmnt_fs *fs)
+{
+       if (!fs)
+               return 0;
+       if (!fs->ns_id && mnt_fs_want_statmount(fs, STATMOUNT_MNT_NS_ID))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_NS_ID);
+       return fs->ns_id;
+}
+
+/**
+ * mnt_fs_set_ns:
+ * @fs: filesystem instance
+ * @id: namespace ID (or 0)
+ *
+ * Returns: 0 or <0 in case of error.
+ *
+ * Sinse: 2.41
+ */
+int mnt_fs_set_ns(struct libmnt_fs *fs, uint64_t id)
+{
+       if (!fs)
+               return -EINVAL;
+       fs->ns_id = id;
+       return 0;
+}
+
+
 /**
  * mnt_fs_get_devno:
  * @fs: /proc/self/mountinfo entry
@@ -1439,7 +1540,12 @@ uint64_t mnt_fs_get_parent_uniq_id(struct libmnt_fs *fs)
  */
 dev_t mnt_fs_get_devno(struct libmnt_fs *fs)
 {
-       return fs ? fs->devno : 0;
+       if (!fs)
+               return 0;
+       if (!fs->devno && mnt_fs_want_statmount(fs, STATMOUNT_SB_BASIC))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_SB_BASIC);
+
+       return fs->devno;
 }
 
 /**
@@ -1472,6 +1578,8 @@ int mnt_fs_get_option(struct libmnt_fs *fs, const char *name,
 
        if (fs->optlist)
                sync_opts_from_optlist(fs, fs->optlist);
+       else if (!fs->vfs_optstr && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC | STATMOUNT_SB_BASIC))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC | STATMOUNT_SB_BASIC);
 
        if (fs->fs_optstr)
                rc = mnt_optstr_get_option(fs->fs_optstr, name, value, valsz);
@@ -1576,7 +1684,11 @@ int mnt_fs_match_target(struct libmnt_fs *fs, const char *target,
 {
        int rc = 0;
 
-       if (!fs || !target || !fs->target)
+       if (!fs || !target)
+               return 0;
+       if (!fs->target && mnt_fs_want_statmount(fs, STATMOUNT_MNT_POINT))
+               mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_POINT);
+       if (!fs->target)
                return 0;
 
        /* 1) native paths */
@@ -1693,7 +1805,7 @@ int mnt_fs_match_source(struct libmnt_fs *fs, const char *source,
  */
 int mnt_fs_match_fstype(struct libmnt_fs *fs, const char *types)
 {
-       return mnt_match_fstype(fs->fstype, types);
+       return mnt_match_fstype(mnt_fs_get_fstype(fs), types);
 }
 
 /**
@@ -1720,16 +1832,26 @@ int mnt_fs_match_options(struct libmnt_fs *fs, const char *options)
  */
 int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file)
 {
+       unsigned long pro = 0;
+       int stmnt_disabled = 1;
+
        if (!fs || !file)
                return -EINVAL;
 
        if (fs->optlist)
                sync_opts_from_optlist(fs, fs->optlist);
 
+       if (fs->stmnt)
+               stmnt_disabled = mnt_statmnt_disable_fetching(fs->stmnt, 1);
+
        fprintf(file, "------ fs:\n");
-       fprintf(file, "source: %s\n", mnt_fs_get_source(fs));
-       fprintf(file, "target: %s\n", mnt_fs_get_target(fs));
-       fprintf(file, "fstype: %s\n", mnt_fs_get_fstype(fs));
+       fprintf(file, "auto-statmount: %s\n", stmnt_disabled ? "off" : "on");
+       if (mnt_fs_get_source(fs))
+               fprintf(file, "source: %s\n", mnt_fs_get_source(fs));
+       if (mnt_fs_get_target(fs))
+               fprintf(file, "target: %s\n", mnt_fs_get_target(fs));
+       if (mnt_fs_get_fstype(fs))
+               fprintf(file, "fstype: %s\n", mnt_fs_get_fstype(fs));
 
        if (mnt_fs_get_options(fs))
                fprintf(file, "optstr: %s\n", mnt_fs_get_options(fs));
@@ -1744,6 +1866,12 @@ int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file)
        if (mnt_fs_get_attributes(fs))
                fprintf(file, "attributes: %s\n", mnt_fs_get_attributes(fs));
 
+       if (mnt_fs_get_propagation(fs, &pro) == 0 && pro)
+               fprintf(file, "propagation: %s %s %s\n",
+                               pro & MS_SHARED ? "shared" : "private",
+                               pro & MS_SLAVE ? "slave" : "",
+                               pro & MS_UNBINDABLE ? "unbindable" : "");
+
        if (mnt_fs_get_root(fs))
                fprintf(file, "root:   %s\n", mnt_fs_get_root(fs));
 
@@ -1770,6 +1898,7 @@ int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file)
                fprintf(file, "uniq-id:     %" PRIu64 "\n", mnt_fs_get_uniq_id(fs));
        if (mnt_fs_get_parent_uniq_id(fs))
                fprintf(file, "uniq-parent: %" PRIu64 "\n", mnt_fs_get_parent_uniq_id(fs));
+
        if (mnt_fs_get_devno(fs))
                fprintf(file, "devno:  %d:%d\n", major(mnt_fs_get_devno(fs)),
                                                minor(mnt_fs_get_devno(fs)));
@@ -1778,6 +1907,8 @@ int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file)
        if (mnt_fs_get_comment(fs))
                fprintf(file, "comment: '%s'\n", mnt_fs_get_comment(fs));
 
+       if (fs->stmnt)
+               mnt_statmnt_disable_fetching(fs->stmnt, stmnt_disabled);
        return 0;
 }
 
diff --git a/libmount/src/fs_statmount.c b/libmount/src/fs_statmount.c
new file mode 100644 (file)
index 0000000..0fad5bd
--- /dev/null
@@ -0,0 +1,389 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * This file is part of libmount from util-linux project.
+ *
+ * Copyright (C) 2024 Karel Zak <kzak@redhat.com>
+ *
+ * libmount is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+/**
+ * SECTION: statmount
+ * @title: statmount setting
+ * @short_description: Fetches information about mount node from the kernel.
+ */
+#include "mountP.h"
+
+#include "mangle.h"
+
+/**
+ * mnt_new_statmnt:
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the filesystem.
+ *
+ * Returns: newly allocated struct libmnt_statmnt.
+ */
+struct libmnt_statmnt *mnt_new_statmnt(void)
+{
+       struct libmnt_statmnt *sm;
+
+       errno = 0;
+       if (ul_statmount(0, 0, 0, NULL, 0, 0) < 0 && errno == ENOSYS) {
+               DBG(FS, ul_debug("statmount: unsuppported"));
+               return NULL;
+       }
+
+       sm = calloc(1, sizeof(*sm));
+       if (!sm)
+               return NULL;
+
+       sm->refcount = 1;
+       DBG(STATMNT, ul_debugobj(sm, "alloc"));
+       return sm;
+}
+
+/**
+ * mnt_ref_statmount:
+ * @sm: statmount setting
+ *
+ * Increments reference counter.
+ */
+void mnt_ref_statmnt(struct libmnt_statmnt *sm)
+{
+       if (sm) {
+               sm->refcount++;
+               /*DBG(STATMNT, ul_debugobj(sm, "ref=%d", sm->refcount));*/
+       }
+}
+
+/**
+ * mnt_unref_statmnt:
+ * @sm: statmount setting
+ *
+ * De-increments reference counter, on zero the @sm is automatically
+ * deallocated.
+ */
+void mnt_unref_statmnt(struct libmnt_statmnt *sm)
+{
+       if (sm) {
+               sm->refcount--;
+               /*DBG(STATMNT, ul_debugobj(sm, "unref=%d", sm->refcount));*/
+               if (sm->refcount <= 0) {
+                       free(sm->buf);
+                       free(sm);
+               }
+       }
+}
+
+/**
+ * mnt_statmnt_set_mask:
+ * @sm: statmount setting
+ * @mask: default mask for statmount() or 0
+ *
+ * Returns: 0 on succees or  or <0 on error.
+ */
+int mnt_statmnt_set_mask(struct libmnt_statmnt *sm, uint64_t mask)
+{
+       if (!sm)
+               return -EINVAL;
+       sm->mask = mask;
+
+       DBG(STATMNT, ul_debugobj(sm, "mask=0x%" PRIx64, sm->mask));
+       return 0;
+}
+
+/**
+ * mnt_statmnt_disable_fetching:
+ * @sm: statmount setting
+ * @disable: 0 or 1
+ *
+ * Disable or enable on-demand statmount() in all libmnt_table of libmnt_fs
+ * onjects that references this @sm.
+ *
+ * Returns: current setting (0 or 1) or <0 on error.
+ *
+ * Since: 2.41
+ */
+int mnt_statmnt_disable_fetching(struct libmnt_statmnt *sm, int disable)
+{
+       int old;
+
+       if (!sm)
+               return -EINVAL;
+       old = sm->disabled;
+       sm->disabled = disable ? 1 : 0;
+
+       DBG(STATMNT, ul_debugobj(sm, "statmount() %s",
+                               sm->disabled ? "off" : "on"));
+       return old;
+}
+
+/**
+ * mnt_fs_refer_statmnt:
+ * @fs: filesystem
+ * @sm: statmount() setting
+ *
+ * Add a reference to the statmount() setting. This setting can be overwritten
+ * if you add @fs into a table that uses a different statmount() setting. See
+ * mnt_table_refer_statmnt(). It is recommended to use the statmount() setting
+ * on a table level if you do not want to work with complete mount table.
+ *
+ * The function mnt_reset_fs() removes this reference too.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ *
+ * Since: 2.41
+ */
+int mnt_fs_refer_statmnt(struct libmnt_fs *fs, struct libmnt_statmnt *sm)
+{
+       if (!fs)
+               return -EINVAL;
+       if (fs->stmnt == sm)
+               return 0;
+
+       mnt_unref_statmnt(fs->stmnt);
+       mnt_ref_statmnt(sm);
+
+       fs->stmnt = sm;
+       return 0;
+}
+
+/**
+ * mnt_fs_get_statmnt:
+ * @fs: filesystem
+ *
+ * Returns: pointer to linmnt_statmnt instance used for the filesystem or NULL
+ *
+ * Since: 2.41
+ */
+struct libmnt_statmnt *mnt_fs_get_statmnt(struct libmnt_fs *fs)
+{
+       return fs ? fs->stmnt : NULL;
+}
+
+#ifdef HAVE_STATMOUNT_API
+
+static inline const char *sm_str(struct ul_statmount *sm, uint32_t offset)
+{
+       return sm->str + offset;
+}
+
+static int apply_statmount(struct libmnt_fs *fs, struct ul_statmount *sm)
+{
+       int rc = 0, merge = 0;
+
+       if (!sm || !sm->size || !fs)
+               return -EINVAL;
+
+       merge = !fs->vfs_optstr || fs->fs_optstr;
+
+       if ((sm->mask & STATMOUNT_FS_TYPE) && !fs->fstype)
+               rc = mnt_fs_set_fstype(fs, sm_str(sm, sm->fs_type));
+
+       if (!rc && (sm->mask & STATMOUNT_MNT_POINT) && !fs->target)
+               rc = mnt_fs_set_target(fs, sm_str(sm, sm->mnt_point));
+
+       if (!rc && (sm->mask & STATMOUNT_MNT_ROOT) && !fs->root)
+               rc = mnt_fs_set_root(fs, sm_str(sm, sm->mnt_root));
+
+       if (!rc && (sm->mask & STATMOUNT_MNT_BASIC)) {
+               if (!fs->propagation)
+                       fs->propagation = sm->mnt_propagation;
+               if (!fs->parent)
+                       fs->parent = sm->mnt_parent_id_old;
+               if (!fs->uniq_parent)
+                       fs->uniq_parent = sm->mnt_parent_id;
+               if (!fs->id)
+                       fs->id = sm->mnt_id_old;
+               if (!fs->uniq_id)
+                       fs->uniq_id = sm->mnt_id;
+               if (!fs->vfs_optstr) {
+                       rc = mnt_optstr_append_option(&fs->vfs_optstr,
+                                       sm->mnt_attr & MOUNT_ATTR_RDONLY ? "ro" : "rw", NULL);
+                       if (!rc && (sm->mnt_attr & MOUNT_ATTR_NOSUID))
+                               rc = mnt_optstr_append_option(&fs->vfs_optstr, "nosuid", NULL);
+                       if (!rc && (sm->mnt_attr & MOUNT_ATTR_NODEV))
+                               rc = mnt_optstr_append_option(&fs->vfs_optstr, "nodev", NULL);
+                       if (!rc && (sm->mnt_attr & MOUNT_ATTR_NOEXEC))
+                               rc = mnt_optstr_append_option(&fs->vfs_optstr, "noexec", NULL);
+                       if (!rc && (sm->mnt_attr & MOUNT_ATTR_NODIRATIME))
+                               rc = mnt_optstr_append_option(&fs->vfs_optstr, "nodiratime", NULL);
+                       if (!rc && (sm->mnt_attr & MOUNT_ATTR_NOSYMFOLLOW))
+                               rc = mnt_optstr_append_option(&fs->vfs_optstr, "nosymfollow", NULL);
+
+                       switch (sm->mnt_attr & MOUNT_ATTR__ATIME) {
+                       case MOUNT_ATTR_STRICTATIME:
+                               rc = mnt_optstr_append_option(&fs->vfs_optstr, "strictatime", NULL);
+                               break;
+                       case MOUNT_ATTR_NOATIME:
+                               rc = mnt_optstr_append_option(&fs->vfs_optstr, "noatime", NULL);
+                               break;
+                       case MOUNT_ATTR_RELATIME:
+                               rc = mnt_optstr_append_option(&fs->vfs_optstr, "relatime", NULL);
+                               break;
+                       }
+
+               }
+       }
+
+       if (!rc && (sm->mask & STATMOUNT_MNT_NS_ID) && !fs->ns_id)
+               fs->ns_id = sm->mnt_ns_id;
+
+       if (!rc && (sm->mask & STATMOUNT_MNT_OPTS) && !fs->fs_optstr)
+               fs->fs_optstr = unmangle(sm_str(sm, sm->mnt_opts), NULL);
+
+       if (!rc && (sm->mask & STATMOUNT_SB_BASIC)) {
+               if (!fs->devno)
+                       fs->devno = makedev(sm->sb_dev_major, sm->sb_dev_minor);
+               if (!fs->fs_optstr) {
+                       rc = mnt_optstr_append_option(&fs->fs_optstr,
+                                       sm->sb_flags & SB_RDONLY ? "ro" : "rw", NULL);
+                       if (!rc && (sm->sb_flags & SB_SYNCHRONOUS))
+                               rc = mnt_optstr_append_option(&fs->fs_optstr, "sync", NULL);
+                       if (!rc && (sm->sb_flags & SB_DIRSYNC))
+                               rc = mnt_optstr_append_option(&fs->fs_optstr, "dirsync", NULL);
+                       if (!rc && (sm->sb_flags & SB_LAZYTIME))
+                               rc = mnt_optstr_append_option(&fs->fs_optstr, "lazytime", NULL);
+               }
+       }
+
+       if (!rc && merge) {
+               free(fs->optstr);
+
+               /* merge VFS and FS options to one string (we do the same in mountinfo parser) */
+               fs->optstr = mnt_fs_strdup_options(fs);
+       }
+
+       fs->flags |= MNT_FS_KERNEL;
+
+       return rc;
+}
+
+/**
+ * mnt_fs_fetch_statmount:
+ * @fs: filesystem instance
+ * @mask: extends the default statmount() mask.
+ *
+ * This function retrieves mount node information from the kernel and applies it
+ * to the @fs. If the @fs is associated with any libmnt_statmnt object (see
+ * mnt_fs_refer_statmnt()), then this object is used to reduce the overhead of
+ * allocating statmount buffer and to define default statmount() mask.
+ *
+ * The @mask is extended (bitwise-OR) by the mask specified for
+ * mnt_statmnt_set_mask() if on-demand fetching is enabled. If the mask is
+ * still 0, then a mask is generated for all missing data in @fs.
+ *
+ * The default namespace is the current namespace. This default can be
+ * overwritten by mnt_fs_set_ns_id(). The namespace ID is also set when @fs
+ * has been created by mnt_table_fetch_statmount() or on-demand (see
+ * mnt_table_enable_listmount()).
+ *
+ * Returns: 0 or negative number in case of error (if @fs is NULL).
+ *
+ * Since: 2.41
+ */
+int mnt_fs_fetch_statmount(struct libmnt_fs *fs, uint64_t mask)
+{
+       int rc = 0, status = 0;
+       struct ul_statmount *buf = NULL;
+       size_t bufsiz = 0;
+       uint64_t ns = 0;
+
+       if (!fs)
+               return -EINVAL;
+
+       DBG(FS, ul_debugobj(fs, "statmount fetch"));
+
+       /* add default mask if on-demand enabled */
+       if (fs->stmnt
+           && !fs->stmnt->disabled
+           && fs->stmnt->mask
+           && !(fs->stmnt_done & fs->stmnt->mask))
+               mask |= fs->stmnt->mask;
+
+       /* ignore repeated requests */
+       if (mask && fs->stmnt_done & mask)
+               return 0;
+
+       /* temporary disable statmount() to avoid recursive
+        * mnt_fs_fetch_statmount() from mnt_fs_get...() functions */
+       if (fs->stmnt)
+               status = mnt_statmnt_disable_fetching(fs->stmnt, 1);
+
+       if (!fs->uniq_id) {
+               if (!fs->target) {
+                       rc = -EINVAL;
+                       goto done;
+               }
+               rc = mnt_id_from_path(fs->target, &fs->uniq_id, NULL);
+               if (rc)
+                       goto done;
+               DBG(FS, ul_debugobj(fs, " uniq-ID=%" PRIu64, fs->uniq_id));
+       }
+
+       /* fetch all missing information by default */
+       if (!mask) {
+               mask = STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC;
+               if (!fs->fstype)
+                       mask |= STATMOUNT_FS_TYPE;
+               if (!fs->target)
+                       mask |= STATMOUNT_MNT_POINT;
+               if (!fs->root)
+                       mask |= STATMOUNT_MNT_ROOT;
+               if (!fs->fs_optstr)
+                       mask |= STATMOUNT_MNT_OPTS;
+               if (!fs->ns_id)
+                       mask |= STATMOUNT_MNT_NS_ID;
+       }
+       if (!mask)
+               goto done;
+
+       if (fs->ns_id)
+               ns = fs->ns_id;
+
+       if (fs->stmnt) {
+               DBG(FS, ul_debugobj(fs, " reuse libmnt_stmnt"));
+               memset(fs->stmnt->buf, 0, fs->stmnt->bufsiz);
+               rc = sys_statmount(fs->uniq_id, 0, mask,
+                                  &fs->stmnt->buf, &fs->stmnt->bufsiz, 0);
+               buf = fs->stmnt->buf;
+               bufsiz = fs->stmnt->bufsiz;
+       } else {
+               DBG(FS, ul_debugobj(fs, " use private buffer"));
+               rc = sys_statmount(fs->uniq_id, 0, mask, &buf, &bufsiz, 0);
+       }
+       DBG(FS, ul_debugobj(fs, " statmount [rc=%d bufsiz=%zu ns=%" PRIu64 " mask: %s%s%s%s%s%s]",
+                               rc, bufsiz, ns,
+                               mask & STATMOUNT_SB_BASIC ? "sb-basic " : "",
+                               mask & STATMOUNT_MNT_BASIC ? "mnt-basic " : "",
+                               mask & STATMOUNT_MNT_ROOT ? "mnt-root " : "",
+                               mask & STATMOUNT_MNT_POINT ? "mnt-point " : "",
+                               mask & STATMOUNT_FS_TYPE ? "fs-type " : "",
+                               mask & STATMOUNT_MNT_OPTS ? "mnt-opts " : ""));
+
+       if (!rc)
+               rc = apply_statmount(fs, buf);
+done:
+
+       if (fs->stmnt)
+               mnt_statmnt_disable_fetching(fs->stmnt, status);
+       else
+               free(buf);
+
+       fs->stmnt_done |= mask;
+       return rc;
+}
+
+#else /* HAVE_STATMOUNT_API */
+
+int mnt_fs_fetch_statmount(struct libmnt_fs *fs __attribute__((__unused__)),
+                          uint64_t mask __attribute__((__unused__)))
+{
+       return -ENOTSUP;
+}
+
+#endif /* HAVE_STATMOUNT_API */
index 146240630b2679d281009d9f97c9fe0cb2e35958..89bae3a0cb8600e8318e66e0826bb1a7f5856493 100644 (file)
@@ -141,6 +141,13 @@ struct libmnt_tabdiff;
  */
 struct libmnt_ns;
 
+/**
+ * libmnt_statmnt
+ *
+ * Setting for statmount()
+ */
+struct libmnt_statmnt;
+
 /*
  * Actions
  */
@@ -541,6 +548,9 @@ extern int mnt_fs_set_uniq_id(struct libmnt_fs *fs, uint64_t id);
 extern int mnt_fs_get_parent_id(struct libmnt_fs *fs);
 extern uint64_t mnt_fs_get_parent_uniq_id(struct libmnt_fs *fs);
 
+extern uint64_t mnt_fs_get_ns(struct libmnt_fs *fs);
+extern int mnt_fs_set_ns(struct libmnt_fs *fs, uint64_t id);
+
 extern dev_t mnt_fs_get_devno(struct libmnt_fs *fs);
 extern pid_t mnt_fs_get_tid(struct libmnt_fs *fs);
 
@@ -571,6 +581,17 @@ extern int mnt_fs_is_regularfs(struct libmnt_fs *fs);
 extern void mnt_free_mntent(struct mntent *mnt);
 extern int mnt_fs_to_mntent(struct libmnt_fs *fs, struct mntent **mnt);
 
+/* fs_statmount.c */
+extern struct libmnt_statmnt *mnt_new_statmnt(void);
+extern void mnt_ref_statmnt(struct libmnt_statmnt *sm);
+extern void mnt_unref_statmnt(struct libmnt_statmnt *sm);
+extern int mnt_statmnt_set_mask(struct libmnt_statmnt *sm, uint64_t mask);
+extern int mnt_statmnt_disable_fetching(struct libmnt_statmnt *sm, int disable);
+
+extern int mnt_fs_refer_statmnt(struct libmnt_fs *fs, struct libmnt_statmnt *sm);
+extern struct libmnt_statmnt *mnt_fs_get_statmnt(struct libmnt_fs *fs);
+extern int mnt_fs_fetch_statmount(struct libmnt_fs *fs, uint64_t mask);
+
 /* tab-parse.c */
 extern struct libmnt_table *mnt_new_table_from_file(const char *filename)
                        __ul_attribute__((warn_unused_result));
@@ -599,6 +620,8 @@ extern int mnt_reset_table(struct libmnt_table *tb);
 extern int mnt_table_get_nents(struct libmnt_table *tb);
 extern int mnt_table_is_empty(struct libmnt_table *tb);
 
+extern int mnt_table_refer_statmnt(struct libmnt_table *tb, struct libmnt_statmnt *sm);
+
 extern int mnt_table_set_userdata(struct libmnt_table *tb, void *data);
 extern void *mnt_table_get_userdata(struct libmnt_table *tb);
 
index af7b2ac21a62b06f448ab4ab377a8729fd1f8692..761eabdc9262f9583996573602777e676eb19969 100644 (file)
@@ -383,8 +383,19 @@ MOUNT_2_40 {
 } MOUNT_2_39;
 
 MOUNT_2_41 {
-       mnt_fs_get_uniq_id;
+       mnt_fs_fetch_statmount;
+       mnt_fs_get_ns_id;
        mnt_fs_get_parent_uniq_id;
+       mnt_fs_get_statmnt;
+       mnt_fs_get_uniq_id;
+       mnt_fs_refer_statmnt;
+       mnt_fs_set_ns;
        mnt_fs_set_uniq_id;
        mnt_id_from_path;
+       mnt_new_statmnt;
+       mnt_ref_statmnt;
+       mnt_statmnt_disable_fetching;
+       mnt_statmnt_set_mask;
+       mnt_table_refer_statmnt;
+       mnt_unref_statmnt;
 } MOUNT_2_40;
index b1374ceb226030f50bc831dceaf05da0c42b6e68..6ea1d89cf721a676274e5d127910d8659e7db7d1 100644 (file)
@@ -52,6 +52,7 @@
 #define MNT_DEBUG_VERITY       (1 << 14)
 #define MNT_DEBUG_HOOK         (1 << 15)
 #define MNT_DEBUG_OPTLIST      (1 << 16)
+#define MNT_DEBUG_STATMNT      (1 << 17)
 
 #define MNT_DEBUG_ALL          0xFFFFFF
 
@@ -189,6 +190,20 @@ struct libmnt_iter {
        } while(0)
 
 
+/*
+ * statmount setting; shared between tables and filesystems
+ */
+struct libmnt_statmnt {
+       int             refcount;
+       uint64_t        mask;           /* default statmount() mask */
+
+       struct ul_statmount *buf;
+       size_t bufsiz;
+
+       unsigned int    disabled: 1;    /* enable or disable statmount() */
+};
+
+
 /*
  * This struct represents one entry in a fstab/mountinfo file.
  * (note that fstab[1] means the first column from fstab, and so on...)
@@ -204,6 +219,8 @@ struct libmnt_fs {
 
        int             id;             /* mountinfo[1]: ID */
        uint64_t        uniq_id;        /* unique node ID; statx(STATX_MNT_ID_UNIQUE); statmount->mnt_id */
+       uint64_t        ns_id;          /* namespace ID; statmount->mnt_ns_id */
+
        int             parent;         /* mountinfo[2]: parent */
        uint64_t        uniq_parent;    /* unique parent ID; statmount->mnt_parent_id */
        dev_t           devno;          /* mountinfo[3]: st_dev */
@@ -241,6 +258,9 @@ struct libmnt_fs {
        int             flags;          /* MNT_FS_* flags */
        pid_t           tid;            /* /proc/<tid>/mountinfo otherwise zero */
 
+       uint64_t        stmnt_done;     /* mask of already called masks */
+       struct libmnt_statmnt *stmnt;   /* statmount() stuff */
+
        char            *comment;       /* fstab comment */
 
        void            *userdata;      /* library independent data */
@@ -255,6 +275,9 @@ struct libmnt_fs {
 #define MNT_FS_KERNEL  (1 << 4) /* data from /proc/{mounts,self/mountinfo} */
 #define MNT_FS_MERGED  (1 << 5) /* already merged data from /run/mount/utab */
 
+#define mnt_fs_want_statmount(_x, _m) \
+               ((_x)->stmnt && !(_x)->stmnt->disabled && !((_x)->stmnt_done & (_m)))
+
 /*
  * fstab/mountinfo file
  */
@@ -274,6 +297,8 @@ struct libmnt_table {
        int             (*fltrcb)(struct libmnt_fs *fs, void *data);
        void            *fltrcb_data;
 
+       struct libmnt_statmnt   *stmnt; /* statmount() stuff */
+
        int             noautofs;       /* ignore autofs mounts */
 
        struct list_head        ents;   /* list of entries (libmnt_fs) */
index 2b89552eb2fbf11a84d0439eb37785975a22f32e..ebb80432831bea84215aaceb5d0466e7cdcbbc09 100644 (file)
@@ -172,6 +172,10 @@ void mnt_free_table(struct libmnt_table *tb)
        mnt_unref_cache(tb->cache);
        free(tb->comm_intro);
        free(tb->comm_tail);
+
+       mnt_unref_statmnt(tb->stmnt);
+       tb->stmnt = NULL;
+
        free(tb);
 }
 
@@ -395,6 +399,40 @@ struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb)
        return tb ? tb->cache : NULL;
 }
 
+/**
+ * mnt_table_refer_statmnt:
+ * @tb: pointer to tab
+ * @sm: statmount setting or NULL
+ *
+ * Add a reference to the statmount() setting in the table (see
+ * mnt_new_statmnt() function, etc.).  This reference will automatically be
+ * used for any newly added filesystems in the @tb, eliminating the need for
+ * extra mnt_fs_refer_statmnt() calls for each filesystem.
+ *
+ * The reference is not removed by mnt_reset_table(), use NULL as @sm to
+ * remove the reference.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ *
+ * Since: 2.41
+ */
+int mnt_table_refer_statmnt(struct libmnt_table *tb, struct libmnt_statmnt *sm)
+{
+       if (!tb)
+               return -EINVAL;
+       if (tb->stmnt == sm)
+               return 0;
+
+       mnt_unref_statmnt(tb->stmnt);
+       mnt_ref_statmnt(sm);
+
+       DBG(TAB, ul_debugobj(tb, "refer statmnt"));
+
+       tb->stmnt = sm;
+       return 0;
+}
+
+
 /**
  * mnt_table_find_fs:
  * @tb: tab pointer
@@ -455,6 +493,10 @@ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
 
        DBG(TAB, ul_debugobj(tb, "add entry: %s %s",
                        mnt_fs_get_source(fs), mnt_fs_get_target(fs)));
+
+       if (tb->stmnt)
+               mnt_fs_refer_statmnt(fs, tb->stmnt);
+
        return 0;
 }
 
@@ -474,6 +516,10 @@ static int __table_insert_fs(
 
        DBG(TAB, ul_debugobj(tb, "insert entry: %s %s",
                        mnt_fs_get_source(fs), mnt_fs_get_target(fs)));
+
+       if (tb->stmnt)
+               mnt_fs_refer_statmnt(fs, tb->stmnt);
+
        return 0;
 }