]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: add pluggable VFS I/O support
authorKarel Zak <kzak@redhat.com>
Wed, 15 Jul 2026 10:21:29 +0000 (12:21 +0200)
committerKarel Zak <kzak@redhat.com>
Thu, 16 Jul 2026 08:30:00 +0000 (10:30 +0200)
Add three new public APIs for pluggable VFS I/O operations:

- mnt_context_set_vfs(cxt, ops) -- set VFS on mount context (owned copy)
- mnt_cache_refer_vfs(cache, vfs) -- set VFS reference on cache (borrowed)
- mnt_table_refer_vfs(tb, vfs) -- set VFS reference on table (borrowed)

The VFS operations are automatically propagated from the context to its
cache and tables when they are created or set.

Convert blkid probe calls in cache.c (read_from_blkid, fstype_from_blkid)
from blkid_new_probe_from_filename() to the VFS-aware 3-step pattern:
blkid_new_probe() + blkid_probe_set_vfs() + blkid_probe_open_device().

Convert mnt_table_parse_file() to use ul_vfs_fopen() for VFS-aware
file opening.

Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/src/cache.c
libmount/src/context.c
libmount/src/libmount.h.in
libmount/src/libmount.sym
libmount/src/mountP.h
libmount/src/tab.c
libmount/src/tab_parse.c

