How to set up a removable USB Drive

From http://www.greenfly.org/tips/usb_drive.html

How to set up a removable USB Drive

Be sure the check out the 2004-04-22 update at the bottom of the page!

For my birthday this last year, I received a USB laptop drive enclosure. Once I upgraded my hard drive in my P2110, I had its 20Gb hard drive to use in it. I decided to set up a FAT32 partition on it, spanning the full 20Gb, and now have set up hotplug and autofs to automatically mount the usb drive when I insert and access it (and umount it when it's idle).

Getting information and setting hotplug up

So, first thing, is for hotplug to be installed. In Debian it was as simple as running apt-get install hotplug. Hotplug isn't necessarily easy to configure, but following some tips on the 'net, I was able to figure it all out. The first step, is to plug in the USB drive, and scan through the logs to make sure it works. Hotplug recognized that it was a usb hard drive, and made sure my usb-storage module was already loaded.

Jan 19 15:46:27 clover kernel: hub.c: new USB device 00:02.0-1, assigned address 4
Jan 19 15:46:27 clover kernel: WARNING: USB Mass Storage data integrity not assured
Jan 19 15:46:27 clover kernel: USB Mass Storage device found at 4
Jan 19 15:46:31 clover usb.agent[10819]: kernel driver usb-storage already loaded

USB drives work as regular scsi hard drives as far as we are concerned (and requires sd_mod to be loaded). And we could just mount the device that is created (/dev/sda1 for me) and be done with it. I wanted it to automatically mount when I plugged it in, and umount when I unplugged it. To do this, first we need some information from /proc:

/proc/bus/usb/devices will show information about the various usb devices on the system. For instance, here is a snip from the file when my usb drive is plugged in:

T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 4 Spd=12 MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=05e3 ProdID=0702 Rev= 0.02
S: Product=USB TO IDE
C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr= 96mA
I: If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
E: Ad=81(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms

Yeah, it's a lot of gibberish, but there's useful information in there for setting up hotplug. I have highlighted the parts that are of interest to us. You can configure hotplug to do certain things when certain devices are plugged in. It already has its own scripts set to run when it detects generic usb-storage devices, but I wanted something specific. To do this, you need to edit the /etc/hotplug/usb.usermap file:

# usb.usermap file
# This is autogenerated by update-usb.usermap program
# Note: you may use /etc/hotplug/usb/*.usermap
# usb module         match_flags idVendor idProduct bcdDevice_lo bcdDevice_hi bDeviceClass bDeviceSubClass 
#bDeviceProtocol bInterfaceClass bInterfaceSubClass bInterfaceProtocol driver_info
usb-storage     0x00f   0x05e3  0x0702  0x00    0x00    0x00    0x00    0x00	0x00    0x00    0x00

In the line I added above, there are only 3 fields of interest, the match_flags, idVendor, and idProduct. I noticed from the usb.distmap file that the match_flags field for usb-storage was 0x00f. Now, the idVendor value I got from that /proc file I listed above. It says "Vendor=05e3" so I put in 0x05e3 in the second field. Likewise I took the "ProdID=0702" and put in 0x0702 in the idProduct field. The rest doesn't matter (the first three fields are enough to match this device) so I just put in 0x00 for those.

This line in usb.usermap tells hotplug to run the /etc/hotplug/usb/usb-storage script whenever it sees that this device is plugged in. That script can then load modules, or run other programs (including setting up programs to run when the drive is removed). I found some examples on the net and created my own that would automount the drive:

#!/bin/sh
case "$PRODUCT" in
   5e3/702/2) # FireXpress usb hard drive
   /bin/mount /mnt/usbdrive
# set up what to do when you remove the drive
   echo -e '#!/bin/sh\n/bin/umount /mnt/usbdrive' > $REMOVER
   chmod a+x $REMOVER
   ;;
esac

If you want to see what kind of enviroment variables are present when this script is run, you can insert a "set >> /tmp/settings" line above the case statement. Reading through that file, you can see what $PRODUCT is set to for your product. In my case it was set to "5e3/702/2". I then set up the case statement to mount this usbdrive when that device is actually plugged in (my fstab entry established the mounting of a /dev/sda1 vfat partition on /mnt/usbdrive). There is another environment variable, $REMOVER, that specifies the name of a script that is run after the device is removed. This script is empty by default, so I echoed in the umount command I wanted to use when I removed the drive.

I then ran chmod a+x /etc/hotplug/usb/usb-storage to make the script executable. Once I restarted the hotplug service with /etc/init.d/hotplug restart I then could plug in the drive and see that /mnt/usbdrive had been mounted by running df. When I unplugged the drive, the drive would then be umounted.

Not the best solution

Now, there are some problems for this. Namely, the drive isn't being umounted until it is already removed. This could result in filesystem corruption over time. The solution is to use autofs to mount the device on demand, and then umount when idle.

Autofs saves the day

First, I installed autofs with apt-get install autofs. Then I modified /etc/auto.master and added an entry for my removable drive:

# $Id: auto.master,v 1.2 1997/10/06 21:52:03 hpa Exp $
# Sample auto.master file
# Format of this file:
# mountpoint map options
# For details of the format look at autofs(5).
/var/autofs/misc        /etc/auto.misc
/var/autofs/net         /etc/auto.net
/var/autofs/removable   /etc/auto.removable     --timeout=2

That last line tells autofs to mount any of the removable devices I will specify in /etc/auto.removable under /var/autofs/removable, and to umount them after 2 seconds of idling. I then created the /etc/auto.removable file:

usbdrive        -fstype=vfat,uid=1002,gid=1002,umask=002        :/dev/sda1

This file sets up a mount point which will end up being /var/autofs/removable/usbdrive, the mounting options to use, and which device to mount. Now, all of this happens outside of /etc/fstab, so I removed my entry for my usbdrive from /etc/fstab, and also removed the /mnt/usbdrive directory. I decided what I would do instead is have my hotplug script create a symlink to the autofs mountpoint when the drive is inserted, and remove the symlink when the drive is removed. That way, I will only deal with /mnt/usbdrive and not worry about any /var/autofs directories. The new and improved /etc/hotplug/usb/usb-storage is the following:

#!/bin/sh
case "$PRODUCT" in
   5e3/702/2) # FireXpress usb hard drive
   ln -s /var/autofs/removable/usbdrive /mnt/usbdrive
