Skip to content

Latest commit

 

History

80 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

StoraOS

A small Linux system and web panel for the Netgear Stora MS2110, running entirely from the device's 256 MB NAND flash so that both drive bays stay free for data and the disks can spin down.

The stock firmware is long unsupported, and OpenMediaVault — the usual replacement — does not fit in flash. Installing it on a hard disk means the system disk never sleeps and several gigabytes of every disk go to system partitions. StoraOS exists to avoid both.

StoraOS

What it is

Component Choice
Base system Debian 13 (trixie) armel, --variant=minbase
Kernel Linux 6.12 LTS, built for this board, 4.4 MB uImage
Root filesystem UBIFS on UBI in NAND, zstd compressed
File sharing SMB (ksmbd, in-kernel), NFSv3, DLNA (minidlna)
Admin panel nasd — a single static Go binary, no dependencies

The panel handles disks (partitioning, formatting, mounting, hot-swap, RAID1), shares and users, SMART, fan control, front-panel LEDs with a night schedule, package updates and live charts. It is one process: no database, no nginx, no container, nothing to keep in sync with it.

It also installs Debian packages without apt, and never wakes a sleeping disk to tell you how the disks are doing. Both are explained under design notes below — they are the two places where 128 MB of RAM and a spun-down drive stopped being constraints and started being the design.

Hardware. Marvell Kirkwood 88F6281 (ARMv5TE, single core at 1 GHz, no FPU), 128 MB RAM, 256 MB NAND, two SATA bays, gigabit Ethernet. These numbers explain most of the design decisions in this repository.

The panel

Dashboard
Everything the box is doing, on one screen. Both disks asleep, the fan at its floor, 59 MB of RAM in use out of 117.

Disks
A sleeping disk reports no temperature, because reading it would mean waking it. That is the honest answer rather than a blank.

Monitor
A day of history in a ring buffer in RAM. Writing it to disk would wake a disk every fifteen seconds and defeat the point of the machine.

Also: shares and users.

Taken against a running panel in English — the interface follows the browser's language. Serial numbers, addresses, share names and account names are placeholders: these are documentation images, not a record of somebody's machine. The script that produces them is kept outside this repository, because to hide those values it has to be given them.

Flash layout

Offset Size Contents
0x0000000x0c0000 768 kB u-boot — read-only, never written
0x0c00000x100000 256 kB u-boot-env
0x1000000x700000 6 MB kernel — raw uImage with an appended DTB
0x7000000x10000000 249 MB ubi → UBI volume rootfs (UBIFS)

U-Boot reads the kernel with a plain nand read and never needs to understand UBI; the kernel mounts UBIFS itself. This is what makes the installation safe: the bootloader is never reflashed, so a broken image in NAND falls back to whatever the machine booted from before.

Installing it

The full procedure is in INSTALL.md — it takes an hour or two and every step up to writing flash is reversible.

The short version: build the kernel and the panel on a PC, build the root filesystem on the device (it is already armel, so there is no qemu anywhere in this repository), write both into NAND from the old system, and add a boot entry that falls back to the old one. You need a machine already running on the Stora — stock firmware, Debian or OpenMediaVault — and a serial console is strongly recommended for the first boot.