index e02c653f51758480ebb09436b23048b5f4ea3351..ed3f44c246bbdc4454513324d7c19170328f2c85 100644 (file)
@@ -65,6 +65,8 @@ struct libmnt_cache {
        int                     probe_sb_extra; /* extra BLKID_SUBLKS_* flags */
        bool                    noprobe;        /* disable libblkid device probing */
 
+       const struct ul_vfs_ops *vfs;           /* borrowed VFS ops (not owned) */
+
        /* blkid_evaluate_tag() works in two ways:
         *
         * 1/ all tags are evaluated by udev /dev/disk/by-* symlinks,
@@ -221,6 +223,27 @@ void mnt_cache_enable_noprobe(struct libmnt_cache *cache, int enable)
                cache->noprobe = !!enable;
 }
 
+/**
+ * mnt_cache_refer_vfs:
+ * @cache: cache pointer
+ * @vfs: VFS operations or NULL
+ *
+ * Set reference to VFS I/O operations. The cache does not own the @vfs
+ * pointer -- the caller is responsible for its lifetime (e.g. the mount
+ * context owns it).
+ *
+ * The VFS is used for blkid device probing (see mnt_cache_read_tags()).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_cache_refer_vfs(struct libmnt_cache *cache, const struct ul_vfs_ops *vfs)
+{
+       if (!cache)
+               return -EINVAL;
+       cache->vfs = vfs;
+       return 0;
+}
+
 /* note that the @key could be the same pointer as @value */
 static int cache_add_entry(struct libmnt_cache *cache, char *key,
                                        char *value, int flag)
@@ -393,10 +416,17 @@ static int read_from_blkid(struct libmnt_cache *cache, const char *devname)
 
        DBG_OBJ(CACHE, cache, ul_debug("%s: reading from blkid", devname));
 
-       pr =  blkid_new_probe_from_filename(devname);
+       pr = blkid_new_probe();
        if (!pr)
                return -EINVAL;
 
+       if (cache->vfs)
+               blkid_probe_set_vfs(pr, cache->vfs);
+       if (blkid_probe_open_device(pr, devname, 0)) {
+               blkid_free_probe(pr);
+               return -errno;
+       }
+
        blkid_probe_enable_superblocks(pr, 1);
        blkid_probe_set_superblocks_flags(pr,
                        BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
@@ -583,16 +613,23 @@ static char *fstype_from_cache(const char *devname, struct libmnt_cache *cache)
        return val;
 }
 
-static char *fstype_from_blkid(const char *devname, int *ambi)
+static char *fstype_from_blkid(const char *devname, int *ambi,
+                              const struct ul_vfs_ops *vfs)
 {
        blkid_probe pr;
        const char *data;
        char *type = NULL;
        int rc;
 
-       pr =  blkid_new_probe_from_filename(devname);
+       pr = blkid_new_probe();
        if (!pr)
                return NULL;
+       if (vfs)
+               blkid_probe_set_vfs(pr, vfs);
+       if (blkid_probe_open_device(pr, devname, 0)) {
+               blkid_free_probe(pr);
+               return NULL;
+       }
 
        blkid_probe_enable_superblocks(pr, 1);
        blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
@@ -627,7 +664,7 @@ char *mnt_get_fstype(const char *devname, int *ambi, struct libmnt_cache *cache)
        if (cache)
                return fstype_from_cache(devname, cache);
 
-       return fstype_from_blkid(devname, ambi);
+       return fstype_from_blkid(devname, ambi, NULL);
 }
 
 static char *canonicalize_path_and_cache(const char *path,
index 9ae857eb40ff8fdad1ec2c0cb19737611ff7dbaf..782ecd96a946e4c99177dfe3e89dd3c6c7c5d53a 100644 (file)
@@ -104,6 +104,12 @@ void mnt_free_context(struct libmnt_context *cxt)
        free(cxt->optstr_pattern);
        free(cxt->tgt_prefix);
 
+       if (cxt->vfs) {
+               mnt_table_refer_vfs(cxt->fstab, NULL);
+               mnt_table_refer_vfs(cxt->mountinfo, NULL);
+               mnt_cache_refer_vfs(cxt->cache, NULL);
+               free(cxt->vfs);
+       }
        mnt_unref_table(cxt->fstab);
        mnt_unref_cache(cxt->cache);
        mnt_unref_fs(cxt->fs);
@@ -1472,6 +1478,8 @@ int mnt_context_get_fstab(struct libmnt_context *cxt, struct libmnt_table **tb)
                        return -MNT_ERR_NAMESPACE;
 
                mnt_table_set_cache(cxt->fstab, mnt_context_get_cache(cxt));
+               if (cxt->vfs)
+                       mnt_table_refer_vfs(cxt->fstab, cxt->vfs);
                rc = mnt_table_parse_fstab(cxt->fstab, NULL);
 
                if (!mnt_context_switch_ns(cxt, ns_old))
@@ -1516,6 +1524,8 @@ int mnt_context_get_mountinfo(struct libmnt_context *cxt, struct libmnt_table **
                                        cxt->table_fltrcb_data);
 
                mnt_table_set_cache(cxt->mountinfo, mnt_context_get_cache(cxt));
+               if (cxt->vfs)
+                       mnt_table_refer_vfs(cxt->mountinfo, cxt->vfs);
        }
 
        /* Read the table; it's empty, because this first mnt_context_get_mountinfo()
@@ -1678,6 +1688,8 @@ int mnt_context_get_table(struct libmnt_context *cxt,
 
        if (cxt->table_errcb)
                mnt_table_set_parser_errcb(*tb, cxt->table_errcb);
+       if (cxt->vfs)
+               mnt_table_refer_vfs(*tb, cxt->vfs);
 
        ns_old = mnt_context_switch_target_ns(cxt);
        if (!ns_old)
@@ -1728,6 +1740,45 @@ int mnt_context_set_tables_errcb(struct libmnt_context *cxt,
        return 0;
 }
 
+/**
+ * mnt_context_set_vfs:
+ * @cxt: mount context
+ * @ops: VFS operations or NULL
+ *
+ * Set pluggable VFS I/O operations. The library stores a private copy of @ops.
+ * If @ops is NULL, the VFS is reset to defaults (standard libc I/O).
+ *
+ * The VFS is automatically propagated to the cache (see mnt_cache_refer_vfs())
+ * and to the tables (see mnt_table_refer_vfs()) when they are created or set.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_vfs(struct libmnt_context *cxt, const struct ul_vfs_ops *ops)
+{
+       if (!cxt)
+               return -EINVAL;
+
+       if (!ops) {
+               free(cxt->vfs);
+               cxt->vfs = NULL;
+               return 0;
+       }
+       if (!cxt->vfs) {
+               cxt->vfs = calloc(1, sizeof(*cxt->vfs));
+               if (!cxt->vfs)
+                       return -ENOMEM;
+       }
+       ul_vfs_init(cxt->vfs, ops);
+
+       if (cxt->cache)
+               mnt_cache_refer_vfs(cxt->cache, cxt->vfs);
+       if (cxt->fstab)
+               mnt_table_refer_vfs(cxt->fstab, cxt->vfs);
+       if (cxt->mountinfo)
+               mnt_table_refer_vfs(cxt->mountinfo, cxt->vfs);
+       return 0;
+}
+
 /**
  * mnt_context_set_cache:
  * @cxt: mount context
@@ -1758,6 +1809,8 @@ int mnt_context_set_cache(struct libmnt_context *cxt, struct libmnt_cache *cache
 
        cxt->cache = cache;
 
+       if (cache && cxt->vfs)
+               mnt_cache_refer_vfs(cache, cxt->vfs);
        if (cxt->mountinfo)
                mnt_table_set_cache(cxt->mountinfo, cache);
        if (cxt->fstab)
index ca9a1ad231909aed7cd2c8c5fa0a15e783186455..f9a5807924b6784bd57660a35de48b7e6766d58b 100644 (file)
@@ -147,6 +147,24 @@ struct libmnt_ns;
  */
 struct libmnt_statmnt;
 
+#ifndef UL_VFS_OPS_DEFINED
+#define UL_VFS_OPS_DEFINED
+
+struct ul_vfs_ops {
+       size_t size;
+
+       ssize_t (*vfs_read)(int fd, void *buf, size_t count);
+       ssize_t (*vfs_write)(int fd, const void *buf, size_t count);
+       int     (*vfs_open)(const char *pathname, int flags, mode_t mode);
+       int     (*vfs_close)(int fd);
+       off_t   (*vfs_lseek)(int fd, off_t offset, int whence);
+       int     (*vfs_fsync)(int fd);
+
+       FILE   *(*vfs_fopen)(const char *pathname, const char *mode);
+};
+
+#endif /* UL_VFS_OPS_DEFINED */
+
 /*
  * Actions
  */
@@ -388,6 +406,7 @@ extern void mnt_unref_cache(struct libmnt_cache *cache);
 extern int mnt_cache_set_targets(struct libmnt_cache *cache,
                                struct libmnt_table *mountinfo);
 extern int mnt_cache_set_sbprobe(struct libmnt_cache *cache, int flags);
+extern int mnt_cache_refer_vfs(struct libmnt_cache *cache, const struct ul_vfs_ops *vfs);
 extern int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname);
 
 extern int mnt_cache_device_has_tag(struct libmnt_cache *cache,
@@ -640,6 +659,7 @@ extern const char *mnt_table_get_trailing_comment(struct libmnt_table *tb);
 extern int mnt_table_append_trailing_comment(struct libmnt_table *tb, const char *comm);
 
 extern int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc);
