Thanks for sharing this udev rule @paoloantinori (and to eklex for creating it)!
I started having a go with Home Assistant OS on a RPi4 in December and thought it would be great to be able to plug in a USB drive and share it to other devices on my LAN as well as zone-control the heating.
This udev rule worked great after plugging in a drive and restarting each add-on which needed access to the drive (Home Assistant to show it in the Media Browser, and Samba Share to share it via Samba).
When I tried it with my NTFS-formatted backup USB drive though, I found I could read, but not write; the old NTFS kernel driver is read-only. I found that to get write access to my NTFS partition, the ntfs-3g fuse driver is needed.
Adding ntfs-3g support the slow way (build the OS yourself):
-
git clone
the https://github.com/home-assistant/operating-system repository
- If you want to build a particular version of the OS, use git to checkout that version
- Edit the
buildroot-external/configs/rpi4_defconfig
file (or the one appropriate for your hardware), adding the following lines at the end:
BR2_PACKAGE_NTFS_3G=y
BR2_PACKAGE_NTFS_3G_NTFSPROGS=y
- Follow the developer instructions to perform a build of the OS, unzip the built image (.img) file, and write it to your uSD card (or other boot device…remember all old content will be lost when you do this, so back sure you back-up everything you want to keep). Note that for me this took 16GB of disk space and many hours to build.
- Restore all your Home Assistant stuff back onto your fresh HassOS.
However, the udev rule will still mount the NTFS partition using the old kernel NTFS module by default. It needs a little enhancement to make it use ntfs-3g!
Here’s what I came up with:
#
# udev rule
# Mount USB drive to the media directory using the partition name as mount point
#
# Source:
# https://gist.github.com/eklex/c5fac345de5be9d9bc420510617c86b5
#
# Description:
# Created for Home Assistant OS, this rule mounts any USB drives
# into the Hassio media directory (/mnt/data/supervisor/media).
# When a USB drive is connected to the board, the rule creates one directory
# per partition under the media directory. The newly created partition is named
# as the partition name. If the partition does not have a name, then the following
# name format is used: "usb-{block-name}" where the block name is sd[a-z][0-9].
#
# Note 1:
# The rule name is always prefixed with a number. In this case, the rule uses 80.
# This represents the order of the rule when multiple rules exists in udev.
# Low numbers run first, high numbers run last. However, low numbers do not have all
# the facilities than high numbers may have.
# For this rule to run properly, use numbers equal or greater than 80.
#
# Note 2:
# This rule will skip mounting the 'CONFIG' USB key.
# https://github.com/home-assistant/operating-system/blob/038f1b4bd69c952769fd020db0eae0f511570d19/Documentation/configuration.md
#
# Note 3:
# This rule will mount the OS partitions if the OS is sorted on a USB drive (i.e. USB booting).
# To prevent this issue from happening, update the rule to skip the booting USB drive.
# See the CAUTION message below.
#
# Source of inspiration:
# https://www.axllent.org/docs/auto-mounting-usb-storage/
#
# Useful links:
# https://wiki.archlinux.org/index.php/Udev
#
# udev commands:
# - Restart udev to reload new rules:
# udevadm control --reload-rules
# - List device attributes of sdb1:
# udevadm info --attribute-walk --name=/dev/sdb1
# - List environment variables of sdb1:
# udevadm info /dev/sdb1
# - Trigger add/remove event for sdb1:
# udevadm trigger --verbose --action=add --sysname-match=sdb1
# udevadm trigger --verbose --action=remove --sysname-match=sdb1
#
# Filter on block devices, exit otherwise
# CAUTION: Change to 'sd[b-z][0-9]' if booting from a USB drive (e.g.: sda)
KERNEL!="sd[a-z][0-9]", GOTO="abort_rule"
# Skip none USB devices (e.g.: internal SATA drive)
ENV{ID_PATH}!="*-usb-*", GOTO="abort_rule"
# Import the partition info into the environment variables
IMPORT{program}="/usr/sbin/blkid -o udev -p %N"
# Exit if partition is not a filesystem
ENV{ID_FS_USAGE}!="filesystem", GOTO="abort_rule"
# Exit if this is the 'CONFIG' USB key
ENV{ID_FS_LABEL}=="CONFIG", GOTO="abort_rule"
# Get the partition name if present, otherwise create one
ENV{ID_FS_LABEL}!="", ENV{dir_name}="%E{ID_FS_LABEL}"
ENV{ID_FS_LABEL}=="", ENV{dir_name}="usb-%k"
# Determine the mount point
ENV{mount_point}="/mnt/data/supervisor/media/%E{dir_name}"
# Special processing for adding NTFS partitions; mount with ntfs-3g (either directly supported by the OS,
# or using the executable from the /etc/modprobe.d directory if present there).
ACTION!="add", GOTO="normal_mount"
ENV{ID_FS_TYPE}!="ntfs", GOTO="normal_mount"
TEST=="/bin/ntfs-3g", ENV{fs_type_arg}="-t ntfs-3g", GOTO="normal_mount"
# No ntfs-3g in the root file-system; check if it's been dropped into /etc/modprobe.d (along with it's library hopefully!)
TEST=="/etc/modprobe.d/ntfs-3g", RUN{program}+="/usr/bin/mkdir -p %E{mount_point}", RUN{program}+="/usr/bin/systemd-run -r -E LD_LIBRARY_PATH=/etc/modprobe.d /etc/modprobe.d/ntfs-3g $devnode %E{mount_point}", GOTO="end_rule"
LABEL="normal_mount"
# Mount the device on 'add' action (a.k.a. plug the USB drive)
ACTION=="add", RUN{program}+="/usr/bin/mkdir -p %E{mount_point}", RUN{program}+="/usr/bin/systemd-mount %E{fs_type_arg} --no-block --automount=no --collect $devnode %E{mount_point}"
# Umount the device on 'remove' action (a.k.a unplug or eject the USB drive)
ACTION=="remove", ENV{dir_name}!="", RUN{program}+="/usr/bin/systemd-umount %E{mount_point}", RUN{program}+="/usr/bin/rmdir %E{mount_point}"
# Exit
LABEL="end_rule"
LABEL="abort_rule"
Now, you may notice that in the above that it first checks whether ntfs-3g
is in the default location it would go when built-in as part of the OS, but then goes on to check whether it’s in the /etc/modprobe.d
directory…
The idea here is that rather than everyone having to rebuild the OS themselves, we can stick with official releases and updates, and just use our CONFIG USB sticks to drop ntfs-3g
and the other NTFS programs and libraries into the /etc/modprobe.d
directory at the same time as adding the udev rule during an ‘Import from USB’.
As the developer documentation describes, you create a modprobe
(note, no ‘.d’ on the end) directory on your CONFIG USB stick and put the files in it you want to end up in the /etc/modprobe.d
directory on your HassOS host.
Here’s a zip with the udev rule and modprobe directory (ntfs-3g built for Raspberry Pi 4) ready to drop onto your CONFIG USB stick:
https://www.dropbox.com/s/aflbixda23sfned/hassos-rpi4-udev-with-ntfs-3g.zip?dl=1
Note: I have seen that people don’t recommend sharing NTFS partitions mounted using ntfs-3g via Samba as access is very slow compared with using Linux-native partitions like ext4. For me it’s been enough to perform a backup, although it may have taken over twice the time compared with my backup drive being directly plugged in my PC’s USB port.