One hard prerequisite: the device must already be running a custom U-Boot (Bodhi's 2017.07-tld-1, from the doozan forum). The procedure needs nand read, bootm and an environment that fw_setenv can write, and the bootloader Netgear ships will not do it. Installing U-Boot is the only step that touches the bootloader; it is out of scope here and has to be done first.

Nothing in the procedure writes the bootloader, so the device cannot be bricked by it.

Being found on the network

Three protocols, because no single one reaches every desktop:

Where the machine shows up Protocol Answered by Icon
Windows, under Media devices UPnP/SSDP the panel itself its own PNG
Windows, under Computers WS-Discovery wsdd2 a Windows category icon
macOS Finder, Linux desktops mDNS avahi-daemon from Apple's fixed set

Only UPnP carries an icon the device chooses for itself; the other two let you set a name and pick from a list. That is why the panel answers SSDP in-process — a few UDP datagrams and a document its own HTTP server already serves, which is cheaper than another daemon on a machine with 128 MB of RAM — while the other two mechanisms are packages.

The same machine therefore appears more than once, and the names are chosen so that this reads as one device rather than three. They all come from internal/netid, which exists because they once drifted apart:

StoraOS (stora)         UPnP — the panel, clicking it opens the interface
StoraOS media (stora)   DLNA — the media server
stora                   WS-Discovery — the shares

The WS-Discovery entry keeps the bare host name on purpose: clicking that one opens \hostname\, which only works if the name resolves.

The icon in Explorer's network view stays generic, and no amount of work on this side will change that. Windows does not read the iconList from the description document there — it looks the icon up in its own registry, under HKEY_CLASSES_ROOT\NetworkExplorerPlugins\<device URN>\DefaultIcon, and the value points at an icon resource on the Windows machine. A device cannot supply its own picture; vendors who show a logo there ship a metadata package through Microsoft. The iconList is still served, because control points other than Explorer do use it.

wsdd2 ships bound to smbd, which does not exist here — this machine serves SMB with ksmbd — so rootfs/files/etc/systemd/system/wsdd2.service replaces the packaged unit. A drop-in is not enough: an empty BindsTo= does not clear the list the packaged unit set.

All of this was removed for a day in August 2026, when browsing a share from Windows crashed the box six times in one evening. The cause was a use-after-free in ksmbd, not in any of this — discovery only made it happen, because Explorer walks into every share it can see. The kernel is patched (the write-up), so the announcements are back.

Design notes worth knowing

Nothing writes to flash in normal operation. /tmp and /var/log are tmpfs, the journal is volatile, swap is zram. Flash wears out; the panel is not allowed to be the reason it does.

Nothing wakes a sleeping disk. Statistics come from kernel counters in /proc and /sys, SMART is only read on request, and the metrics history for the charts lives in a RAM ring buffer that is lost on reboot. That is a deliberate trade: a disk that never sleeps was the reason for replacing the previous system.

ksmbd needs two settings that are not the defaults. Durable handles are disabled: with them, copying a large file and then re-authenticating sends a kernel thread into an endless loop in ksmbd_smb_check_shared_mode, eating a full core, and the thread cannot be killed — only a reboot clears it. And the panel never restarts ksmbd, only reloads it: restarting the service while a client is connected wedges the kernel module.

NFS is version 3 only. UBIFS has no export_operations, so it cannot be exported over NFS at all. The NFSv4 pseudo-root starts at /, which is UBIFS here, so mountd refuses to build it and every v4 path fails. Linux clients must mount with -o vers=3; macOS and Windows are unaffected.

Installing packages does not go through apt. apt needs about 60 MB of anonymous memory to answer any question at all, because it indexes all 155 000 packages in the archive before looking at the one you asked for — even apt-cache policy bash costs 47 MB. On a machine with 117 MB of RAM that is most of it, and it is what took the box down mid-install: memory pressure starved systemd until the hardware watchdog fired. internal/apt does the same job by streaming the index instead of indexing it — measured at 12 MB to refresh, 15 MB to list what is upgradable and 17 MB for a complete install including dpkg. apt's own lists are not kept between runs — the panel rebuilds them when it needs the fallback and removes them again if it was the one that downloaded them. It reimplements nothing: dpkg unpacks and orders, dpkg --compare-versions decides which version is newer, sqv checks the archive signature, xz decompresses the indexes that are only published that way. What it will not do is decide anything — conflicts, removals, a package appearing out of nowhere during an upgrade all make it stop and say to use apt. Run it with nasd apt list|install|upgrade.

The default free-page reserve is too small here, and it costs throughput. vm.min_free_kbytes is derived from the amount of RAM, which on 128 MB gives 1386 kB — low enough that a plain file copy runs the machine down to the watermark and reclaim starts competing with the write. Raising it to 8192 measured 38.0 → 41.7 MB/s writing, 48.3 → 56.7 MB/s reading, and 35.8 → 40.0 MB/s with two writers and a reader at once. 16384, which the doozan forum recommends for boxes this size, adds 0.4 MB/s and costs another 25 MB of available memory — which is the memory a distribution upgrade needs, so the gain stops being worth it there.

The drives' own idle timer does not work here, so the panel keeps the clock. hdparm -S is accepted, the drive advertises the power management feature set, and the timer never fires — measured with the delay cut to two minutes, no I/O at all and every service stopped, the disks were still spinning after six minutes. An explicit standby command works and holds: a parked disk stayed parked for six minutes with everything running. So internal/spindown counts idle time from /proc/diskstats — asking a disk how busy it is would be self-defeating, since every question is a command — and sends hdparm -y when the configured delay runs out. This is what hd-idle exists for elsewhere; at a few dozen lines it is cheaper here than a fourth daemon.

A hardware watchdog is armed at 20 seconds. Kirkwood's timer cannot go beyond 21. It does not catch a spinning kernel thread — PID 1 stays alive and keeps feeding it — but it does catch the total freeze where both the network and the serial console go silent.

Repository layout

kernel/    kernel build script, config, device tree and local patches
rootfs/    debootstrap and system configuration (run on the device)
scripts/   diagnostics, NAND backup, flashing, u-boot environment
panel/     nasd — the admin panel (Go, no external dependencies)
tools/     serial console bridge, SMB test clients, interface checks
docs/      write-ups of the failures that shaped the design
assets/    logo

Status

In daily use. Verified on the hardware: boot from NAND, both disks, i2c sensors, LEDs with the night schedule, formatting and mounting from the panel, SMART, the nightly backup, NFSv3 from macOS, DLNA, and 22 GB copied from Windows over SMB in two overlapping streams with nothing in the kernel log. A file written over SMB is visible over NFS and the other way round.

Two things are worth knowing about how this repository is kept. The kernel carries a local fix for a use-after-free in ksmbd that crashed this machine six times in one evening — found here, patched here, and not reported upstream, which was a deliberate decision. And the work of 12 August 2026 was audited by a different model than the one that wrote it; seventeen findings, three of them serious, all fixed and each with a test that fails against the old code: docs/audit-2026-08-12.md.

Not verified: live SATA hot-swap, RAID1 under load, and how the panel's loading indicator actually looks in a browser — there is none on the build machine.

Licence

BSD 2-Clause — see LICENSE. It covers every file that does not say otherwise in its own header. Two do, and both were inherited rather than chosen: kernel/files/kirkwood-stora.dts comes from OpenWrt and stays GPL-2.0-or-later, and the patches in kernel/patches/ are GPL-2.0 because the kernel they patch is. Having them here does not put the rest under the GPL — the panel is a separate program that execs dpkg and hdparm and links nothing but the Go standard library.

Trademarks

NETGEAR is a trademark of NETGEAR, Inc. Stora was the name of the hardware; the US registration for it (serial 77823061) is dead, cancelled for a missing declaration of continued use.

This project is not affiliated with, endorsed by, or connected to NETGEAR, Inc. The hardware names are here to say which machine the software is for, and for no other reason. Nothing in this repository is a NETGEAR product.

About

Debian and a dependency-free Go web panel running entirely from the 256 MB NAND of a Netgear Stora MS2110 — both drive bays stay free for data, and the disks are allowed to sleep.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages