]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libfdisk: add pluggable VFS I/O support
authorKarel Zak <kzak@redhat.com>
Wed, 29 Jul 2026 10:12:27 +0000 (12:12 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 3 Aug 2026 12:49:55 +0000 (14:49 +0200)
Add fdisk_set_vfs() to allow replacing standard I/O syscalls
(read/write/lseek/open/close/fsync) with custom implementations,
matching the existing VFS support in libblkid and libmount.

All direct I/O calls in the library (context.c, utils.c, dos.c,
gpt.c, bsd.c, sgi.c, sun.c) are replaced with ul_vfs_* wrappers
that fall back to standard syscalls when no VFS is set.

The VFS is also propagated to libblkid probes used in wipe.c
for collision detection and signature wiping.

Signed-off-by: Karel Zak <kzak@redhat.com>
12 files changed:
libfdisk/docs/libfdisk-sections.txt
libfdisk/src/bsd.c
libfdisk/src/context.c
libfdisk/src/dos.c
libfdisk/src/fdiskP.h
libfdisk/src/gpt.c
libfdisk/src/libfdisk.h.in
libfdisk/src/libfdisk.sym
libfdisk/src/sgi.c
libfdisk/src/sun.c
libfdisk/src/utils.c
libfdisk/src/wipe.c

index 53fa1a179d6701617766383c2859f1956e05f02f..87223162845c2681a16241d38fa8c6905fb851af 100644 (file)
@@ -356,6 +356,7 @@ fdisk_set_first_lba
 fdisk_set_last_lba
 fdisk_set_size_unit
 fdisk_set_unit
+fdisk_set_vfs
 FDISK_SINGULAR
 fdisk_unref_context
 fdisk_use_cylinders
index 6ecbdef6026aeda3b838090c8fd76355deda5cf6..92931e95f583b1086d5f45ea48aa71b00f5641f2 100644 (file)
@@ -638,19 +638,19 @@ static int bsd_get_bootstrap(struct fdisk_context *cxt,
 {
        int fd;
 
-       if ((fd = open(path, O_RDONLY)) < 0) {
+       if ((fd = ul_vfs_open(cxt->vfs, path, O_RDONLY, 0)) < 0) {
                fdisk_warn(cxt, _("cannot open %s"), path);
                return -errno;
        }
 
-       if (ul_read_all(fd, ptr, size) != size) {
+       if (ul_vfs_read_all(cxt->vfs, fd, ptr, size) != size) {
                fdisk_warn(cxt, _("cannot read %s"), path);
-               close(fd);
+               ul_vfs_close(cxt->vfs, fd);
                return -errno;
        }
 
        fdisk_info(cxt, _("The bootstrap file %s successfully loaded."), path);
-       close (fd);
+       ul_vfs_close(cxt->vfs, fd);
        return 0;
 }
 
@@ -715,12 +715,12 @@ int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt)
 #if defined (__alpha__)
        alpha_bootblock_checksum(l->bsdbuffer);
 #endif
-       if (lseek(cxt->dev_fd, (off_t) sector * DEFAULT_SECTOR_SIZE, SEEK_SET) == -1) {
+       if (ul_vfs_lseek(cxt->vfs, cxt->dev_fd, (off_t) sector * DEFAULT_SECTOR_SIZE, SEEK_SET) == -1) {
                fdisk_warn(cxt, _("seek on %s failed"), cxt->dev_path);
                rc = -errno;
                goto done;
        }
-       if (ul_write_all(cxt->dev_fd, l->bsdbuffer, BSD_BBSIZE)) {
+       if (ul_vfs_write_all(cxt->vfs, cxt->dev_fd, l->bsdbuffer, BSD_BBSIZE)) {
                fdisk_warn(cxt, _("cannot write %s"), cxt->dev_path);
                rc = -errno;
                goto done;
@@ -837,9 +837,9 @@ static int bsd_readlabel(struct fdisk_context *cxt)
                 * partition. Note that DOS uses native sector size. */
                offset = dos_partition_get_start(l->dos_part) * cxt->sector_size;
 
-       if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1)
+       if (ul_vfs_lseek(cxt->vfs, cxt->dev_fd, offset, SEEK_SET) == -1)
                return -1;
-       if (ul_read_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer)) < 0)
+       if (ul_vfs_read_all(cxt->vfs, cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer)) < 0)
                return errno ? -errno : -1;
 
        /* The offset to begin of the disk label. Note that BSD uses
@@ -899,11 +899,11 @@ static int bsd_write_disklabel(struct fdisk_context *cxt)
        /* Write the checksum to the end of the first sector. */
        alpha_bootblock_checksum(l->bsdbuffer);
 #endif
-       if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1) {
+       if (ul_vfs_lseek(cxt->vfs, cxt->dev_fd, offset, SEEK_SET) == -1) {
                fdisk_warn(cxt, _("seek on %s failed"), cxt->dev_path);
                return -errno;
        }
-       if (ul_write_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer))) {
+       if (ul_vfs_write_all(cxt->vfs, cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer))) {
                fdisk_warn(cxt, _("cannot write %s"), cxt->dev_path);
                return -errno;
        }
