#!/bin/bash

# Copyright (C) 2005, Kay Sievers <kay.sievers@vrfy.org>
# Copyright (C) 2006, David Zeuthen <david@fubar.dk>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2.

MOUNT_ROOT="/media"

# Check for environment variables
if [ "$HAL_PROP_BLOCK_DEVICE" == "" ] || [ "$HAL_PROP_INFO_UDI" == "" ]; then
    echo "Missing or empty environment variable(s)." >&2
    echo "This script should be started by hald." >&2
    exit 1
fi

if [ "$HAL_METHOD_INVOKED_BY_UID" == "" ]; then
    echo "org.freedesktop.Hal.Device.Volume.PermissionDenied" >&2
    echo "" >&2
    exit 1
fi

# check if device is already mounted
if [ "$HAL_PROP_VOLUME_IS_MOUNTED" = "true" ]; then
    echo "org.freedesktop.Hal.Device.Volume.AlreadyMounted" >&2
    echo "Device is already mounted." >&2
    exit 1
fi

# check if device should be ignored
if [ "$HAL_PROP_VOLUME_IGNORE" = "true" ]; then
    echo "org.freedesktop.Hal.Device.Volume.PermissionDenied" >&2
    echo "Device has volume.ignore set to TRUE. Refusing to mount." >&2
    exit 1
fi

check_fstab()
{
	awk -v dev="$1" -v mp="$2"\
	'
	BEGIN {
		sub(/^\/dev\//,"", dev)
		("/usr/bin/udevinfo -q symlink -n " dev " 2> /dev/null") | getline
		for(i=1; i <= NF; i++) {
			a["/dev/" $i] = 1
		}
		a["/dev/" dev] = 1
	}
	{
		if ($1 in a) {
			# "user" option is needed
			n = split ($4, options, ",")
			for (i = 1; i <= n; i++) {
				if (options[i] == "user") {
					# return specified mountpoint if below /media
					mount = $2
					if (match(mount, "^/media/") > 0) {
						sub(/^\/media\//,"", mount)
						print mount
						exit 0
					}
				}
			}

			print "org.freedesktop.Hal.Device.Volume.PermissionDenied" >"/dev/stderr"
			print "/etc/fstab configuration does not allow unprivileged operation" >"/dev/stderr"
			exit 1
		}
	}
	' /etc/fstab
}

# read parameters
# "MyDisk\n"
# "fuse\n"
# "ro\tsync\n"
# Only allow ^a-zA-Z0-9_= in the string because otherwise someone may
# pass e.g. umask=0600,suid,dev or umask=`/bin/evil`

read GIVEN_MOUNTPOINT
read GIVEN_MOUNTTYPE
GIVEN_MOUNTTYPE=${GIVEN_MOUNTTYPE//[^a-zA-Z0-9_=]/_}
read GIVEN_MOUNTOPTIONS
GIVEN_MOUNTOPTIONS=${GIVEN_MOUNTOPTIONS//[^-a-zA-Z0-9_=[:space:]]/_}

# deny to handle devices listed in fstab, unless the "user" option is given
# allow only use of specified mountpoint and fail for a different one
FSTAB_MOUNTPOINT=$(check_fstab "$HAL_PROP_BLOCK_DEVICE") || exit 1
if [ -n "$FSTAB_MOUNTPOINT" ]; then
    # fail if we got a request for a different mountpoint
    if [ -n "$GIVEN_MOUNTPOINT" ] && [ "$GIVEN_MOUNTPOINT" != "$FSTAB_MOUNTPOINT" ]; then
	echo "org.freedesktop.Hal.Device.Volume.PermissionDenied" >&2
	echo "/etc/fstab configuration does not allow this mountpoint" >&2
	exit 1;
    fi
    # use mountpoint from fstab
    GIVEN_MOUNTPOINT="$FSTAB_MOUNTPOINT"
fi

# if no mountpoint is given, use filesystem label, or drive_type, or "disk"
if [ -z "$GIVEN_MOUNTPOINT" ]; then
    if [ -n "$HAL_PROP_VOLUME_LABEL" ]; then
	GIVEN_MOUNTPOINT="$HAL_PROP_VOLUME_LABEL"
    elif [ -n "$HAL_PROP_STORAGE_DRIVE_TYPE" ]; then
	GIVEN_MOUNTPOINT="$HAL_PROP_STORAGE_DRIVE_TYPE"
    else
        GIVEN_MOUNTPOINT="disk"
    fi
fi

# sanitize
GIVEN_MOUNTPOINT=${GIVEN_MOUNTPOINT//[^a-zA-Z0-9_+-]/_}
GIVEN_MOUNTPOINT=${GIVEN_MOUNTPOINT/./_}

MOUNTPOINT="$GIVEN_MOUNTPOINT"

# pass only whitelisted types
if [ "$GIVEN_MOUNTTYPE" != "" ]; then
    case "$GIVEN_MOUNTTYPE" in
	subfs)
	    MOUNTTYPE="subfs"
	    ;;
	ntfsmount)
	    if [ "$HAL_PROP_VOLUME_FSTYPE" == "ntfs" ]; then
                MOUNTTYPE="ntfsmount"
            else
                echo "org.freedesktop.Hal.Device.Volume.InvalidMountType" >&2
                echo "Could not use ntfsmount with $HAL_PROP_VOLUME_FSTYPE" >&2
                exit 1;
	    fi
            ;;
	captive)
	    if [ "$HAL_PROP_VOLUME_FSTYPE" == "ntfs" ]; then
                MOUNTTYPE="captive"
            else
                echo "org.freedesktop.Hal.Device.Volume.InvalidMountType" >&2
                echo "Could not use ntfsmount with $HAL_PROP_VOLUME_FSTYPE" >&2
                exit 1;
	    fi
            ;;
	msdos)
            if ["$HAL_PROP_VOLUME_FSTYPE" == "vfat" ]; then
                MOUNTTYPE="msdos"
            else
                echo "org.freedesktop.Hal.Device.Volume.InvalidMountType" >&2
                echo "Replace other filesystems then vfat with msdos is not allowed." >&2
                exit 1
            fi
            ;;
	*)
	    echo "org.freedesktop.Hal.Device.Volume.InvalidFilesystemType" >&2
	    echo "Invalid filesystem type." >&2
	    exit 1
   esac
