7

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.)

So I made that specific problem into this new question.

Apparently removable devices listed in /sys/block end with 1. It is stated here and several other places in this site 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
PhilR
  • 149
MountainX
  • 17,948

3 Answers3

4

Ok, so to pull this in a list, you can use the same command I gave you before, but just drop the removeable requirement:

% for blk in $(lsblk -ndo name) ; do
>        udevadm info --query=all --name "$/dev/$blk" |\
>        grep -q ID_BUS=usb && printf \
>            'findmnt %s -no TARGET ;'\
>                "/dev/$blk" /dev/"$blk"[0-9]
>        } ; done 2>&- |. /dev/stdin 

It's no big deal - it will now only output active mountpoints for block devices on the usb bus and I, for one, can't think of many examples of non-removable usb block devices anyway.

Regarding why your current device does not register as removeable, the most likely answer is that its driver simply doesn't list it that way. When devices are detected by udev it pulls in information on them as soon as it can based on its rules database. You can see all of those classifications with:

% udevadm info --query=all --name /dev/$BLOCK_DEVICE

That will return a list of all of the key=value entries the system uses to classify the hardware referenced by /dev/$BLOCK_DEVICE. At the bottom of this post slm advises how this information can be easily parsed to serve your needs - and it's pretty simple indeed. The documentation rightly describes it as "human-readable, grep-friendly." Above I filter all block devices to only those on the usb bus with:

% grep -q ID_BUS=usb

You can also attribute-walk all the way up the device tree for your current device and another whose attributes you think it should reflect. If it doesn't, it is because udev assigned it differently than you think it should have. Thankfully, though, you can see every datapoint that affected its assignment with:

udevadm info --attribute-walk --name /dev/$BLOCK_DEVICE

You can use this data to model new assignment rules as you please.

mikeserv
  • 58,310
2

USB removable storage selector: USBKeyChooser

Rewrited 2022-01-05:

USBKEYS=()
while read _{,,,,,,} dev _ rdev;do
    [[ $rdev == */usb[0-9]* ]] &&
    grep -q '^DRIVER=sd$' /sys/block/$dev/device/uevent &&
    (( $(</sys/block/$dev/size) )) &&
    (( $(</sys/block/$dev/removable) )) &&
    USBKEYS+=($dev)
done < <(/bin/ls --color=never -g /sys/block)

Rewind:

In this I

  • Ensure this is USB

  • Ensure device is removable

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

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

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=() while read _{,,,,,,} dev _ rdev;do [[ $rdev == /usb[0-9] ]] && grep -q '^DRIVER=sd$' /sys/block/$dev/device/uevent && (( $(</sys/block/$dev/size) )) && (( $(</sys/block/$dev/removable) )) && USBKEYS+=($dev) done < <(/bin/ls --color=never -g /sys/block)

    (( ${#USBKEYS[@]} )) &amp;&amp;
        title=&quot;Choose wich USB stick have to be installed&quot; ||
        title=&quot;No key found&quot;

    menu=(R &quot;Re scan devices&quot;)
    for dev in ${USBKEYS[@]} ;do
        read model &lt;/sys/block/$dev/device/model
        menu+=($dev &quot;$model&quot;)
    done
    ans=$($DIALOG --menu &quot;$title&quot; 21 72 14 &quot;${menu[@]}&quot; 2&gt;&amp;1 &gt;/dev/tty)
    if [ ! &quot;$ans&quot; ]; then echo &quot;User aborted.&quot;; return 1; fi
    [[ $ans == R ]] || STICK=$ans
done

} usbKeyChoose [[ $0 == "$BASH_SOURCE" ]] && [[ $STICK ]] && echo $STICK true

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

0

This is F. Hauri's answer adapted for kdialog:

#!/bin/bash

DIALOG=kdialog

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")
        i=0
        for dev in ${USBKEYS[@]} ;do
            read model </sys/block/$dev/device/model
            #echo $i $dev "$model"
            menu+=("$i" "$dev $model")
            i=$[i + 1]
        done
        num=$($DIALOG --menu "$title"  "${menu[@]}")
        #echo "num=$num"
        #echo "USBKEYS[num]=${USBKEYS[num]}"
        if [ ! "$num" ] ; then
            echo "User aborted."
            exit 0;
        fi
        [ ! "$num" = "R" ] && [ "${USBKEYS[num]}" ] && STICK=${USBKEYS[num]}
    done; }

usbKeyChoose
echo $STICK
MountainX
  • 17,948