From c1c7a95f0bbd50af35867ad31f0663f7080df7f4 Mon Sep 17 00:00:00 2001 From: Michel Machado Date: Thu, 14 Aug 2014 17:19:01 -0400 Subject: [PATCH] f3probe: map block device to underlying USB device The code is based on udev_example.c available on the following page: libudev and Sysfs Tutorial http://www.signal11.us/oss/udev/ f3probe nows requires library libudev. This patch also updates the following files to reflect the dependency of f3probe on libudev, and the experimental status of f3probe: Makefile and README. --- Makefile | 2 +- README | 26 ++++++++++++-- changelog | 4 +++ libprobe.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 126 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index ea240d9..5fdb42c 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ f3read: utils.o f3read.o $(CC) -o $@ $^ f3probe: libprobe.o f3probe.o - $(CC) -o $@ $^ + $(CC) -o $@ $^ -ludev -include *.d diff --git a/README b/README index f743b8a..3e77723 100644 --- a/README +++ b/README @@ -1,8 +1,27 @@ -### Compile on Linux, Apple Mac, Windows/Cygwin, and FreeBSD +### Compile stable software on Linux, Apple Mac, Windows/Cygwin, and FreeBSD make -### Use example + +### Compile experimental software on Linux + +make experimental + +NOTES: + - Experimental software might compile on non-Linux platforms, but + there is no guarantee given that they are only tested on Linux. + - Please do not e-mail me saying that you want an experimental software + to run on your platform; I already know that. + - If you want experimental software to run on your platform, + help to port them, or find someone that can port them for you. + If you do port the software, please send me a patch to help others. + - Currently, only f3probe is experimental. + - f3proble requires the library libudev to compile. + On Ubuntu, you can install this library with the following command: + sudo apt-get install libudev-dev + + +### Use example of f3write/f3read ./f3write /media/5EBD-5C80/ ./f3read /media/5EBD-5C80/ @@ -10,7 +29,8 @@ make Please replace "/media/5EBD-5C80/" with the appropriate path. USB devices are mounted in "/Volumes" on Macs. -### For more information see http://oss.digirati.com.br/f3/ +For more information see http://oss.digirati.com.br/f3/ + ### Files diff --git a/changelog b/changelog index 2553fe3..bab27f9 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,7 @@ +Version 5.0 - ??? ??, 201? + + * add f3probe (experimental). + Version 4.0 - Sep 9, 2014 * add support for FreeBSD. diff --git a/libprobe.c b/libprobe.c index 99d74dd..6cf1abd 100644 --- a/libprobe.c +++ b/libprobe.c @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -15,6 +16,7 @@ #include #include #include +#include #include "libprobe.h" @@ -277,11 +279,87 @@ static void bdev_free(struct device *dev) free((void *)bdev->filename); } +static bool is_block_dev(int fd) +{ + struct stat stat; + assert(!fstat(fd, &stat)); + return S_ISBLK(stat.st_mode); +} + +static char *map_block_to_usb_dev(const char *block_dev) +{ + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + char *usb_dev_path = NULL; + + udev = udev_new(); + if (!udev) + err(errno, "Can't create udev"); + + /* XXX Avoid the enumeration using udev_device_new_from_devnum(). */ + enumerate = udev_enumerate_new(udev); + assert(enumerate); + assert(!udev_enumerate_add_match_subsystem(enumerate, "block")); + assert(!udev_enumerate_scan_devices(enumerate)); + devices = udev_enumerate_get_list_entry(enumerate); + assert(devices); + + udev_list_entry_foreach(dev_list_entry, devices) { + const char *sys_path, *dev_path; + struct udev_device *dev, *parent_dev; + + /* Get the filename of the /sys entry for the device, + * and create a udev_device object (dev) representing it. + */ + sys_path = udev_list_entry_get_name(dev_list_entry); + dev = udev_device_new_from_syspath(udev, sys_path); + + /* usb_device_get_devnode() returns the path to + * the device node itself in /dev. + */ + dev_path = udev_device_get_devnode(dev); + if (strcmp(block_dev, dev_path)) { + assert(!udev_device_unref(dev)); + continue; + } + + /* The device pointed to by dev contains information about + * the USB device. + * In order to get information about the USB device, + * get the parent device with the subsystem/devtype pair of + * "usb"/"usb_device". + * This will be several levels up the tree, + * but the function will find it. + */ + parent_dev = udev_device_get_parent_with_subsystem_devtype( + dev, "usb", "usb_device"); + if (!parent_dev) + err(errno, "Unable to find parent usb device of `%s'", + block_dev); + + usb_dev_path = strdup(udev_device_get_devnode(parent_dev)); + /* @parent_dev is not referenced, and will be freed when + * the child (i.e. @dev) is freed. + * See udev_device_get_parent_with_subsystem_devtype() for + * details. + */ + assert(!udev_device_unref(dev)); + break; + } + /* Free the enumerator object. */ + assert(!udev_enumerate_unref(enumerate)); + + assert(!udev_unref(udev)); + return usb_dev_path; +} + /* XXX Test if it's a device, or a partition. * If a partition, warn user, and ask for confirmation before * going ahead. * Suggest how to call f3probe with the correct device name if * the block device is a partition. + * Use udev to do these tests. */ /* XXX Test for write access of the block device to give * a nice error message. @@ -289,10 +367,10 @@ static void bdev_free(struct device *dev) */ struct device *create_block_device(const char *filename) { - /* TODO */ - const char *usb_filename = "/dev/bus/usb/002/015"; + const char *usb_filename; + struct block_device *bdev; - struct block_device *bdev = malloc(sizeof(*bdev)); + bdev = malloc(sizeof(*bdev)); if (!bdev) goto error; @@ -306,10 +384,23 @@ struct device *create_block_device(const char *filename) goto filename; } + if (!is_block_dev(bdev->fd)) { + err(EINVAL, "File `%s' is not a block device", filename); + goto fd; + } + + /* XXX Add support for block devices backed by SCSI, SATA, and ATA. */ + usb_filename = map_block_to_usb_dev(filename); + if (!usb_filename) { + err(EINVAL, "Block device `%s' is not backed by a USB device", + filename); + goto fd; + } + bdev->hw_fd = open(usb_filename, O_WRONLY | O_NONBLOCK); if (bdev->hw_fd < 0) { err(errno, "Can't open device `%s'", usb_filename); - goto fd; + goto usb_filename; } bdev->dev.read_block = bdev_read_block; @@ -318,8 +409,11 @@ struct device *create_block_device(const char *filename) bdev->dev.get_size_gb = bdev_get_size_gb; bdev->dev.free = bdev_free; + free((void *)usb_filename); return &bdev->dev; +usb_filename: + free((void *)usb_filename); fd: assert(!close(bdev->fd)); filename: