diff mbox

[v4,5/5] raw-posix: Introduce hdev_is_sg()

Message ID 1432115859-11413-6-git-send-email-dimara@arrikto.com
State New
Headers show

Commit Message

Dimitris Aragiorgis May 20, 2015, 9:57 a.m. UTC
Until now, an SG device was identified only by checking if its path
started with "/dev/sg". Then, hdev_open() set bs->sg accordingly.
This is very fragile, e.g. it fails with symlinks or relative paths.
We should rely on the actual properties of the device instead of the
specified file path.

Test for an SG device (e.g. /dev/sg0) by ensuring that all of the
following holds:

 - The device supports the SG_GET_VERSION_NUM ioctl
 - The device supports the SG_GET_SCSI_ID ioctl
 - The specified file name corresponds to a character device

Signed-off-by: Dimitris Aragiorgis <dimara@arrikto.com>
---
 block/raw-posix.c |   39 ++++++++++++++++++++++++++++-----------
 1 file changed, 28 insertions(+), 11 deletions(-)

Comments

Eric Blake June 18, 2015, 8:16 p.m. UTC | #1
On 05/20/2015 03:57 AM, Dimitris Aragiorgis wrote:
> Until now, an SG device was identified only by checking if its path
> started with "/dev/sg". Then, hdev_open() set bs->sg accordingly.
> This is very fragile, e.g. it fails with symlinks or relative paths.
> We should rely on the actual properties of the device instead of the
> specified file path.
> 
> Test for an SG device (e.g. /dev/sg0) by ensuring that all of the
> following holds:
> 
>  - The device supports the SG_GET_VERSION_NUM ioctl
>  - The device supports the SG_GET_SCSI_ID ioctl
>  - The specified file name corresponds to a character device

I'd check stat first. ioctl's on the wrong device type might have
unexpected side effects.


