From 7571ec08a15d54ad05e4b619ccef5a6f10ce30bc Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 18 Jun 2019 13:30:42 +0200 Subject: libfdisk: add fdisk_assign_device_by_fd() It's possible that caller has the device already opened for some other task, so let's reuse the file descriptor. Requested-by: Lennart Poettering Signed-off-by: Karel Zak --- libfdisk/docs/libfdisk-sections.txt | 1 + libfdisk/src/context.c | 142 ++++++++++++++++++++++++------------ libfdisk/src/fdiskP.h | 1 + libfdisk/src/libfdisk.h.in | 2 + libfdisk/src/libfdisk.sym | 1 + 5 files changed, 99 insertions(+), 48 deletions(-) diff --git a/libfdisk/docs/libfdisk-sections.txt b/libfdisk/docs/libfdisk-sections.txt index d0d362f60..f148da527 100644 --- a/libfdisk/docs/libfdisk-sections.txt +++ b/libfdisk/docs/libfdisk-sections.txt @@ -291,6 +291,7 @@ fdisk_unref_table context fdisk_context fdisk_assign_device +fdisk_assign_device_by_fd fdisk_deassign_device fdisk_reassign_device fdisk_device_is_used diff --git a/libfdisk/src/context.c b/libfdisk/src/context.c index 1171f28a8..b62d2b101 100644 --- a/libfdisk/src/context.c +++ b/libfdisk/src/context.c @@ -541,7 +541,7 @@ static void reset_context(struct fdisk_context *cxt) free(cxt->firstsector); } else { /* we close device only in primary context */ - if (cxt->dev_fd > -1) + if (cxt->dev_fd > -1 && cxt->private_fd) close(cxt->dev_fd); free(cxt->firstsector); } @@ -559,6 +559,7 @@ static void reset_context(struct fdisk_context *cxt) memset(&cxt->dev_st, 0, sizeof(cxt->dev_st)); cxt->dev_fd = -1; + cxt->private_fd = 0; cxt->firstsector = NULL; cxt->firstsector_bufsz = 0; @@ -572,39 +573,12 @@ static void reset_context(struct fdisk_context *cxt) fdisk_free_wipe_areas(cxt); } -/** - * fdisk_assign_device: - * @cxt: context - * @fname: path to the device to be handled - * @readonly: how to open the device - * - * Open the device, discovery topology, geometry, detect disklabel, check for - * collisions and switch the current label driver to reflect the probing - * result. - * - * If in standard mode (!= non-listonly mode) than also detects for collisions. - * The result is accessible by fdisk_get_collision() and - * fdisk_is_ptcollision(). The collision (e.g. old obsolete PT) may be removed - * by fdisk_enable_wipe(). Note that new PT and old PT may be on different - * locations. - * - * Note that this function resets all generic setting in context. - * - * If the @cxt is nested context (necessary for example to edit BSD or PMBR) - * then the device is assigned to the parental context and necessary properties - * are copied to the @cxt. The change is propagated in child->parent direction - * only. It's impossible to use a different device for primary and nested - * contexts. - * - * Returns: 0 on success, < 0 on error. - */ -int fdisk_assign_device(struct fdisk_context *cxt, - const char *fname, int readonly) +/* fdisk_assign_device() body */ +static int fdisk_assign_fd(struct fdisk_context *cxt, int fd, + const char *fname, int readonly, int privfd) { - int fd; - - DBG(CXT, ul_debugobj(cxt, "assigning device %s", fname)); assert(cxt); + assert(fd >= 0); /* redirect request to parent */ if (cxt->parent) { @@ -615,7 +589,7 @@ int fdisk_assign_device(struct fdisk_context *cxt, * unwanted extra warnings. */ fdisk_enable_listonly(cxt->parent, fdisk_is_listonly(cxt)); - rc = fdisk_assign_device(cxt->parent, fname, readonly); + rc = fdisk_assign_fd(cxt->parent, fd, fname, readonly, privfd); fdisk_enable_listonly(cxt->parent, org); if (!rc) @@ -627,16 +601,13 @@ int fdisk_assign_device(struct fdisk_context *cxt, reset_context(cxt); - fd = open(fname, (readonly ? O_RDONLY : O_RDWR ) | O_CLOEXEC); - if (fd < 0) - goto fail; - if (fstat(fd, &cxt->dev_st) != 0) goto fail; cxt->readonly = readonly; cxt->dev_fd = fd; - cxt->dev_path = strdup(fname); + cxt->private_fd = privfd; + cxt->dev_path = fname ? strdup(fname) : NULL; if (!cxt->dev_path) goto fail; @@ -649,7 +620,6 @@ int fdisk_assign_device(struct fdisk_context *cxt, goto fail; fdisk_probe_labels(cxt); - fdisk_apply_label_device_properties(cxt); /* warn about obsolete stuff on the device if we aren't in @@ -664,13 +634,78 @@ int fdisk_assign_device(struct fdisk_context *cxt, fail: { int rc = -errno; - if (fd >= 0) - close(fd); DBG(CXT, ul_debugobj(cxt, "failed to assign device [rc=%d]", rc)); return rc; } } +/** + * fdisk_assign_device: + * @cxt: context + * @fname: path to the device to be handled + * @readonly: how to open the device + * + * Open the device, discovery topology, geometry, detect disklabel, check for + * collisions and switch the current label driver to reflect the probing + * result. + * + * If in standard mode (!= non-listonly mode) than also detects for collisions. + * The result is accessible by fdisk_get_collision() and + * fdisk_is_ptcollision(). The collision (e.g. old obsolete PT) may be removed + * by fdisk_enable_wipe(). Note that new PT and old PT may be on different + * locations. + * + * Note that this function resets all generic setting in context. + * + * If the @cxt is nested context (necessary for example to edit BSD or PMBR) + * then the device is assigned to the parental context and necessary properties + * are copied to the @cxt. The change is propagated in child->parent direction + * only. It's impossible to use a different device for primary and nested + * contexts. + * + * Returns: 0 on success, < 0 on error. + */ +int fdisk_assign_device(struct fdisk_context *cxt, + const char *fname, int readonly) +{ + int fd, rc; + + DBG(CXT, ul_debugobj(cxt, "assigning device %s", fname)); + assert(cxt); + + fd = open(fname, (readonly ? O_RDONLY : O_RDWR ) | O_CLOEXEC); + if (fd < 0) { + int rc = -errno; + DBG(CXT, ul_debugobj(cxt, "failed to assign device [rc=%d]", rc)); + return rc; + } + + rc = fdisk_assign_fd(cxt, fd, fname, readonly, 1); + if (rc) + close(fd); + return rc; +} + +/** + * fdisk_assign_device_by_fd: + * @cxt: context + * @fd: device file descriptor + * @fname: path to the device (used for dialogs, debugging, partition names, ...) + * @readonly: how to use the device + * + * Like fdisk_assign_device(), but caller is responsible to open and close the + * device. The library only fsync() the device on fdisk_deassign_device(). + * + * The device has to be open O_RDWR on @readonly=0. + * + * Returns: 0 on success, < 0 on error. + */ +int fdisk_assign_device_by_fd(struct fdisk_context *cxt, int fd, + const char *fname, int readonly) +{ + return fdisk_assign_fd(cxt, fd, fname, readonly, 0); +} + /** * fdisk_deassign_device: * @cxt: context @@ -696,15 +731,19 @@ int fdisk_deassign_device(struct fdisk_context *cxt, int nosync) DBG(CXT, ul_debugobj(cxt, "de-assigning device %s", cxt->dev_path)); - if (cxt->readonly) + if (cxt->readonly && cxt->private_fd) close(cxt->dev_fd); else { - if (fsync(cxt->dev_fd) || close(cxt->dev_fd)) { + if (fsync(cxt->dev_fd)) { + fdisk_warn(cxt, _("%s: fsync device failed"), + cxt->dev_path); + return -errno; + } + if (cxt->private_fd && close(cxt->dev_fd)) { fdisk_warn(cxt, _("%s: close device failed"), cxt->dev_path); return -errno; } - if (!nosync) { fdisk_info(cxt, _("Syncing disks.")); sync(); @@ -713,7 +752,6 @@ int fdisk_deassign_device(struct fdisk_context *cxt, int nosync) free(cxt->dev_path); cxt->dev_path = NULL; - cxt->dev_fd = -1; return 0; @@ -733,7 +771,7 @@ int fdisk_deassign_device(struct fdisk_context *cxt, int nosync) int fdisk_reassign_device(struct fdisk_context *cxt) { char *devname; - int rdonly, rc; + int rdonly, rc, fd, privfd; assert(cxt); assert(cxt->dev_fd >= 0); @@ -745,11 +783,19 @@ int fdisk_reassign_device(struct fdisk_context *cxt) return -ENOMEM; rdonly = cxt->readonly; + fd = cxt->dev_fd; + privfd = cxt->private_fd; fdisk_deassign_device(cxt, 1); - rc = fdisk_assign_device(cxt, devname, rdonly); - free(devname); + if (privfd) + /* reopen and assign */ + rc = fdisk_assign_device(cxt, devname, rdonly); + else + /* assign only */ + rc = fdisk_assign_fd(cxt, fd, devname, rdonly, privfd); + + free(devname); return rc; } diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h index 19c1c7953..fefebae2a 100644 --- a/libfdisk/src/fdiskP.h +++ b/libfdisk/src/fdiskP.h @@ -389,6 +389,7 @@ struct fdisk_context { pt_collision : 1, /* another PT detected by libblkid */ no_disalogs : 1, /* disable dialog-driven partititoning */ dev_model_probed : 1, /* tried to read from sys */ + private_fd : 1, /* open by libfdisk */ listonly : 1; /* list partition, nothing else */ char *collision; /* name of already existing FS/PT */ diff --git a/libfdisk/src/libfdisk.h.in b/libfdisk/src/libfdisk.h.in index 69c6fd820..0669c0a7c 100644 --- a/libfdisk/src/libfdisk.h.in +++ b/libfdisk/src/libfdisk.h.in @@ -195,6 +195,8 @@ int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id); int fdisk_assign_device(struct fdisk_context *cxt, const char *fname, int readonly); +int fdisk_assign_device_by_fd(struct fdisk_context *cxt, int fd, + const char *fname, int readonly); int fdisk_deassign_device(struct fdisk_context *cxt, int nosync); int fdisk_reassign_device(struct fdisk_context *cxt); diff --git a/libfdisk/src/libfdisk.sym b/libfdisk/src/libfdisk.sym index 8ce943acc..96fcadd71 100644 --- a/libfdisk/src/libfdisk.sym +++ b/libfdisk/src/libfdisk.sym @@ -306,4 +306,5 @@ FDISK_2.33 { FDISK_2.35 { fdisk_script_set_table; + fdisk_assign_device_by_fd; } FDISK_2.33; -- cgit v1.2.3-55-g7522