#!/bin/sh # /etc/dphys-hotplug.d/usb-storage - # mount/umount USB storage devices when their hotplug events happen # author franklin, last modification 2006.09.29 # This script is copyright ETH Zuerich Physics Departement, # use under either modified/non-advertising BSD or GPL license ### ------ 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 /dphys-hotplug-usb-storage.debug #DEBUG_LOG_STEP=yes # --- SYS_*, various system internal values # where we place files with umount and re-mount information SYS_RUN_DIR=/var/run/dphys-hotplug-usb-storage # 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 # sanitise this place, else some commands may fail # must be before any commands are executed, incl config/input processing 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:" # and also put out some permanent messages, in case running as cron/init.d job BASENAME="`/usr/bin/basename $0`" # something within the system that user does not expect, give up CMD_LOG_FATAL="/usr/bin/logger -t ${BASENAME} -p user.error FATAL:" # something from users input, user can possibly correct this and continue CMD_LOG_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_LOG_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_LOG_NOTE="/usr/bin/logger -t ${BASENAME} -p user.notice NOTE:" # normal stuff users expect, so no marking CMD_LOG_INFO="/usr/bin/logger -t ${BASENAME} -p user.info" # stuff users asked for, but mark it as special CMD_LOG_DEBUG="/usr/bin/logger -t ${BASENAME} -p user.debug DEBUG:" # other stuff we may want to use CMD_SLEEP="/bin/sleep 2" CMD_WAIT="read -p ---DEBUG-wait-after-step--- dummy" # --- config file stuff # what we are NAME=dphys-hotplug-usb-storage PNAME=dphys-hotplug-usb-storage # check user config file(s), let user override settings if [ -e /etc/${PNAME} ] ; then . /etc/${PNAME} fi # allow us to set logging despite no command line options and parser if [ -f /${NAME}.debug ] ; then DEBUG_LOG_STEP=yes fi # --- control variable output # set config option controllable stuff if [ x${CONF_LOG_DONE} = xyes ] ; then CMD_LOG_IF_DONE="${CMD_INFO}" else CMD_LOG_IF_DONE=/bin/true fi # set debug option controllable stuff if [ x${DEBUG_LOG_STEP} = xyes ] ; then CMD_LOG_IF_DEBUG="${CMD_LOG_DEBUG}" else CMD_LOG_IF_DEBUG=/bin/true fi # show what config settings this debug run is using ${CMD_LOG_IF_DEBUG} CONF_ACCESS_USER=${CONF_ACCESS_USER} \ CONF_ACCESS_GROUP=${CONF_ACCESS_GROUP} CONF_ACCESS_MODE=${CONF_ACCESS_MODE} \ CONF_FAT_UMASK=${CONF_FAT_UMASK} CONF_LOG_DONE=${CONF_LOG_DONE} # --- where is the device on the bus # this depends on usbfs being mounted on its standard /proc/bus/usb # resulting in ${DEVICE} like this: DEVICE=/proc/bus/usb/002/002 # we need this for add and remove, so put it here if [ x${DEVICE} = x ] ; then ${CMD_FATAL} no USB device given, perhaps usbfs missing, aborting ... exit 1 fi BUS=`/bin/echo ${DEVICE} | /usr/bin/cut -f 5 -d '/' | /bin/sed -e 's/^0*//'` DEV=`/bin/echo ${DEVICE} | /usr/bin/cut -f 6 -d '/' | /bin/sed -e 's/^0*//'` ${CMD_LOG_IF_DEBUG} ACTION=${ACTION} PRODUCT=${PRODUCT} INTERFACE=${INTERFACE}\ TYPE=${TYPE} DEVFS=${DEVFS} DEVICE=${DEVICE} BUS=${BUS} DEV=${DEV} # --- for removal and reinster 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 list files, for 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 # one line per mountpoint, so that multiple are possible REMOVE_MPS=${SYS_RUN_DIR}/remove-${BUS}-${DEV} # work depending on inserting or removing device case $ACTION in add) # --- only interests us if it is an storage device DRIVER=`/bin/sed -ne \ "/^T: Bus=0*${BUS} .* Dev#= *${DEV} /,/^T: /{ /^I: /p }" \ /proc/bus/usb/devices | /usr/bin/tr ' ' '\n' | \ /bin/grep '^Driver=' | /usr/bin/cut -f 2 -d '='` if [ x${DRIVER} != xusb-storage ] ; then # no usb-storage, so device doesn't interest us here, goodbye # must be here in case add, because for remove it fails, info is gone ${CMD_LOG_IF_DEBUG} DRIVER=${DRIVER} exit 0 fi # --- identify the device and where it is # serial number is far more unique than vendor or product name SERIAL=`/bin/sed -ne \ "/^T: Bus=0*${BUS} .* Dev#= *${DEV} /,/^T: /{ /^S: Serial/p }" \ /proc/bus/usb/devices | /usr/bin/cut -f 2 -d '='` # reduce to max 31 chars, /proc/scsi/usb-storage*/* fails us when more! # yes, this may be an broken device, but /proc/bus/usb/devices delivers SERIAL=`/bin/echo "${SERIAL}" | /usr/bin/cut -c 1-31` # chop off any spaces before/after/in the serial number # yes, such broken devices exist, I have seen one, scream! if [ x`/bin/echo ${SERIAL} | /usr/bin/tr -d ' '` != x ] ; then # got a serial, so where did this storage device get placed # /dev/null included so grep produces file:line even if only one file STORAGE=`/bin/grep -i "Serial Number: ${SERIAL}" \ /proc/scsi/usb-storage*/* /dev/null | /usr/bin/cut -f 1 -d ":"` ${CMD_LOG_IF_DEBUG} "SERIAL=\"${SERIAL}\"" STORAGE=${STORAGE} else # no serial, fall back on product name, hope no collision # combination vendor and product names is quite a bit more work # yes such crap really exists, I have seen one specimen ${CMD_NOTE} no serial number, falling back on product name ... PRODNAME=`/bin/sed -ne \ "/^T: Bus=0*${BUS} .* Dev#= *${DEV} /,/^T: /{ /^S: Product/p }" \ /proc/bus/usb/devices | /usr/bin/cut -f 2 -d '='` # also reduce this reduce to max 31 chars, same as serial number above PRODNAME=`/bin/echo "${PRODNAME}" | /usr/bin/cut -c 1-31` STORAGE=`/bin/grep -i "Product: ${PRODNAME}" \ /proc/scsi/usb-storage*/* /dev/null | /usr/bin/cut -f 1 -d ":"` ${CMD_LOG_IF_DEBUG} "PRODNAME=\"${PRODNAME}\"" STORAGE=${STORAGE} fi if [ x${STORAGE} = x ] ; then ${CMD_FATAL} ${BUS}-${DEV} no '/proc/scsi/usb-storage-*/*' file with \ serial number "\"${SERIAL}\"" or product name "\"${PRODNAME}\"", \ can not find scsi host, aborting ... exit 1 fi # --- find out where this storage devices scsi host has been placed if [ -d /dev/scsi ] ; then # system has got devfs on it # path directly contains host name, just insert number # host number = all digits before ":" NHOST=`/bin/grep 'Host scsi' ${STORAGE} | \ /usr/bin/cut -f 1 -d ":" | /usr/bin/tr -d ' [A-Za-z]'` DISCBASE=/dev/scsi/host${NHOST}/bus0/target0/lun0 # take full disk as initial "partition" PARTPATH=${DISCBASE}/disc ${CMD_LOG_IF_DEBUG} NHOST=${NHOST} DISCBASE=${DISCBASE} \ PARTPATH=${PARTPATH} else # system has got standard /dev directory # need to convert scsi host number to sd device name # host name scsi = last word before ":" SHOST=`/bin/grep 'Host scsi' ${STORAGE} | \ /usr/bin/cut -f 1 -d ":" | /usr/bin/rev | \ /usr/bin/cut -f 1 -d " " | /usr/bin/rev` # for re-finding this hosts /dev/sd file on re-insert # is a script fragment, setting variables, generated when first insert SHOST_REINSERT=${SYS_RUN_DIR}/reinsert-${SHOST} # and find the device of this hosts being attached in dmesg # only works if CONFIG_BLK_DEV_SD compiled in, or module sd_mod loaded # and even then only works the first time round, after not provided SDISK=`/bin/dmesg | /bin/grep "Attached scsi .* at ${SHOST}" | \ /usr/bin/tail -n1 | /bin/sed -e 's/^.* disk \(sd.\) .*$/\1/'` if [ x${SDISK} = x ] ; then # try to see if this was one before inserter, fall back on that if [ -f ${SHOST_REINSERT} ] ; then # at least that, add config from there, is simply an script fragment . ${SHOST_REINSERT} ${CMD_LOG_IF_DEBUG} SHOST=${SHOST} SHOST_REINSERT=${SHOST_REINSERT} \ DISCBASE=${DISCBASE} PARTPATH=${PARTPATH} else # we are at the end of our wits here, scream and give up ${CMD_FATAL} ${BUS}-${DEV} no \'Attached scsi\' line for ${SHOST}, \ possibly missing CONFIG_BLK_DEV_SD or module sd_mod, aborting ... exit 1 fi else DISCBASE=/dev/${SDISK} # take full disk as initial "partition" PARTPATH=${DISCBASE} # and register this stuff for later re-insert, as script fragment /bin/echo "DISCBASE=${DISCBASE}" > ${SHOST_REINSERT} /bin/echo "PARTPATH=${PARTPATH}" >> ${SHOST_REINSERT} ${CMD_LOG_IF_DEBUG} SHOST=${SHOST} SDISK=${SDISK} \ DISCBASE=${DISCBASE} PARTPATH=${PARTPATH} fi fi if [ ! -b ${PARTPATH} ] ; then ${CMD_FATAL} ${BUS}-${DEV} no disk drive ${PARTPATH}, aborting ... exit 1 fi # --- find first usable partition on this device (may be raw device) # can't rely on partition table, may be missing, uses heuristic, may fail # yes such crap really exists, I have seen one specimen # initially assume partition may be entire disk, and no partition table # read 4 sectors because needed for ext2 and ext3 bootsector and superblock # but also only 1 sector because else file may missread rest of content FILEINFO1=`/bin/dd if=${PARTPATH} bs=512 count=1 2> /dev/null | \ /usr/bin/file - | /usr/bin/tr -d ' ' | /usr/bin/cut -f 2 -d ':'` FILEINFO4=`/bin/dd if=${PARTPATH} bs=512 count=4 2> /dev/null | \ /usr/bin/file - | /usr/bin/tr -d ' ' | /usr/bin/cut -f 2 -d ':'` ${CMD_LOG_IF_DEBUG} FILEINFO1=${FILEINFO1} FILEINFO4=${FILEINFO4} # every PC conform disk should have this, we hope ... if [ x`/bin/echo ${FILEINFO1} | /bin/grep 'x86bootsector'` = x ] ; then ${CMD_FATAL} ${BUS}-${DEV} ${PARTPATH} file info \'${FILEINFO1}\' \ has no 55AA mbr/boot identity, aborting ... exit 1 fi # test for ext2|3 needs the full 4 sectors, as first one may be empty(!) if [ x`/bin/echo ${FILEINFO4} | /bin/grep 'Linux.*ext'` != x ] ; then # unpartitioned, direct ext2 or ext3 filesys - not yet seen in the wild # but test for it anyway, some user surely has done so ${CMD_NOTE} ${PARTPATH} seems to be direct partition-less ext2|ext3 ... FSTYPE=ext # test for FAT is not allowed the full 4 sectors, as it may missidentify elif [ x`/bin/echo ${FILEINFO1} | /bin/grep 'FAT'` != x ] ; then # unpartitioned, direct FAT filesys - yes such crap exists out there! # this covers MSDOS.*FAT, WINDOWS.*FAT, WIN.*FAT, FAT without specific ${CMD_NOTE} ${PARTPATH} seems to be direct partition-less FAT ... FSTYPE=fat else # assume partitioned disk, as should actually allways be, but see above # may still be boot-less file system, showing as empty partiton table FSTYPE=parttable fi ${CMD_LOG_IF_DEBUG} FSTYPE=${FSTYPE} # now this may be a real partitioned disk, or just bootcode-less filesys # try this broken special case, no partition or no/partial boot code # after above, else partial boot code may missread as 4 nonsense partit if [ x${FSTYPE} = xparttable ] ; then FIRST_PART=`/sbin/fdisk -l ${PARTPATH} | /bin/grep ${DISCBASE} | \ /bin/grep -v "^Disk ${DISCBASE}.*: " | /usr/bin/head -n 1 | \ /usr/bin/cut -f 1 -d " "` if [ x${FIRST_PART} = x ] ; then # no partitions and empty first sector (boot-less), can't know what ${CMD_NOTE} ${PARTPATH} empty parttion table and no code, \ assume non-bootable direct partition-less FAT ... FSTYPE=fat ${CMD_LOG_IF_DEBUG} FIRST_PART=${FIRST_PART} FSTYPE=${FSTYPE} else # now we assume we have found real partitions, use them # !!! missing part1..partn loop, will screw up with non-partition case # unlikely multiple partitions used, leave out for moment # simply use the first partition to be found, ignore rest PARTPATH=${FIRST_PART} ${CMD_LOG_IF_DEBUG} PARTPATH=${PARTPATH} if [ ! -b ${PARTPATH} ] ; then ${CMD_FATAL} ${BUS}-${DEV} no first partition ${PARTPATH}, \ not readable, aborting ... exit 1 fi fi fi if [ x${FSTYPE} = xparttable ] ; then FILEINFO1=`/bin/dd if=${PARTPATH} bs=512 count=1 2> /dev/null | \ /usr/bin/file - | /usr/bin/tr -d ' ' | /usr/bin/cut -f 2 -d ':'` FILEINFO4=`/bin/dd if=${PARTPATH} bs=512 count=4 2> /dev/null | \ /usr/bin/file - | /usr/bin/tr -d ' ' | /usr/bin/cut -f 2 -d ':'` ${CMD_LOG_IF_DEBUG} FILEINFO1=${FILEINFO1} FILEINFO4=${FILEINFO4} if [ x`/bin/echo ${FILEINFO1} | /bin/grep 'x86bootsector'` = x ] ; then # only note, as ext2|3 do not need to have an boot sector and identity ${CMD_NOTE} ${BUS}-${DEV} ${PARTPATH} file info \'${FILEINFO}\' \ has no 55AA mbr/boot identity fi if [ x`/bin/echo ${FILEINFO4} | /bin/grep 'Linux.*ext'` != x ] ; then FSTYPE=ext elif [ x`/bin/echo ${FILEINFO1} | /bin/grep 'FAT'` != x ] ; then FSTYPE=fat else ${CMD_NOTE} ${PARTPATH} has not identifiable file info \ \'${FILEINFO}\', assuming non bootable FAT ... FSTYPE=fat fi fi ${CMD_LOG_IF_DEBUG} FSTYPE=${FSTYPE} # --- user access raw device, for change label reformatting/repartitioning /bin/chown ${CONF_ACCESS_USER}:${CONF_ACCESS_GROUP} ${DISCBASE} /bin/chmod ${CONF_ACCESS_MODE} ${DISCBASE} # !!! again missing part1..partn loop /bin/chown ${CONF_ACCESS_USER}:${CONF_ACCESS_GROUP} ${PARTPATH} /bin/chmod ${CONF_ACCESS_MODE} ${PARTPATH} # --- collect file system specifig mount options if [ x${FSTYPE} = xfat ] ; then MOUNTOPTS=${SYS_MOUNTOPTS}${SYS_MOUNTOPTSFAT} elif [ x${FSTYPE} = xext ] ; then MOUNTOPTS=${SYS_MOUNTOPTS}${SYS_MOUNTOPTSEXT} else MOUNTOPTS=${SYS_MOUNTOPTS} fi ${CMD_LOG_IF_DEBUG} MOUNTOPTS=${MOUNTOPTS} # --- where do we want to mount on, extract possible label for this if [ x${FSTYPE} = xfat ] ; then MTOOLS_CONF=~/.mtoolsrc if [ -f ${MTOOLS_CONF} ] ; then # save existing config, as we need to overwrite, no options possible /bin/cp -p ${MTOOLS_CONF} ${MTOOLS_CONF}.${NAME}.bak fi /bin/echo "drive u: file=\"${PARTPATH}\"" >> ${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_LOG_IF_DEBUG} FSTYPE=${FSTYPE} LABEL=${LABEL} elif [ x${FSTYPE} = xext ] ; then LABEL=`/sbin/tune2fs -l ${PARTPATH} | \ /bin/grep '^Filesystem volume name:' | /bin/grep -v '' | \ /usr/bin/cut -f 2 -d ':' | /usr/bin/tr -d ' '` ${CMD_LOG_IF_DEBUG} FSTYPE=${FSTYPE} LABEL=${LABEL} else ${CMD_NOTE} no identifiable filesystem ${FSTYPE}, can not extract \ an mountpoint name, using generic mountpoint ... VENDOR=`/bin/grep 'Vendor: ' ${STORAGE} | /usr/bin/cut -f 2 -d ":" | \ /usr/bin/tr -d ' !"#$%&\047()*,./:;<=>?@\[\\]^\0140{|}~'` PRODNAME=`/bin/grep 'Product: ' ${STORAGE} | /usr/bin/cut -f 2 -d ":" | \ /usr/bin/tr -d ' !"#$%&\047()*,./:;<=>?@\[\\]^\0140{|}~'` LABEL=usb-unknownfs-${VENDOR}-${PRODNAME} ${CMD_LOG_IF_DEBUG} FSTYPE=${FSTYPE} VENDOR=${VENDOR} \ PRODNAME=${PRODNAME} LABEL=${LABEL} fi if [ x${LABEL} != x ] ; then # we have found an label, use that as mountpoint name MOUNTPOINT=/usb-${LABEL} ${CMD_LOG_IF_DEBUG} LABEL=${LABEL} MOUNTPOINT=${MOUNTPOINT} else # no label, generate generic name, hope user recognises part of it ${CMD_NOTE} no label filesystem, nothing to extract \ for mountpoint name, using generic mountpoint ... # tr only filter for ASCII stuff, will fail if 8bit is allowed and used VENDOR=`/bin/grep 'Vendor: ' ${STORAGE} | /usr/bin/cut -f 2 -d ":" | \ /usr/bin/tr -d ' !"#$%&\047()*,./:;<=>?@\[\\]^\0140{|}~'` PRODNAME=`/bin/grep 'Product: ' ${STORAGE} | /usr/bin/cut -f 2 -d ":" | \ /usr/bin/tr -d ' !"#$%&\047()*,./:;<=>?@\[\\]^\0140{|}~'` MOUNTPOINT=/usb-nolabel-${VENDOR}-${PRODNAME} ${CMD_LOG_IF_DEBUG} VENDOR=${VENDOR} PRODNAME=${PRODNAME} \ MOUNTPOINT=${MOUNTPOINT} fi # --- prevent collision of mountpoints, and resulting umount trouble while [ -e ${MOUNTPOINT} ] ; do MOUNTPOINT=${MOUNTPOINT}-avoided-collision ${CMD_LOG_IF_DEBUG} MOUNTPOINT=${MOUNTPOINT} done # --- make and register our mountpoint # get rid of any stray remove/umount file from previous session(s) # rm here, and later >> allow multiple mount points per device # say for multiple partitions on one device /bin/rm -f ${REMOVE_MPS} # and create mointpoint /bin/mkdir -p ${MOUNTPOINT} # and register it for later umount/deletion # >> allows multiple mounts per device (say for multiple partitions) /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} # do mount as this user, so that it gets recorded and umount will work CONSOLEUSER=`/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${CONSOLEUSER} = x ] ; then CONSOLEUSER=root fi ${CMD_LOG_IF_DEBUG} CONSOLEUSER=${CONSOLEUSER} # --- 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 "${PARTPATH} ${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_LOG_IF_DONE} has mounted ${DEVICE} as ${PARTPATH} on ${MOUNTPOINT} ;; remove) # --- only interests us if we mounted it # we can not use Driver= from /proc/bus/usb/devices # or even worse /proc/scsi/usb-storage-*/* derived from that # 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 # allow multiple mount points per device (say for multiple partitions) # even though we presently never produce this situation for MOUNTPOINT in `/bin/cat ${REMOVE_MPS}` ; do 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 # tell the world what we have (un)done ${CMD_LOG_IF_DONE} has unmounted ${MOUNTPOINT} done # only delete after doing all, so it stays if we aborted on faillure /bin/rm -f ${REMOVE_MPS} # and get rid of the usb-storage driver, so it gets reloaded next insert # needed for broken USB stick, reinsert brings sda: Unit Not Ready # abbreviated procedure seems to not reset stick fully, so force full # the ${SYS_RUN_DIR}/reinsert-${SHOST} stuff is still needed # as unload only works if this was the only/last stick to be removed /sbin/rmmod usb-storage ;; *) ${CMD_ERROR} unknown action "${ACTION}", aborting ... exit 1 ;; esac # --- finish off exit 0