summaryrefslogtreecommitdiffstats
path: root/libblkid/src/topology
diff options
context:
space:
mode:
Diffstat (limited to 'libblkid/src/topology')
-rw-r--r--libblkid/src/topology/dm.c133
-rw-r--r--libblkid/src/topology/evms.c77
-rw-r--r--libblkid/src/topology/ioctl.c74
-rw-r--r--libblkid/src/topology/lvm.c144
-rw-r--r--libblkid/src/topology/md.c154
-rw-r--r--libblkid/src/topology/sysfs.c124
-rw-r--r--libblkid/src/topology/topology.c381
-rw-r--r--libblkid/src/topology/topology.h25
8 files changed, 1112 insertions, 0 deletions
diff --git a/libblkid/src/topology/dm.c b/libblkid/src/topology/dm.c
new file mode 100644
index 0000000..b210a80
--- /dev/null
+++ b/libblkid/src/topology/dm.c
@@ -0,0 +1,133 @@
+/*
+ * device-mapper (dm) topology
+ * -- this is fallback for old systems where the topology information is not
+ * exported by sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "topology.h"
+
+static int is_dm_device(dev_t devno)
+{
+ return blkid_driver_has_major("device-mapper", major(devno));
+}
+
+static int probe_dm_tp(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ const char *paths[] = {
+ "/usr/local/sbin/dmsetup",
+ "/usr/sbin/dmsetup",
+ "/sbin/dmsetup"
+ };
+ int dmpipe[] = { -1, -1 }, stripes, stripesize;
+ const char *cmd = NULL;
+ FILE *stream = NULL;
+ long long offset, size;
+ size_t i;
+ dev_t devno = blkid_probe_get_devno(pr);
+
+ if (!devno)
+ goto nothing; /* probably not a block device */
+ if (!is_dm_device(devno))
+ goto nothing;
+
+ for (i = 0; i < ARRAY_SIZE(paths); i++) {
+ struct stat sb;
+ if (stat(paths[i], &sb) == 0) {
+ cmd = paths[i];
+ break;
+ }
+ }
+
+ if (!cmd)
+ goto nothing;
+ if (pipe(dmpipe) < 0) {
+ DBG(LOWPROBE, ul_debug("Failed to open pipe: errno=%d", errno));
+ goto nothing;
+ }
+
+ switch (fork()) {
+ case 0:
+ {
+ const char *dmargv[7];
+ char maj[16], min[16];
+
+ /* Plumbing */
+ close(dmpipe[0]);
+
+ if (dmpipe[1] != STDOUT_FILENO)
+ dup2(dmpipe[1], STDOUT_FILENO);
+
+ if (drop_permissions() != 0)
+ exit(1);
+
+ snprintf(maj, sizeof(maj), "%d", major(devno));
+ snprintf(min, sizeof(min), "%d", minor(devno));
+
+ dmargv[0] = cmd;
+ dmargv[1] = "table";
+ dmargv[2] = "-j";
+ dmargv[3] = maj;
+ dmargv[4] = "-m";
+ dmargv[5] = min;
+ dmargv[6] = NULL;
+
+ execv(dmargv[0], (char * const *) dmargv);
+
+ DBG(LOWPROBE, ul_debug("Failed to execute %s: errno=%d", cmd, errno));
+ exit(1);
+ }
+ case -1:
+ DBG(LOWPROBE, ul_debug("Failed to forking: errno=%d", errno));
+ goto nothing;
+ default:
+ break;
+ }
+
+ stream = fdopen(dmpipe[0], "r" UL_CLOEXECSTR);
+ if (!stream)
+ goto nothing;
+
+ if (fscanf(stream, "%lld %lld striped %d %d ",
+ &offset, &size, &stripes, &stripesize) != 0)
+ goto nothing;
+
+ blkid_topology_set_minimum_io_size(pr, stripesize << 9);
+ blkid_topology_set_optimal_io_size(pr, (stripes * stripesize) << 9);
+
+ fclose(stream);
+ close(dmpipe[1]);
+ return 0;
+
+nothing:
+ if (stream)
+ fclose(stream);
+ else if (dmpipe[0] != -1)
+ close(dmpipe[0]);
+ if (dmpipe[1] != -1)
+ close(dmpipe[1]);
+ return 1;
+}
+
+const struct blkid_idinfo dm_tp_idinfo =
+{
+ .name = "dm",
+ .probefunc = probe_dm_tp,
+ .magics = BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/src/topology/evms.c b/libblkid/src/topology/evms.c
new file mode 100644
index 0000000..1aa32f9
--- /dev/null
+++ b/libblkid/src/topology/evms.c
@@ -0,0 +1,77 @@
+/*
+ * Evms topology
+ * -- this is fallback for old systems where the topology information is not
+ * exported by sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "topology.h"
+
+#define EVMS_MAJOR 117
+
+#ifndef _IOT__IOTBASE_u_int32_t
+#define _IOT__IOTBASE_u_int32_t IOT_SIMPLE(uint32_t)
+#endif
+#define _IOT_evms_stripe_info _IOT (_IOTS(uint32_t), 2, 0, 0, 0, 0)
+#define EVMS_GET_STRIPE_INFO _IOR(EVMS_MAJOR, 0xF0, struct evms_stripe_info)
+
+struct evms_stripe_info {
+ uint32_t size; /* stripe unit 512-byte blocks */
+ uint32_t width; /* the number of stripe members or RAID data disks */
+};
+
+static int is_evms_device(dev_t devno)
+{
+ if (major(devno) == EVMS_MAJOR)
+ return 1;
+ return blkid_driver_has_major("evms", major(devno));
+}
+
+static int probe_evms_tp(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ struct evms_stripe_info evms;
+ dev_t devno = blkid_probe_get_devno(pr);
+
+ if (!devno)
+ goto nothing; /* probably not a block device */
+
+ if (!is_evms_device(devno))
+ goto nothing;
+
+ memset(&evms, 0, sizeof(evms));
+
+ if (ioctl(pr->fd, EVMS_GET_STRIPE_INFO, &evms))
+ goto nothing;
+
+ blkid_topology_set_minimum_io_size(pr, evms.size << 9);
+ blkid_topology_set_optimal_io_size(pr, (evms.size * evms.width) << 9);
+
+ return 0;
+
+nothing:
+ return 1;
+}
+
+const struct blkid_idinfo evms_tp_idinfo =
+{
+ .name = "evms",
+ .probefunc = probe_evms_tp,
+ .magics = BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/src/topology/ioctl.c b/libblkid/src/topology/ioctl.c
new file mode 100644
index 0000000..3aba09e
--- /dev/null
+++ b/libblkid/src/topology/ioctl.c
@@ -0,0 +1,74 @@
+/*
+ * ioctl based topology -- gathers topology information
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "topology.h"
+
+/*
+ * ioctl topology values
+ */
+static struct topology_val {
+
+ long ioc;
+
+ /* functions to set probing result */
+ int (*set_ulong)(blkid_probe, unsigned long);
+ int (*set_int)(blkid_probe, int);
+
+} topology_vals[] = {
+ { BLKALIGNOFF, NULL, blkid_topology_set_alignment_offset },
+ { BLKIOMIN, blkid_topology_set_minimum_io_size },
+ { BLKIOOPT, blkid_topology_set_optimal_io_size },
+ { BLKPBSZGET, blkid_topology_set_physical_sector_size }
+ /* we read BLKSSZGET in topology.c */
+};
+
+static int probe_ioctl_tp(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(topology_vals); i++) {
+ struct topology_val *val = &topology_vals[i];
+ int rc = 1;
+ unsigned int data;
+
+ if (ioctl(pr->fd, val->ioc, &data) == -1)
+ goto nothing;
+
+ if (val->set_int)
+ rc = val->set_int(pr, (int) data);
+ else
+ rc = val->set_ulong(pr, (unsigned long) data);
+ if (rc)
+ goto err;
+ }
+
+ return 0;
+nothing:
+ return 1;
+err:
+ return -1;
+}
+
+const struct blkid_idinfo ioctl_tp_idinfo =
+{
+ .name = "ioctl",
+ .probefunc = probe_ioctl_tp,
+ .magics = BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/src/topology/lvm.c b/libblkid/src/topology/lvm.c
new file mode 100644
index 0000000..6ab7a50
--- /dev/null
+++ b/libblkid/src/topology/lvm.c
@@ -0,0 +1,144 @@
+/*
+ * lvm topology
+ * -- this is fallback for old systems where the topology information is not
+ * exported by sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "topology.h"
+
+#ifndef LVM_BLK_MAJOR
+# define LVM_BLK_MAJOR 58
+#endif
+
+static int is_lvm_device(dev_t devno)
+{
+ if (major(devno) == LVM_BLK_MAJOR)
+ return 1;
+ return blkid_driver_has_major("lvm", major(devno));
+}
+
+static int probe_lvm_tp(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ const char *paths[] = {
+ "/usr/local/sbin/lvdisplay",
+ "/usr/sbin/lvdisplay",
+ "/sbin/lvdisplay"
+ };
+ int lvpipe[] = { -1, -1 }, stripes = 0, stripesize = 0;
+ FILE *stream = NULL;
+ char *cmd = NULL, *devname = NULL, buf[1024];
+ size_t i;
+ dev_t devno = blkid_probe_get_devno(pr);
+
+ if (!devno)
+ goto nothing; /* probably not a block device */
+ if (!is_lvm_device(devno))
+ goto nothing;
+
+ for (i = 0; i < ARRAY_SIZE(paths); i++) {
+ struct stat sb;
+ if (stat(paths[i], &sb) == 0) {
+ cmd = (char *) paths[i];
+ break;
+ }
+ }
+
+ if (!cmd)
+ goto nothing;
+
+ devname = blkid_devno_to_devname(devno);
+ if (!devname)
+ goto nothing;
+
+ if (pipe(lvpipe) < 0) {
+ DBG(LOWPROBE, ul_debug("Failed to open pipe: errno=%d", errno));
+ goto nothing;
+ }
+
+ switch (fork()) {
+ case 0:
+ {
+ char *lvargv[3];
+
+ /* Plumbing */
+ close(lvpipe[0]);
+
+ if (lvpipe[1] != STDOUT_FILENO)
+ dup2(lvpipe[1], STDOUT_FILENO);
+
+ if (drop_permissions() != 0)
+ exit(1);
+
+ lvargv[0] = cmd;
+ lvargv[1] = devname;
+ lvargv[2] = NULL;
+
+ execv(lvargv[0], lvargv);
+
+ DBG(LOWPROBE, ul_debug("Failed to execute %s: errno=%d", cmd, errno));
+ exit(1);
+ }
+ case -1:
+ DBG(LOWPROBE, ul_debug("Failed to forking: errno=%d", errno));
+ goto nothing;
+ default:
+ break;
+ }
+
+ stream = fdopen(lvpipe[0], "r" UL_CLOEXECSTR);
+ if (!stream)
+ goto nothing;
+
+ while (fgets(buf, sizeof(buf), stream) != NULL) {
+ if (!strncmp(buf, "Stripes", 7))
+ ignore_result( sscanf(buf, "Stripes %d", &stripes) );
+
+ if (!strncmp(buf, "Stripe size", 11))
+ ignore_result( sscanf(buf, "Stripe size (KByte) %d", &stripesize) );
+ }
+
+ if (!stripes)
+ goto nothing;
+
+ blkid_topology_set_minimum_io_size(pr, stripesize << 10);
+ blkid_topology_set_optimal_io_size(pr, (stripes * stripesize) << 10);
+
+ free(devname);
+ fclose(stream);
+ close(lvpipe[1]);
+ return 0;
+
+nothing:
+ free(devname);
+ if (stream)
+ fclose(stream);
+ else if (lvpipe[0] != -1)
+ close(lvpipe[0]);
+ if (lvpipe[1] != -1)
+ close(lvpipe[1]);
+ return 1;
+}
+
+const struct blkid_idinfo lvm_tp_idinfo =
+{
+ .name = "lvm",
+ .probefunc = probe_lvm_tp,
+ .magics = BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/src/topology/md.c b/libblkid/src/topology/md.c
new file mode 100644
index 0000000..02f27a8
--- /dev/null
+++ b/libblkid/src/topology/md.c
@@ -0,0 +1,154 @@
+/*
+ * Linux Software RAID (md) topology
+ * -- this is fallback for old systems where the topology information is not
+ * exported by sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "topology.h"
+
+#ifndef MD_MAJOR
+#define MD_MAJOR 9
+#endif
+
+#ifndef _IOT__IOTBASE_uint32_t
+#define _IOT__IOTBASE_uint32_t IOT_SIMPLE(uint32_t)
+#endif
+#define _IOT_md_array_info _IOT (_IOTS(uint32_t), 18, 0, 0, 0, 0)
+#define GET_ARRAY_INFO _IOR (MD_MAJOR, 0x11, struct md_array_info)
+
+struct md_array_info {
+ /*
+ * Generic constant information
+ */
+ uint32_t major_version;
+ uint32_t minor_version;
+ uint32_t patch_version;
+ uint32_t ctime;
+ uint32_t level;
+ uint32_t size;
+ uint32_t nr_disks;
+ uint32_t raid_disks;
+ uint32_t md_minor;
+ uint32_t not_persistent;
+
+ /*
+ * Generic state information
+ */
+ uint32_t utime; /* 0 Superblock update time */
+ uint32_t state; /* 1 State bits (clean, ...) */
+ uint32_t active_disks; /* 2 Number of currently active disks */
+ uint32_t working_disks; /* 3 Number of working disks */
+ uint32_t failed_disks; /* 4 Number of failed disks */
+ uint32_t spare_disks; /* 5 Number of spare disks */
+
+ /*
+ * Personality information
+ */
+ uint32_t layout; /* 0 the array's physical layout */
+ uint32_t chunk_size; /* 1 chunk size in bytes */
+
+};
+
+static int is_md_device(dev_t devno)
+{
+ if (major(devno) == MD_MAJOR)
+ return 1;
+ return blkid_driver_has_major("md", major(devno));
+}
+
+static int probe_md_tp(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ int fd = -1;
+ dev_t disk = 0;
+ dev_t devno = blkid_probe_get_devno(pr);
+ struct md_array_info md;
+
+ if (!devno)
+ goto nothing; /* probably not a block device */
+
+ if (!is_md_device(devno))
+ goto nothing;
+
+ if (blkid_devno_to_wholedisk(devno, NULL, 0, &disk))
+ goto nothing;
+
+ if (disk == devno)
+ fd = pr->fd;
+ else {
+ char *diskpath = blkid_devno_to_devname(disk);
+
+ if (!diskpath)
+ goto nothing;
+
+ fd = open(diskpath, O_RDONLY|O_CLOEXEC);
+ free(diskpath);
+
+ if (fd == -1)
+ goto nothing;
+ }
+
+ memset(&md, 0, sizeof(md));
+
+ if (ioctl(fd, GET_ARRAY_INFO, &md))
+ goto nothing;
+
+ if (fd >= 0 && fd != pr->fd) {
+ close(fd);
+ fd = -1;
+ }
+
+ /*
+ * Ignore levels we don't want aligned (e.g. linear)
+ * and deduct disk(s) from stripe width on RAID4/5/6
+ */
+ switch (md.level) {
+ case 6:
+ md.raid_disks--;
+ /* fallthrough */
+ case 5:
+ case 4:
+ md.raid_disks--;
+ /* fallthrough */
+ case 1:
+ case 0:
+ case 10:
+ break;
+ default:
+ goto nothing;
+ }
+
+ blkid_topology_set_minimum_io_size(pr, md.chunk_size);
+ blkid_topology_set_optimal_io_size(pr, (unsigned long) md.chunk_size * md.raid_disks);
+
+ return 0;
+
+nothing:
+ if (fd >= 0 && fd != pr->fd)
+ close(fd);
+ return 1;
+}
+
+const struct blkid_idinfo md_tp_idinfo =
+{
+ .name = "md",
+ .probefunc = probe_md_tp,
+ .magics = BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/src/topology/sysfs.c b/libblkid/src/topology/sysfs.c
new file mode 100644
index 0000000..745cd11
--- /dev/null
+++ b/libblkid/src/topology/sysfs.c
@@ -0,0 +1,124 @@
+/*
+ * sysfs based topology -- gathers topology information from Linux sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * For more information see Linux kernel Documentation/ABI/testing/sysfs-block.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "sysfs.h"
+#include "topology.h"
+
+/*
+ * Sysfs topology values (since 2.6.31, May 2009).
+ */
+static struct topology_val {
+
+ /* /sys/dev/block/<maj>:<min>/<ATTR> */
+ const char *attr;
+
+ /* functions to set probing result */
+ int (*set_ulong)(blkid_probe, unsigned long);
+ int (*set_int)(blkid_probe, int);
+
+} topology_vals[] = {
+ { "alignment_offset", NULL, blkid_topology_set_alignment_offset },
+ { "queue/minimum_io_size", blkid_topology_set_minimum_io_size },
+ { "queue/optimal_io_size", blkid_topology_set_optimal_io_size },
+ { "queue/physical_block_size", blkid_topology_set_physical_sector_size },
+ { "queue/dax", blkid_topology_set_dax },
+};
+
+static int probe_sysfs_tp(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ dev_t dev;
+ int rc, set_parent = 1;
+ struct path_cxt *pc;
+ size_t i, count = 0;
+
+ dev = blkid_probe_get_devno(pr);
+ if (!dev)
+ return 1;
+ pc = ul_new_sysfs_path(dev, NULL, NULL);
+ if (!pc)
+ return 1;
+
+ rc = 1; /* nothing (default) */
+
+ for (i = 0; i < ARRAY_SIZE(topology_vals); i++) {
+ struct topology_val *val = &topology_vals[i];
+ int ok = ul_path_access(pc, F_OK, val->attr) == 0;
+
+ rc = 1; /* nothing */
+
+ if (!ok && set_parent) {
+ dev_t disk = blkid_probe_get_wholedisk_devno(pr);
+ set_parent = 0;
+
+ /*
+ * Read attributes from "disk" if the current device is
+ * a partition. Note that sysfs ul_path_* API is able
+ * to redirect requests to attributes if parent is set.
+ */
+ if (disk && disk != dev) {
+ struct path_cxt *parent = ul_new_sysfs_path(disk, NULL, NULL);
+ if (!parent)
+ goto done;
+
+ sysfs_blkdev_set_parent(pc, parent);
+ ul_unref_path(parent);
+
+ /* try it again */
+ ok = ul_path_access(pc, F_OK, val->attr) == 0;
+ }
+ }
+ if (!ok)
+ continue; /* attribute does not exist */
+
+ if (val->set_ulong) {
+ uint64_t data;
+
+ if (ul_path_read_u64(pc, &data, val->attr) != 0)
+ continue;
+ rc = val->set_ulong(pr, (unsigned long) data);
+
+ } else if (val->set_int) {
+ int64_t data;
+
+ if (ul_path_read_s64(pc, &data, val->attr) != 0)
+ continue;
+ rc = val->set_int(pr, (int) data);
+ }
+
+ if (rc < 0)
+ goto done; /* error */
+ if (rc == 0)
+ count++;
+ }
+
+done:
+ ul_unref_path(pc); /* unref pc and parent */
+ if (count)
+ return 0; /* success */
+ return rc; /* error or nothing */
+}
+
+const struct blkid_idinfo sysfs_tp_idinfo =
+{
+ .name = "sysfs",
+ .probefunc = probe_sysfs_tp,
+ .magics = BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/src/topology/topology.c b/libblkid/src/topology/topology.c
new file mode 100644
index 0000000..53007d1
--- /dev/null
+++ b/libblkid/src/topology/topology.c
@@ -0,0 +1,381 @@
+/*
+ * topology - gathers information about device topology
+ *
+ * Copyright 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+#include "topology.h"
+
+/**
+ * SECTION:topology
+ * @title: Topology information
+ * @short_description: block device topology information.
+ *
+ * The topology chain provides details about Linux block devices, for more
+ * information see:
+ *
+ * Linux kernel Documentation/ABI/testing/sysfs-block
+ *
+ * NAME=value (tags) interface is enabled by blkid_probe_enable_topology(),
+ * and provides:
+ *
+ * @LOGICAL_SECTOR_SIZE: this is the smallest unit the storage device can
+ * address. It is typically 512 bytes.
+ *
+ * @PHYSICAL_SECTOR_SIZE: this is the smallest unit a physical storage device
+ * can write atomically. It is usually the same as the
+ * logical sector size but may be bigger.
+ *
+ * @MINIMUM_IO_SIZE: minimum size which is the device's preferred unit of I/O.
+ * For RAID arrays it is often the stripe chunk size.
+ *
+ * @OPTIMAL_IO_SIZE: usually the stripe width for RAID or zero. For RAID arrays
+ * it is usually the stripe width or the internal track size.
+ *
+ * @ALIGNMENT_OFFSET: indicates how many bytes the beginning of the device is
+ * offset from the disk's natural alignment.
+ *
+ * The NAME=value tags are not defined when the corresponding topology value
+ * is zero. The MINIMUM_IO_SIZE should be always defined if kernel provides
+ * topology information.
+ *
+ * Binary interface:
+ *
+ * blkid_probe_get_topology()
+ *
+ * blkid_topology_get_'VALUENAME'()
+ */
+static int topology_probe(blkid_probe pr, struct blkid_chain *chn);
+static void topology_free(blkid_probe pr, void *data);
+static int topology_is_complete(blkid_probe pr);
+static int topology_set_logical_sector_size(blkid_probe pr);
+
+/*
+ * Binary interface
+ */
+struct blkid_struct_topology {
+ unsigned long alignment_offset;
+ unsigned long minimum_io_size;
+ unsigned long optimal_io_size;
+ unsigned long logical_sector_size;
+ unsigned long physical_sector_size;
+ unsigned long dax;
+};
+
+/*
+ * Topology chain probing functions
+ */
+static const struct blkid_idinfo *idinfos[] =
+{
+#ifdef __linux__
+ &sysfs_tp_idinfo,
+ &ioctl_tp_idinfo,
+ &md_tp_idinfo,
+ &dm_tp_idinfo,
+ &lvm_tp_idinfo,
+ &evms_tp_idinfo
+#endif
+};
+
+
+/*
+ * Driver definition
+ */
+const struct blkid_chaindrv topology_drv = {
+ .id = BLKID_CHAIN_TOPLGY,
+ .name = "topology",
+ .dflt_enabled = FALSE,
+ .idinfos = idinfos,
+ .nidinfos = ARRAY_SIZE(idinfos),
+ .probe = topology_probe,
+ .safeprobe = topology_probe,
+ .free_data = topology_free
+};
+
+/**
+ * blkid_probe_enable_topology:
+ * @pr: probe
+ * @enable: TRUE/FALSE
+ *
+ * Enables/disables the topology probing for non-binary interface.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_enable_topology(blkid_probe pr, int enable)
+{
+ pr->chains[BLKID_CHAIN_TOPLGY].enabled = enable;
+ return 0;
+}
+
+/**
+ * blkid_probe_get_topology:
+ * @pr: probe
+ *
+ * This is a binary interface for topology values. See also blkid_topology_*
+ * functions.
+ *
+ * This function is independent on blkid_do_[safe,full]probe() and
+ * blkid_probe_enable_topology() calls.
+ *
+ * WARNING: the returned object will be overwritten by the next
+ * blkid_probe_get_topology() call for the same @pr. If you want to
+ * use more blkid_topology objects in the same time you have to create
+ * more blkid_probe handlers (see blkid_new_probe()).
+ *
+ * Returns: blkid_topology, or NULL in case of error.
+ */
+blkid_topology blkid_probe_get_topology(blkid_probe pr)
+{
+ return (blkid_topology) blkid_probe_get_binary_data(pr,
+ &pr->chains[BLKID_CHAIN_TOPLGY]);
+}
+
+/*
+ * The blkid_do_probe() backend.
+ */
+static int topology_probe(blkid_probe pr, struct blkid_chain *chn)
+{
+ size_t i;
+
+ if (chn->idx < -1)
+ return -1;
+
+ if (!S_ISBLK(pr->mode))
+ return -EINVAL; /* nothing, works with block devices only */
+
+ if (chn->binary) {
+ DBG(LOWPROBE, ul_debug("initialize topology binary data"));
+
+ if (chn->data)
+ /* reset binary data */
+ memset(chn->data, 0,
+ sizeof(struct blkid_struct_topology));
+ else {
+ chn->data = calloc(1,
+ sizeof(struct blkid_struct_topology));
+ if (!chn->data)
+ return -ENOMEM;
+ }
+ }
+
+ blkid_probe_chain_reset_values(pr, chn);
+
+ DBG(LOWPROBE, ul_debug("--> starting probing loop [TOPOLOGY idx=%d]",
+ chn->idx));
+
+ i = chn->idx < 0 ? 0 : chn->idx + 1U;
+
+ for ( ; i < ARRAY_SIZE(idinfos); i++) {
+ const struct blkid_idinfo *id = idinfos[i];
+
+ chn->idx = i;
+
+ if (id->probefunc) {
+ DBG(LOWPROBE, ul_debug("%s: call probefunc()", id->name));
+ if (id->probefunc(pr, NULL) != 0)
+ continue;
+ }
+
+ if (!topology_is_complete(pr))
+ continue;
+
+ /* generic for all probing drivers */
+ topology_set_logical_sector_size(pr);
+
+ DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [TOPOLOGY idx=%d]",
+ id->name, chn->idx));
+ return BLKID_PROBE_OK;
+ }
+
+ DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed) [TOPOLOGY idx=%d]",
+ chn->idx));
+ return BLKID_PROBE_NONE;
+}
+
+static void topology_free(blkid_probe pr __attribute__((__unused__)),
+ void *data)
+{
+ free(data);
+}
+
+static int topology_set_value(blkid_probe pr, const char *name,
+ size_t structoff, unsigned long data)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+ if (!chn)
+ return -1;
+ if (!data)
+ return 0; /* ignore zeros */
+
+ if (chn->binary) {
+ memcpy((char *) chn->data + structoff, &data, sizeof(data));
+ return 0;
+ }
+ return blkid_probe_sprintf_value(pr, name, "%lu", data);
+}
+
+
+/* the topology info is complete when we have at least "minimum_io_size" which
+ * is provided by all blkid topology drivers */
+static int topology_is_complete(blkid_probe pr)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+ if (!chn)
+ return FALSE;
+
+ if (chn->binary && chn->data) {
+ blkid_topology tp = (blkid_topology) chn->data;
+ if (tp->minimum_io_size)
+ return TRUE;
+ }
+
+ return __blkid_probe_lookup_value(pr, "MINIMUM_IO_SIZE") ? TRUE : FALSE;
+}
+
+int blkid_topology_set_alignment_offset(blkid_probe pr, int val)
+{
+ unsigned long xval;
+
+ /* Welcome to Hell. The kernel is able to return -1 as an
+ * alignment_offset if no compatible sizes and alignments
+ * exist for stacked devices.
+ *
+ * There is no way how libblkid caller can respond to the value -1, so
+ * we will hide this corner case...
+ *
+ * (TODO: maybe we can export an extra boolean value 'misaligned' rather
+ * then complete hide this problem.)
+ */
+ xval = val < 0 ? 0 : val;
+
+ return topology_set_value(pr,
+ "ALIGNMENT_OFFSET",
+ offsetof(struct blkid_struct_topology, alignment_offset),
+ xval);
+}
+
+int blkid_topology_set_minimum_io_size(blkid_probe pr, unsigned long val)
+{
+ return topology_set_value(pr,
+ "MINIMUM_IO_SIZE",
+ offsetof(struct blkid_struct_topology, minimum_io_size),
+ val);
+}
+
+int blkid_topology_set_optimal_io_size(blkid_probe pr, unsigned long val)
+{
+ return topology_set_value(pr,
+ "OPTIMAL_IO_SIZE",
+ offsetof(struct blkid_struct_topology, optimal_io_size),
+ val);
+}
+
+/* BLKSSZGET is provided on all systems since 2.3.3 -- so we don't have to
+ * waste time with sysfs.
+ */
+static int topology_set_logical_sector_size(blkid_probe pr)
+{
+ unsigned long val = blkid_probe_get_sectorsize(pr);
+
+ if (!val)
+ return -1;
+
+ return topology_set_value(pr,
+ "LOGICAL_SECTOR_SIZE",
+ offsetof(struct blkid_struct_topology, logical_sector_size),
+ val);
+}
+
+int blkid_topology_set_physical_sector_size(blkid_probe pr, unsigned long val)
+{
+ return topology_set_value(pr,
+ "PHYSICAL_SECTOR_SIZE",
+ offsetof(struct blkid_struct_topology, physical_sector_size),
+ val);
+}
+
+int blkid_topology_set_dax(blkid_probe pr, unsigned long val)
+{
+ return topology_set_value(pr,
+ "DAX",
+ offsetof(struct blkid_struct_topology, dax),
+ val);
+}
+
+/**
+ * blkid_topology_get_alignment_offset:
+ * @tp: topology
+ *
+ * Returns: alignment offset in bytes or 0.
+ */
+unsigned long blkid_topology_get_alignment_offset(blkid_topology tp)
+{
+ return tp->alignment_offset;
+}
+
+/**
+ * blkid_topology_get_minimum_io_size:
+ * @tp: topology
+ *
+ * Returns: minimum io size in bytes or 0.
+ */
+unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp)
+{
+ return tp->minimum_io_size;
+}
+
+/**
+ * blkid_topology_get_optimal_io_size
+ * @tp: topology
+ *
+ * Returns: optimal io size in bytes or 0.
+ */
+unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp)
+{
+ return tp->optimal_io_size;
+}
+
+/**
+ * blkid_topology_get_logical_sector_size
+ * @tp: topology
+ *
+ * Returns: logical sector size (BLKSSZGET ioctl) in bytes or 0.
+ */
+unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp)
+{
+ return tp->logical_sector_size;
+}
+
+/**
+ * blkid_topology_get_physical_sector_size
+ * @tp: topology
+ *
+ * Returns: logical sector size (BLKSSZGET ioctl) in bytes or 0.
+ */
+unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp)
+{
+ return tp->physical_sector_size;
+}
+
+/**
+ * blkid_topology_get_dax
+ * @tp: topology
+ *
+ * Returns: 1 if dax is supported, 0 otherwise.
+ *
+ * Since: 2.36
+ */
+unsigned long blkid_topology_get_dax(blkid_topology tp)
+{
+ return tp->dax;
+}
diff --git a/libblkid/src/topology/topology.h b/libblkid/src/topology/topology.h
new file mode 100644
index 0000000..3e46af9
--- /dev/null
+++ b/libblkid/src/topology/topology.h
@@ -0,0 +1,25 @@
+#ifndef BLKID_TOPOLOGY_H
+#define BLKID_TOPOLOGY_H
+
+#include "blkidP.h"
+
+extern int blkid_topology_set_alignment_offset(blkid_probe pr, int val);
+extern int blkid_topology_set_minimum_io_size(blkid_probe pr, unsigned long val);
+extern int blkid_topology_set_optimal_io_size(blkid_probe pr, unsigned long val);
+extern int blkid_topology_set_physical_sector_size(blkid_probe pr, unsigned long val);
+extern int blkid_topology_set_dax(blkid_probe pr, unsigned long val);
+
+/*
+ * topology probers
+ */
+#ifdef __linux__
+extern const struct blkid_idinfo ioctl_tp_idinfo;
+extern const struct blkid_idinfo md_tp_idinfo;
+extern const struct blkid_idinfo evms_tp_idinfo;
+extern const struct blkid_idinfo sysfs_tp_idinfo;
+extern const struct blkid_idinfo dm_tp_idinfo;
+extern const struct blkid_idinfo lvm_tp_idinfo;
+#endif
+
+#endif /* BLKID_TOPOLOGY_H */
+