# include <blkid.h>
#endif
+#include "blkdev.h"
+#ifdef __linux__
+# include "partx.h"
+#endif
+#include "loopdev.h"
#include "fdiskP.h"
*
* 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
*
* - 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().
*/
/**
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;
}
cxt->label = lb;
DBG(CXT, ul_debugobj(cxt, "--> switching context to %s!", lb->name));
+
+ fdisk_apply_label_device_properties(cxt);
return 0;
}
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
* 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.
*
* </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.
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);
}
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;
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) {
* 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)
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;
goto fail;
fdisk_probe_labels(cxt);
-
fdisk_apply_label_device_properties(cxt);
/* warn about obsolete stuff on the device if we aren't in
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.
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();
free(cxt->dev_path);
cxt->dev_path = NULL;
-
cxt->dev_fd = -1;
return 0;
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);
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;
}
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:
* 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)
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