How to make read-only 'virtual' exFAT directories for FTP users on Synology NAS running DSM 6

Update, December 2019: installing third-party scripts or package managers which run on boot may overwrite the file /etc/rc.local which I initially recommended using. I've revised this article to recommend a better autostart script directory; feel free to borrow the simple start/stop script I included at the end of this post.

I recently purchased a Synology NAS running DSM 6, and sharing directories via FTP is easy. In Control Panel, make sure the Shared Folder is defined, then using File Station, define access permissions (read, write, execute) for each group or user.

If you want an additional user for FTP access, you make your user (or make a group then add your user to that) then Allow access to the FTP application inside Control Panel -> Users or Groups. The permissions are inherited, UNIX style, to effectively restrict rights over folders and their files.

So far, so simple; this works great for everything on the NAS' internal storage, because by default it uses EXT4 filesystem which supports file & directory permissions and ACLs. On the terminal, a plus symbol at the end of an ls directory listing denotes the file or directory has additional ACLs applied, which can overrule standard UNIX permissions.

However, on any external drives connected to the Synology, for example a large USB3 drive for temporary storage of additional material, those drives may use file systems other than EXT4 so they're accessible by, say, Windows PCs. In this case, given we're probably also dealing with very large files, exFAT is a sensible choice - and the Synology does support exFAT, albeit there's a long story about that. tl;dr - pay $4 and just get the official exFAT Access package from Synology through the Package Manager, it's zero-hassle and has full read/write support. More info on supported external devices here.

One thing exFAT volumes lack when used through the Synology is support for any UNIX file and directory permissions. Normally that's acceptable, but if you're sharing files to other users, either via NFS, SMB or FTP, you may wish to use permissions to prevent accidental deletion - and on an exFAT volume, this means you can't.

But we can do read-only access with exFAT! It just requires some creative thinking...

