USB Drive – Removable USB Stick Listed as Non-Removable in /sys/block?

automountingremovable-storageusb-drive

In my question Bash script to output path to USB flash memory stick I got stuck on a problem nobody else seems to be having. (The issue also impedes my desire to use this answer: https://unix.stackexchange.com/a/60335/15010.)

So I made that specific problem into this new question.

Apparently removable devices listed in /sys/block end with 1. It is stated here: https://unix.stackexchange.com/a/80880/15010 and several other places in this http://unix.stackexchange.com and this principle is used in the answers I referenced above.

My removable device, a Sandisk 64GB flash memory stick, is listed as:

/sys/block/sdl/removable:0

Apparently, removable devices should end in 1 (and my others do). Why does my USB memory stick not follow the rule?

It was automounted by Dolphin. I'm running Kubuntu 12.04.

Dolphin shows it as "59.6GiB Removable Media".

And it is mounted (automatically) at /media/me/70E8-1567

sudo blkid shows it as:

/dev/sdl1: UUID="70E8-1567" TYPE="vfat".

'lsblk -do name,rm` shows:

sdl   0

And lsusb -vv shows:

Bus 001 Device 008: ID 0781:5530 SanDisk Corp. Cruzer
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x0781 SanDisk Corp.
  idProduct          0x5530 Cruzer
  bcdDevice            2.01
  iManufacturer           1 
  iProduct                2 
  iSerial                 3 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           32
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              200mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         8 Mass Storage
      bInterfaceSubClass      6 SCSI
      bInterfaceProtocol     80 Bulk-Only
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               1

Best Answer

USB removable storage selector: USBKeyChooser

I think I have it:

Replacing grep -Hv 0 /sys/block/*/removable by grep -Hv ^ATA\ *$ /sys/block/*/device/vendor seem work:

export USBKEYS=($(
    grep -Hv ^ATA\ *$ /sys/block/*/device/vendor |
    sed s/device.vendor:.*$/device\\/uevent/ |
    xargs grep -H ^DRIVER=sd |
    sed s/device.uevent.*$/size/ |
    xargs grep -Hv ^0$ |
    cut -d / -f 4
))
for dev in ${USBKEYS[@]} ;do
    echo $dev \"$(
        sed -e s/\ *$//g </sys/block/$dev/device/model
        )\" ;
  done

Or even using readlink to ensure this is USB could be more accurate...

export USBKEYS=($(
    xargs -n1 readlink < <(echo /sys/block/*) |
    sed -ne 's+^.*/usb[0-9].*/\([^/]*\)$+/sys/block/\1/device/uevent+p' |
    xargs grep -H ^DRIVER=sd |
    sed s/device.uevent.*$/size/ |
    xargs grep -Hv ^0$ |
    cut -d / -f 4
))
for dev in ${USBKEYS[@]} ;do
    echo $dev \"$(
        sed -e s/\ *$//g </sys/block/$dev/device/model
        )\" ;
  done

Rewind:

In this I

  1. Ensure this is USB (or removable

  2. Ensure this work as an hard drive (not a CD-Rom)

  3. Ensure they have size greater than 0 (not an empty Card reader)

Golfed version:

US=($(cut -d/ -f4 <(grep -vl ^0$ $(sed s@device/.*@size@ <(grep -l ^DRIVER=sd $(
  sed s+/device.*$+/dev*/ue*+ <(grep -Hv ^ATA\ *$ /sys/block/*/device/vendor)) <(:))) <(:))))

set | grep ^US=

This could be written

US=($(
    cut -d/ -f4 <(
        grep -vl ^0$ $(
            sed s@device/.*@size@ <(
                grep -l ^DRIVER=sd $(
                    sed -ne 's+^.*/usb[0-9].*/\([^/]*\)$+/sys/block/\1/dev*/ue*+p' <(
                        xargs -n1 readlink < <(echo /sys/block/*)
                    )
                ) /dev/null # equivalant but quicker than <(:)
            )) /dev/null)))

With readlink, golfed version become:

US=($(cut -d/ -f4 <(grep -vl ^0$ $(sed s@device/.*@size@ <(grep -l ^DRIVER=sd $(
    sed -ne 's+^.*/usb[0-9].*/\([^/]*\)$+/sys/block/\1/dev*/ue*+p' <(
    xargs -n1 readlink < <(echo /sys/block/*)) ) <(:))) <(:))))

In fine: usbKeyChooser

There is the final version of usbKeyChooser subroutine in my live installer:

#!/bin/bash
DIALOG=whiptail

usbKeyChoose() {
    while [ ! -b /dev/$STICK ] ;do
        USBKEYS=($(
                xargs -n1 readlink < <(echo /sys/block/*) |
                sed -ne 's+^.*/usb[0-9].*/\([^/]*\)$+/sys/block/\1/device/uevent+p' |
                xargs grep -H ^DRIVER=sd |
                sed s/device.uevent.*$/size/ |
                xargs grep -Hv ^0$ |
                cut -d / -f 4                 ))
        if [ ${#USBKEYS[@]} -eq 0 ];then
            title="No key found"
        else
            title="Choose wich USB stick have to be installed"
        fi

        menu=(R "Re scan devices")
        for dev in ${USBKEYS[@]} ;do
            read model </sys/block/$dev/device/model
            menu+=($dev "$model")
        done
        num=$($DIALOG --menu "$title" 21 72 14 "${menu[@]}" 2>&1 >/dev/tty)
        if [ ! "$num" ] ; then
            echo "User aborted."
            exit 0;
        fi
        [ ! "$num" = "R" ] && [ "${USBKEYS[num]}" ] && STICK=${USBKEYS[num]}
    done; }

usbKeyChoose
echo $STICK

I like this looping solution because they

  • let insert many keys,
  • wait for kernel registration,
  • valid the choice,
  • default to nothing and
  • permit user abort.

Anyway, even if user did wrong ok choice, next screen is another choice asking user for which image have to be written on key defaulting to create new image wich is a very long process where user could hit Ctrl+c

Related Question