diff options
Diffstat (limited to 'libfdisk/src/context.c')
-rw-r--r-- | libfdisk/src/context.c | 163 |
1 files changed, 114 insertions, 49 deletions
diff --git a/libfdisk/src/context.c b/libfdisk/src/context.c index 56ebb6c1e..f97f0a612 100644 --- a/libfdisk/src/context.c +++ b/libfdisk/src/context.c @@ -17,13 +17,13 @@ * * The library distinguish between three types of partitioning objects. * - * on-disk data + * on-disk label data * - disk label specific * - probed and read by disklabel drivers when assign device to the context * or when switch to another disk label type * - only fdisk_write_disklabel() modify on-disk data * - * in-memory data + * in-memory label data * - generic data and disklabel specific data stored in struct fdisk_label * - all partitioning operations are based on in-memory data only * @@ -31,8 +31,12 @@ * - provides abstraction to present partitions to users * - fdisk_partition is possible to gather to fdisk_table container * - used as unified template for new partitions + * - used (with fdisk_table) in fdisk scripts * - the struct fdisk_partition is always completely independent object and * any change to the object has no effect to in-memory (or on-disk) label data + * + * Don't forget to inform kernel about changes by fdisk_reread_partition_table() + * or more smart fdisk_reread_changes(). */ /** @@ -483,7 +487,7 @@ int fdisk_is_ptcollision(struct fdisk_context *cxt) * </informalexample> * * Note that the recommended way to list partitions is to use - * fdisk_get_partitions() and struct fdisk_table than ask disk driver for each + * fdisk_get_partitions() and struct fdisk_table then ask disk driver for each * individual partitions. * * Returns: maximal number of partitions for the current label. @@ -537,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); } @@ -555,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; @@ -568,30 +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 and switch - * the current label driver to reflect the probing result. - * - * Note that this function resets all generic setting in context. If the @cxt - * is nested context 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) { @@ -602,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) @@ -614,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; @@ -635,15 +619,20 @@ int fdisk_assign_device(struct fdisk_context *cxt, if (fdisk_read_firstsector(cxt) < 0) goto fail; - fdisk_probe_labels(cxt); + /* warn about obsolete stuff on the device if we aren't in list-only */ + if (!fdisk_is_listonly(cxt) && fdisk_check_collisions(cxt) < 0) + goto fail; + fdisk_probe_labels(cxt); fdisk_apply_label_device_properties(cxt); - /* warn about obsolete stuff on the device if we aren't in - * list-only mode and there is not PT yet */ - if (!fdisk_is_listonly(cxt) && !fdisk_has_label(cxt) - && fdisk_check_collisions(cxt) < 0) - goto fail; + /* Don't report collision if there is already a valid partition table. + * The bootbits are wiped when we create a *new* partition table only. */ + if (fdisk_is_ptcollision(cxt) && fdisk_has_label(cxt)) { + cxt->pt_collision = 0; + free(cxt->collision); + cxt->collision = NULL; + } DBG(CXT, ul_debugobj(cxt, "initialized for %s [%s]", fname, readonly ? "READ-ONLY" : "READ-WRITE")); @@ -651,19 +640,84 @@ 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) then 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) { + 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 * @nosync: disable fsync() * - * Close device and call fsync(). If the @cxt is nested context than the + * Close device and call fsync(). If the @cxt is nested context then the * request is redirected to the parent. * * Returns: 0 on success, < 0 on error. @@ -683,15 +737,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(); @@ -700,7 +758,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; @@ -720,7 +777,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); @@ -732,11 +789,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; } @@ -774,7 +839,7 @@ int fdisk_reread_partition_table(struct fdisk_context *cxt) fdisk_info(cxt, _( "The kernel still uses the old table. The " "new table will be used at the next reboot " - "or after you run partprobe(8) or kpartx(8).")); + "or after you run partprobe(8) or partx(8).")); return -errno; } |