index 0c5cf52bc3d50281c226767ace9e228428c90d15..2c407029e110a328ba2d064888bc9a7717a491ba 100644 (file)
@@ -110,6 +110,7 @@ static int init_nested_from_parent(struct fdisk_context *cxt, int isnew)
        cxt->user_geom =        parent->user_geom;
        cxt->user_log_sector =  parent->user_log_sector;
        cxt->user_pyh_sector =  parent->user_pyh_sector;
+       cxt->vfs =              parent->vfs;
 
        /* parent <--> nested independent setting, initialize for new nested
         * contexts only */
@@ -347,6 +348,47 @@ int fdisk_enable_bootbits_protection(struct fdisk_context *cxt, int enable)
        cxt->protect_bootbits = enable ? 1 : 0;
        return 0;
 }
+
+/**
+ * fdisk_set_vfs:
+ * @cxt: fdisk context
+ * @ops: VFS operations or NULL to reset to defaults
+ *
+ * Sets custom I/O operations for the context. This allows replacing
+ * standard read/write/lseek/etc. with custom implementations
+ * (e.g., fiber-aware I/O).
+ *
+ * The @ops struct is copied into a private allocation owned by the
+ * context. The caller sets ops->size to sizeof(struct ul_vfs_ops) to
+ * enable forward/backward compatibility. NULL function pointers fall
+ * back to standard syscalls. Passing @ops as NULL frees the private
+ * copy and resets the context to default (direct syscall) I/O.
+ *
+ * Note: fdisk_set_vfs() should be called before fdisk_assign_device().
+ *
+ * Since: 2.43
+ *
+ * Returns: 0 on success, or <0 in case of error.
+ */
+int fdisk_set_vfs(struct fdisk_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);
+       return 0;
+}
+
 /**
  * fdisk_disable_dialogs
  * @cxt: fdisk context
@@ -560,9 +602,11 @@ static void reset_context(struct fdisk_context *cxt)
        } else {
                /* we close device only in primary context */
                if (cxt->dev_fd > -1 && cxt->is_priv)
-                       close(cxt->dev_fd);
+                       ul_vfs_close(cxt->vfs, cxt->dev_fd);
                DBG_OBJ(CXT, cxt, ul_debug("  freeing firstsector"));
                free(cxt->firstsector);
+               free(cxt->vfs);
+               cxt->vfs = NULL;
        }
 
        free(cxt->dev_path);
