From: Karel Zak Date: Wed, 29 Jul 2026 10:12:27 +0000 (+0200) Subject: libfdisk: add pluggable VFS I/O support X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7eaac640e2339b318967fc47df94b35f24991170;p=thirdparty%2Futil-linux.git libfdisk: add pluggable VFS I/O support 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 --- diff --git a/libfdisk/docs/libfdisk-sections.txt b/libfdisk/docs/libfdisk-sections.txt index 53fa1a179..872231628 100644 --- a/libfdisk/docs/libfdisk-sections.txt +++ b/libfdisk/docs/libfdisk-sections.txt @@ -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 diff --git a/libfdisk/src/bsd.c b/libfdisk/src/bsd.c index 6ecbdef60..92931e95f 100644 --- a/libfdisk/src/bsd.c +++ b/libfdisk/src/bsd.c @@ -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; } diff --git a/libfdisk/src/context.c b/libfdisk/src/context.c index 0c5cf52bc..2c407029e 100644 --- a/libfdisk/src/context.c +++ b/libfdisk/src/context.c @@ -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; diff --git a/libfdisk/src/dos.c b/libfdisk/src/dos.c index 1731c1ef1..68fd4e050 100644 --- a/libfdisk/src/dos.c +++ b/libfdisk/src/dos.c @@ -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; } diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h index d9a9d9666..446f3962f 100644 --- a/libfdisk/src/fdiskP.h +++ b/libfdisk/src/fdiskP.h @@ -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 */ diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c index cf4f77808..b8db94d34 100644 --- a/libfdisk/src/gpt.c +++ b/libfdisk/src/gpt.c @@ -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]", diff --git a/libfdisk/src/libfdisk.h.in b/libfdisk/src/libfdisk.h.in index c23ad475c..0aa8f9533 100644 --- a/libfdisk/src/libfdisk.h.in +++ b/libfdisk/src/libfdisk.h.in @@ -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); diff --git a/libfdisk/src/libfdisk.sym b/libfdisk/src/libfdisk.sym index eaeea0988..6f02bcf77 100644 --- a/libfdisk/src/libfdisk.sym +++ b/libfdisk/src/libfdisk.sym @@ -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; diff --git a/libfdisk/src/sgi.c b/libfdisk/src/sgi.c index 1ec14f0e0..a5dfd9bdf 100644 --- a/libfdisk/src/sgi.c +++ b/libfdisk/src/sgi.c @@ -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; } diff --git a/libfdisk/src/sun.c b/libfdisk/src/sun.c index 2328b03ec..d72deba22 100644 --- a/libfdisk/src/sun.c +++ b/libfdisk/src/sun.c @@ -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; diff --git a/libfdisk/src/utils.c b/libfdisk/src/utils.c index 58b2d9bae..805c40abe 100644 --- a/libfdisk/src/utils.c +++ b/libfdisk/src/utils.c @@ -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 */ diff --git a/libfdisk/src/wipe.c b/libfdisk/src/wipe.c index 64b8a3a96..af7ee34b3 100644 --- a/libfdisk/src/wipe.c +++ b/libfdisk/src/wipe.c @@ -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;