# set up what to do when you remove the drive
   echo -e '#!/bin/sh\nrm /mnt/usbdrive' > $REMOVER
   chmod a+x $REMOVER
   ;;
esac

Now, I can plug in my drive, and see that /mnt/usbdrive is created. After 2 seconds, autofs will umount the drive. You can monitor /var/log/syslog to watch this happening:

Jan 19 16:24:35 clover automount[14059]: mount(generic): calling mkdir_path /var/autofs/removable/usbdrive
Jan 19 16:24:35 clover automount[14059]: mount(generic): calling mount -t vfat -s -o uid=1002,gid=1002,umask=002 
/dev/sda1 /var/autofs/removable/usbdrive
Jan 19 16:24:36 clover automount[14059]: mount(generic): mounted /dev/sda1 type vfat 
on /var/autofs/removable/usbdrive
Jan 19 16:24:39 clover automount[14066]: running expiration on path 
/var/autofs/removable/usbdrive
Jan 19 16:24:39 clover automount[14066]: expired /var/autofs/removable/usbdrive 

As you can see, the drive unmounted seconds after the command finished, since it had been idling. It would now be safe to remove.

For the future, I am planning on setting up a similar system for my digital camera (which appears as a normal usb storage device as well) to not only automatically mount the camera, but also sync up any photos on it.

iPod-specific configuration

First of all, a caveat. I don't actually own an iPod (if you want to remedy that, email me) so all of this is based on a 3rd generation iPod configuration I helped a friend, whiprush, set up.

The configuration is really just about the same, with a few distinct changes. For one, the Product ID is different on the iPod, so /etc/hotplug/usb/usb-storage would look something like:

#!/bin/sh
set >> /tmp/settings
case "$PRODUCT" in
   5ac/1201/0) # Apple iPod
   sleep 5
   ln -s /var/autofs/removable/ipod /mnt/ipod
# set up what to do when you remove the drive
   echo -e '#!/bin/sh\nrm /mnt/ipod' > $REMOVER
   chmod a+x $REMOVER
   ;;
esac

Make sure to make this script executable. As you can see, the main thing that changed from the previous usb-storage script is the value of $PRODUCT. Also added was a sleep 5, as the iPod seemed to take a bit longer to initialize than my regular usb drive. If this script doesn't work for your iPod, look through /tmp/settings and see what PRODUCT= is set to.

This script will create a symlink, /mnt/ipod, to the autofs directory that you will then set up. When you unplug the iPod, the symlink will be deleted.

Now to edit autofs settings. First add the following line to /etc/auto.master:

/var/autofs/removable /etc/auto.removable --timeout=2