fi

# if no type is given, use default type, or let the kernel find out
if [ "$MOUNTTYPE" == "" ]; then
    if [ "$HAL_PROP_VOLUME_FSTYPE" != "" ]; then
	MOUNTTYPE=$HAL_PROP_VOLUME_FSTYPE
    else
	MOUNTTYPE="auto"
    fi
fi

# retrieve white-list from device properties (see fdi/policy/osvendor/20-storage-methods.fdi)
LEGAL_MOUNT_OPTIONS="$HAL_PROP_VOLUME_MOUNT_VALID_OPTIONS "
# pass only whitelisted mount options, bail out on anything not in the whitelist
if [ "$GIVEN_MOUNTOPTIONS" != "" ]; then
    for OPTION in $GIVEN_MOUNTOPTIONS; do
	OPTION_WAS_OK="0"
	for LEGAL_OPTION in $LEGAL_MOUNT_OPTIONS; do
	    if [ "$OPTION" == "$LEGAL_OPTION" ]; then
		MOUNTOPTIONS="$MOUNTOPTIONS,$OPTION"
		OPTION_WAS_OK="1"
	    elif [ "${LEGAL_OPTION:${#LEGAL_OPTION}-1}" == "=" ]; then
		# support for LEGAL_OPTION="umask=", e.g. support any $OPTION that starts with "umask="
		if [ "${OPTION:0:${#LEGAL_OPTION}}" == "$LEGAL_OPTION" ]; then			

		    # special handling for uid; only allow uid=$HAL_METHOD_INVOKED_BY_UID expect if
		    # $HAL_METHOD_INVOKED_BY_UID is 0
		    if [ "$LEGAL_OPTION" == "uid=" ]; then
			if [ "$HAL_METHOD_INVOKED_BY_UID" != "0" ]; then
			    if [ "uid=$HAL_METHOD_INVOKED_BY_UID" != "$OPTION" ]; then
				echo "org.freedesktop.Hal.Device.Volume.InvalidMountOption" >&2
				echo "The option '$OPTION' is not allowed for uid=$HAL_METHOD_INVOKED_BY_UID" >&2
				exit 1
			    fi
			fi
		    fi
		    MOUNTOPTIONS="$MOUNTOPTIONS,$OPTION"
		    OPTION_WAS_OK="1"
		fi
	    fi
	done
	if [ "$OPTION_WAS_OK" != "1" ]; then
	    echo "org.freedesktop.Hal.Device.Volume.InvalidMountOption" >&2
	    echo "The option '$OPTION' is not allowed" >&2
	    exit 1
	fi
    done
