Difference between revisions of "Map Custom Device Entries with udev"

From ArchWiki
Jump to: navigation, search
(1. Get the udev info for your usb device)
m (Get USB device udev information)
(47 intermediate revisions by 24 users not shown)
Line 1: Line 1:
[[Category:Hardware]]
+
[[Category:Hardware detection and troubleshooting]]
 +
[[Category:File systems]]
 +
[[es:Map Custom Device Entries with udev]]
 +
This process allows to map a specific device to a constant device-node, located in {{ic|/dev}}, by using [[udev]] rules. This can then be used in [[fstab]], among other places, to ensure that the device can be mounted with a unchanging device-node--ideal for desktop shortcuts and other mounting operations.
  
WIP - DibbleTheWrecker
+
This article focuses on USB devices and was copied almost verbatim from the [http://en.gentoo-wiki.com Gentoo wiki], later supplemented with additional hints.
  
=== Use udev and hotplug to map multiple nodes to a device===
+
==Get USB device udev information==
 +
Here is script that facilitates this operation. Simply plug in the device before running the script. Save it as {{ic|usb-show-serial}}, for example, and run it like so:
 +
{{hc|$ ./usb-show-serial /dev/''sdX'' ''label''|2=
 +
add the following to /etc/udev/rules.d/90-automounter.rules
 +
BUS=="usb", ATTRS{serial}=="23560716050834460007" , KERNEL=="sd?1", NAME="%k", SYMLINK+="foo",  GROUP="storage"
 +
}}
  
This information is basically mirrored from the gentoo wiki with some additional hints.
+
==Manual method==
 +
Make sure one of the target devices is plugged in and then run the following (replacing {{ic|sda}} as needed):
 +
{{hc|$ udevadm info -a -p $(udevadm info -q path -n /dev/sda)|2=
 +
Udevadm starts with the device specified by the devpath and then
 +
walks up the chain of parent devices. It prints for every device
 +
found, all possible attributes in the udev rules key format.
 +
A rule to match, can be composed by the attributes of the device
 +
and the attributes from one single parent device.
  
This process allows you to always map a specific device to the same /dev node.  This can then be used in fstab to ensure you can always mount the device same device in exactly the same place - which is great for desktop shortcuts!
+
  looking at device '/block/sda':
 +
    KERNEL=="sda"
 +
    SUBSYSTEM=="block"
 +
    DRIVER==""
 +
    ATTR{stat}=="      19      111      137      160        0        0        0        0        0      152      160"
 +
    ATTR{size}=="2007040"
 +
    ATTR{removable}=="1"
 +
    ATTR{range}=="16"
 +
    ATTR{dev}=="8:0"
  
==== 1. Get the udev info for your usb device====
+
  looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0':
 +
    KERNELS=="5:0:0:0"
 +
    SUBSYSTEMS=="scsi"
 +
    DRIVERS=="sd"
 +
    ATTRS{ioerr_cnt}=="0x0"
 +
    ATTRS{iodone_cnt}=="0x1c"
 +
    ATTRS{iorequest_cnt}=="0x1c"
 +
    ATTRS{iocounterbits}=="32"
 +
    ATTRS{timeout}=="30"
 +
    ATTRS{state}=="running"
 +
    ATTRS{rev}=="1.20"
 +
    ATTRS{model}=="01GB Tiny      "
 +
    ATTRS{vendor}=="Pretec  "
 +
    ATTRS{scsi_level}=="3"
 +
    ATTRS{type}=="0"
 +
    ATTRS{queue_type}=="none"
 +
    ATTRS{queue_depth}=="1"
 +
    ATTRS{device_blocked}=="0"
 +
    ATTRS{max_sectors}=="240"
  
Make sure one of your target devices is plugged in and then run the following as root:
+
  looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1/1-5/1-5:1.0/host5/target5:0:0':
 +
    KERNELS=="target5:0:0"
 +
    SUBSYSTEMS==""
 +
    DRIVERS==""
  
<pre>udevinfo -a -p `udevinfo -q path -n /dev/sda`
+
  looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1/1-5/1-5:1.0/host5':
</pre>
+
    KERNELS=="host5"
 +
    SUBSYSTEMS==""
 +
    DRIVERS==""
  
If this method works for you then just and "| grep SYSFS{serial}" when you get to the next part.
+
  looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1/1-5/1-5:1.0':
 +
    KERNELS=="1-5:1.0"
 +
    SUBSYSTEMS=="usb"
 +
    DRIVERS=="usb-storage"
 +
    ATTRS{modalias}=="usb:v4146pBA01d0100dc00dsc00dp00ic08isc06ip50"
 +
    ATTRS{bInterfaceProtocol}=="50"
 +
    ATTRS{bInterfaceSubClass}=="06"
 +
    ATTRS{bInterfaceClass}=="08"
 +
    ATTRS{bNumEndpoints}=="03"
 +
    ATTRS{bAlternateSetting}==" 0"
 +
    ATTRS{bInterfaceNumber}=="00"
  
This gets the udev device info for the device on /dev/sda - if your device is not mapped to /dev/sda then obviously use the correct mapping :)
+
  looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1/1-5':
 +
    KERNELS=="1-5"
 +
    SUBSYSTEMS=="usb"
 +
    DRIVERS=="usb"
 +
    ATTRS{configuration}==""
 +
    ATTRS{serial}=="14AB0000000096"
 +
    ATTRS{product}=="USB Mass Storage Device"
 +
    ATTRS{maxchild}=="0"
 +
    ATTRS{version}==" 2.00"
 +
    ATTRS{devnum}=="7"
 +
    ATTRS{speed}=="480"
 +
    ATTRS{bMaxPacketSize0}=="64"
 +
    ATTRS{bNumConfigurations}=="1"
 +
    ATTRS{bDeviceProtocol}=="00"
 +
    ATTRS{bDeviceSubClass}=="00"
 +
    ATTRS{bDeviceClass}=="00"
 +
    ATTRS{bcdDevice}=="0100"
 +
    ATTRS{idProduct}=="ba01"
 +
    ATTRS{idVendor}=="4146"
 +
    ATTRS{bMaxPower}==" 98mA"
 +
    ATTRS{bmAttributes}=="80"
 +
    ATTRS{bConfigurationValue}=="1"
 +
    ATTRS{bNumInterfaces}==" 1"
  
You should get some output like this:
+
  looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1':
 +
    KERNELS=="usb1"
 +
    SUBSYSTEMS=="usb"
 +
    DRIVERS=="usb"
 +
    ATTRS{configuration}==""
 +
    ATTRS{serial}=="0000:00:02.2"
 +
    ATTRS{product}=="EHCI Host Controller"
 +
    ATTRS{manufacturer}=="Linux 2.6.18-ARCH ehci_hcd"
 +
    ATTRS{maxchild}=="6"
 +
    ATTRS{version}==" 2.00"
 +
    ATTRS{devnum}=="1"
 +
    ATTRS{speed}=="480"
 +
    ATTRS{bMaxPacketSize0}=="64"
 +
    ATTRS{bNumConfigurations}=="1"
 +
    ATTRS{bDeviceProtocol}=="01"
 +
    ATTRS{bDeviceSubClass}=="00"
 +
    ATTRS{bDeviceClass}=="09"
 +
    ATTRS{bcdDevice}=="0206"
 +
    ATTRS{idProduct}=="0000"
 +
    ATTRS{idVendor}=="0000"
 +
    ATTRS{bMaxPower}=="  0mA"
 +
    ATTRS{bmAttributes}=="e0"
 +
    ATTRS{bConfigurationValue}=="1"
 +
    ATTRS{bNumInterfaces}==" 1"
  
<pre>
+
  looking at parent device '/devices/pci0000:00/0000:00:02.2':
udevinfo starts with the device the node belongs to and then walks up the
+
    KERNELS=="0000:00:02.2"
device chain, to print for every device found, all possibly useful attributes
+
    SUBSYSTEMS=="pci"
in the udev key format.
+
    DRIVERS=="ehci_hcd"
Only attributes within one device section may be used together in one rule,
+
    ATTRS{broken_parity_status}=="0"
to match the device for which the node will be created.
+
    ATTRS{enable}=="1"
 +
    ATTRS{modalias}=="pci:v000010DEd00000068sv00001043sd00000C11bc0Csc03i20"
 +
    ATTRS{local_cpus}=="f"
 +
    ATTRS{irq}=="17"
 +
    ATTRS{class}=="0x0c0320"
 +
    ATTRS{subsystem_device}=="0x0c11"
 +
    ATTRS{subsystem_vendor}=="0x1043"
 +
    ATTRS{device}=="0x0068"
 +
    ATTRS{vendor}=="0x10de"
  
device '/sys/block/sda/sda1' has major:minor 8:1
+
   looking at parent device '/devices/pci0000:00':
   looking at class device '/sys/block/sda/sda1':
+
     KERNELS=="pci0000:00"
     SYSFS{dev}="8:1"
+
     SUBSYSTEMS==""
     SYSFS{size}="499195"
+
     DRIVERS==""
     SYSFS{start}="4"
+
}}
    SYSFS{stat}="      84      84      202      202"
