#!/bin/bash
#Copyright 2005-2021, Lime Technology
#License: GPLv2 only

# mount_image filepath mountpoint size

# If filepath ends in ".img" extension, we (first create if necessary and then) mount filepath
# as a loopback filesystem.  For create, size is the initial size in GiB to fallocate for filepath,
# subsequently, size indicates a possibly larger size to extend filepath.  filepath is then
# mounted onto mountpoint.

# The filesystem created when we create a loopback file specified using a filename suffix.  If
# suffix is "-xfs" then we create xfs filesystem.  If no suffix or suffix is "-btrfs" then we
# create a btrfs filesystem.

# If filepath does not end in ".img" extension, we assume filepath refers to a directory which
# we will bind-mount onto mountpath.  The size parameter is not required in this case.

FILEPATH=$1
FILESIZE=$3
MOUNTPOINT=$2

# if filepath does not end in .img then this is a directory to be bind-mounted.
if [[ "${FILEPATH:(-4)}" != ".img" ]]; then
  if ! mkdir -p "$FILEPATH" 2>/dev/null ; then
    echo "Cannot create '$FILEPATH' or is not a directory"
    exit 1
  fi
  # if filepath is located on a "user share" dereference to get real device path
  DISK=$(getfattr -n system.LOCATION --only-values --absolute-names "$FILEPATH" 2>/dev/null)
  if [[ $DISK != "" ]]; then
    FILEPATH=${FILEPATH/user/$DISK}
  fi
  mount --bind "$FILEPATH" "$MOUNTPOINT"
  exit
fi

# if filepath ends in .img then file name suffix indicates xfs or btrfs
if [[ "${FILEPATH:(-8)}" = "-xfs.img" ]]; then
  MAKEFS="mkfs.xfs -n ftype=1"
  GROWFS="xfs_growfs"
  MOUNTFS="mount -o noatime"
else
  MAKEFS="mkfs.btrfs"
  GROWFS="btrfs filesystem resize max"
  MOUNTFS="mount -o noatime,space_cache=v2"
fi

# if no image file we'll create one
if [[ ! -e "$FILEPATH" ]]; then
  echo "Creating new image file: '$FILEPATH' size: ${FILESIZE}G"
  # ensure parent path exists
  mkdir -p "$(dirname "$FILEPATH")"
  # create 0-length file so we can set NOCOW attribute on the new file and then extend size of file
  touch "$FILEPATH"
  # if image file is located on a "user share" dereference to get real device path
  DISK=$(getfattr -n system.LOCATION --only-values --absolute-names "$FILEPATH" 2>/dev/null)
  if [[ $DISK != "" ]]; then
    FILEPATH=${FILEPATH/user/$DISK}
  fi
  # setting NOCOW attribute will fail if not btrfs/xfs, but so what?
  chattr +C "$FILEPATH" 2>/dev/null
  # try to use fallocate first
  if ! fallocate -l ${FILESIZE}G "$FILEPATH" 2>/dev/null ; then
    # try truncate
    if ! truncate -s +${FILESIZE}G "$FILEPATH" 2>/dev/null ; then
      echo "failed to create image file"
      rm -f "$FILEPATH" 2>/dev/null
      exit 1
    fi
  fi
  # set ownership
  chown nobody:users "$FILEPATH"
  # create file system in the image file
  if ! $MAKEFS "$FILEPATH" ; then
    echo "failed to create file system"
    rm -f "$FILEPATH" 2>/dev/null
    exit 1
  fi
  # mount
  if ! $MOUNTFS "$FILEPATH" "$MOUNTPOINT" ; then
    echo "mount error"
    rm -f "$FILEPATH" 2>/dev/null
    exit 1
  fi
else
  # exists, check that it's a regular file
  if [[ ! -f "$FILEPATH" ]]; then
    echo "'$FILEPATH' is not a file"
    exit 1
  fi
  # check that the file is not already in-use (or being moved by 'mover')
  if /usr/local/sbin/in_use "$FILEPATH" ; then
    echo "'$FILEPATH' is in-use, cannot mount"
    exit 1
  fi
  # if image file is located on a "user share" dereference to get real device path
  DISK=$(getfattr -n system.LOCATION --only-values --absolute-names "$FILEPATH" 2>/dev/null)
  if [[ $DISK != "" ]]; then
    FILEPATH=${FILEPATH/user/$DISK}
  fi
  # (maybe) extend file size
  # try to use fallocate first, will never make the file smaller
  if ! fallocate -l ${FILESIZE}G "$FILEPATH" 2>/dev/null ; then
    # try truncate
    truncate -s \>${FILESIZE}G "$FILEPATH"
  fi
  # mount
  if ! $MOUNTFS "$FILEPATH" "$MOUNTPOINT" ; then
    echo "mount error"
    exit 1
  fi
  # (maybe) extend file system size
  $GROWFS "$MOUNTPOINT"
fi
exit 0
