#!/bin/bash

#
#  Copyright Red Hat Inc., 2002
#  Copyright Mission Critical Linux, 2000
#
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation; either version 2, or (at your option) any
#  later version.
#
#  This program is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; see the file COPYING.  If not, write to the
#  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
#  MA 02139, USA.
#

# $Revision: 1.5 $
#
# Author: Gregory P. Myrdal <Myrdal@MissionCriticalLinux.Com>

#
# Shell library for filesystem functions
#

SH_LIB=$(dirname $0)

LIBRARIES=

for library in $LIBRARIES
do
	if [ -f $library ]; then
	  . $library
	fi
done

#
# mountInUse device mount_point
#
# Check to see if either the device or mount point are in use anywhere on
# the system.  It is not required that the device be mounted on the named
# moint point, just if either are in use.
#
mountInUse () {
	typeset mp tmp_mp
	typeset dev tmp_dev
	typeset junk

	if [ $# -ne 2 ]; then
		logAndPrint $LOG_ERR "Usage: mountInUse device mount_point".
		return $FAIL
	fi

	dev=$1
	mp=$2

	while read tmp_dev tmp_mp junk
	do
		if [ -n "$tmp_dev" -a "$tmp_dev" = "$dev" ]; then
			return $YES
		fi
		
		if [ -n "$tmp_mp" -a "$tmp_mp" = "$mp" ]; then
			return $YES
		fi
	done < <(mount | awk '{print $1,$3}')

	return $NO
}

#
# isMounted device mount_point
#
# Check to see if the device is mounted.  Print a warning if its not
# mounted on the directory we expect it to be mounted on.
#
isMounted () {

	typeset mp tmp_mp
	typeset dev tmp_dev

	if [ $# -ne 2 ]; then
		logAndPrint $LOG_ERR "Usage: isMounted device mount_point"
		return $FAIL
	fi

	dev=$1
	mp=$2

	
	while read tmp_dev tmp_mp ; do
		if [ -n "$tmp_dev" -a "$tmp_dev" = "$dev" ]; then
			#
			# Check to see if its mounted in the right
			# place
			#
			if [ -n "$tmp_mp"  -a "$tmp_mp"  != "$mp" ]; then
				logAndPrint $LOG_WARNING "\
Device $dev is mounted on $tmp_mp instead of $mp"
			fi
			return $YES
		fi
	done < <(mount | awk '{print $1,$3}')

	return $NO
}

#
# setDeviceOwners device mount_point serviceID deviceID
#
setDeviceOwners()
{
	typeset tmp_dev

	if [ $# -ne 4 ]; then
	  logAndPrint $LOG_ERR \
	     "Usage: setDeviceOwners device mount_point serviceID deviceID"
	  return $FAIL
	fi

	typeset dev=$1
	typeset mp=$2
	typeset svcID=$3
	typeset devN=$4

	typeset svc_name=$(getSvcName $DB $svcID)

	#
	# Get the device owner, if it exists.
	#
	dev_owner=$(getSvcDeviceOwner $DB $svcID $devN)
	case $? in
	0) 
	  case "$dev_owner" in 
	  ""|"[ 	]*")
	    dev_owner="";;			# clugetconfig returns white space
	  *) : ;;				# found it
	  esac
	  ;;
	2) dev_owner="";;	# clugetconfig returns "not found", erase it 
	*) logAndPrint $LOG_ERR "\
setDeviceOwners: Cannot get device owner $devN for service $svc_name, err=$?"
	  return $FAIL ;;
	esac

	#
	# Get the device group, if it exists.
	#
	dev_group=$(getSvcDeviceGroup $DB $svcID $devN)
	case $? in
	0) 
	  case "$dev_group" in 
	  ""|"[ 	]*")
	    dev_group="";;			# clugetconfig returns white space
	  *) : ;;				# found it
	  esac
	  ;;
	2) dev_group="";;	# clugetconfig returns "not found", erase it 
	*) logAndPrint $LOG_ERR "\
setDeviceOwners: Cannot get device group $devN for service $svc_name, err=$?"
	  return $FAIL ;;
	esac

	#
	# Get the device mode, if it exists.
	#
	dev_mode=$(getSvcDeviceMode $DB $svcID $devN)
	case $? in
	0) 
	  case "$dev_mode" in 
	  ""|"[ 	]*")
	    dev_mode="";;			# clugetconfig returns white space
	  *) : ;;				# found it
	  esac
	  ;;
	2) dev_mode="";;	# clugetconfig returns "not found", erase it 
	*) logAndPrint $LOG_ERR "\
setDeviceOwners: Cannot get device mode $devN for service $svc_name, err=$?"
	  return $FAIL ;;
	esac

	#
	# Set the permissions.  If we do not have a mount point we
	# must be setting the permissions on a raw device.
	#
	if [ -n "$mp" ]; then
	  tmp_dev=$mp 
	else
	  tmp_dev=$dev
	fi

	if [ -n "$dev_mode" ]; then
	  logAndPrint $LOG_DEBUG "Setting permissions on $tmp_dev to $dev_mode"
	  chmod $dev_mode $tmp_dev
	  ret_val=$?
	  if [ $ret_val -ne 0 ]; then
	    logAndPrint $LOG_ERR \
	                "'chmod $dev_mode $tmp_dev' failed, error=$ret_val"
	    return $FAIL
	  fi
	fi

	if [ -n "$dev_group" ]; then
	  logAndPrint $LOG_DEBUG "Setting group of $tmp_dev to $dev_group"
	  chgrp $dev_group $tmp_dev
	  ret_val=$?
	  if [ $ret_val -ne 0 ]; then
	    logAndPrint $LOG_ERR \
	                "'chgrp $dev_group $tmp_dev' failed, error=$ret_val"
	    return $FAIL
	  fi
	fi

	if [ -n "$dev_owner" ]; then
	  logAndPrint $LOG_DEBUG "Setting owner of $tmp_dev to $dev_owner"
	  chown $dev_owner $tmp_dev
	  ret_val=$?
	  if [ $ret_val -ne 0 ]; then
	    logAndPrint $LOG_ERR \
	                "'chown $dev_owner $tmp_dev' failed, error=$ret_val"
	    return $FAIL
	  fi
	fi
}