+
  
follow the class device's "device"
+
The only part of this that is needed is {{ic|<nowiki>ATTRS{serial}</nowiki>}}, so use {{ic|grep}} to filter the information:
  looking at the device chain at '/sys/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1:1.0/host0/target0:0:0/0:0:0:0':
+
{{hc|$ udevadm info -a -p $(udevadm info -q path -n /dev/sda) <nowiki>| grep ATTRS{serial}</nowiki>|2=
    BUS="scsi"
+
  ATTRS{serial}=="14AB0000000096"
    ID="0:0:0:0"
+
  ATTRS{serial}=="0000:00:02.2"
    SYSFS{detach_state}="0"
+
}}
    SYSFS{device_blocked}="0"
+
    SYSFS{max_sectors}="240"
+
    SYSFS{model}="disgo          "
+
    SYSFS{queue_depth}="1"
+
    SYSFS{rev}="4.70"
+
    SYSFS{scsi_level}="3"
+
    SYSFS{state}="running"
+
    SYSFS{timeout}="30"
+
    SYSFS{type}="0"
+
    SYSFS{vendor}="        "
+
  
  looking at the device chain at '/sys/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1:1.0/host0/target0:0:0':
+
This narrows down the search to a much greater degree; however, one of the two serials is irrelevant. Trim down the {{ic|grep}}:
    BUS=""