+extern int mnt_table_refer_vfs(struct libmnt_table *tb, const struct ul_vfs_ops *vfs);
 extern struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb);
 extern int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs);
 extern int mnt_table_find_fs(struct libmnt_table *tb, struct libmnt_fs *fs);
@@ -923,6 +943,8 @@ extern int mnt_context_get_mtab(struct libmnt_context *cxt,
 extern int mnt_context_get_table(struct libmnt_context *cxt,
                                const char *filename,
                                struct libmnt_table **tb);
+extern int mnt_context_set_vfs(struct libmnt_context *cxt,
+                               const struct ul_vfs_ops *ops);
 extern int mnt_context_set_cache(struct libmnt_context *cxt,
                                 struct libmnt_cache *cache);
 extern struct libmnt_cache *mnt_context_get_cache(struct libmnt_context *cxt);
index de757eda48272cd4448634923bb4d16a8659a7f4..cbccd89b68d24c81c3a4493e5e28cd927715ec19 100644 (file)
@@ -426,3 +426,9 @@ MOUNT_2_43 {
        mnt_table_disable_useropts;
        mnt_table_parse_utab;
 } MOUNT_2_42;
+
+MOUNT_2_44 {
+       mnt_cache_refer_vfs;
+       mnt_context_set_vfs;
+       mnt_table_refer_vfs;
+} MOUNT_2_43;
index 0f6ee4f6df7285d911231b866fb2722da0c4b3a3..d8a8da35021ef1856e10ef0fadd3d3c2c0d98a1f 100644 (file)
@@ -31,6 +31,7 @@
 #include "list.h"
 #include "debug.h"
 #include "buffer.h"
+#include "vfs.h"
 #include "libmount.h"
 
 #include "mountutils.h"
@@ -327,6 +328,7 @@ struct libmnt_table {
        char            *comm_tail;     /* Last comment in file */
 
        struct libmnt_cache *cache;             /* canonicalized paths/tags cache */
+       const struct ul_vfs_ops *vfs;           /* borrowed VFS ops (not owned) */
 
         int            (*errcb)(struct libmnt_table *tb,
                                 const char *filename, int line);
@@ -477,6 +479,7 @@ struct libmnt_context
        const void      *mountdata;     /* final mount(2) data, string or binary data */
 
        struct libmnt_cache     *cache;         /* paths cache */
+       struct ul_vfs_ops       *vfs;           /* pluggable I/O ops (owned, or NULL) */
        struct libmnt_lock      *lock;          /* utab lock */
        struct libmnt_update    *update;        /* utab update */
 
index 98ccd7cf32cae0b14db7db02ea72ecd9a3e755d1..a1d79f291a2b85decba1a2e224e900c92b134530 100644 (file)
@@ -393,6 +393,26 @@ int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc)
        return 0;
 }
 
+/**
+ * mnt_table_refer_vfs:
+ * @tb: pointer to tab
+ * @vfs: VFS operations or NULL
+ *
+ * Set reference to VFS I/O operations. The table does not own the @vfs
+ * pointer -- the caller is responsible for its lifetime.
+ *
+ * The VFS is used for table parsing (see mnt_table_parse_file()).
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_refer_vfs(struct libmnt_table *tb, const struct ul_vfs_ops *vfs)
+{
+       if (!tb)
+               return -EINVAL;
+       tb->vfs = vfs;
+       return 0;
+}
+
 /**
  * mnt_table_get_cache:
  * @tb: pointer to tab
index ddbdebf1caaaaace2363f093a2737953c055b0ed..ecb7400320203feb66f40f586007f73713e48b0e 100644 (file)
@@ -844,7 +844,7 @@ int mnt_table_parse_file(struct libmnt_table *tb, const char *filename)
        if (!filename || !tb)
                return -EINVAL;
 
-       f = fopen(filename, "r" UL_CLOEXECSTR);
+       f = ul_vfs_fopen(tb->vfs, filename, "r" UL_CLOEXECSTR);
        if (f) {
                rc = mnt_table_parse_stream(tb, f, filename);
                fclose(f);