[HOW TO] Binding an existing folder under a Samba share (at startup)

Share your awesome tips and tricks here.

Moderator: Lillian.W@AST

Post Reply
ndl101
Posts: 57
youtube meble na wymiar Warszawa
Joined: Sun Jul 11, 2021 4:32 pm

[HOW TO] Binding an existing folder under a Samba share (at startup)

Post by ndl101 »


Preface

As ADM's Samba "implementation" does not allow sharing existing folders and manually editing the configuration file is not an option either, both which in my opinion is both limited and flawed (I have made feature request regarding this), I found myself needing to "add" pre-existing video data located on /volume2/Video existing share on /volume1/Video. The data amount alone made in unfeasible to move it into the new Samba share as it would not fit on /volume1. I was attempted to modify the the share in the configuration file but this did not solve it and symlinking did not work as it needs to be enabled in the configuration as well. Luckily, it is possible to create bind mounts, which essentially is making a resource mounted in one location available in another location as well. Creating a bind mount is ephemeral, lasting only until the NAS NAS is powered off, so an init script is needed as well.

DISCALIMER: Deleting the Samba share via ADM while having a bind mount active under it, will also delete the content as is currently the case when deleting a Samba share via ADM.

The syntax for creating a bind mount is:

Code: Select all

$ mount --bind /source /destination

Verifying the approach
  1. I created a Samba share under /volume1/Video
  2. I ssh'ed to the NAS and from the terminal I executed:

    Code: Select all

    $ sudo mount --bind /volume1/Video /volume2/Video
    
  3. and to verify the contents were the same:

    Code: Select all

    $ diff -r /volume1/Video /volume2/Video
    

Persisting the bind mount

The easiest way to persist the bind mount is via an init script. I probably over-engineered this but thought I would make a solution generic enough for reuse as a template. The template takes advantage of the builtin utility "/usr/bin/confutil" which is used to write statements related to a service's state to e.g. "/var/log/bootup.log". I have posted the template in the [HOW TO] Run your own script on system startup thread.
  1. ensure the target Samba share is created and empty (if the target share is not empty it will not delete the data just hide it until the bind id unmounted)
  2. update the MOUNT_SOURCE and MOUNT_TARGET variables in the script to reflect your use case.
  3. scp the script to your NAS

    Code: Select all

    $ scp S21bind-mount-video-share <nas-admin-username>@<your-nas-ip>:/usr/local/etc/init.d/S21bind-mount-video-share
    
  4. ssh to your NAS and make the make it executable

    Code: Select all

    $ sudo chmod +x /usr/local/etc/init.d/S21bind-mount-video-share
    
  5. enable the bind mount

    Code: Select all

    $ sudo /usr/local/etc/init.d/S21bind-mount-video-share start
    
  6. ensure the expected content is available
  7. profit

The init script

Code: Select all

#!/bin/sh
#
# Author: ndl101/Nicolas Damgaard Larsen
# initializes custom bind mount for video data under a shared folder
#

SERVICE_NAME="bind-mount-video-share"
LOG_FILE="/var/log/custom-services.log"

MOUNT_SOURCE="/volume2/Video"
MOUNT_TARGET="/volume1/Video"

updateLog() {
  confUtil="/usr/bin/confutil"
  ( [ -f $confUtil ] && [ "$#" -eq 3 ] ) && {
    echo "updating log"
    /usr/bin/confutil "-$1" $LOG_FILE $SERVICE_NAME "$2" "$3"
  }

}

die() {
  [ ! -z "$1" ] && echo "$1"
  updateLog "set" "progress" "failed"
  exit 1
}

isMounted() {
  [ -z "$1" ] && {
    echo "mount point not provided"
    return 1
  }
  $(mountpoint -q "$1")
}

doStart() {
  [ ! -d "$MOUNT_SOURCE" ] && {
    echo "mount source was not found, exiting..."
    exit 1
    }

  [ ! -d "$MOUNT_TARGET" ] && {
    echo "mount target was not found, exiting..."
    exit 1
    }

  isMounted "$MOUNT_TARGET" && {
    echo "mount bind already exists, exiting..."
    exit 1
    }

  mount --bind "$MOUNT_SOURCE" "$MOUNT_TARGET" || {
    echo "failed to mount bind $MOUNT_SOURCE -> $MOUNT_TARGET"
    exit 1
  }
  echo "mounted bind $MOUNT_SOURCE -> $MOUNT_TARGET successfully"
}

doStop() {
  isMounted "$MOUNT_TARGET" || {
    echo "mount bind was not mounted, skipping umount"
    exit 0
  }
  umount "$MOUNT_TARGET" || die "failed to unmount bind, exiting..."
}

doReload() {
  # The shell does not allow an empty function definition so I just added a
  # return statement. I left the definition for consistency but alternatively
  # delete this definition and remove the call to it below.
  return
}

case "$1" in

  ismounted)
    isMounted "$MOUNT_TARGET" && {
      echo "mount bind is mounted"
    } || {
      echo "mount bind is not mounted"
    }
    ;;
  
  start)
    echo "Starting $SERVICE_NAME"
    doStart
    updateLog "set" "progress" "started"
    ;;

  stop)
    echo "Stopping $SERVICE_NAME"
    doStop
    updateLog "set" "progress" "stopped"
    ;;

  restart)
    "$0" stop
    "$0" start
    ;;

  reload)
    echo "reloading $SERVICE_NAME"
    updateLog "set" "reloading" "in-progress"
    doReload
    updateLog "del" "reloading"
    ;;
  
  *)
    echo "Usage: $0 {start|stop|restart}"
    exit 1
    ;;
  
esac

exit $?

I made it long as I lacked the time to make it short.

---
Help to self-help:
How to ask (good) questions in a forum
---
General information
Location: Denmark
OS: Ubuntu 20.04
NAS: Lockerstor 4 (AS6604T)
Post Reply

Return to “Tips & Tricks”