0

I'm running Arch Linux and systemd.

In /etc/fstab I have something like this:

LABEL=XYZ       /mypath       vfat    noauto,[...]
/mypath/main    /newplace     none    bind,noauto    0 0

I currently have to mount /newplace with two commands:

mount /mypath
mount /newplace

I also have to umount both.

I need to simplify that to just one mount (or umount) command:

mount /newplace

I need to use the exact mount (and related umount) command shown immediately above because of an existing script. To clarify:

The single command mount /newplace should first mount /mypath, then mount /newplace and /mypath should remain mounted.

The command umount /newplace should first umount /newplace and then also umount /mypath.

I can change /etc/fstab and some other details of my device. But I cannot change the script that calls mount /newplace. I also cannot automount /newplace because it needs to usually remain unmounted except when the script is run.

I have been reading about recursive bind mounts, shared, private, slave and other mount options, but I have not seen a way to achieve what I'm seeking.

Update: in response to comments, this is obviously not a recursive mount, so I called it a "prerequisite" mount. I hope the term is appropriate. I thought about the term "reverse recursive mount" but that seems very bad. I am taking "prerequisite" to mean something that must come before and that provides the continuing foundation for the requisite. Generally, the prerequisite cannot be forgotten or removed while continuing with the requisite.

In this case, /mypath (prerequisite) and /newplace (requisite) both remain mounted when /newplace is mounted, and they would both be unmounted (probably in reverse order of course) when calling umount /newplace.

The ideal solution will use either systemd, Python 3 or Xonsh. (A Bash script is also acceptable. I do not have zsh or other shells installed.)

4
  • Are you able to automount /mypath? Nov 6, 2021 at 7:37
  • @KeithLyons: no, /mypath should not be automounted. If I could do that, it would solve the entire issue.
    – MountainX
    Nov 7, 2021 at 21:47
  • It's neither "prerequisite" nor "recursive". You can unmount /mypath immediately after having bind-mounted /mypath/main (that's what the script from the answer does, btw). /mypath doesn't have to stay mounted while /newplace is mounted, and ("of course") they don't have to be unmounted in reverse order.
    – user313992
    Nov 8, 2021 at 0:04
  • @user499944: RE: /mypath doesn't have to stay mounted while /newplace is mounted. Yes, my requirement is that it has to stay mounted.
    – MountainX
    Nov 8, 2021 at 4:31

1 Answer 1

1

If you do want the "main" subdir of the FS at LABEL=XYZ to be mounted on /newplace, without particularly needing the whole FS to be mounted at /mypath, you could add a line like:

LABEL=XYZ /newplace subdir subdir=main,srctype=vfat,noauto,... 0 0

And create the /sbin/mount.subdir helper (called by mount /newplace, not meant to be called directly) as a script like:

#! /bin/zsh -p
(( EUID == 0 )) || exec sudo -- "$0" "$@"
PATH=/bin:/sbin:/usr/bin:/usr/sbin

# mount -t subdir -o subdir=foo -o otheroption source /dest calls us
# as mount.subdir source /dest -o rw,subdir=foo,otheroption
dev=${1?} dest=${2?} opts=( ${(s[,])4?} )

