summaryrefslogtreecommitdiffstats
path: root/misc-utils/eject.c
diff options
context:
space:
mode:
authorKarel Zak2012-04-05 12:06:23 +0200
committerKarel Zak2012-04-05 12:06:23 +0200
commitde12e1bba1c4318dadfde0d3d69eabe913d072a3 (patch)
tree6cc4fcb80da1a2422ec76dcf0f40975cdfaff840 /misc-utils/eject.c
parenteject: remove obsolete code, use EXIT_* macros (diff)
downloadkernel-qcow2-util-linux-de12e1bba1c4318dadfde0d3d69eabe913d072a3.tar.gz
kernel-qcow2-util-linux-de12e1bba1c4318dadfde0d3d69eabe913d072a3.tar.xz
kernel-qcow2-util-linux-de12e1bba1c4318dadfde0d3d69eabe913d072a3.zip
eject: check for hotplug/removable attribute
* based on code from Fedora * supported are only: - /sys/dev/block/<maj:min>/removable devices - devices connected by usb, ieee1394, pcmcia, mmc or ccw subsystem. Frankly, this heuristic sucks -- all is hot-pluggable in this days, but it's probably good idea to restrict the default behavior to avoid user's mistakes. Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'misc-utils/eject.c')
-rw-r--r--misc-utils/eject.c125
1 files changed, 123 insertions, 2 deletions
diff --git a/misc-utils/eject.c b/misc-utils/eject.c
index 82a836dfa..1d98e0828 100644
--- a/misc-utils/eject.c
+++ b/misc-utils/eject.c
@@ -61,6 +61,16 @@
*/
#define TRAY_WAS_ALREADY_OPEN_USECS 200000 /* about 0.2 seconds */
+/* eject(1) is able to eject only 'removable' devices (attribute in /sys)
+ * _or_ devices connected by hotplug subsystem.
+ */
+static const char *hotplug_subsystems[] = {
+ "usb",
+ "ieee1394",
+ "pcmcia",
+ "mmc",
+ "ccw"
+};
/* Global Variables */
static int a_option; /* command flags and arguments */
@@ -749,6 +759,116 @@ done:
sysfs_deinit(&cxt);
}
+static int is_hotpluggable_subsystem(const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(hotplug_subsystems); i++)
+ if (strcmp(name, hotplug_subsystems[i]) == 0)
+ return 1;
+
+ return 0;
+}
+
+#define SUBSYSTEM_LINKNAME "/subsystem"
+
+/*
+ * For example:
+ *
+ * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \
+ * 1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb
+ *
+ * The function check if <chain>/subsystem symlink exists, if yes then returns
+ * basename of the readlink result, and remove the last subdirectory from the
+ * <chain> path.
+ */
+static char *get_subsystem(char *chain, char *buf, size_t bufsz)
+{
+ size_t len;
+ char *p;
+
+ if (!chain || !*chain)
+ return NULL;
+
+ len = strlen(chain);
+ if (len + sizeof(SUBSYSTEM_LINKNAME) > PATH_MAX)
+ return NULL;
+
+ do {
+ ssize_t sz;
+
+ /* append "/subsystem" to the path */
+ memcpy(chain + len, SUBSYSTEM_LINKNAME, sizeof(SUBSYSTEM_LINKNAME));
+
+ /* try if subsystem symlink exists */
+ sz = readlink(chain, buf, bufsz);
+
+ /* remove last subsystem from chain */
+ chain[len] = '\0';
+ p = strrchr(chain, '/');
+ if (p) {
+ *p = '\0';
+ len = p - chain;
+ }
+
+ if (sz > 0) {
+ /* we found symlink to subsystem, return basename */
+ buf[sz] = '\0';
+ return basename(buf);
+ }
+
+ } while (p);
+
+ return NULL;
+}
+
+static int is_hotpluggable(const char* device)
+{
+ struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
+ char devchain[PATH_MAX];
+ char subbuf[PATH_MAX];
+ dev_t devno;
+ int rc = 0;
+ ssize_t sz;
+ char *sub;
+
+ devno = sysfs_devname_to_devno(device, NULL);
+ if (sysfs_init(&cxt, devno, NULL) != 0)
+ return 0;
+
+ /* check /sys/dev/block/<maj>:<min>/removable attribute */
+ if (sysfs_read_int(&cxt, "removable", &rc) == 0 && rc == 1) {
+ verbose(_("%s: is removable device"), device);
+ goto done;
+ }
+
+ /* read /sys/dev/block/<maj>:<min> symlink */
+ sz = sysfs_readlink(&cxt, NULL, devchain, sizeof(devchain));
+ if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > sizeof(devchain))
+ goto done;
+
+ devchain[sz++] = '\0';
+
+ /* create absolute patch from the link */
+ memmove(devchain + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, devchain, sz);
+ memcpy(devchain, _PATH_SYS_DEVBLOCK "/",
+ sizeof(_PATH_SYS_DEVBLOCK "/") - 1);
+
+ while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) {
+ rc = is_hotpluggable_subsystem(sub);
+ if (rc) {
+ verbose(_("%s: connected by hotplug subsystem: %s"),
+ device, sub);
+ break;
+ }
+ }
+
+done:
+ sysfs_deinit(&cxt);
+ return rc;
+}
+
+
/* handle -x option */
void set_device_speed(char *name)
{
@@ -827,6 +947,9 @@ int main(int argc, char **argv)
} else
verbose(_("%s: is whole-disk device"), device);
+ if (!is_hotpluggable(device))
+ errx(EXIT_FAILURE, _("%s: is not hot-pluggable device"), device);
+
/* handle -n option */
if (n_option) {
info(_("device is `%s'"), device);
@@ -834,8 +957,6 @@ int main(int argc, char **argv)
return EXIT_SUCCESS;
}
- /* TODO: Check if device has removable flag */
-
/* handle -i option */
if (i_option) {
fd = open_device(device);