Recently, Windows 10 is refusing to connect to my SMB service on my desktop Gentoo Linux machine. It basically refuses to use anything other than domain auth, and my Gentoo machine doesn't know anything about a domain, most especially my work domain (which is what the laptop wants to use -- .\username
doesn't seem to help, and giving my Gentoo machine's name as the domain doesn't help either, most likely because I don't have a domain controller running -- and I really don't need one at home).
After about 1/2 an hour of trying to convince the Windows machine to connect to SMB shares it had had no problem with a few months ago (before the last Great Windows Update), I thought "stuff it, I'll just go old-school and install VSFTPD on my Gentoo machine so I can copy some stuff off to watch during the impending load-shedding".
For those of you lucky enough not to live in South Africa, "load-shedding" is a phenomenon where the state-owned electricity provider, due to rampant corruption and mismanagement for the last 20 years, is unable to provide power to the entire country, so, for fear of rolling blackouts caused by overloading the grid, certain neighborhoods are literally turned off for two hours at a time.
Yes, it's about as much fun as it sounds. Fortunately, I have a nice laptop and plenty of media stored offline. I just have to get the media onto the laptop. Hence the fun with connections.
Now, VSFTPD (the Very Secure File Transfer Protocol Daemon) lives up to its name: it's Very Secure. Part of that security is that users who log in to the server -- even the anonymous user -- are confined to a chroot jail so that they cannot possibly access files outside of their allowed domain. This works quite well for regular users, who are confined to their home folders by default, as well as the anonymous user, who is also confined to a special place on disk. The problem comes in when you'd like to allow access to some parts of the filesystem: VSFTPD does not follow symlinks (for security reasons), so I can't just symlink in my media drives. Another plan has to be made.
The generally accepted answer is to use bind mounts, which worked, but leaving these mounts in /etc/fstab
as auto-mounting resulted in odd behavior: after booting, the mounts wouldn't be showing the same contents as the folders they were linked against. If I unmounted and remounted them, they worked just fine. I have a feeling they were being mounted before their targets were being mounted, perhaps because my original script, run via cron at @reboot
wasn't waiting for local mounts to complete.
So first attempt was to:
- set the mounts as
noauto
- alter my hacky script to do the mounting for me
- that script could take arguments like
-d 120
to delay the mounting
- that script could take arguments like
- add this as a
@reboot
line in my crontab for root (withcrontab -e
)
here it is (don't judge me! I know it's hacky! I was trying to figure out wtf was going on, and I also was getting tired of debugging this issue with only a few minutes until the power is cut, and no media to watch on my laptop (: ):
#!/bin/sh
BASE=/var/ftp/pub
LAST=""
DELAY=0
while test ! -z "$1"; do
if test -z "$LAST"; then
LAST="$1"
else
if test "$LAST" = "-d"; then
DELAY="$1"
fi
LAST=""
fi
shift
done
if test "$DELAY" != "0"; then
echo "delaying by $DELAY seconds"
sleep $DELAY
fi
for m in $BASE/*; do
umount $m &> /dev/null
if test -z "$(mount | grep $m)"; then
mount $m
else
echo "Unable to (re-)mount $m: already mounted and umount denied"
fi
done
If you're new to bash, the while loop at the top, with the shift
command near the end, is a way of cycling through arguments provided to the script until we run out:
-
shift
, like Javascript'sArray.prototype.shift
, removes the first element from the array of arguments that this script was invoked with -
test ! -z "$1"
tests the first argument in the args list to see if it's empty -- so once all arguments have been shifted off,$1
is empty (:
I figured someone else might like to have a look at the OpenRC init script:
- it's an example of how simple OpenRC scripts can be, unlike the dumpster-fire that is systemd 🔥
- all flaming aside: everything sucks in its own special way, so I'm sure OpenRC isn't perfect -- but systemd has so many "features" which make me dislike it intensely
- it's an example of
- line-by-line file reading with bash
- some of the simplest, but very powerful GNU utils
awk
-
grep
#!/sbin/openrc-run
depend() {
need localmount
}
start() {
for MOUNT_POINT in $(find_bind_mounts); do
do_mount $MOUNT_POINT
done
}
stop() {
for MOUNT_POINT in $(find_bind_mounts); do
do_umount $MOUNT_POINT
done
}
do_mount() {
if test -z "$(mount | grep -E "\s$1\s")"; then
echo " mount $1"
mount $1
else
echo " already mounted: $1"
fi
}
do_umount() {
if test -z "$(mount | grep -E "\s$1\s")"; then
echo " already unmounted: $1"
else
echo " umount $1"
umount $1
fi
}
find_bind_mounts() {
while read LINE; do
OPTS="$(echo $LINE | awk '{print $4}' | grep -E '(,)?bind(,)?')"
if test -z "$OPTS"; then
continue
fi
echo $LINE | awk '{print $2}'
done < /etc/fstab
}
Some explanations:
- This script should be run with
/sbin/openrc-run
(see the hash-bang line) because that stub provides a lot of base functionality:- provides default
start
,stop
andstatus
functions - understands how to get dependencies from
depend()
- provides default
- we declare services that this one depends on in the
depend
function, one per-line. Simple! - OpenRC scripts can be as short as just a few lines long because there are default implementations for
start
,stop
andstatus
(see here: https://github.com/OpenRC/openrc/blob/master/service-script-guide.md- in this case though, I'm not starting a daemon process: I want to do some work at startup and some more at shutdown, so I implement
start
andstop
myself
- in this case though, I'm not starting a daemon process: I want to do some work at startup and some more at shutdown, so I implement
- from
start()
andstop()
-
while read LINE; do echo $LINE; done < some_file
is how you can work over a file line-by-line in bash. Now you know (:
-
-
from
find_bind_mounts()
-
awk '{print $1}'
prints the first field found on a line -- this is a really simple way to get a field out of a line which is broken up by whitespace -- like fstab entries! Field 4 in /etc/fstab describes the mount options... -
so bind mounts will have
bind
in there somewhere. Now we need to invokegrep
with-E
to use Extended syntax and the regex I'm using searches for the word "bind" preceded either by whitespace or a comma, and followed either by whitespace or a comma, so matches would be, eg:auto,bind
,bind,noatime,noauto
ornoatime,bind,noauto
, remembering that the mount options field is not the last field, so there would be whitespace before and after it.- we get lines like:
/mnt/monolith /var/ftp/pub/monolith none defaults,bind,noauto 0 0
-
- once we have a line describing a bind mount, we select out the second field with
awk
, since this is the directory into which we would be mounting, so the shorthand syntax ofmount <mount-point>
would work- from the above line, we would get
/mnt/monolith
- from the above line, we would get
- from
do_mount()
anddo_umount()
- just to be safe, we check if the mount point retrieved from
/etc/fstab
is already mounted withmount | grep -E "\s$MOUNT_POINT\s"
, which would look for that mount-point by name, surrounded by whitespace, from themount
command -
mount
would print lines like:/dev/sdh1 on /mnt/monolith type fuseblk (rw,nosuid,nodev,noexec,noatime,user_id=0,group_id=0,default_permissions,allow_other,blksize=4096)
and the second field in that line is the mount-point - if we're starting up and the mount-point is already mounted, we don't do it again (a bind mount can be done again onto the same directory!)
- if we're shutting down and the mount-point is not mounted, we also don't attempt to unmount it
- just to be safe, we check if the mount point retrieved from
And whilst booting, we get the following output from OpenRC:
mount-binds | mount /var/ftp/pub/monolith
mount-binds | mount /var/ftp/pub/piggy
mount-binds | mount /var/ftp/pub/dump
[ ok ]
And that's about all (:
Top comments (1)
If you are looking for a solution for delayed bind in fstab when using ZFS (and you don't want to change the default zfs dataset mountpoint) and you are using OpenRC (because systemd has a solution in the form x-systemd.requires=zfs-mount.service), then don't forget add dependency service to openrc for this:
Thanks to the author!