Now edit /etc/auto.removable:

ipod -fstype=vfat,uid=1000,gid=1000 :/dev/sdc2

If your iPod partition sets itself up as something other than /dev/sdc2 (check /var/log/syslog or /var/log/messages if you don't know), then simply change the /dev/ entry to match it.

Now, restart autofs and a symlink, /mnt/ipod, should be created that then links to the autofs directory. When you access that directory, your iPod should become mounted. It will then umount after 2 seconds of idle time.

IEEE1394 Support

If you have a IEEE1394 card in your Linux system, you can add some extra configuration so that it will work the same as when you plug it in USB.

First you need to create an /etc/hotplug/ieee1394 directory. The iPod registers as an sdp2 device, and you will need to create a script at /etc/hotplug/ieee1394/sdp2 to handle the device:

#!/bin/sh
set >> /tmp/settings
case "$GUID" in
   000a27000252a1ee) # Apple iPod
   sleep 5
   ln -s /var/autofs/removable/ipod /mnt/ipod
# set up what to do when you remove the drive
   echo -e '#!/bin/sh\nrm /mnt/ipod' > $REMOVER
   chmod a+x $REMOVER
   ;; 
esac

Unlike the usb-storage script, the iPod as an ieee1394 device doesn't have a PRODUCT environment variable. Instead we use GUID to identify it. Now restart hotplug, and try plugging in your iPod to your IEEE1394 port. The /mnt/ipod symlink should be created, and accessing it should mount your iPod.

UPDATE 2004-04-22

I have modified this hotplug script rather heavily since I got started, and thought I might share some of the updates on this page.

I realize that hard-coding the scsi device doesn't work for anyone who might use multiple usb devices at once. The difficulty in generating the scsi device on the fly is that the kernel doesn't directly provide access to determine which scsi hard drive a certain GUID was assigned. Really the only way to do so required a hack that would read certain /proc entries along with /var/log/syslog. I call these pair of hacks usbguid2sd and sd2usbguid. As the names suggest, these scripts convert between a usb GUID and a scsi drive, and back again.

Using these scripts I can create a default autofs setup for sda1, sdb1, sdc1 and sdd1 and then map new usb-storage devices on the fly. The following hotplug script will use usbguid2sd and do special things for recognized usb drives, and otherwise create a /mnt/ symlink:

#!/bin/sh
DEVICE=`/usr/local/sbin/usbguid2sd $PRODUCT`
set > /tmp/settings
case "$PRODUCT" in
# FireXpress usb hard drive
   5e3/702/2)
   ln -s /var/autofs/usb/$DEVICE /mnt/firexpress
   # set up what to do when you remove the drive
   echo -e '#!/bin/sh\nrm /mnt/firexpress' > $REMOVER
   chmod a+x $REMOVER
   ;;
# Olympus C750 camera
   7b4/105/1)
   ln -s /var/autofs/usb/$DEVICE/dcim/100olymp /mnt/camera
   echo -e '#!/bin/sh\nrm /mnt/camera' > $REMOVER
   chmod a+x $REMOVER
   export DISPLAY=":0.0"
   su greenfly -c /home/greenfly/bin/eterm_camera_sync 
   ;;
# Attache usb key drive
   ea0/6828/110)
   ln -s /var/autofs/usb/$DEVICE /mnt/attache
   ATTACHE=`diff /home/greenfly/.gnupg/secring.gpg /mnt/attache/.gnupg/secring.gpg`
   if [ "$ATTACHE" = "" ]
   then
      killall xlock
      echo -e '#!/bin/sh\nrm /mnt/attache\nsu greenfly -c "/usr/bin/X11/xlock -display :0.0 -mode blank"' > $REMOVER
   else
      echo -e '#!/bin/sh\nrm /mnt/attache' > $REMOVER
   fi
   # set up what to do when you remove the drive
   chmod a+x $REMOVER
   ;;
# default
   * )
   ln -s /var/autofs/usb/$DEVICE /mnt/$DEVICE
   # set up what to do when you remove the drive
   echo -e "#!/bin/sh\nrm /mnt/$DEVICE" > $REMOVER
   chmod a+x $REMOVER
   ;;
esac

You might notice I did something extra to the attache device. It is a usb keychain drive I carry around and I thought it might be slick if it killed xlock when I inserted it, and started xlock when I removed it. That section of the script compares the gpg secret keyring on the device with the one on the laptop (a crude form of authentication) and then if they match, it kills xlock. This way someone can't just plug in any attache drive and unlock my screen.