X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=libfdisk%2Fsrc%2Fcontext.c;h=770aa4d5203ec8f7f2199a12274a4957a0854686;hb=34813bdd29cd2012bec61156286018ed9e13817a;hp=e8dfa0e691471b823fe330d74e6fe0fc4d7c316c;hpb=2db3cc4cf3245a3522911dfa2a0e569a249d11df;p=thirdparty%2Futil-linux.git diff --git a/libfdisk/src/context.c b/libfdisk/src/context.c index e8dfa0e691..770aa4d520 100644 --- a/libfdisk/src/context.c +++ b/libfdisk/src/context.c @@ -2,6 +2,11 @@ # include #endif +#include "blkdev.h" +#ifdef __linux__ +# include "partx.h" +#endif +#include "loopdev.h" #include "fdiskP.h" @@ -12,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 * @@ -26,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(). */ /** @@ -107,6 +116,10 @@ static int init_nested_from_parent(struct fdisk_context *cxt, int isnew) cxt->protect_bootbits = parent->protect_bootbits; } + free(cxt->dev_model); + cxt->dev_model = NULL; + cxt->dev_model_probed = 0; + free(cxt->dev_path); cxt->dev_path = NULL; @@ -296,6 +309,8 @@ int __fdisk_switch_label(struct fdisk_context *cxt, struct fdisk_label *lb) } cxt->label = lb; DBG(CXT, ul_debugobj(cxt, "--> switching context to %s!", lb->name)); + + fdisk_apply_label_device_properties(cxt); return 0; } @@ -339,6 +354,40 @@ int fdisk_enable_bootbits_protection(struct fdisk_context *cxt, int enable) cxt->protect_bootbits = enable ? 1 : 0; return 0; } +/** + * fdisk_disable_dialogs + * @cxt: fdisk context + * @disable: 1 or 0 + * + * The library uses dialog driven partitioning by default. + * + * Returns: 0 on success, < 0 on error. + * + * Since: 2.31 + */ +int fdisk_disable_dialogs(struct fdisk_context *cxt, int disable) +{ + if (!cxt) + return -EINVAL; + + cxt->no_disalogs = disable; + return 0; +} + +/** + * fdisk_has_dialogs + * @cxt: fdisk context + * + * See fdisk_disable_dialogs() + * + * Returns: 1 if dialog driven partitioning enabled (default), or 0. + * + * Since: 2.31 + */ +int fdisk_has_dialogs(struct fdisk_context *cxt) +{ + return cxt->no_disalogs == 0; +} /** * fdisk_enable_wipe @@ -394,7 +443,7 @@ const char *fdisk_get_collision(struct fdisk_context *cxt) * fdisk_is_ptcollision: * @cxt: fdisk context * - * The collision detected by libblkid (usally another partition table). Note + * The collision detected by libblkid (usually another partition table). Note * that libfdisk does not support all partitions tables, so fdisk_has_label() * may return false, but fdisk_is_ptcollision() may return true. * @@ -438,7 +487,7 @@ int fdisk_is_ptcollision(struct fdisk_context *cxt) * * * 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. @@ -492,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); } @@ -500,12 +549,17 @@ static void reset_context(struct fdisk_context *cxt) free(cxt->dev_path); cxt->dev_path = NULL; + free(cxt->dev_model); + cxt->dev_model = NULL; + cxt->dev_model_probed = 0; + free(cxt->collision); cxt->collision = NULL; memset(&cxt->dev_st, 0, sizeof(cxt->dev_st)); cxt->dev_fd = -1; + cxt->private_fd = 0; cxt->firstsector = NULL; cxt->firstsector_bufsz = 0; @@ -519,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) { @@ -553,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) @@ -565,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; @@ -587,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 @@ -602,19 +634,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. @@ -634,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(); @@ -651,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; @@ -671,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); @@ -683,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; } @@ -732,6 +840,152 @@ int fdisk_reread_partition_table(struct fdisk_context *cxt) return 0; } +#ifdef __linux__ +static inline int add_to_partitions_array( + struct fdisk_partition ***ary, + struct fdisk_partition *pa, + size_t *n, size_t nmax) +{ + if (!*ary) { + *ary = calloc(nmax, sizeof(struct fdisk_partition *)); + if (!*ary) + return -ENOMEM; + } + (*ary)[*n] = pa; + (*n)++; + return 0; +} +#endif + +/** + * fdisk_reread_changes: + * @cxt: context + * @org: original layout (on disk) + * + * Like fdisk_reread_partition_table() but don't forces kernel re-read all + * partition table. The BLKPG_* ioctls are used for individual partitions. The + * advantage is that unmodified partitions maybe mounted. + * + * The function behavies like fdisk_reread_partition_table() on systems where + * are no available BLKPG_* ioctls. + * + * Returns: <0 on error, or 0. + */ +#ifdef __linux__ +int fdisk_reread_changes(struct fdisk_context *cxt, struct fdisk_table *org) +{ + struct fdisk_table *tb = NULL; + struct fdisk_iter itr; + struct fdisk_partition *pa; + struct fdisk_partition **rem = NULL, **add = NULL, **upd = NULL; + int change, rc = 0, err = 0; + size_t nparts, i, nadds = 0, nupds = 0, nrems = 0; + + DBG(CXT, ul_debugobj(cxt, "rereading changes")); + + fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); + + /* the current layout */ + fdisk_get_partitions(cxt, &tb); + /* maximal number of partitions */ + nparts = max(fdisk_table_get_nents(tb), fdisk_table_get_nents(org)); + + while (fdisk_diff_tables(org, tb, &itr, &pa, &change) == 0) { + if (change == FDISK_DIFF_UNCHANGED) + continue; + switch (change) { + case FDISK_DIFF_REMOVED: + rc = add_to_partitions_array(&rem, pa, &nrems, nparts); + break; + case FDISK_DIFF_ADDED: + rc = add_to_partitions_array(&add, pa, &nadds, nparts); + break; + case FDISK_DIFF_RESIZED: + rc = add_to_partitions_array(&upd, pa, &nupds, nparts); + break; + case FDISK_DIFF_MOVED: + rc = add_to_partitions_array(&rem, pa, &nrems, nparts); + if (!rc) + rc = add_to_partitions_array(&add, pa, &nadds, nparts); + break; + } + if (rc != 0) + goto done; + } + + for (i = 0; i < nrems; i++) { + pa = rem[i]; + DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_DEL_PARTITION", pa->partno)); + if (partx_del_partition(cxt->dev_fd, pa->partno + 1) != 0) { + fdisk_warn(cxt, _("Failed to remove partition %zu from system"), pa->partno + 1); + err++; + } + } + for (i = 0; i < nupds; i++) { + pa = upd[i]; + DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_RESIZE_PARTITION", pa->partno)); + if (partx_resize_partition(cxt->dev_fd, pa->partno + 1, pa->start, pa->size) != 0) { + fdisk_warn(cxt, _("Failed to update system information about partition %zu"), pa->partno + 1); + err++; + } + } + for (i = 0; i < nadds; i++) { + pa = add[i]; + DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_ADD_PARTITION", pa->partno)); + if (partx_add_partition(cxt->dev_fd, pa->partno + 1, pa->start, pa->size) != 0) { + fdisk_warn(cxt, _("Failed to add partition %zu to system"), pa->partno + 1); + err++; + } + } + if (err) + fdisk_info(cxt, _( + "The kernel still uses the old partitions. The new " + "table will be used at the next reboot. ")); +done: + free(rem); + free(add); + free(upd); + fdisk_unref_table(tb); + return rc; +} +#else +int fdisk_reread_changes(struct fdisk_context *cxt, + struct fdisk_table *org __attribute__((__unused__))) { + return fdisk_reread_partition_table(cxt); +} +#endif + +/** + * fdisk_device_is_used: + * @cxt: context + * + * On systems where is no BLKRRPART ioctl the function returns zero and + * sets errno to ENOSYS. + * + * Returns: 1 if the device assigned to the context is used by system, or 0. + */ +int fdisk_device_is_used(struct fdisk_context *cxt) +{ + int rc = 0; + + assert(cxt); + assert(cxt->dev_fd >= 0); + + errno = 0; + +#ifdef BLKRRPART + /* it seems kernel always return EINVAL for BLKRRPART on loopdevices */ + if (S_ISBLK(cxt->dev_st.st_mode) + && major(cxt->dev_st.st_rdev) != LOOPDEV_MAJOR) { + DBG(CXT, ul_debugobj(cxt, "calling re-read ioctl")); + rc = ioctl(cxt->dev_fd, BLKRRPART) != 0; + } +#else + errno = ENOSYS; +#endif + DBG(CXT, ul_debugobj(cxt, "device used: %s [errno=%d]", rc ? "TRUE" : "FALSE", errno)); + return rc; +} /** * fdisk_is_readonly: @@ -1036,6 +1290,11 @@ fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt) * partition tables like GPT protective MBR or hybrid partition tables on * bootable media where the first partition may start on very crazy offsets. * + * Note that this function changes only runtime information. It does not update + * any range in on-disk partition table. For example GPT Header contains First + * and Last usable LBA fields. These fields are not updated by this function. + * Be careful. + * * Returns: 0 on success, <0 on error. */ fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba) @@ -1139,6 +1398,54 @@ const char *fdisk_get_devname(struct fdisk_context *cxt) return cxt->dev_path; } +/** + * fdisk_get_devno: + * @cxt: context + * + * Returns: device number or zero for non-block devices + * + * Since: 2.33 + */ +dev_t fdisk_get_devno(struct fdisk_context *cxt) +{ + assert(cxt); + return S_ISBLK(cxt->dev_st.st_mode) ? cxt->dev_st.st_rdev : 0; +} + +/** + * fdisk_get_devmodel: + * @cxt: context + * + * Returns: device model string or NULL. + * + * Since: 2.33 + */ +#ifdef __linux__ +const char *fdisk_get_devmodel(struct fdisk_context *cxt) +{ + assert(cxt); + + if (cxt->dev_model_probed) + return cxt->dev_model; + + if (fdisk_get_devno(cxt)) { + struct path_cxt *pc = ul_new_sysfs_path(fdisk_get_devno(cxt), NULL, NULL); + + if (pc) { + ul_path_read_string(pc, &cxt->dev_model, "device/model"); + ul_unref_path(pc); + } + } + cxt->dev_model_probed = 1; + return cxt->dev_model; +} +#else +const char *fdisk_get_devmodel(struct fdisk_context *cxt __attribute__((__unused__))) +{ + return NULL; +} +#endif + /** * fdisk_get_devfd: * @cxt: context