@@ -715,11 +759,11 @@ int fdisk_assign_device(struct fdisk_context *cxt,
                flags |= (O_RDWR | O_EXCL);
 
        errno = 0;
-       fd = open(fname,flags);
+       fd = ul_vfs_open(cxt->vfs, fname, flags, 0);
        if (fd < 0 && errno == EBUSY && (flags & O_EXCL)) {
                flags &= ~O_EXCL;
                errno = 0;
-               fd = open(fname, flags);
+               fd = ul_vfs_open(cxt->vfs, fname, flags, 0);
        }
 
        if (fd < 0) {
@@ -730,7 +774,7 @@ int fdisk_assign_device(struct fdisk_context *cxt,
 
        rc = fdisk_assign_fd(cxt, fd, fname, readonly, 1, flags & O_EXCL);
        if (rc)
-               close(fd);
+               ul_vfs_close(cxt->vfs, fd);
        return rc;
 }
 
@@ -784,14 +828,14 @@ int fdisk_deassign_device(struct fdisk_context *cxt, int nosync)
        DBG_OBJ(CXT, cxt, ul_debug("de-assigning device %s", cxt->dev_path));
 
        if (cxt->readonly && cxt->is_priv)
-               close(cxt->dev_fd);
+               ul_vfs_close(cxt->vfs, cxt->dev_fd);
        else {
-               if (fsync(cxt->dev_fd)) {
+               if (ul_vfs_fsync(cxt->vfs, cxt->dev_fd)) {
                        fdisk_warn(cxt, _("%s: fsync device failed"),
                                        cxt->dev_path);
                        return -errno;
                }
-               if (cxt->is_priv && close(cxt->dev_fd)) {
+               if (cxt->is_priv && ul_vfs_close(cxt->vfs, cxt->dev_fd)) {
                        fdisk_warn(cxt, _("%s: close device failed"),
                                        cxt->dev_path);
                        return -errno;
index 1731c1ef123637f38f56878afa3a2947182c52a7..68fd4e0509f2e9b50fff68ba318e02985955466e 100644 (file)
@@ -229,7 +229,7 @@ static int seek_sector(struct fdisk_context *cxt, fdisk_sector_t secno)
 {
        off_t offset = (off_t) secno * cxt->sector_size;
 
-       return lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1 ? -errno : 0;
+       return ul_vfs_lseek(cxt->vfs, cxt->dev_fd, offset, SEEK_SET) == (off_t) -1 ? -errno : 0;
 }
 
 static int read_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
@@ -241,7 +241,7 @@ static int read_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
        if (rc < 0)
                return rc;
 
-       r = read(cxt->dev_fd, buf, cxt->sector_size);
+       r = ul_vfs_read(cxt->vfs, cxt->dev_fd, buf, cxt->sector_size);
        if (r == (ssize_t) cxt->sector_size)
                return 0;
        if (r < 0)
@@ -2126,7 +2126,7 @@ static int write_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
 
        DBG(LABEL, ul_debug("DOS: writing to sector %ju", (uintmax_t) secno));
 
-       if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
+       if (ul_vfs_write(cxt->vfs, cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
                return -errno;
        return 0;
 }
index d9a9d96661553982bc126d0df95038e71d046125..446f3962f735ccabfc7eba883650d1e39516d377 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "c.h"
 #include "libfdisk.h"
+#include "vfs.h"
 
 #include "list.h"
 #include "debug.h"
@@ -438,6 +439,8 @@ struct fdisk_context {
 
        struct fdisk_context    *parent;        /* for nested PT */
        struct fdisk_script     *script;        /* what we want to follow */
+
+       struct ul_vfs_ops       *vfs;           /* pluggable I/O ops (owned, or NULL) */
 };
 
 /* table */
index cf4f778085878d18b7dcd3f465240edff0bea3f3..b8db94d34e761d055a6bfa540d2d1255a2672a02 100644 (file)
@@ -999,9 +999,9 @@ static ssize_t read_lba(struct fdisk_context *cxt, uint64_t lba,
 {
        off_t offset = lba * cxt->sector_size;
 
-       if (lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1)
+       if (ul_vfs_lseek(cxt->vfs, cxt->dev_fd, offset, SEEK_SET) == (off_t) -1)
                return -1;
-       return (size_t)read(cxt->dev_fd, buffer, bytes) != bytes;
+       return (size_t)ul_vfs_read(cxt->vfs, cxt->dev_fd, buffer, bytes) != bytes;
 }
 
 
@@ -1033,10 +1033,10 @@ static unsigned char *gpt_read_entries(struct fdisk_context *cxt,
        offset = (off_t) le64_to_cpu(header->partition_entry_lba) *
                       cxt->sector_size;
 
-       if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+       if (offset != ul_vfs_lseek(cxt->vfs, cxt->dev_fd, offset, SEEK_SET))
                goto fail;
 
-       ssz = read(cxt->dev_fd, ret, sz);
+       ssz = ul_vfs_read(cxt->vfs, cxt->dev_fd, ret, sz);
        if (ssz < 0 || (size_t) ssz != sz)
                goto fail;
 
@@ -2064,10 +2064,10 @@ static int gpt_set_partition(struct fdisk_context *cxt, size_t n,
 
 static int gpt_read(struct fdisk_context *cxt, off_t offset, void *buf, size_t count)
 {
-       if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+       if (offset != ul_vfs_lseek(cxt->vfs, cxt->dev_fd, offset, SEEK_SET))
                return -errno;
 
-       if (ul_read_all(cxt->dev_fd, buf, count))
+       if (ul_vfs_read_all(cxt->vfs, cxt->dev_fd, buf, count))
                return -errno;
 
        DBG(GPT, ul_debug("  read OK [offset=%zu, size=%zu]",
@@ -2077,13 +2077,13 @@ static int gpt_read(struct fdisk_context *cxt, off_t offset, void *buf, size_t c
 
 static int gpt_write(struct fdisk_context *cxt, off_t offset, void *buf, size_t count)
 {
-       if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+       if (offset != ul_vfs_lseek(cxt->vfs, cxt->dev_fd, offset, SEEK_SET))
                return -errno;
 
-       if (ul_write_all(cxt->dev_fd, buf, count))
+       if (ul_vfs_write_all(cxt->vfs, cxt->dev_fd, buf, count))
                return -errno;
 
-       if (fsync(cxt->dev_fd) != 0)
+       if (ul_vfs_fsync(cxt->vfs, cxt->dev_fd) != 0)
                return -errno;
 
        DBG(GPT, ul_debug("  write OK [offset=%zu, size=%zu]",
index c23ad475c26839ffabc16aaec360b82c5a4c0945..0aa8f953356820a3184eb04626cab51e7b2e7d50 100644 (file)
@@ -240,6 +240,25 @@ int fdisk_get_devfd(struct fdisk_context *cxt);
 dev_t fdisk_get_devno(struct fdisk_context *cxt);
 const char *fdisk_get_devmodel(struct fdisk_context *cxt);
 
+#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 */
+
+extern int fdisk_set_vfs(struct fdisk_context *cxt, const struct ul_vfs_ops *ops);
 
 unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt);
 fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt);
index eaeea09880a28b7d87aa86bf534646268fd5f74d..6f02bcf7776d1e99b819943862f359c82ddb6011 100644 (file)
@@ -337,4 +337,5 @@ FDISK_2_43 {
        fdisk_script_disable_devnames;
        fdisk_script_has_devnames;
        fdisk_get_recommended_labelname;
+       fdisk_set_vfs;
 } FDISK_2_42;
index 1ec14f0e03f53fa1ed0f1a18ca955cd72673e5ef..a5dfd9bdf77500d23d2227ce64fcda5eccb3f1b9 100644 (file)
@@ -482,9 +482,9 @@ static int sgi_write_disklabel(struct fdisk_context *cxt)
 
        assert(sgi_pt_checksum(sgilabel) == 0);
 
-       if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0)
+       if (ul_vfs_lseek(cxt->vfs, cxt->dev_fd, 0, SEEK_SET) < 0)
                goto err;
-       if (ul_write_all(cxt->dev_fd, sgilabel, DEFAULT_SECTOR_SIZE))
+       if (ul_vfs_write_all(cxt->vfs, cxt->dev_fd, sgilabel, DEFAULT_SECTOR_SIZE))
                goto err;
        if (!strncmp((char *) sgilabel->volume[0].name, "sgilabel", 8)) {
                /*
@@ -494,13 +494,13 @@ static int sgi_write_disklabel(struct fdisk_context *cxt)
                int infostartblock
                        = be32_to_cpu(sgilabel->volume[0].block_num);
 
-               if (lseek(cxt->dev_fd, (off_t) infostartblock *
+               if (ul_vfs_lseek(cxt->vfs, cxt->dev_fd, (off_t) infostartblock *
                                        DEFAULT_SECTOR_SIZE, SEEK_SET) < 0)
                        goto err;
                info = sgi_new_info();
                if (!info)
                        goto err;
-               if (ul_write_all(cxt->dev_fd, info, sizeof(*info)))
+               if (ul_vfs_write_all(cxt->vfs, cxt->dev_fd, info, sizeof(*info)))
                        goto err;
        }
 
index 2328b03ec224b7e89ddda80a078a479d1d93cb82..d72deba22893283592be08e20753b8bf55301662 100644 (file)
@@ -1024,9 +1024,9 @@ static int sun_write_disklabel(struct fdisk_context *cxt)
        sunlabel->csum = 0;
        sunlabel->csum = sun_pt_checksum(sunlabel);
 
-       if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0)
+       if (ul_vfs_lseek(cxt->vfs, cxt->dev_fd, 0, SEEK_SET) < 0)
                return -errno;
-       if (ul_write_all(cxt->dev_fd, sunlabel, sz) != 0)
+       if (ul_vfs_write_all(cxt->vfs, cxt->dev_fd, sunlabel, sz) != 0)
                return -errno;
 
        return 0;
index 58b2d9baed05b5b1bef1bb50954cbca0d558102a..805c40abe875b44ac640f3d51abfbb9c9d90d01f 100644 (file)
@@ -22,7 +22,7 @@ static int read_from_device(struct fdisk_context *cxt,
        DBG_OBJ(CXT, cxt, ul_debug("reading: offset=%ju, size=%zu",
                                start, size));
 
-       r = lseek(cxt->dev_fd, start, SEEK_SET);
+       r = ul_vfs_lseek(cxt->vfs, cxt->dev_fd, start, SEEK_SET);
        if (r == -1)
        {
                DBG_OBJ(CXT, cxt, ul_debug("failed to seek to offset %ju: %m", start));
@@ -30,7 +30,7 @@ static int read_from_device(struct fdisk_context *cxt,
        }
 
        errno = 0;
-       r = read(cxt->dev_fd, buf, size);
+       r = ul_vfs_read(cxt->vfs, cxt->dev_fd, buf, size);
        if (r < 0 || (size_t)r != size) {
                if (!errno)
                        errno = EINVAL; /* probably too small file/device */
index 64b8a3a9646625ec3eca887eabd8713ca75edca3..af7ee34b3a32f65d5ac3d787b370f6e960836a19 100644 (file)
@@ -120,6 +120,9 @@ int fdisk_do_wipe(struct fdisk_context *cxt)
        if (!pr)
                return -ENOMEM;
 
+       if (cxt->vfs)
+               blkid_probe_set_vfs(pr, cxt->vfs);
+
        list_for_each(p, &cxt->wipes) {
                struct fdisk_wipe *wp = list_entry(p, struct fdisk_wipe, wipes);
                blkid_loff_t start = (blkid_loff_t) wp->start * cxt->sector_size,
@@ -169,6 +172,10 @@ int fdisk_check_collisions(struct fdisk_context *cxt)
        pr = blkid_new_probe();
        if (!pr)
                return -ENOMEM;
+
+       if (cxt->vfs)
+               blkid_probe_set_vfs(pr, cxt->vfs);
+
        rc = blkid_probe_set_device(pr, cxt->dev_fd, 0, 0);
        if (rc)
                return rc;