Support for encrypted drives?

Interestingly, the diskmount-osmc package ships a file called 60-cdrom_id.rules which uses the following logic to detect optical media:

ACTION=="remove", GOTO="cdrom_end"
SUBSYSTEM!="block", GOTO="cdrom_end"
KERNEL!="sr[0-9]*|xvd*", GOTO="cdrom_end"
ENV{DEVTYPE}!="disk", GOTO="cdrom_end"

# unconditionally tag device as CDROM
...
LABEL="cdrom_end"

So, the above section can be replaced with the following udev rule:

ACTION=="remove", GOTO="osmc_disk_end"
SUBSYSTEM!="block", GOTO="osmc_disk_end"
ENV{DEVTYPE}!="disk", GOTO="osmc_disk_end"

# optical disk: set spin-down time to 20min
KERNEL=="sr[0-9]*|xvd*", RUN+="/sbin/hdparm -S 240 $env{DEVNAME}", GOTO="osmc_disk_end"                                            

# non-optical disk: set read-ahead amount to 1MB
RUN+="/bin/sh -c 'echo 1024 > /sys/dev/block/$major:$minor/queue/read_ahead_kb'"

LABEL="osmc_disk_end"

@DBMandrake - do you want to take a look at this one?

The rest of auto-mounting can also be implemented with udev rules and one or two helper scripts that make use of udisksctl from udisks2. This way the udisks-osmc and udisks-glue-osmc packages (both obsolete) can be dropped and replaced with udisks2.

Holler if you want me to proceed. Sadly, I’ve just burnt my RPi4 which I was using for testing, and I either need to figure out how to fix it or find another box I can (ab)use for testing… :unamused:

Yes, I’d like to move forward. UDisks 2 could work.

We could also look at udevil. LibreELEC use udevil but they run as everything as root – which means they don’t have the permissions issues we have. I don’t want to do this.

There are some pitfalls with UDisks1. For example sometimes it does not populate filesystem promptly enough and the mount will fail. Although this is rare and reports on this seem to have calmed down for a few years.

I have some notes from what I wanted in an improved auto-mounter. Keep in mind that I wrote these notes in 2019 and hadn’t had a chance to work out how to implement it yet. If UDisks2 can do this, then it would be great.

Some of the ideas I had:

Detecting disk events

  • Mount disk if not in fstab
  • Mount if disk is not in a blacklist.d path (allow some mounts to be ignored)
  • Inform Kodi that the drive has been mounted (Kodi supports UDisks2 subscription via DBus polling so not much should be needed if you take this path)
  • If the device is not mounted on /, configure a spindown

Mounting rules

  • Each filesystem has mounting rules under a directory like /usr/osmc/automounter/fs/X.conf, where X.conf is the name of filesystem, i.e. ext4.conf. Each config file provides configuration of:
    • Mount-as-root: true/false. Useful for some drives with permissions issues
    • Hide-directories: allow some directories to be hidden like .DS_STORE etc
    • Filesystem options: allows custom fs options
    • Mount command: the command to actually mount the filesystem

Customisation

  • Support this being overwritten on a line by line basis by sourcing a downstream /etc/osmc/prefs.d/automounter/fs/X.conf
  • Add postmount.d, postrm.d rules in a similar fashion which have a standard set of rules such as net use (to Samba share); adjust read-ahead and net remove in rm.d (checking for presence of Samba server). Supplemental postmount.d and postrm.d rules which run commands after the above and are also in /etc/osmc/prefs.d/fs/post{mount|rm}.d. You could even use your postmounts.d to handle LUKS for you. Perhaps we could also support postmounts.d/UUID-*.conf or postmounts.d/LABEL.conf as well as general 000-do-foo-task.conf.
  • Add blacklist-drives.d in prefs directory to allow blacklisting of mounting based on label or UUID
  • Add diskidle.d which lets you override disk idling based on name of mountpoint (label) or UUID.

Do you reckon we could adapt UDisks2 to handle this?

Sam

I think so.

But, instead of having per-FS and per-drive individual config files, I would suggest using INI-style config files with sections for each FS/drive. This way we can offload parsing and overrides to the configparser module. The files can be, like you said, in:

