#!/bin/sh # /etc/dev.d/block/usb-storage.dev - # mount/umount USB storage block devices when their udev events happen # author franklin, last modification 2006.09.14 # This script is copyright ETH Zuerich Physics Departement, # use under either modified/non-advertising BSD or GPL license # this script is called by udev once per device or partition, with: # $ACTION = what to do, such as "add" or "remove" # $DEVNAME = device name, such as "/dev/sda" # $DEVPATH = sysfs device path, such as "/block/sda" (relative to /sys) # this script is installed by dphys-config # together with called dpkys-config-usb-storage for the needed modules # it requires in /etc/dphys-config.list 2 lines: # usb-storage.dev:/etc/dev.d/block/:/bin/chmod 755 {} # local/sbin/dphys-config-usb-storage:/usr/:/bin/chmod 755 {}; {} ### ------ configuration for this site # --- CONF_* various site or subnet dependant user config variables # make sure user gets usable access rights on storage device # allow console users group to access # note that group=plugdev temporarily given to any console logged in user CONF_ACCESS_USER=root CONF_ACCESS_GROUP=plugdev # prevent other users write access to storage device #CONF_ACCESS_MODE=664 # prevent other users any access to storage device CONF_ACCESS_MODE=660 # prevent other users write access to FAT filesystems #CONF_FAT_UMASK=002 # prevent other users any access to FAT filesystems CONF_FAT_UMASK=007 # log what we have done CONF_LOG_DONE=yes # --- DEBUG_*, various debugging settings # force lots of debugging output # this can also be enabled by touch /usb-storage.dev.debug #DEBUG_LOG=yes # --- SYS_*, various system internal values # where we place files with umount and re-mount information SYS_RUN_DIR=/var/run/etc-dev.d-block-usb-storage.dev # set up mount so that stuff works as user most likely wants it to # noauto: prevents accidental fsck if device left plugged in while reboot # noatime: prevents non-intuitive atime writes and risks when "only reading" # sync: makes kernel write immediately at cost of slower, but safer # allow user to umount the filesystems themselves, for sync or repartition # group: allows user who is member of group of device file to run umount # and also so that we don't open up security holes # exec: allows user to store and run programs from storage media # allows foreign compiled code import, is ok here so long not suid or dev # nosuid,nodev: prevent users from smuggling in bad things SYS_MOUNTOPTS="noauto,noatime,sync,group,exec,nosuid,nodev" # user who uses FAT is assumed to not know anything about access rights # needs , at beginning, as it is added to above SYS_MOUNTOPTS SYS_MOUNTOPTSFAT=",uid=${CONF_ACCESS_USER},gid=${CONF_ACCESS_GROUP},umask=${CONF_FAT_UMASK}" # user who uses ext2 or ext3 is assumed to have set UIDs that work # needs , at beginning, as it is added to above SYS_MOUNTOPTS SYS_MOUNTOPTSEXT="" # /etc/fstab spinlock file, so that 2 inserts/removes at same time do not kill SYS_FSTAB_LOCK=/etc/fstab.lock ### ------ actual implementation from here on # no user settings any more below this point set -e # --- get ready to work PATH=/sbin:/bin:/usr/sbin:/usr/bin export PATH # --- tidy up some commands, make systematic BASENAME="`/usr/bin/basename $0`" # stuff that goes wrong, not expected by user # something within the system that user does not expect, give up CMD_FATAL="/usr/bin/logger -t ${BASENAME} -p user.error FATAL:" # something from users input, user can possibly correct this and continue CMD_ERROR="/usr/bin/logger -t ${BASENAME} -p user.error ERROR:" # something we can continue with, but may be wrong, and user may not suspect it CMD_WARNING="/usr/bin/logger -t ${BASENAME} -p user.warning WARNING:" # something most likely not wrong, but tell user for the odd case it is wrong CMD_NOTE="/usr/bin/logger -t ${BASENAME} -p user.notice NOTE:" # normal stuff users expect, so no marking CMD_INFO="/usr/bin/logger -t ${BASENAME} -p user.info" # stuff users asked for, but mark it as special CMD_DEBUG="/usr/bin/logger -t ${BASENAME} -p user.debug DEBUG:" # --- config file stuff # what we are NAME=usb-storage.dev # allow us to set logging despite no command line options if [ -f /${NAME}.debug ] ; then DEBUG_LOG=yes fi # switch done logging off or on CMD_DONE_LOG=/bin/true if [ x${CONF_LOG_DONE} = xyes ] ; then CMD_DONE_LOG="${CMD_INFO}" fi # switch debug logging off or on CMD_DEBUG_LOG=/bin/true if [ x${DEBUG_LOG} = xyes ] ; then CMD_DEBUG_LOG="${CMD_DEBUG}" fi # --- ensure that we are in the right place here, getting fitting events # extract device name from device path if /bin/echo ${DEVPATH} | /bin/grep -q '/block/' ; then DEVICE=`usr/bin/basename ${DEVNAME}` ${CMD_DEBUG_LOG} ACTION=${ACTION} DEVNAME=${DEVNAME} DEVPATH=${DEVPATH} \ DEVICE=${DEVICE} else ${CMD_FATAL} ${DEVPATH} not a block device, why were we called, aborting ... exit 1 fi # --- for removal stuff, as some info is not available by then # place to store our auxillary files, ensure it exists /bin/mkdir -p ${SYS_RUN_DIR} # where we place per-device mountpoint info files, for later umount # register what mountpoint we use for this device # used by add for writing into it, and remove for then using it # we need this for add and remove, so set it here REMOVE_MPS=${SYS_RUN_DIR}/remove-${DEVICE} # work depending on inserting or removing device case $ACTION in add) # --- ths call only interests us if it is an USB storage device # full devices /sys/block/sd[a-z]/ have */device, if /bin/readlink "/sys${DEVPATH}/device" | /bin/grep -q /usb ; then USB=1 # but partitions /sys/block/sd[a-z]/sd[a-z][1-] don't elif /bin/readlink "/sys${DEVPATH}/../device" | /bin/grep -q /usb ; then USB=1 else # no usb-storage, so device doesn't interest us here, goodbye # must be here in add case, because for remove it fails, info is gone ${CMD_DEBUG_LOG} USB=0 exit 0 fi # --- user access raw device, for change label reformatting/repartitioning /bin/chown ${CONF_ACCESS_USER}:${CONF_ACCESS_GROUP} ${DEVNAME} /bin/chmod ${CONF_ACCESS_MODE} ${DEVNAME} # --- find out file system type for this device/partition # we use the old udev 056 (in Debian 3.1 (sarge)) filename if [ -x /sbin/udev_volume_id ] ; then FILESYS="`/sbin/udev_volume_id -t \"$DEVNAME\" | /usr/bin/tr -d ' '`" else ${CMD_FATAL} /sbin/udev_volume_id missing, too newer udev?, aborting ... exit 1 fi # flatten filesystem types to classes, to avoid duplicate code if [ x${FILESYS} = xunknownvolumetype ] ; then # most likely raw disk is a partition sector, don't mount FSCLASS=partition elif [ x${FILESYS} = xmsdos ] ; then FSCLASS=fat elif [ x${FILESYS} = xvfat ] ; then FSCLASS=fat elif [ x${FILESYS} = xext2 ] ; then FSCLASS=ext elif [ x${FILESYS} = xext3 ] ; then FSCLASS=ext else FSCLASS=unknown fi ${CMD_DEBUG_LOG} FILESYS=${FILESYS} FSCLASS=${FSCLASS} # --- don't mount an partition sector, will get partition calls later if [ x${FSCLASS} = xpartition ] ; then ${CMD_DEBUG_LOG} abort FSCLASS=${FSCLASS} exit 0 fi # --- collect file system specifig mount options if [ x${FSCLASS} = xfat ] ; then MOUNTOPTS=${SYS_MOUNTOPTS}${SYS_MOUNTOPTSFAT} elif [ x${FSCLASS} = xext ] ; then MOUNTOPTS=${SYS_MOUNTOPTS}${SYS_MOUNTOPTSEXT} else MOUNTOPTS=${SYS_MOUNTOPTS} fi ${CMD_DEBUG_LOG} MOUNTOPTS=${MOUNTOPTS} # --- where do we want to mount on, extract possible label for this if [ x${FSCLASS} = xfat ] ; then MTOOLS_CONF=~/.mtoolsrc if [ -f ${MTOOLS_CONF} ] ; then # save existing config, as we need to overwrite it, no options possible /bin/cp -p ${MTOOLS_CONF} ${MTOOLS_CONF}.${NAME}.bak fi /bin/echo "drive u: file=\"${DEVNAME}\"" >> ${MTOOLS_CONF} # disable newly added test, which gives aborting error instead of warning /bin/echo "mtools_skip_check=1" >> ${MTOOLS_CONF} LABEL=`/usr/bin/mlabel -s u: | /usr/bin/cut -f 5 -d ' ' | \ /usr/bin/tr [A-Z] [a-z]` if [ -f ${MTOOLS_CONF}.${NAME}.bak ] ; then # and restore config, if there was one saved /bin/mv ${MTOOLS_CONF}.${NAME}.bak ${MTOOLS_CONF} else /bin/rm ${MTOOLS_CONF} fi ${CMD_DEBUG_LOG} fat LABEL=${LABEL} elif [ x${FSCLASS} = xext ] ; then # unlabled appear as Filesystem volume name: LABEL=`/sbin/tune2fs -l ${DEVNAME} | \ /bin/grep '^Filesystem volume name:' | /bin/grep -v '' | \ /usr/bin/cut -f 2 -d ':' | /usr/bin/tr -d ' '` ${CMD_DEBUG_LOG} ext LABEL=${LABEL} else ${CMD_NOTE} unknown/non-supported filesystem ${FSCLASS}, \ can not extract label, using an generic pseudo-label ... if [ -r "/sys$DEVPATH/device/vendor" ] ; then VENDOR=`cat \"/sys$DEVPATH/device/vendor\" | /usr/bin/tr -d ' '` elif [ -r "/sys$DEVPATH/../device/vendor" ] ; then VENDOR=`cat \"/sys$DEVPATH/../device/vendor\" | /usr/bin/tr -d ' '` fi if [ -r "/sys$DEVPATH/device/model" ] ; then PRODNAME=`cat \"/sys$DEVPATH/device/model\" | /usr/bin/tr -d ' '` elif [ -r "/sys$DEVPATH/../device/model" ] ; then PRODNAME=`cat \"/sys$DEVPATH/../device/model\" | /usr/bin/tr -d ' '` fi LABEL=usb-storage-unknownfs-${VENDOR}-${PRODNAME}-${DEVICE} ${CMD_DEBUG_LOG} VENDOR=${VENDOR} PRODNAME=${PRODNAME} LABEL=${LABEL} fi # --- generate mount point name from label, if one is there if [ x${LABEL} != x ] ; then # we have found an label, use that as mountpoint name MOUNTPOINT=/usb-${LABEL} ${CMD_DEBUG_LOG} LABEL=${LABEL} MOUNTPOINT=${MOUNTPOINT} else # no label, generate generic name, hope user recognises part of it ${CMD_NOTE} non-labeled filesystem, nothing to extract for \ mountpoint name, using generic mountpoint ... if [ -r "/sys$DEVPATH/device/vendor" ] ; then VENDOR=`cat \"/sys$DEVPATH/device/vendor\" | /usr/bin/tr -d ' '` elif [ -r "/sys$DEVPATH/../device/vendor" ] ; then VENDOR=`cat \"/sys$DEVPATH/../device/vendor\" | /usr/bin/tr -d ' '` fi if [ -r "/sys$DEVPATH/device/model" ] ; then PRODNAME=`cat \"/sys$DEVPATH/device/model\" | /usr/bin/tr -d ' '` elif [ -r "/sys$DEVPATH/../device/model" ] ; then PRODNAME=`cat \"/sys$DEVPATH/../device/model\" | /usr/bin/tr -d ' '` fi MOUNTPOINT=/usb-nolabel-${VENDOR}-${PRODNAME}-${DEVICE} ${CMD_DEBUG_LOG} VENDOR=${VENDOR} PRODNAME=${PRODNAME} \ MOUNTPOINT=${MOUNTPOINT} fi # --- prevent collision of mountpoints, and security and umount trouble while [ -e ${MOUNTPOINT} ] ; do MOUNTPOINT=${MOUNTPOINT}-avoided-collision ${CMD_DEBUG_LOG} MOUNTPOINT=${MOUNTPOINT} done # --- make and register our mountpoint # create mointpoint /bin/mkdir -p ${MOUNTPOINT} # register it for later umount/deletion /bin/echo ${MOUNTPOINT} > ${REMOVE_MPS} # --- find console user to allow user running umount # grab last/newest user from list added to group ${CONF_ACCESS_GROUP} CONS_USER=`/bin/grep "^${CONF_ACCESS_GROUP}" /etc/group | \ /usr/bin/cut -f 4 -d ':' | /usr/bin/rev | \ /usr/bin/cut -f 1 -d ',' | /usr/bin/rev` if [ x${CONS_USER} = x ] ; then CONS_USER=root fi # --- at long last do it # add to /etc/fstab so that it can be user mounted and then umounted # spinlock so that 2 inserts/removes at same time do not kill /etc/fstab while ! /bin/mkdir ${SYS_FSTAB_LOCK} ; do sleep 1 done # first remove possible stale entry with other user /bin/grep -v " ${MOUNTPOINT} " /etc/fstab > /etc/fstab.tmp || /bin/true /bin/mv /etc/fstab.tmp /etc/fstab /bin/echo "${DEVNAME} ${MOUNTPOINT} auto ${MOUNTOPTS} 0 0" >> /etc/fstab if [ -d ${SYS_FSTAB_LOCK} ] ; then /bin/rmdir ${SYS_FSTAB_LOCK} fi # and banzaaaiiii!!!!! /bin/su ${CONSOLEUSER} /bin/mount ${MOUNTPOINT} # tell the world what we have done ${CMD_DONE_LOG} mounted ${DEVICE} as ${DEVNAME} on ${MOUNTPOINT} ;; remove) # --- only interests us if we mounted it # we can not use info from /sys/block/* # as the device is now gone, so we stored info for direct access if [ ! -f ${REMOVE_MPS} ] ; then # device is not storage, or at least not one mounted by us, do nothing exit 0 fi # --- auto umount an device that has already vanished MOUNTPOINT=`/bin/cat ${REMOVE_MPS}` if [ x`/bin/mount | /bin/grep ${MOUNTPOINT} | \ /usr/bin/awk '{ print $1; }'` = x ] ; then # mountpoint is not in use, stray entry ${CMD_NOTE} stray mountpoint ${MOUNTPOINT} in ${REMOVE_MPS}, \ user may have umounted already, no umount ... else # get rid of it, lazy umount, gone from filesys, kernel tidy up later /bin/umount -l ${MOUNTPOINT} fi # remove from /etc/fstab so no user mounts or umounts any more # spinlock that 2 inserts/removes at same time do not kill /etc/fstab while ! /bin/mkdir ${SYS_FSTAB_LOCK} ; do sleep 1 done /bin/grep -v " ${MOUNTPOINT} " /etc/fstab > /etc/fstab.tmp || /bin/true /bin/mv /etc/fstab.tmp /etc/fstab if [ -d ${SYS_FSTAB_LOCK} ] ; then /bin/rmdir ${SYS_FSTAB_LOCK} fi # and get rid of mount point directory, to avoid future collisions if [ -d ${MOUNTPOINT} ] ; then /bin/rmdir ${MOUNTPOINT} fi # only delete after doing all, so it stays if we aborted on faillure /bin/rm -f ${REMOVE_MPS} # tell the world what we have (un)done ${CMD_DONE_LOG} unmounted ${MOUNTPOINT} ;; *) ${CMD_ERROR} unknown action "${ACTION}", aborting ... exit 1 ;; esac exit 0