Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,9 @@ folder on it, and plug it into the board.
- The daemon shows the update screen while it works; the stick can be removed
once the screen says "Update complete".
- `usb_import = false` in `agent.toml` disables it (the helper exits at once).
- The helper's sandbox (`ProtectKernelModules=yes`) cannot load filesystem
drivers, so `exfat` and `ntfs3` are loaded at boot from
`/etc/modules-load.d/musallahboard.conf`. NTFS always mounts with `ntfs3`.

### 9.7 Admin CLI

Expand Down
33 changes: 6 additions & 27 deletions internal/usbimport/usbimport.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ type Helper struct {
DevDir string
// Exec runs a command and returns its combined output.
Exec func(ctx context.Context, name string, args ...string) (string, error)
// NTFS3 reports whether the kernel's ntfs3 driver can be used.
NTFS3 func() bool
// Chown gives a copied file the inbox directory's owner, so the
// unprivileged daemon can read it and delete it once processed.
Chown func(path, dir string)
Expand All @@ -82,7 +80,6 @@ func New() *Helper {
WaitTimeout: WaitTimeout,
Poll: time.Second,
}
h.NTFS3 = h.ntfs3Available
return h
}

Expand Down Expand Up @@ -141,39 +138,21 @@ func (h *Helper) Run(ctx context.Context, name string) error {
}

// mountType maps blkid's type to the mount -t value, and whether it is
// accepted at all. NTFS goes through the kernel's ntfs3 driver when there is
// one; the FUSE ntfs-3g driver is not used, it runs a userspace process over
// the stick's contents.
// accepted at all. NTFS goes through the kernel's ntfs3 driver; the FUSE
// ntfs-3g driver is not used, it runs a userspace process over the stick's
// contents. The exfat and ntfs3 modules are loaded at boot
// (/etc/modules-load.d/musallahboard.conf): this helper's sandbox
// (ProtectKernelModules=yes) cannot load them itself.
func (h *Helper) mountType(fsType string) (string, bool) {
switch fsType {
case "vfat", "exfat", "ext4", "ntfs3":
return fsType, true
case "ntfs":
if h.NTFS3 != nil && h.NTFS3() {
return "ntfs3", true
}
return "ntfs3", true
}
return "", false
}

func (h *Helper) ntfs3Available() bool {
if b, err := os.ReadFile("/proc/filesystems"); err == nil && hasFS(string(b), "ntfs3") {
return true
}
_, err := h.Exec(context.Background(), "modprobe", "ntfs3")
return err == nil
}

func hasFS(procFilesystems, name string) bool {
for _, line := range strings.Split(procFilesystems, "\n") {
f := strings.Fields(line)
if len(f) > 0 && f[len(f)-1] == name {
return true
}
}
return false
}