> +static int hdev_is_sg(BlockDriverState *bs)
> +{
> +
> +#if defined(__linux__)
> +
> +    struct stat st;
> +    struct sg_scsi_id scsiid;
> +    int sg_version;
> +
> +    if (!bdrv_ioctl(bs, SG_GET_VERSION_NUM, &sg_version) &&
> +        !bdrv_ioctl(bs, SG_GET_SCSI_ID, &scsiid) &&
> +        stat(bs->filename, &st) >= 0 && S_ISCHR(st.st_mode)) {
> +        DPRINTF("SG device found: type=%d, version=%d\n",
> +            scsiid.scsi_type, sg_version);
> +        return 1;
> +    }
> +
> +#endif
> +
> +    return 0;

Make this return 'bool', using 'true' and 'false'.  We require a C99
compiler, after all.
Stefan Hajnoczi June 19, 2015, 12:33 p.m. UTC | #2
On Wed, May 20, 2015 at 12:57:39PM +0300, Dimitris Aragiorgis wrote:
> This is very fragile, e.g. it fails with symlinks or relative paths.

This is not true since realpath(3) is used to resolve symlinks and
product an absolute path.

Is this patch really necessary?
Dimitris Aragiorgis June 22, 2015, 10:18 a.m. UTC | #3
Hello Stefan,

Yes, you are right. Using realpath() is a workaround for supporting
symlinks, as long as they point to a path starting with "/dev/sg". I
will remove this reference in the revised version of this patch.

However, it still holds that determining whether a filename is an SG
device or not is based on textual (and hardcoded) examination, i.e.,
whether it starts with "/dev/sg".

The point is that a device node is a device node no matter what its
filename or its location in the fs tree is. A device node with  major 1,
minor 3 is the null device whether it's called /dev/null or not.

The proposed patch allows the use of any device node which is capable of
acting as a scsi-generic device; it checks for the required intrinsic
property ("does this device support the SG ioctl?") versus trying to
second-guess the nature of the device node by checking its filename.

Similarly, the programming example from SG's documentation does not
check the node of the device node at all:
http://sg.danny.cz/sg/p/sg_v3_ho.html#pexample

    /* It is prudent to check we have a sg device by trying an ioctl */
    if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
        printf("%s is not an sg device, or old sg driver\n", argv[1]);
        return 1;

The above has the advantage that it works with device nodes of any name,
and in any directory.

So I suggest we go with the submitted patch taking into account Eric's
proposal: The code first stat()s the given filename to ensure it is a
character device node, and then it issues the SG_GET_VERSION_NUM and
SG_GET_SCSI_ID ioctl()s.

Thanks,
dimara

* Stefan Hajnoczi <stefanha@redhat.com> [2015-06-19 13:33:02 +0100]:

> On Wed, May 20, 2015 at 12:57:39PM +0300, Dimitris Aragiorgis wrote:
> > This is very fragile, e.g. it fails with symlinks or relative paths.
> 
> This is not true since realpath(3) is used to resolve symlinks and
> product an absolute path.
> 
> Is this patch really necessary?
Stefan Hajnoczi June 22, 2015, 12:33 p.m. UTC | #4
On Mon, Jun 22, 2015 at 01:18:43PM +0300, Dimitris Aragiorgis wrote:
> So I suggest we go with the submitted patch taking into account Eric's
> proposal: The code first stat()s the given filename to ensure it is a
> character device node, and then it issues the SG_GET_VERSION_NUM and
> SG_GET_SCSI_ID ioctl()s.

That's fine.

Regarding Eric's comment about ioctl number collisions, I've checked
Linux Documentation/ioctl/ioctl-number.txt.  At least under Linux there
is no collision for SG_GET_VERSION_NUM/SG_GET_SCSI_ID.

Stefan
diff mbox

Patch

diff --git a/block/raw-posix.c b/block/raw-posix.c
index ace228f..d84e5a0 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -57,6 +57,7 @@ 
 #include <linux/fd.h>
 #include <linux/fs.h>
 #include <linux/hdreg.h>
+#include <scsi/sg.h>
 #ifdef __s390__
 #include <asm/dasd.h>
 #endif
@@ -2081,15 +2082,38 @@  static void hdev_parse_filename(const char *filename, QDict *options,
     qdict_put_obj(options, "filename", QOBJECT(qstring_from_str(filename)));
 }
 
+static int hdev_is_sg(BlockDriverState *bs)
+{
+
+#if defined(__linux__)
+
+    struct stat st;
+    struct sg_scsi_id scsiid;
+    int sg_version;
+
+    if (!bdrv_ioctl(bs, SG_GET_VERSION_NUM, &sg_version) &&
+        !bdrv_ioctl(bs, SG_GET_SCSI_ID, &scsiid) &&
+        stat(bs->filename, &st) >= 0 && S_ISCHR(st.st_mode)) {
+        DPRINTF("SG device found: type=%d, version=%d\n",
+            scsiid.scsi_type, sg_version);
+        return 1;
+    }
+
+#endif
+
+    return 0;
+}
+
 static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
                      Error **errp)
 {
     BDRVRawState *s = bs->opaque;
     Error *local_err = NULL;
     int ret;
-    const char *filename = qdict_get_str(options, "filename");
 
 #if defined(__APPLE__) && defined(__MACH__)
+    const char *filename = qdict_get_str(options, "filename");
+
     if (strstart(filename, "/dev/cdrom", NULL)) {
         kern_return_t kernResult;
         io_iterator_t mediaIterator;
@@ -2118,16 +2142,6 @@  static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
 #endif
 
     s->type = FTYPE_FILE;
-#if defined(__linux__)
-    {
-        char resolved_path[ MAXPATHLEN ], *temp;
-
-        temp = realpath(filename, resolved_path);
-        if (temp && strstart(temp, "/dev/sg", NULL)) {
-            bs->sg = 1;
-        }
-    }
-#endif
 
     ret = raw_open_common(bs, options, flags, 0, &local_err);
     if (ret < 0) {
@@ -2137,6 +2151,9 @@  static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
         return ret;
     }
 
+    /* Since this does ioctl the device must be already opened */
+    bs->sg = hdev_is_sg(bs);
+
     if (flags & BDRV_O_RDWR) {
         ret = check_hdev_writable(s);
         if (ret < 0) {