+
{{hc|$ udevadm info -a -p $(udevadm info -q path -n /dev/sda) <nowiki>| grep ATTRS{product}</nowiki>|2=
    ID="target0:0:0"
+
  '''ATTRS{product}=="USB Mass Storage Device"'''
    SYSFS{detach_state}="0"
+
  ATTRS{product}=="EHCI Host Controller"
 +
}}
 +
The first choice is obviously the correct one.
  
  looking at the device chain at '/sys/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1:1.0/host0':
+
==Create udev rule==
    BUS=""
+
Use the {{ic|<nowiki>ATTRS{serial}</nowiki>}} in a udev rule as follows. Place it in {{ic|/etc/udev/rules.d/00.rules}}:
    ID="host0"
+
SUBSYSTEMS=="usb", ATTRS{serial}=="14AB0000000096", KERNEL=="sd?1", NAME="%k", SYMLINK+="usbdrive", GROUP="storage"
    SYSFS{detach_state}="0"
+
{{note|When creating a file with a different naming scheme that those in the directory, remember that udev processes these files in alphabetical order.}}
  
  looking at the device chain at '/sys/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1:1.0':
+
==Make fstab entry==
    BUS="usb"
+
Create a directory for mounting:
    ID="1-1:1.0"
+
# mkdir /mnt/usbdrive
    SYSFS{bAlternateSetting}=" 0"
+
    SYSFS{bInterfaceClass}="08"
+
    SYSFS{bInterfaceNumber}="00"
+
    SYSFS{bInterfaceProtocol}="50"
+
    SYSFS{bInterfaceSubClass}="06"
+
    SYSFS{bNumEndpoints}="02"
+
    SYSFS{detach_state}="0"
+
  
  looking at the device chain at '/sys/devices/pci0000:00/0000:00:1d.0/usb1/1-1':
+
In {{ic|/etc/[[fstab]]}}, create an entry as:
    BUS="usb"
+
/dev/usbdrive /mnt/usbdrive vfat rw,noauto,group,flush,quiet,nodev,nosuid,noexec,noatime,dmask=000,fmask=111 0 0
    ID="1-1"
+
    SYSFS{bConfigurationValue}="1"
+
    SYSFS{bDeviceClass}="00"
+
    SYSFS{bDeviceProtocol}="00"
+
    SYSFS{bDeviceSubClass}="00"
+
    SYSFS{bMaxPower}="178mA"
+
    SYSFS{bNumConfigurations}="1"
+
    SYSFS{bNumInterfaces}=" 1"
+
    SYSFS{bcdDevice}="0200"
+
    SYSFS{bmAttributes}="80"
+
    SYSFS{detach_state}="0"
+
    SYSFS{devnum}="2"
+
    SYSFS{idProduct}="0012"
+
    SYSFS{idVendor}="08ec"
+
    SYSFS{manufacturer}="M-Sys"
+
    SYSFS{maxchild}="0"
+
    SYSFS{product}="disgo"
+
    SYSFS{serial}="1730C13B18000B84"
+
    SYSFS{speed}="12"
+
    SYSFS{version}=" 2.00"
+
  
  looking at the device chain at '/sys/devices/pci0000:00/0000:00:1d.0/usb1':
+
Options {{ic|nodev, nosuid, and noexec}} are unnecesary; they are stated for security reasons only. Additionally, depending on your locale preferences, add {{ic|codepage}} and {{ic|iocharset}} options (such as {{ic|<nowiki>codepage=866,iocharset=utf-8</nowiki>}}) in order to be able to display non-Latin file-names correctly.
    BUS="usb"
+
    ID="usb1"
+
    SYSFS{bConfigurationValue}="1"
+
    SYSFS{bDeviceClass}="09"
+
    SYSFS{bDeviceProtocol}="00"
+
    SYSFS{bDeviceSubClass}="00"