func (h *Helper) unmount(ctx context.Context, name, mp string) {
if _, err := h.Exec(ctx, "umount", mp); err != nil {
// Something still holds it (it should not); detach it anyway so a
Expand Down
22 changes: 6 additions & 16 deletions internal/usbimport/usbimport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ func newHelper(t *testing.T, f *fakeSys) (*Helper, *[]string) {
MountRoot: filepath.Join(dir, "usb"),
DevDir: "/dev",
Exec: f.exec,
NTFS3: func() bool { return false },
Logf: func(format string, a ...any) { mu.Lock(); log = append(log, fmt.Sprintf(format, a...)); mu.Unlock() },
WaitTimeout: 2 * time.Second,
Poll: 10 * time.Millisecond,
Expand Down Expand Up @@ -242,22 +241,20 @@ func TestRunTimesOutWithoutDaemon(t *testing.T) {
func TestRunRefusals(t *testing.T) {
cases := []struct {
name, dev, fs string
ntfs3 bool
unsupported bool
mounted string
}{
{"bad name", "mmcblk0p1", "vfat", false, false, ""},
{"path", "../sda1", "vfat", false, false, ""},
{"no filesystem", "sda1", "", false, true, ""},
{"iso", "sda1", "iso9660", false, true, ""},
{"ntfs without ntfs3", "sda1", "ntfs", false, true, ""},
{"ntfs with ntfs3", "sda1", "ntfs", true, false, "-t ntfs3"},
{"bad name", "mmcblk0p1", "vfat", false, ""},
{"path", "../sda1", "vfat", false, ""},
{"no filesystem", "sda1", "", true, ""},
{"iso", "sda1", "iso9660", true, ""},
{"ntfs goes through ntfs3", "sda1", "ntfs", false, "-t ntfs3"},
{"exfat", "sda1", "exfat", false, "-t exfat"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
f := &fakeSys{fs: tc.fs, stick: map[string]int{}}
h, _ := newHelper(t, f)
h.NTFS3 = func() bool { return tc.ntfs3 }
err := h.Run(context.Background(), tc.dev)
cmds := strings.Join(f.cmds, "\n")
switch {
Expand All @@ -277,10 +274,3 @@ func TestRunRefusals(t *testing.T) {
})
}
}

func TestHasFS(t *testing.T) {
proc := "nodev\tsysfs\n\text4\n\tvfat\n\tntfs3\n"
if !hasFS(proc, "ntfs3") || hasFS(proc, "exfat") {
t.Fatal("hasFS")
}
}
11 changes: 11 additions & 0 deletions packaging/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,17 @@ if systemd_running && command -v udevadm >/dev/null; then
fi
info "Installed ${UDEV_RULES[*]} in $UDEV_DIR"

# Filesystem drivers for USB sticks, loaded at boot: the USB helper's sandbox
# (ProtectKernelModules=yes) cannot load them itself.
install -d -m 0755 /etc/modules-load.d
install -o root -g root -m 0644 "${AGENT_DIR}/packaging/modules-load.d/musallahboard.conf" \
/etc/modules-load.d/musallahboard.conf
if systemd_running; then
modprobe exfat 2>/dev/null || warn "Could not load exfat now; exFAT sticks work after a reboot"
modprobe ntfs3 2>/dev/null || warn "Could not load ntfs3 now; NTFS sticks work after a reboot"
fi
info "Installed /etc/modules-load.d/musallahboard.conf (exfat, ntfs3)"

# ── Kiosk (cage + Chromium) ───────────────────────────────────────────────────
if [[ "$WANT_KIOSK" == "yes" ]]; then
section "Kiosk (cage)"
Expand Down
5 changes: 5 additions & 0 deletions packaging/modules-load.d/musallahboard.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# MusallahBoard: filesystem drivers for USB update sticks. Loaded at boot
# because the USB import helper runs with ProtectKernelModules=yes and cannot
# load them itself. vfat and ext4 are loaded by the base system already.
exfat
ntfs3
1 change: 1 addition & 0 deletions packaging/uninstall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ systemctl stop 'musallahboard-usb-import@*.service' 2>/dev/null || true
# udev before the units, so a stick plugged in now starts nothing.
rm -f /etc/udev/rules.d/90-musallahboard-usb.rules
rm -f /etc/udev/rules.d/90-musallahboard-rtc.rules
rm -f /etc/modules-load.d/musallahboard.conf
udevadm control --reload 2>/dev/null || true
rm -f /lib/systemd/system/musallahboard-agent.service
rm -f /lib/systemd/system/musallahboard-kiosk.service
Expand Down
11 changes: 11 additions & 0 deletions setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,17 @@ EOF
# Apply the rtc0 permissions now. Not the block subsystem: that would
# start an import from a stick that happens to be plugged in.
sudo udevadm trigger --subsystem-match=rtc --action=change || true

# KEEP IN SYNC with packaging/modules-load.d/. The USB helper's sandbox
# (ProtectKernelModules=yes) cannot load filesystem drivers, so exFAT and
# NTFS sticks need them loaded at boot, and now.
sudo mkdir -p /etc/modules-load.d
printf '%s\n' \
"# MusallahBoard: filesystem drivers for USB update sticks (see setup.sh)." \
exfat ntfs3 | sudo tee /etc/modules-load.d/musallahboard.conf > /dev/null
sudo chmod 0644 /etc/modules-load.d/musallahboard.conf
sudo modprobe exfat 2>/dev/null || warn "Could not load exfat now; exFAT sticks work after a reboot"
sudo modprobe ntfs3 2>/dev/null || warn "Could not load ntfs3 now; NTFS sticks work after a reboot"
info "USB sticks start musallahboard-usb-import@<dev>.service; rtc0 is writable by $SERVICE_USER"
}

Expand Down
1 change: 1 addition & 0 deletions uninstall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ section "Removing udev rules"
rm -f /etc/udev/rules.d/90-musallahboard-usb.rules
rm -f /etc/udev/rules.d/90-musallahboard-rtc.rules
rm -f /etc/udev/rules.d/85-musallahboard-rtc.rules
rm -f /etc/modules-load.d/musallahboard.conf
udevadm control --reload 2>/dev/null || true
info "udev rules removed (USB import, RTC access, RTC boot-time read)"

Expand Down
Loading