I wanted to share some files with a group of users which were in a directory on some USB media. By default, the Synology mounts USB drives using its own scripted methods. In the DSM web interface, in the File Station app you see them as "usbshare1" and "usbshare2". In the filesystem (via SSH or telnet - but you're not using telnet, right?) it's /volumeUSB1 and /volumeUSB2 - if there's multiple partitions, it uses the /volumeUSB1/usbshare1-1, /volumeUSB1/usbshare1-2 filesystem structure.

Sidenote - you can alias 'usbshare1' and 'usbshare2' to better names by just renaming them inside File Station. The Synology will remember this by saving the name mapping in the file /usr/syno/etc/usbno_guid.map. However, sometimes DSM will also get confused, particularly if you're unplugging and replugging drives often, but you can easily fix it.

If I share a subdirectory via FTP, annoyingly the entire folder structure is shown, including directories the user is not permitted to access (a feature of UNIX permissions). Using traditional symlinks (aka soft links) also doesn't work because they're displayed as shortcuts in an FTP session, and the client cannot ever resolve that link to the correct destination.

As I don't wish to expose all potential directories in a folder, the workaround is to use a 'virtual' folder which is actually a mountpoint, combining this with a bind mount:

  1. On an SSH session, create a directory in the root of the DSM storage, e.g. /ftp-shared
  2. In File Station (in the DSM web UI) grant the shared FTP user full access rights.
  3. Create a subdirectory inside it, e.g. /volume1/ftp-shared/catvideos.
  4. Work out what the file system path to the desired folder on the USB drive is (e.g. /volumeUSB1/usbshare1-2/catvideos/, make a note of it).
  5. At the command prompt, (your user account will need DSM admin privileges for this), execute the following: sudo mount --bind "/volumeUSB1/usbshare1-2/catvideos/" "/volume1/ftp-shared/catvideos/"
    • This creates a bind mount of the location to the target mount point. If you ls the /volume1/ftp-shared folder afterwards, you will notice it's now displayed as highlighted green with full permissions (drwxrwxrwx).
  6. The 'virtual' folder is now accessible, but because the source folder's file system is exFAT, it's wide open with no permissions. What you need to do now is 'remount' that mount as read only. Do this by executing: sudo mount -o remount,ro "/volume1/ftp-shared/catvideos/"
  7. Although the file permissions won't change when you ls the folder, you will now be unable to modify or delete the files when you access them via the virtual folder. (The 'bound' mount).
  8. To make this setting permanent and reapply on reboot, you have a couple of options:
    1. If you are using 'vanilla' DSM with no additional packages, add those two lines to the /etc/rc.local file - nano /etc/rc.local to open it in the nano editor.
    2. If you are using Optware, Entware or any other package manager, they commonly use /etc/rc.local to run, and overwrite whatever contents are in rc.local. I lost my original changes when I installed Entware.
      Instead, make a bash script with a start and stop action, place your mount commands inside that script. and save it in the /usr/syno/etc.defaults/rc.sysv/ directory. An example script is included at the end of this article.
  9. FTP to the Synology and try to access the folder - because you created the 'ftp-shared' directory at the root, it will appear after logging in, and the virtual mount directory will show beneath that. Done! You should have read-only access to the files in that directory structure.

If you need to remove the USB drive, it's wise to unmount this folder (umount /volume1/ftp-shared/catvideos) before unplugging the USB drive, otherwise it may fail to cleanly dismount and show "target is busy" errors.

Occasionally you may need to dismount the target folder more aggressively if something is 'stuck' on a file inside; with standard Linux you can use various methods to identify what is holding the mount open. On Synology devices with their reduced busybox shell Linux build, tools like lsof aren't immediately available, so you can either creatively work around it or install third party package managers to add the tools back to your DSM.

Beware that some aggressive dismount methods can sometimes cause data loss or NFS access issues.

Some other uses for bind mounts include mapping directories in one Synology "volume" into a virtual directory in another (e.g., if you expand the storage with more disks and make a second volume, but you decide for DLNA or media server purposes that you need files in one logical location).

For much more detail on bind mounts and what you can do with them, check this wonderfully written Unix Stack Exchange answer.

Let me know in the comments if you have any problems with this method, I use it without any problems on my Synology 918+ running DSM 6, resharing multiple virtual directories to multiple mount points.

Sample script for user-level autostart script at boot

This is a very quick example script for /usr/local/etc/rc.d (as it's inadvisable to use the /etc/rc.local method). You'll need to sudo to root at a shell prompt to write/modify in this directory. Use sudo -i on a Synology to get a root prompt, provided your user account is in the Administrators group in DSM.

The script has start and stop functions, as required for startup scripts, but only the start function does anything as that's all I required.

As long as it's a viable script (has a shebang, has valid syntax and is CHMODed correctly, e.g. chmod 700 filename.sh) this should run.

Remember, to use the bind mount method, the destination directory must exist prior to running the command. Otherwise mount will literally have no place to mount the source directory.

#!/bin/sh
# customcommands.sh - a startup script for doing stuff.
# this lives in /usr/local/etc/rc.d
# Recommended to CHMOD to 700

case $1 in
start)
# start stuff here

echo "Executing start tasks."
# example: sudo mount --bind "/volume1/source/path" "/volume1/destination/path"
sudo mount --bind "/volumeUSB1/usbshare1-2/cats/photos" "/volume1/ftphomes/crazycatlady/catphotos" 
sudo mount -o remount,ro "/volume1/ftphomes/crazycatlady/catphotos"
# mount -o remount,ro remounts an existing mount as read-only, irrespective of source filesystem permissions
# This is deal for ExFAT sources as they don't support UNIX permissions.

# Remember, you'll need to unmount all bind mounts first to eject a USB disk. Perhaps a good use for the 'stop' action.

;;
stop)
# stop stuff here

echo "Executing stop tasks."
# Section will be empty unless you want to perform any tasks before rebooting (unmounting anything?).
;;

*)
     echo "Usage: $0 [start|stop]"
;;
esac

You may also be able to have that script live in /usr/syno/etc.defaults/rc.sysv/ , however that directory is a default scripts repository used by DSM 6 which other developers also seem to use. On my DS918+, I have loads of at-boot scripts located in the /usr/syno/etc.defaults/rc.sysv directory, however /usr/local/etc/rc.d is the best-practice location to store these files.

Irrespective of wherever you save your scripts, ALWAYS make sure you keep a backup. People have remarked about custom files like these sometimes being lost during a DSM upgrade, although the unit should backup and restore the files itself as part of the process.

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via email. You can also subscribe without commenting.

I