+
    SYSFS{bMaxPower}="  0mA"
+
    SYSFS{bNumConfigurations}="1"
+
    SYSFS{bNumInterfaces}=" 1"
+
    SYSFS{bcdDevice}="0206"
+
    SYSFS{bmAttributes}="c0"
+
    SYSFS{detach_state}="0"
+
    SYSFS{devnum}="1"
+
    SYSFS{idProduct}="0000"
+
    SYSFS{idVendor}="0000"
+
    SYSFS{manufacturer}="Linux 2.6.10-ARCH uhci_hcd"
+
    SYSFS{maxchild}="2"
+
    SYSFS{product}="Intel Corp. 82801CA/CAM USB (Hub #1)"
+
    SYSFS{serial}="0000:00:1d.0"
+
    SYSFS{speed}="12"
+
    SYSFS{version}=" 1.10"
+
  
  looking at the device chain at '/sys/devices/pci0000:00/0000:00:1d.0':
+
Now, root and users who belongs to the {{ic|storage}} [[group]] can mount the USB device by:
    BUS="pci"
+
mount /mnt/usbdrive
    ID="0000:00:1d.0"
+
    SYSFS{class}="0x0c0300"
+
    SYSFS{detach_state}="0"
+
    SYSFS{device}="0x2482"
+
    SYSFS{irq}="11"
+
    SYSFS{local_cpus}="1"
+
    SYSFS{subsystem_device}="0x4541"
+
    SYSFS{subsystem_vendor}="0x8086"
+
    SYSFS{vendor}="0x8086"
+
  
  looking at the device chain at '/sys/devices/pci0000:00':
+
To allow a non-root user to access to USB devices, add them to the storage group:
    BUS=""
+
# gpasswd -a ''username'' storage
    ID="pci0000:00"
+
    SYSFS{detach_state}="0"
+
</pre>
+
  
Bit too much information!  The only bit of this you actaully need is the =SYSFS{serial}= part - so now you know what the above command does just grep out the bit you want in future cases:
+
==Restart udev==
 +
To load the updated rules, run:
 +
# udevadm control --reload-rules
  
<pre>udevinfo -a -p `udevinfo -q path -n /dev/sda` | grep SYSFS{serial}
+
==Examples==
</pre>
+
Here are some mapping and mounting examples. This system's devices sometimes made nodes as {{ic|sda}} or {{ic|sda1}} so I two rules for each needed to be specified, which aid "device not found" problems. The {{ic|sda}} node is also needed for disk-level activities; e.g., {{ic|fdisk /dev/sda}}.
 
+
==== 2. Create a udev rule====
+
 
+
You then use the SYSFS{serial} in a udev rule as follows:
+
 
+
Note: The convention for Arch Linux is to place custom rules into /etc/udev/rules.d/00.rules
+
 
+
<pre>BUS=="usb", SYSFS{serial}=="0402170100000020EB5D00000000000", KERNEL=="sd?1", NAME="%k", SYMLINK="usbdrive", GROUP="storage"
+
</pre>
+
 
+
==== 3. Create an fstab entry and mount point====
+
 
+
Create a directory: mkdir /mnt/usbdrive
+
 
+
In your /etc/fstab, create an entry like this:
+
 
+
<pre>
+
/dev/usbdrive    /mnt/usbdrive    vfat noauto,user,noexec,nodev,nosuid    0 0
+
</pre>
+
 
+
Now root or any user who belongs to the "storage" group can mount the usb stick by simply doing
+
mount /mnt/usbdrive
+
 
+
Btw, all the last 3 additional mount options are meant to increase your system's security, e.g. they will prevent you running an executable file directly from the usb drive.
+
 
+
Check if /etc/group contains an entry called "storage".
+
 
+
If it does not, simply do a "groupadd storage"
+
 
+
Then edit /etc/group to make the new storage group entry look like this:
+
<pre>
+
storage:x:102:user1,user2
+
</pre>
+
user1 and user2 are the names of the non-root users you wish to have access to the usb stick.
+
 
+
==== 4. Restart udev====
+
Only if really needed, you may restart udev like this. As root, run those 3 commands:
+
<pre>
+
/etc/./start_udev
+
mount /dev/pts
+
mount /dev/shm
+
</pre>
+
 
+
==== 5. Examples====
+
 
+
Here are some examples from my system. My devices sometimes mount on sda or sda1 so I have two rules for each - this is a work around for device not found problems. The sda node is also needed for disk-level activities e.g. fdisk /dev/sda.
+
 
+
Note: The convention for Arch Linux is to place custom rules into /etc/udev/rules.d/00.rules
+
 
+
This always maps my disgo USB pen to /dev/usbpen which i then map in fstab to mount on /mnt/usbpen
+
  
 +