# extract mandatory subdir option
(( i = $opts[(I)subdir=*] )) || exit
subdir=${opts[i]#*=}
opts[i]=()

# extract optional srctype option
if (( i = $opts[(I)srctype=*] )); then
  type=(-t "$opts[i]")
  opts[i]=()
else
  type=()
fi

tmpdir=$(mktemp -d) || exit

mounted()true
if
  mount "$type[@]" -o "${(j[,])opts}" -- "$dev" "$tmpdir"
then
  mount --bind -- "$tmpdir/$subdir" "$dest" || mounted()false
  umount -- "$tmpdir"
fi && rmdir -- "$tmpdir" && mounted

Which mounts the whole FS in a tmpdir, bind-mounts the subdir into the destination and then unmounts and removes the tmpdir.


If, as clarified in your later edit, you do want both /newplace and /mypath to be mounted and /mypath is not just some intermediary step for the end goal of having a subdir of the FS mounted in /myplace, you can take a similar approach, and that becomes easier as you just need the helper to mount the prerequisite FS(s) instead:

LABEL=XYZ /mypath vfat noauto,[...]
/myplace/main /newplace prereq prereq=/myplace,noauto,... 0 0

And create the /sbin/mount.prereq helper as a script like:

#! /bin/zsh -p
(( EUID == 0 )) || exec sudo -- "$0" "$@"
PATH=/bin:/sbin:/usr/bin:/usr/sbin

src=${1?} dst=${2?} opts=( ${(s[,])4?} )

# extract prereq options
for prereq in ${(M)opts:#prereq=*}; do
  prereq=${prereq#*=}
  mountpoint -q -- $prereq ||
    mount -- $prereq ||
    exit
done
opts=(${opts:#prereq=*})

exec mount --bind -o "${(j[,])opts}" -- "$src" "$dst"

Note that /mypath is not automatically unmounted after /newplace is unmounted.

The same with POSIX shell syntax (so should even work in bash) could look like:

#! /bin/sh -
[ "$(id -u)" -eq 0 ] || exec sudo -- "$0" "$@"
PATH=/bin:/sbin:/usr/bin:/usr/sbin

src="${1?}" dst="${2?}"

set -o noglob
IFS=,
set -- ${4?}

# extract prereq options
for opt do
  case $opt in
    (prereq=*)
      prereq=${opt#*=}
      mountpoint -q -- "$prereq" ||
        mount -- "$prereq" ||
        exit
      ;;
    (*)
      set -- "$@" "$opt"
  esac
  shift
done
opts="$*"

exec mount --bind -o "$opts" -- "$src" "$dst"

(untested).

If you can't edit your script, you could possibly instrument it. For instance, if it's written in bash, call it as:

bash -c '
  mount() {
    if [[ $1 = "/newplace" ]]; then
      command mount /mypath || return
    fi
    command mount "$@"
  }
  export -f mount
  exec "$0" "$@"' your-script its args

Or if it's written in zsh, add to ~/.zshenv (or /some/dir/.zshenv and set ZDOTDIR=/some/dir for that script):

if [[ $ZSH_SCRIPT:P = /path/to/your-script ]]; then
  mount() {
    if [[ $1 = /newplace ]]; then
      command mount /mypath || return
    fi
    command mount "$@"
  }
fi

Or for any shell, add a mount script in /some/dir that does the same:

#! /bin/sh -
if [ "$1" = /newplace ]; then
  /bin/mount /mypath || exit
fi
exec /bin/mount "$@"

And call your script as:

PATH="/some/dir:$PATH" your-script its args
15
  • 1
    Thank you, but I was hoping for a much simpler solution. Maybe there is no solution like that...
    – MountainX
    Nov 6, 2021 at 18:45
  • @MountainX: Your comment suggests that you should edit your question to define what exactly you mean by much simpler. Does it mean an acceptable answer contains no scripts... or perhaps scripts with no more than 6 lines, etc? I thought your question was interesting & had begun fiddling around with a systemd solution - but perhaps that's not simple enough??
    – Seamus
    Nov 7, 2021 at 3:14
  • @Seamus - I will attempt to clarify my question. However, I can say that a systemd solution would be very interesting. I don't see why that would not meet my needs. Other approaches would be OK too, so I probably need to think more deeply about how to clarify my question.
    – MountainX
    Nov 7, 2021 at 21:24
  • @MountainX: Here are some ideas that may help you get started w/ a systemd solution. IIRC, systemd actually mounts drives by reading /etc/fstab, but it doesn't mount them in a predictable order unless it is coaxed to do so. This tutorial may be useful fpr learning how to create your /etc/fstab entries such that systemd mounts them in the desired order. N.B. there may be distro-dependent differences that require adjustments. Arch has excellent docs, so check there first.
    – Seamus
    Nov 7, 2021 at 21:52
  • 1
    @StéphaneChazelas - I'm slowly digesting this answer. Now that I understand it better, I see its excellence. I am going to try to alter my approach and use your first answer. However, I need it in Python, Xonsh or bash. If you (or anyone) wants to provide an alternative implementation in one of those languages, I'll accept that answer. I'll also accept a systemd solution, although this concept is really interesting and I now see it is quite good. Sorry it took me a while to catch on.
    – MountainX
    Nov 12, 2021 at 6:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .