From: Karel Zak Date: Wed, 15 Jul 2026 10:21:29 +0000 (+0200) Subject: libmount: add pluggable VFS I/O support X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fcaf3d82e765e0fc8030b0c8a4da88775354f49b;p=thirdparty%2Futil-linux.git libmount: add pluggable VFS I/O support 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 --- diff --git a/libmount/src/cache.c b/libmount/src/cache.c index e02c653f5..ed3f44c24 100644 --- a/libmount/src/cache.c +++ b/libmount/src/cache.c @@ -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, diff --git a/libmount/src/context.c b/libmount/src/context.c index 9ae857eb4..782ecd96a 100644 --- a/libmount/src/context.c +++ b/libmount/src/context.c @@ -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) diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index ca9a1ad23..f9a580792 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -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); diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index de757eda4..cbccd89b6 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -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; diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 0f6ee4f6d..d8a8da350 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -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 */ diff --git a/libmount/src/tab.c b/libmount/src/tab.c index 98ccd7cf3..a1d79f291 100644 --- a/libmount/src/tab.c +++ b/libmount/src/tab.c @@ -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 diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c index ddbdebf1c..ecb740032 100644 --- a/libmount/src/tab_parse.c +++ b/libmount/src/tab_parse.c @@ -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);