This always maps a specific USB device (in this case, a pendrive) to {{ic|/dev/usbpen}}, which is then set in {{ic|fstab}} to mount on {{ic|/mnt/usbpen}}:
 
  # Symlink USB pen
 
  # Symlink USB pen
  BUS=="usb", SYSFS{serial}=="1730C13B18000B84", KERNEL=="sd?", NAME="%k", SYMLINK+="usbpen", GROUP="storage"
+
  SUBSYSTEMS=="usb", ATTRS{serial}=="1730C13B18000B84", KERNEL=="sd?", NAME="%k", SYMLINK+="usbpen", GROUP="storage"
  BUS=="usb", SYSFS{serial}=="1730C13B18000B84", KERNEL=="sd?1", NAME="%k", SYMLINK+="usbpen", GROUP="storage"
+
  SUBSYSTEMS=="usb", ATTRS{serial}=="1730C13B18000B84", KERNEL=="sd?1", NAME="%k", SYMLINK+="usbpen", GROUP="storage"
 
+
If you have a device with with multiple partitions, the following example maps the device to /dev/usbdisk, and partitions 1,2,3,etc to usbdisk1, usbdisk2, usbdisk3, etc
+
  
 +
If for devices with multiple partitions, the following example maps the device to {{ic|/dev/usbdisk}}, and partitions 1, 2, 3 etc., to {{ic|/dev/usbdisk1}}, {{ic|/dev/usbdisk2}}, {{ic|/dev/usbdisk3}}, etc.
 
  # Symlink multi-part device
 
  # Symlink multi-part device
  BUS=="usb", SYSFS{serial}=="1730C13B18000B84", KERNEL=="sd?", NAME="%k", SYMLINK+="usbdisk", GROUP="storage"
+
  SUSSYSTEMS=="usb", ATTRS{serial}=="1730C13B18000B84", KERNEL=="sd?", NAME="%k", SYMLINK+="usbdisk", GROUP="storage"
  BUS=="usb", SYSFS{serial}=="1730C13B18000B84", KERNEL=="sd?[1-9]", NAME="%k", SYMLINK+="usbdisk%n", GROUP="storage"
+
  SUBSYSTEMS=="usb", ATTRS{serial}=="1730C13B18000B84", KERNEL=="sd?[1-9]", NAME="%k", SYMLINK+="usbdisk%n", GROUP="storage"
 
+
These rules are equivalent to the following one:
+
  
 +
The above rules are equivalent to the following one:
 
  # Symlink multi-part device
 
  # Symlink multi-part device
  BUS=="usb", SYSFS{serial}=="1730C13B18000B84", KERNEL=="sd*", NAME="%k", SYMLINK+="usbdisk%n", GROUP="storage"
+
  SUBSYSTEMS=="usb", ATTRS{serial}=="1730C13B18000B84", KERNEL=="sd*", NAME="%k", SYMLINK+="usbdisk%n", GROUP="storage"
 
+
You can also omit the NAME and GROUP statements, so that the defaults from udev.rules are used. So the shortest and simplest solution would be adding this rule:
+
  
 +
It's also possible to omit the {{ic|NAME}} and {{ic|GROUP}} statements, so that the defaults from {{ic|udev.rules}} are used. Meaning that the shortest and simplest solution would be adding this rule:
 
  # Symlink multi-part device
 
  # Symlink multi-part device
  BUS=="usb", SYSFS{serial}=="1730C13B18000B84", KERNEL=="sd*", SYMLINK+="usbdisk%n"
+
  SUBSYSTEMS=="usb", ATTRS{serial}=="1730C13B18000B84", KERNEL=="sd*", SYMLINK+="usbdisk%n"
 
+
This always maps our Olympus digicam to /dev/usbcam which i then map in fstab to mount on /mnt/usbcam
+
  
 +
This always maps a Olympus digicam to {{ic|/dev/usbcam}}, which can be stated in {{ic|fstab}} to mount on {{ic|/mnt/usbcam}}:
 
  # Symlink USB camera
 
  # Symlink USB camera
  BUS=="usb", SYSFS{serial}=="000207532049", KERNEL=="sd?", NAME="%k", SYMLINK+="usbcam", GROUP="storage"
+
  SUBSYSTEMS=="usb", ATTRS{serial}=="000207532049", KERNEL=="sd?", NAME="%k", SYMLINK+="usbcam", GROUP="storage"
  BUS=="usb", SYSFS{serial}=="000207532049", KERNEL=="sd?1", NAME="%k", SYMLINK+="usbcam", GROUP="storage"
+
  SUBSYSTEMS=="usb", ATTRS{serial}=="000207532049", KERNEL=="sd?1", NAME="%k", SYMLINK+="usbcam", GROUP="storage"
 
+
And this maps my Packard Bell MP3 player to /dev/mp3player
+
  
 +