/usr/osmc/automount.d/*.conf
/etc/osmc/automount.d/*.conf

with files in /etc/ taking precedence.

Any options in the [DEFAULT] section would apply to every device. The remaining section names would consist of comma-separated key/value pairs that are matched against device properties provided by udev.

The list of device properties can be obtained with udevadm info /dev/blah.

Here is an example of what it could look like (I’ve stripped ID_ prefix from the property names):

[DEFAULT]    
# options common to ALL devices go here

# mount and unmount filesystems
[FS_USAGE=filesystem]
mount = true
post_mount = /usr/osmc/bin/samba-helper add $DEVPATH
unmount = true
post_unmount = /usr/osmc/bin/samba-helper remove $DEVPATH

# use different options for optical media
[FS_USAGE=filesystem,CDROM]
mount_opts = uid=1000,gid=1000,mode=0444,dmode=0555

# set spin-down time for optical media
[DEVTYPE=disk,CDROM]
add = /sbin/hdparm -S 1024 $DEVPATH

# set read-ahead for non-optical media
[DEVTYPE=disk,!CDROM]
add = /bin/sh -c "echo 1024 > /sys/dev/block/$MAJOR:$MINOR/queue/read_ahead_kb"

# don't mount this drive
[FS_UUID=996D-BDEB]
mount = false

If all goes well, I should have something working by tomorrow.

This (from experience) can be racy and has caused us problems in the past. Checking as soon as a udev event comes in doesn’t guarantee that you’ll have all of the properties populated yet. It seems to be some race condition in the kernel.

Make it /etc/osmc/prefs.d as a prefix if possible (see existing architecture)

Excited to see what you come up with.

Yep. But that’s what udisks-glue, udevil and other packages do under the hood (except they do it through DBus), so we shouldn’t have more problems than they do.

Will do.

FWIW, I’ve found a few more typos in the below code:

  • line 83: ${thname,,} => ${othname,,}
  • line 96: $onothname => $unothname

Except it never does. :slight_smile: I got it working… mostly… using a few udev rules and about a page worth of python. But, it turns out it’s no longer possible to mount devices from within udev events.

So, I am going to rewrite it as a systemd service using udisks2 dbus API instead. The basic idea will stay the same. I am thinking of making it into a standalone Debian package, as I want to use it for other things as well (outside of OSMC).

@sam_nazarko since this will be it’s own package, I am thinking the config files should go into /usr/share/automount/*.conf and /etc/automount/*.conf. The package will provide a vary basic config and all OSMC-specific stuff can be provided by the diskmount-osmc package.

1 Like

Don’t worry about packaging yet. That’s easy to do. You’ll probably get stuck on things like deprecating the old packages which is something we can easily build up and test to make sure that udisks2 and scripts are working in a truly isolated environment.

The hard part will be making it all work, not the packaging per se.

Appreciate the solid efforts.

1 Like

@sam_nazarko I got it working. Ran into a few corner cases, so took a bit longer than expected.

I ended up creating a separate package called automount, which you can find here:

It is not directly tied to OSMC, and all the glue to make it work for OSMC can be provided by the existing diskmount-osmc package. Here are the steps to test it:

  1. Disable the existing udisks and udisks-glue daemons:

    sudo systemctl mask --now udisks udisks-glue
    
  2. OPTIONAL: Reboot the box and make sure auto-mounting is now disabled.

  3. Download and install the automount package (it will pull in udisks2 as a dependency):

    wget https://github.com/armbian/automount/releases/download/v0.4/automount_0.4_all.deb
    sudo apt install ./automount_0.4_all.deb
    
  4. By default udisks2 mounts drives in /media/<user>/. To make it mount in /media/, create /usr/lib/udev/rules.d/99-osmc-diskmount.conf with the following contents:

    ENV{ID_FS_USAGE}=="filesystem|crypto|other", ENV{UDISKS_FILESYSTEM_SHARED}="1"
    
  5. Enable polkit rules for udisks2 by adding (or replacing) the following to /lib/polkit-1/localauthority/90-mandatory.d/osmc-diskmount.pkla:

    [Mounting, checking, etc. of internal drives]
    Identity=unix-user:osmc
    Action=org.freedesktop.udisks2.filesystem-mount-system;org.freedesktop.udisks2.encrypted-unlock-system;org.freedesktop.udisks2.filesystem-fstab;org.freedesktop.udisks2.filesystem-mount-other-seat
    ResultAny=yes
    ResultActive=yes
    
  6. Create /etc/automount/50-osmc-diskmount.conf with the following contents:

    # 50-osmc-diskmount.conf
    
    [ID_FS_USAGE=filesystem]
    auto-mount = yes
    mount-as = osmc:osmc
    on-mount = /usr/osmc/bin/samba-helper add ${DEVNAME} ${ID_TYPE} ${MOUNT_POINT}
    on-unmount = /usr/osmc/bin/samba-helper remove ${DEVNAME} ${ID_TYPE} ${MOUNT_POINT}
    
    # set spin-down time for optical media
    [DEVTYPE=disk ID_TYPE=cd]
    on-add = /sbin/hdparm -S 240 ${DEVNAME}
    
    # set read-ahead for non-optical media
    [DEVTYPE=disk ID_TYPE=disk]
    on-add = /bin/sh -c "echo 1024 > /sys/dev/block/${MAJOR}:${MINOR}/queue/read_ahead_kb"
    
  7. Create /usr/osmc/bin/samba-helper with the following contents:

    #!/bin/bash
    
    action="$1" dev_name="$2" dev_type="$3" mount_point="$4"
    
    if [[ "$action" == "add" ]]; then
        [[ "$dev_type" == "disk" ]] && chmod a+rwx "$mount_point" 2>/dev/null
    
        if [[ -x /usr/bin/net ]] && /bin/systemctl is-enabled samba > /dev/null 2>&1; then
            share=$(basename "$mount_point")
            id -u "${share,,}" > /dev/null 2>&1 && share+="-auto"
    
            for n in $(seq 0 5 120); do
                /usr/bin/net usershare add "$share" "$mount_point" "Auto-mount Volume" "$(hostname -s)\\osmc:f" && break
                sleep 5
            done
        fi
    
    elif [[ "$action" == "remove" ]]; then
        if [[ -x /usr/bin/net ]] && /bin/systemctl is-enabled samba > /dev/null 2>&1; then
            share=$(basename "$mount_point")
            id -u "${share,,}" > /dev/null 2>&1 && share+="-auto"
    
            /usr/bin/net usershare delete "$share"
        fi
    
        [[ "$dev_type" == "cd" ]] && /usr/bin/eject "$dev_name" 2>/dev/null || true
    fi
    

    And make it executable:

    sudo chmod +x /usr/osmc/bin/samba-helper
    
  8. Reboot the box (preferred) or run the following commands instead:

    sudo udevadm control --reload
    sudo systemctl reload automount.service
    
  9. Share and enjoy.

1 Like

Hi @dimitry_ishenko

Are you an Armbian developer?

I’ll have a look at this shortly.

I saw that a lot of what we were trying to achieve, like mounting filesystems with specific options is already possible by default with UDisks2.

From the very first cursory glance of your package on GitHub, it would be good if you could use a shebang to indicate the Python 3 interpreter.

Can you let me know which filesystems you have tested this on?

Part time.

Yep, I’ve looked at that too, but unfortunately they don’t have hooks.

I do in the automount file. The other two modules are not meant to be launched directly, so I didn’t use it there. It prevents people from being able to launch them directly.

UDF (blu-ray disk), FAT, NTFS and ext4.

Say hi :wave: to Igor, he does great work.

OK, you can blame me for skimming that then.

I’ll fork your automount repo. I’d like to create a Debian package for it / convert it to OSMC diskmount package and add some postinst rules to make sure the service (udisks2) and automount are running as that doesn’t seem to be provisioned in the Debian rules from a quick glance.

Will do. :slight_smile:

No worries.

Hmm… I thought it was already. It should be in the postint script… I can test it on a clean install later tomorrow. :thinking:

It might be in our postinst script, but it won’t restart your automount service or udisks2.

FWIW, here’s what I found in postinst. It’s auto-generated by one of the debhelper scripts:

# Automatically added by dh_installsystemd/13.14.1ubuntu5
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then
        if [ -d /run/systemd/system ]; then
                systemctl --system daemon-reload >/dev/null || true
                if [ -n "$2" ]; then
                        _dh_action=restart
                else
                        _dh_action=start
                fi
                deb-systemd-invoke $_dh_action 'automount.service' >/dev/null || true
        fi
fi
# End automatically added section

Yes, that will need manually porting over to OSMC as we don’t use debhelper.

If you are ever interested in switching to debhelper, let me know. That seems to be the standard way of making Debian packages