fi

echo "options = '$MOUNTOPTIONS'"

# cleanup no longer used mount points
if [ -e /usr/lib/hal/scripts/hal-system-storage-cleanup-mountpoints ]; then
        /usr/lib/hal/scripts/hal-system-storage-cleanup-mountpoints
elif [ -e /usr/lib64/hal/scripts/hal-system-storage-cleanup-mountpoints ]; then
        /usr/lib64/hal/scripts/hal-system-storage-cleanup-mountpoints
fi

# append number to mountpoint if it is already in use
while read dev dir type options; do
    if [ "$dir" = "$MOUNT_ROOT/$MOUNTPOINT" ]; then
	MOUNTPOINT_BUSY=1
	break;
    fi
done < /proc/mounts
if [ -n "$MOUNTPOINT_BUSY" ]; then
    NUM=1
    while [ -e "$MOUNT_ROOT/$MOUNTPOINT-$NUM" ]; do
	NUM=$(($NUM + 1))
    done
    MOUNTPOINT="$MOUNTPOINT-$NUM"
fi

# possibly create mountpoint directory and mark it for
# later cleanup, if we did create it
if [ ! -e "$MOUNT_ROOT/$MOUNTPOINT" ]; then
    MOUNTPOINT_CREATED=1
    mkdir "$MOUNT_ROOT/$MOUNTPOINT"
    touch "$MOUNT_ROOT/$MOUNTPOINT/.created-by-hal"
fi

if [ ! -e "$MOUNT_ROOT/$MOUNTPOINT" ]; then
    echo "org.freedesktop.Hal.Device.Volume.FailedToCreateMountpoint" >&2
    echo "Failed to create moutpoint $MOUNT_ROOT/$MOUNTPOINT." >&2
    exit 1
fi

# mount and return status
RESULT=$(mount -o "nosuid,nodev$MOUNTOPTIONS" -t "$MOUNTTYPE" "$HAL_PROP_BLOCK_DEVICE" "$MOUNT_ROOT/$MOUNTPOINT" 2>&1)
if [ $? -ne 0 ]; then
    case "$RESULT" in
	*"unknown filesystem"*)
	    echo "org.freedesktop.Hal.Device.Volume.UnknownFilesystemType" >&2
	    echo "Invalid filesystem type." >&2
	    ;;
	*)
	    echo "org.freedesktop.Hal.Device.Volume.UnknownFailure" >&2
	    echo "Failed to mount device." >&2
    esac
    if [ -n "$MOUNTPOINT_CREATED" ]; then
	rm -f "$MOUNT_ROOT/$MOUNTPOINT/.created-by-hal"
	rmdir "$MOUNT_ROOT/$MOUNTPOINT"
    fi
    exit 1
fi

hal-set-property --udi $UDI --key info.hal_mount.created_mount_point --string "$MOUNT_ROOT/$MOUNTPOINT" > /dev/null 2>&1
hal-set-property --udi $UDI --key info.hal_mount.mounted_by_uid --int "$HAL_METHOD_INVOKED_BY_UID" > /dev/null 2>&1

exit 0