And this maps a Packard Bell MP3 player to {{ic|/dev/mp3player}}:
 
  # Symlink MP3 player
 
  # Symlink MP3 player
  BUS=="usb", SYSFS{serial}=="0002F5CF72C9C691", KERNEL=="sd?", NAME="%k", SYMLINK+="mp3player", GROUP="storage"
+
  SUBSYSTEMS=="usb", ATTRS{serial}=="0002F5CF72C9C691", KERNEL=="sd?", NAME="%k", SYMLINK+="mp3player", GROUP="storage"
  BUS=="usb", SYSFS{serial}=="0002F5CF72C9C691", KERNEL=="sd?1", NAME="%k", SYMLINK+="mp3player", GROUP="storage"
+
  SUBSYSTEMS=="usb", ATTRS{serial}=="0002F5CF72C9C691", KERNEL=="sd?1", NAME="%k", SYMLINK+="mp3player", GROUP="storage"
 
+
To map your own usb key to /dev/mykey and all of other keys to /dev/otherkey
+
  
 +
To map a selected usb-key to {{ic|/dev/mykey}} and all of other keys to {{ic|/dev/otherkey}}:
 
  # Symlink USB keys
 
  # Symlink USB keys
  BUS=="usb", SYSFS{serial}=="insert serial key", KERNEL=="sd?1", NAME="%k", SYMLINK+="mykey"
+
  SUBSYSTEMS=="usb", ATTRS{serial}=="insert serial key", KERNEL=="sd?1", NAME="%k", SYMLINK+="mykey"
  BUS=="usb", KERNEL=="sd?1", NAME="%k", SYMLINK+="otherkey"
+
  SUBSYSTEMS=="usb", KERNEL=="sd?1", NAME="%k", SYMLINK+="otherkey"
 +
Note the order of the lines. Since all the USB keys should create the {{ic|<nowiki>/dev/sd<a||b></nowiki>}} node, udev will first check if it is a rules-stated USB-key, defined by serial number. But if an unknown USB-key is plugged, it will create ''also'' create a node, using the previously stated generic name, "otherkey". That rule should be the last one in rules file so that it does not override the others.
  
Note the order of the lines. Since all the usb keys should create the /dev/sd<a||b> node, udev will first check if it is your own usb key, defined with the serial number. But if you plug another key witch you don't know the serial number, it will create a node too, with a generic name "otherkey". That rule should be the last one your rules file.
+
This is an example on how to distinguish USB HDD drives and USB sticks:
 +
BUS=="usb", ATTRS{product}=="USB2.0 Storage Device", KERNEL=="sd?", NAME="%k", SYMLINK+="usbdisk", GROUP="storage"
 +
BUS=="usb", ATTRS{product}=="USB2.0 Storage Device", KERNEL=="sd?[1-9]", NAME="%k", SYMLINK+="usbdisk%n", GROUP="storage"
 +
BUS=="usb", ATTRS{product}=="USB Mass Storage Device", KERNEL=="sd?1", NAME="%k", SYMLINK+="usbflash", GROUP="storage"
 +
Note that this udev rule doesn't use serials at all.

Revision as of 14:17, 26 September 2012

This process allows to map a specific device to a constant device-node, located in /dev, by using udev rules. This can then be used in fstab, among other places, to ensure that the device can be mounted with a unchanging device-node--ideal for desktop shortcuts and other mounting operations.

This article focuses on USB devices and was copied almost verbatim from the Gentoo wiki, later supplemented with additional hints.

Get USB device udev information

Here is script that facilitates this operation. Simply plug in the device before running the script. Save it as usb-show-serial, for example, and run it like so:

$ ./usb-show-serial /dev/sdX label
add the following to /etc/udev/rules.d/90-automounter.rules
BUS=="usb", ATTRS{serial}=="23560716050834460007" , KERNEL=="sd?1", NAME="%k", SYMLINK+="foo",  GROUP="storage"

Manual method

Make sure one of the target devices is plugged in and then run the following (replacing sda as needed):