#
# killMountProcesses device mount_point
#
# Using lsof or fuser try to unmount the mount by killing of the processes
# that might be keeping it busy.
#
killMountProcesses()
{
	typeset have_lsof=""
	typeset have_fuser=""
	typeset try

	if [ $# -ne 2 ]; then
	  logAndPrint $LOG_ERR "Usage: killMountProcesses device mount_point"
	  return $FAIL
	fi

	typeset dev=$1
	typeset mp=$2

	logAndPrint $LOG_INFO "Forcefully unmounting $dev ($mp)"

	#
	# Not all distributions have lsof.  If not use fuser.  If it
	# does, try both.
  	#
	file=$(which lsof 2>/dev/null)
	if [ -f "$file" ]; then
	  have_losf=$YES
	fi

	file=$(which fuser 2>/dev/null)
	if [ -f "$file" ]; then
	  have_fuser=$YES
	fi             
			
	for try in 1 2
	do

	  if [ -n "$have_lsof" ]; then
	    #
	    # Use lsof to free up mount point
	    #
	    while read command pid user name
	    do
	      if [ -z "$pid" ]; then
	        continue
	      fi

	      if [ $try -eq 1 ]; then
	        logAndPrint $LOG_INFO \
	                    "killing process $pid ($user $command $dev)"
	      fi

	      if [ $try -gt 1 ]; then
	        kill -9 $pid
	      else
	        kill -TERM $pid
	      fi
	    done < <(lsof $dev | \
	             grep -v PID | \
	             awk '{print $1,$2,$3,$9}' | \
	             sort -u -k 1,3)
	  fi

	  if [ -n "$have_fuser" ]; then
	    #
	    # Use fuser to free up mount point
	    #
	    while read command pid user
	    do
	      if [ -z "$pid" ]; then
	        continue
	      fi

	      if [ $try -eq 1 ]; then
	        logAndPrint $LOG_INFO \
	                    "killing process $pid ($user $command $dev)"
	      fi

	      if [ $try -gt 1 ]; then
	        kill -9 $pid
	      else
	        kill -TERM $pid
	      fi
	    done < <(fuser -vm $dev | \
                     grep -v PID | \
	             sed 's;^'$dev';;' | \
                     awk '{print $4,$2,$1}' | \
                     sort -u -k 1,3)
	  fi
	done

	if [ -z "$have_lsof" -a -z "$have_fuser" ]; then
	  logAndPrint $LOG_WARNING \
	  "Cannot forcefully unmount $dev; cannot find lsof or fuser commands"
	  return $FAIL
	fi

	return $SUCCESS
}

#
# startFilesystem serviceID deviceID
#
startFilesystem() {
	typeset -i ret_val=$SUCCESS
	typeset mp=""			# mount point
	typeset dev=""			# device
	typeset dev_owner=""
	typeset dev_group=""
	typeset dev_mode=""
	typeset fstype=""
	typeset opts=""
	typeset device_in_use=""
	typeset mount_options=""

	if [ $# -ne 2 ]; then
	  logAndPrint $LOG_ERR "Usage: startFilesystem serviceID deviceID"
	fi

	typeset svcID=$1
	typeset devN=$2

	typeset svc_name=$(getSvcName $DB $svcID)

	#
	# Get the mount point, if it exists.  If not, no need to continue.
	#
	mp=$(getSvcMountPoint $DB $svcID $devN)
	case $? in
	0) 
	  case "$mp" in 
	  ""|"[ 	]*")
	    return $SUCCESS	# nothing to mount
	    ;;
	  /*) :			# found it
	    ;;
	  *)	 		# invalid format
	    logAndPrint $LOG_ERR \
"startFilesystem: Invalid mount point format (must begin with a '/'): \'$mp\'"
	    return $FAIL
	    ;;
	  esac
	  ;;
	2) return $SUCCESS	# nothing to mount
	  ;;
	*) logAndPrint $LOG_ERR "\
startFilesystem: Cannot get mount point $devN for service $svc_name, err=$?"
	  return $FAIL ;;
	esac
	
	#
	# Get the device
	#
	dev=$(getSvcDevice $DB $svcID $devN)
	case $? in
	0) : ;;		# found it
	*) logAndPrint $LOG_ERR "\
startFilesystem: Cannot find device $devN for service $svc_name in $DB, err=$?"
	  return $FAIL ;;
	esac

	#
	# Ensure we've got a valid directory
	#
	if [ -e "$mp" ]; then
		if ! [ -d "$mp" ]; then
			logAndPrint $LOG_ERR "\
startFilesystem: Mount point $mp exists but is not a directory"
			return $FAIL
		fi
	else
		logAndPrint $LOG_INFO "\
startFilesystem: Creating mount point $mp for device $dev"
		mkdir -p $mp
	fi

	#
	# Get the filesystem type, if specified.
	#
	fstype_option=""
	fstype=$(getSvcMountFstype $DB $svcID $devN)
	case $? in 
	0) 
	  case "$fstype" in 
	  ""|"[ 	]*")
	    fstype="";;				# clugetconfig returns white space
	  *)  fstype_option="-t $fstype" ;;	# found it
	  esac
	  ;;
	2) fstype="";;	   # clugetconfig returns "not found", erase it 
	*) logAndPrint $LOG_ERR "\
startFilesystem: Cannot get mount fstype $devN for service $svc_name, err=$?"
	  return $FAIL ;;
        esac

	#
	# See if the device is already mounted.
	# 
	isMounted $dev $mp
	case $? in
	$YES)				# already mounted
	  logAndPrint $LOG_DEBUG "$dev already mounted"
	  return $SUCCESS
	  ;;
	$NO) : ;;			# not mounted, continue
	$FAIL) return $FAIL ;;
	esac

	#
	# Make sure that neither the device nor the mount point are mounted
	# (i.e. they may be mounted in a different location).  The'mountInUse'
	# function checks to see if either the device or mount point are in
	# use somewhere else on the system.
	#
	mountInUse $dev $mp
	case $? in
	$YES)		# uh oh, someone is using the device or mount point
	  logAndPrint $LOG_ERR "\
Cannot mount $dev on $mp, the device or mount point is already in use!"
	  return $FAIL
	  ;;
	$NO) : ;;	# good, no one else is using it
	$FAIL)
	  return $FAIL
	  ;;
	*)
	  logAndPrint $LOG_ERR "Unknown return from mountInUse"
	  return $FAIL
	  ;;
	esac

	#
	# Make sure the mount point exists.
	#
	if [ ! -d $mp ]; then
	  rm -f $mp			# rm in case its a plain file
	  mkdir -p $mp			# create the mount point
	  ret_val=$?
	  if [ $ret_val -ne 0 ]; then
	    logAndPrint $LOG_ERR "'mkdir -p $mp' failed, error=$ret_val"
	    return $FAIL
	  fi
	fi

	#
	# Get the mount options, if they exist.
	#
	mount_options=""
	opts=$(getSvcMountOptions $DB $svcID $devN)
	case $? in
	0) 
	  case "$opts" in 
	  ""|"[ 	]*")
	    opts="";;				# clugetconfig returns white space
	  *) mount_options="-o $opts" ;;	# found it
	  esac
	  ;;
	2) opts="";;		# clugetconfig returns "not found", erase it 
	*) logAndPrint $LOG_ERR "\
startFilesystem: Cannot get mount options $devN for service $svc_name, err=$?"
	  return $FAIL ;;
	esac

	#
	# Check to determine if we need to fsck the filesystem.
	#
	# Note: this code should not indicate in any manner suggested
	# file systems to use in the cluster.  Known filesystems are
	# listed here for correct operation.
	#
        case "$fstype" in
        reiserfs) typeset fsck_needed="" ;;
        ext3)     typeset fsck_needed="" ;;
        jfs)      typeset fsck_needed="" ;;
        xfs)      typeset fsck_needed="" ;;
        ext2)     typeset fsck_needed=yes ;;
        minix)    typeset fsck_needed=yes ;;
        vfat)     typeset fsck_needed=yes ;;
        msdos)    typeset fsck_needed=yes ;;
	"")       typeset fsck_needed=yes ;;		# assume fsck
	*)        typeset fsck_needed=yes 		# assume fsck
	          logAndPrint $LOG_WARNING "\
Unknown file system type '$fstype' for device $dev.  Assuming fsck is required."
	          ;;
	esac

	#
	# Fsck the device, if needed.
	#
	if [ -n "$fsck_needed" ]; then
	  typeset fsck_log=/tmp/$(basename $dev).fsck.log
	  logAndPrint $LOG_DEBUG "Running fsck on $dev"
	  fsck -p $dev >> $fsck_log 2>&1
	  ret_val=$?
	  if [ $ret_val -gt 1 ]; then
	    logAndPrint $LOG_ERR "\
'fsck -p $dev' failed, error=$ret_val; check $fsck_log for errors"
	    logAndPrint $LOG_DEBUG "Invalidating buffers for $dev"
	    $INVALIDATEBUFFERS -f $dev
	    return $FAIL
	  fi
	  rm -f $fsck_log
	fi

	#
	# Mount the device
	#
	logAndPrint $LOG_DEBUG "mount $fstype_option $mount_options $dev $mp"
	mount $fstype_option $mount_options $dev $mp
	ret_val=$?
	if [ $ret_val -ne 0 ]; then
	  logAndPrint $LOG_ERR "\
'mount $fstype_option $mount_options $dev $mp' failed, error=$ret_val"
	  return $FAIL
	fi
	
	return $SUCCESS
}

#
# stopFilesystem serviceID deviceID
#
# Run the stop actions
#
stopFilesystem() {
	typeset -i ret_val=0
	typeset -i try=1
	typeset -i max_tries=3		# how many times to try umount
	typeset -i sleep_time=2		# time between each umount failure
	typeset done=""
	typeset umount_failed=""
	typeset force_umount=""

	if [ $# -ne 2 ]; then
	  logAndPrint $LOG_ERR "Usage: stopFilesystem serviceID deviceID"
	  return $FAIL
	fi

	typeset svcID=$1
	typeset devN=$2

	#
	# Get the device
	#
	dev=$(getSvcDevice $DB $svcID $devN)
	case $? in
	  0) : ;;			# found it
	  *) logAndPrint $LOG_ERR "\
stopFilesystem: Cannot find device for $mp in $DB, err=$?"
	     return $FAIL ;;
	esac

	#
	# Get the mount point, if it exists.
	#
	mp=$(getSvcMountPoint $DB $svcID $devN)
	case $? in
	  0) : ;;			# found it
	  2) return $SUCCESS;;		# nothing to unmount
	  *) logAndPrint $LOG_ERR "\
stopFilesystem: Cannot get mount point $devN for $svc_name, err=$?"
	     return $FAIL ;;
	esac

	#
	# Get the force unmount setting if there is a mount point.
	#
	if [ -n "$mp" ]; then
	  force_umount=$(getSvcForceUnmount $DB $svcID $devN)
	  case $? in
	    0) 				# found it
	      case $force_umount in
	        $YES_STR)	force_umount=$YES ;;
	        *)		force_umount="" ;;
	      esac
	      ;;
	    2) force_umount="" ;;	# no force umount set
	    *) logAndPrint $LOG_ERR "\
stopFilesystem: Cannot get force umount setting $devN for $svc_name, err=$?"
	       return $FAIL ;;
	  esac
	fi

	#
	# Unmount the device.  
	#
	# If we fail, try to forcefully unmount the filesystem by killing any
	# processes using that file system (The poor man's forced unmount).
	#
	while [ ! "$done" ]; do
	  isMounted $dev $mp
	  case $? in
	  $NO)
	    logAndPrint $LOG_INFO "$dev is not mounted"
	    umount_failed=
	    done=$YES
	    ;;
	  $FAIL)
	    return $FAIL
	    ;;
	  $YES)
	    sync; sync; sync
	    logAndPrint $LOG_INFO "unmounting $dev ($mp)"

	    umount $dev
	    let ret_val=$?
	    if  [ $ret_val -eq 0 ]; then
	      #
	      # The Linux kernel will not invalidate buffers
	      # after an unmount.  Since this disk might come
	      # back with different data on the disk we want
	      # to make sure that the system buffers are
	      # invalidated before it goes.  NOTE: this is
	      # fixed in the 2.4 kernel and all calls to
	      # $INVALIDATEBUFFERS can be removed when all
	      # clusters are running this version of the
	      # kernel.
	      #
	      logAndPrint $LOG_DEBUG "Invalidating buffers for $dev"
	      $INVALIDATEBUFFERS -f $dev

	      umount_failed=
	      done=$YES
	      continue
	    fi

	    umount_failed=yes

	    if [ "$force_umount" ]; then
	      killMountProcesses $dev $mp
	    fi

	    if [ $try -ge $max_tries ]; then
	      done=$YES
	    else
	      sleep $sleep_time
	      let try=try+1
	    fi
	    ;;
	  *)
	    return $FAIL
	    ;;
	  esac

	  if [ $try -ge $max_tries ]; then
	    done=$YES
	  else
	    sleep $sleep_time
	    let try=try+1
	  fi

	done

	if [ -n "$umount_failed" ]; then
	  logAndPrint $LOG_ERR "'umount $dev' failed ($mp), error=$ret_val"
	  return $FAIL
	else
	  return $SUCCESS
	fi
}

#
# startFilesystems serviceID
#
startFilesystems()
{
	typeset dev
	typeset i devN=$MIN_TOKEN_ID
	typeset -i ret_val
	typeset mp mount_point
	typeset TMPFILE_STR=/tmp/$MYNAME.startDevices.XXXXXX
	typeset TMPFILE=$(mktemp $TMPFILE_STR; ret_val=$?)

	if [ $# -ne 1 ]; then
	  logAndPrint $LOG_ERR "Usage: startFilesystems serviceID"
	  return $FAIL
	fi

	typeset svcID=$1

	if [ -z "$TMPFILE" ]; then
	  logAndPrint $LOG_ERR 
"Cannot create temporary file name with 'mktemp $TMPFILE_STR', err=$ret_val"
	  return $FAIL
	fi

	#
	# Get all mounts points in the correct heirarchical order.
	# Save the mount point ID so we can grab other associated
	# attributes for that mount later.
	#
	while : ; do
	  #
	  # We loop on devices looking for devices that have mount points
	  #
	  dev=$(getSvcDevice $DB $svcID $devN)
	  case $? in
	    0) : ;;		# found it, let it fall through
	    2) break ;;		# done getting devices, no more left
	    *) logAndPrint $LOG_ERR "\
startDevices: Cannot get device $devN of service $svc_name, err=$?"
	       rm -f $TMPFILE
	       return $FAIL;;
	  esac

	  mp=$(getSvcMountPoint $DB $svcID $devN)
	  case $? in
	    0) : ;;			# found a mount point
	    2) let devN=devN+1	# no mount point, go to next device
	       continue ;;
	    *) logAndPrint $LOG_ERR "\
startDevices: Cannot get mount point $devN of service $svc_name, err=$?"
	       rm -f $TMPFILE
	       return $FAIL ;;
	  esac

	  echo "$mp $devN" >> $TMPFILE
	  let devN=devN+1
	done

	#
	# Sort all mounts in heirarchical order and attempt to mount them.
	#
	if [ -s $TMPFILE ]; then
	  while read mount_point devN
	  do
	    startFilesystem $svcID $devN
	    if [ $? -ne $SUCCESS ]; then
	      rm -f $TMPFILE
	      return $FAIL
	    fi
	  done < <(sort $TMPFILE)
	fi

	rm -f $TMPFILE
	return $SUCCESS
}

#
# stopFilesystems serviceID
#
stopFilesystems()
{
	typeset dev
	typeset -i devN=$MIN_TOKEN_ID
	typeset mp mount_point
	typeset -i ret_val
	typeset TMPFILE_STR=/tmp/$MYNAME.stopDevices.XXXXXX
	typeset TMPFILE=$(mktemp $TMPFILE_STR; ret_val=$?)

	if [ $# -ne 1 ]; then
	  logAndPrint $LOG_ERR "Usage: stopFilesystems serviceID"
	  return $FAIL
	fi

	typeset svcID=$1

	if [ -z "$TMPFILE" ]; then
	  logAndPrint $LOG_ERR 
"Cannot create temporary file name with 'mktemp $TMPFILE_STR', err=$ret_val"
	  return $FAIL
	fi

	#
	# Get all mounts points in the correct heirarchical order.
	# Save the mount point ID so we can grab other associated
	# attributes for that mount later.
	#
	while : ; do
	  #
	  # We loop on devices looking for devices that have mount points
	  #
	  dev=$(getSvcDevice $DB $svcID $devN)
	  case $? in
	    0) : ;;		# found it, let it fall through
	    2) break ;;		# done getting devices, no more left
	    *) logAndPrint $LOG_ERR "\
stopDevices: Cannot get device $devN of service $svc_name, err=$?"
	       rm -f $TMPFILE
	       return $FAIL;;
	  esac

	  mp=$(getSvcMountPoint $DB $svcID $devN)
	  case $? in
	    0) : ;;			# found a mount point
	    2) let devN=devN+1	# no mount point, go to next device
	       continue ;;
	    *) logAndPrint $LOG_ERR "\
stopDevices: Cannot get mount point $devN of service $svc_name, err=$?"
	       rm -f $TMPFILE
	       return $FAIL ;;
	  esac

	  echo "$mp $devN" >> $TMPFILE
	  let devN=devN+1
	done

	#
	# Sort the mount points in reverse order and umount them
	#
	if [ -s $TMPFILE ]; then
	  while read mount_point devN
	  do
	    stopFilesystem $svcID $devN
	    if [ $? -ne $SUCCESS ]; then
	      rm -f $TMPFILE
	      return $FAIL
	    fi
	  done < <(sort -r $TMPFILE)
	fi

	rm -f $TMPFILE
	return $SUCCESS
}

#
#
#
statusFilesystems()
{
	return $SUCCESS
}

#
# filesystem action serviceID
#
filesystem()
{
	if [ $# -ne 2 ]; then
	  logAndPrint $LOG_ERR \
	              "Usage: filesystem [start, stop, status] serviceID"
	  return $FAIL
	fi

	typeset action=$1
	typeset svcID=$2

	case "$action" in
	'start')
	  startFilesystems $svcID
	  return $?
	  ;;

	'stop')
	  stopFilesystems $svcID
	  return $?
	  ;;

	'status')
	  statusFilesystems $svcID
	  return $?
	  ;;

	*)
	  logAndPrint $LOG_ERR \
	            "filesystem: Cannot decipher action argument \'$action\'"
	  return $FAIL
	  ;;
	esac
}
