Wednesday, December 16, 2009

Unmounting --rbind mounts in Linux

My latest confusion with using mount and its handy --bind and --rbind options is with unmounting such mounts. It should have been as simple as:

umount /my/mount/point

But when executing such a command I end up with:
umount: /my/mount/point: device is busy.
(In some cases useful info about processes that use
the device is found by lsof(8) or fuser(1))

So the error had me baffled as the mount shouldn't have been in use. I initially thought some process was grabbing a hold of it and causing me some headache. So I used lsof and fuser command to identify who was tying my mount up. It turned out that there were lots of processes tying it up. Here is a more specific example that I was working with:
mount --rbind /home /export/home

I used --rbind because /home contains a couple of mount points that I also wanted to export as part of binding /home to /export/home. Seemed simple enough. Well, after using the umount command and getting the aforementioned error I used lsof and fuser to see that just about every process from /home showed up as using both /home and /export/home.


So, I did a bit more research and found this RFE (although old): https://bugzilla.redhat.com/show_bug.cgi?id=194342


Basically it was talking about recursive binds not being able to be unmounted due to mounts inside the mount having to be unmounted first. So I immediately ran:

umount /export/home/share1
umount /export/home/share2

Yes! No error! So, now:
umount /export/home

No! The error is still there. I couldn't figure this out. I banged my head against the keyboard a few times and then decided to analyse the workaround command provided in the RFE's comments:
mount | awk '{print $3}' | grep ^/dev | sed -e s,/dev,/tmp/dev, | xargs umount
umount /tmp/dev

I plugged /home in for /dev and /export/home for /tmp/dev and executed without the xargs command on the end (I wanted to see what it was going to output):
/export/home
/export/home/share1
/export/home/share2
/export/home/user01/.gvfs

Well wouldn't you know it! I forgot about gvfs. Anyway, that prompted me to throw a quick script together. It is very raw and might not be 100% correct but it gets the job done for me and makes it so I don't have to go back and figure out where all my child-mounts are.


#!/bin/sh

MOUNTS=`mount`
OFS=$IFS

IFS=$'\n'
if [ -d "$1" ]; then
OLDWD="`pwd`"
cd "$1"
rootMount="`pwd`"
cd "$OLDWD"
else
rootMount="${1%/}"
fi

for mount in $MOUNTS; do
umountTgt=`echo $mount | awk '{print $3}'`
if [ "$umountTgt" = "$rootMount" ]; then
mountSrc=`echo $mount | awk '{print $1}'`
umountList=$umountList`mount | awk '{print $3}' | grep ^$mountSrc | sed -e s,$mountSrc,$rootMount, `$'\n'
for umount in $umountList; do
if [ "$umount" != "$rootMount" ]; then
echo "Unmounting $umount..."
/bin/umount "$umount"
fi
done
fi
done

IFS=$OFS

echo "Unmounting $rootMount..."
/bin/umount "$rootMount"