$ udevadm info -a -p $(udevadm info -q path -n /dev/sda)
Udevadm starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/block/sda':
    KERNEL=="sda"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{stat}=="      19      111      137      160        0        0        0        0        0      152      160"
    ATTR{size}=="2007040"
    ATTR{removable}=="1"
    ATTR{range}=="16"
    ATTR{dev}=="8:0"

  looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0':
    KERNELS=="5:0:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS=="sd"
    ATTRS{ioerr_cnt}=="0x0"
    ATTRS{iodone_cnt}=="0x1c"
    ATTRS{iorequest_cnt}=="0x1c"
    ATTRS{iocounterbits}=="32"
    ATTRS{timeout}=="30"
    ATTRS{state}=="running"
    ATTRS{rev}=="1.20"
    ATTRS{model}=="01GB Tiny       "
    ATTRS{vendor}=="Pretec  "
    ATTRS{scsi_level}=="3"
    ATTRS{type}=="0"
    ATTRS{queue_type}=="none"
    ATTRS{queue_depth}=="1"
    ATTRS{device_blocked}=="0"
    ATTRS{max_sectors}=="240"

  looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1/1-5/1-5:1.0/host5/target5:0:0':
    KERNELS=="target5:0:0"
    SUBSYSTEMS==""
    DRIVERS==""

  looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1/1-5/1-5:1.0/host5':
    KERNELS=="host5"
    SUBSYSTEMS==""
    DRIVERS==""

  looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1/1-5/1-5:1.0':
    KERNELS=="1-5:1.0"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb-storage"
    ATTRS{modalias}=="usb:v4146pBA01d0100dc00dsc00dp00ic08isc06ip50"
    ATTRS{bInterfaceProtocol}=="50"
    ATTRS{bInterfaceSubClass}=="06"
    ATTRS{bInterfaceClass}=="08"
    ATTRS{bNumEndpoints}=="03"
    ATTRS{bAlternateSetting}==" 0"
    ATTRS{bInterfaceNumber}=="00"

  looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1/1-5':
    KERNELS=="1-5"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{configuration}==""
    ATTRS{serial}=="14AB0000000096"
    ATTRS{product}=="USB Mass Storage Device"
    ATTRS{maxchild}=="0"
    ATTRS{version}==" 2.00"
    ATTRS{devnum}=="7"
    ATTRS{speed}=="480"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceClass}=="00"
    ATTRS{bcdDevice}=="0100"
    ATTRS{idProduct}=="ba01"
    ATTRS{idVendor}=="4146"
    ATTRS{bMaxPower}==" 98mA"
    ATTRS{bmAttributes}=="80"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bNumInterfaces}==" 1"

  looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1':
    KERNELS=="usb1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{configuration}==""
    ATTRS{serial}=="0000:00:02.2"
    ATTRS{product}=="EHCI Host Controller"
    ATTRS{manufacturer}=="Linux 2.6.18-ARCH ehci_hcd"
    ATTRS{maxchild}=="6"
    ATTRS{version}==" 2.00"
    ATTRS{devnum}=="1"
    ATTRS{speed}=="480"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bDeviceProtocol}=="01"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceClass}=="09"
    ATTRS{bcdDevice}=="0206"
    ATTRS{idProduct}=="0000"
    ATTRS{idVendor}=="0000"
    ATTRS{bMaxPower}=="  0mA"
    ATTRS{bmAttributes}=="e0"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bNumInterfaces}==" 1"

  looking at parent device '/devices/pci0000:00/0000:00:02.2':
    KERNELS=="0000:00:02.2"
    SUBSYSTEMS=="pci"
    DRIVERS=="ehci_hcd"
    ATTRS{broken_parity_status}=="0"
    ATTRS{enable}=="1"
    ATTRS{modalias}=="pci:v000010DEd00000068sv00001043sd00000C11bc0Csc03i20"
    ATTRS{local_cpus}=="f"
    ATTRS{irq}=="17"
    ATTRS{class}=="0x0c0320"
    ATTRS{subsystem_device}=="0x0c11"
    ATTRS{subsystem_vendor}=="0x1043"
    ATTRS{device}=="0x0068"
    ATTRS{vendor}=="0x10de"

  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""

The only part of this that is needed is ATTRS{serial}, so use grep to filter the information:

$ udevadm info -a -p $(udevadm info -q path -n /dev/sda) | grep ATTRS{serial}
ATTRS{serial}=="14AB0000000096"
   ATTRS{serial}=="0000:00:02.2"

This narrows down the search to a much greater degree; however, one of the two serials is irrelevant. Trim down the grep:

$ udevadm info -a -p $(udevadm info -q path -n /dev/sda) | grep ATTRS{product}
ATTRS{product}=="USB Mass Storage Device"
   ATTRS{product}=="EHCI Host Controller"

The first choice is obviously the correct one.

Create udev rule

Use the ATTRS{serial} in a udev rule as follows. Place it in /etc/udev/rules.d/00.rules:

SUBSYSTEMS=="usb", ATTRS{serial}=="14AB0000000096", KERNEL=="sd?1", NAME="%k", SYMLINK+="usbdrive", GROUP="storage"
Note: When creating a file with a different naming scheme that those in the directory, remember that udev processes these files in alphabetical order.

Make fstab entry

Create a directory for mounting:

# mkdir /mnt/usbdrive

In /etc/fstab, create an entry as:

/dev/usbdrive /mnt/usbdrive vfat rw,noauto,group,flush,quiet,nodev,nosuid,noexec,noatime,dmask=000,fmask=111 0 0

Options nodev, nosuid, and noexec are unnecesary; they are stated for security reasons only. Additionally, depending on your locale preferences, add codepage and iocharset options (such as codepage=866,iocharset=utf-8) in order to be able to display non-Latin file-names correctly.

Now, root and users who belongs to the storage group can mount the USB device by:

mount /mnt/usbdrive

To allow a non-root user to access to USB devices, add them to the storage group:

# gpasswd -a username storage

Restart udev

To load the updated rules, run:

# udevadm control --reload-rules

Examples

Here are some mapping and mounting examples. This system's devices sometimes made nodes as sda or sda1 so I two rules for each needed to be specified, which aid "device not found" problems. The sda node is also needed for disk-level activities; e.g., fdisk /dev/sda.

This always maps a specific USB device (in this case, a pendrive) to /dev/usbpen, which is then set in fstab to mount on /mnt/usbpen:

# Symlink USB pen
SUBSYSTEMS=="usb", ATTRS{serial}=="1730C13B18000B84", KERNEL=="sd?", NAME="%k", SYMLINK+="usbpen", GROUP="storage"
SUBSYSTEMS=="usb", ATTRS{serial}=="1730C13B18000B84", KERNEL=="sd?1", NAME="%k", SYMLINK+="usbpen", GROUP="storage"

If for devices with multiple partitions, the following example maps the device to /dev/usbdisk, and partitions 1, 2, 3 etc., to /dev/usbdisk1, /dev/usbdisk2, /dev/usbdisk3, etc.

# Symlink multi-part device
SUSSYSTEMS=="usb", ATTRS{serial}=="1730C13B18000B84", KERNEL=="sd?", NAME="%k", SYMLINK+="usbdisk", GROUP="storage"
SUBSYSTEMS=="usb", ATTRS{serial}=="1730C13B18000B84", KERNEL=="sd?[1-9]", NAME="%k", SYMLINK+="usbdisk%n", GROUP="storage"

The above rules are equivalent to the following one:

# Symlink multi-part device
SUBSYSTEMS=="usb", ATTRS{serial}=="1730C13B18000B84", KERNEL=="sd*", NAME="%k", SYMLINK+="usbdisk%n", GROUP="storage"

It's also possible to omit the NAME and GROUP statements, so that the defaults from udev.rules are used. Meaning that the shortest and simplest solution would be adding this rule:

# Symlink multi-part device
SUBSYSTEMS=="usb", ATTRS{serial}=="1730C13B18000B84", KERNEL=="sd*", SYMLINK+="usbdisk%n"

This always maps a Olympus digicam to /dev/usbcam, which can be stated in fstab to mount on /mnt/usbcam:

# Symlink USB camera
SUBSYSTEMS=="usb", ATTRS{serial}=="000207532049", KERNEL=="sd?", NAME="%k", SYMLINK+="usbcam", GROUP="storage"
SUBSYSTEMS=="usb", ATTRS{serial}=="000207532049", KERNEL=="sd?1", NAME="%k", SYMLINK+="usbcam", GROUP="storage"

And this maps a Packard Bell MP3 player to /dev/mp3player:

# Symlink MP3 player
SUBSYSTEMS=="usb", ATTRS{serial}=="0002F5CF72C9C691", KERNEL=="sd?", NAME="%k", SYMLINK+="mp3player", GROUP="storage"
SUBSYSTEMS=="usb", ATTRS{serial}=="0002F5CF72C9C691", KERNEL=="sd?1", NAME="%k", SYMLINK+="mp3player", GROUP="storage"

To map a selected usb-key to /dev/mykey and all of other keys to /dev/otherkey:

# Symlink USB keys
SUBSYSTEMS=="usb", ATTRS{serial}=="insert serial key", KERNEL=="sd?1", NAME="%k", SYMLINK+="mykey"
SUBSYSTEMS=="usb", KERNEL=="sd?1", NAME="%k", SYMLINK+="otherkey"

Note the order of the lines. Since all the USB keys should create the /dev/sd<a||b> node, udev will first check if it is a rules-stated USB-key, defined by serial number. But if an unknown USB-key is plugged, it will create also create a node, using the previously stated generic name, "otherkey". That rule should be the last one in rules file so that it does not override the others.

This is an example on how to distinguish USB HDD drives and USB sticks:

BUS=="usb", ATTRS{product}=="USB2.0 Storage Device", KERNEL=="sd?", NAME="%k", SYMLINK+="usbdisk", GROUP="storage"
BUS=="usb", ATTRS{product}=="USB2.0 Storage Device", KERNEL=="sd?[1-9]", NAME="%k", SYMLINK+="usbdisk%n", GROUP="storage"
BUS=="usb", ATTRS{product}=="USB Mass Storage Device", KERNEL=="sd?1", NAME="%k", SYMLINK+="usbflash", GROUP="storage"

Note that this udev rule doesn't use serials at all.