The filesystem hierarchies of operating systems have never been the most robust. They exist solely to provide a background for the actual operating system to run on top of. Because of that, they have evolved naturally over time in weird and occasionally detrimental ways. This blog exists to learn from the mistakes of previous filesystems and to attempt to come up with something better.

Sections will be split into logical components based on the type of files that would be contained in each directory, going from the toplevel to the deepest point of the layout. At the end, a visual layout of the filesystem will be put together to finalize the design.

Vocabulary

vendor - The distributor of the system. Commonly referred to on Linux as “distros”.

state - The difference between the running system and the vendor’s image.

The Vendor-User split

The first thing needed to be considered is how vendor and user files will be separated. Different operating systems have taken different approaches to this. The most common solution is to just give up and lump them both together. This generally works fine as long as the user does not modify any vendor-provided files, which leads to configuration drift and eventual breakage. Although, lumping the two together loses out on the ablity to reset the system back to a prestine state without a full reinstall. This has been attempted to be fixed through a system called “hermetic-usr”, which puts all vendor files in /usr/ and all user and state files outside of it.

Hermetic-usr fails where not every directory from the vendor is put under /usr/. Mount points like /boot/ or /efi/ lay on the root, which makes sense from a logical stance, since if /usr/ is immutable, the two writable mountpoints can not go underneith it. This falls apart when you try wiping the system and hit those mount points. As such, it would make sense to contain user files in a similar manner to vendor files. That way, the user directory can be wiped with one run of rm instead of manually removing each directory individually.

The layout we get from this naturally falls into three directories.

  • /Vendor/, which contains all vendor provided files.
  • /User/, which contains all user-modifiable files.
  • /State/, which contains all directories related to system state.

/Vendor/ and /User/ contain the same subdirectories, but /Vendor/ is read only and /User/ is read-write. /State/ is also read-write, but it should only be written to by programs and does not need manual user intervention.

Sub-Directories

Since /Vendor/ and /User/ have the same subdirectories, each will only be presented once. All the following directories would need to go under a directory that separates them from one another by package. I considered calling this directory Programs/, but it is possible that installed packages may not be programs at all, so Packages/ makes more sense. Under Packages/ would lie individual package directories. For example: if zsh is available on the system, all its files would be located under Packages/zsh/.

There are a few different types of files that would be installed that need to be considered.

Binaries

In this system layout, for simplicity’s sake, I am considering all binaries to be fully statically linked. This makes it easier to sandbox them through limiting their access to the wider filesystem beyond their Programs/ sub-directory and config directory, which the layout design lends itself well to.

Within the category of binaries, there are binaries that the user should be able to run themselves and binaries that should be limited to services. This is seen in modern Linux with /usr/libexec/ or /usr/lib/ for service binaries and /usr/bin/ for user binaries. If a binary is used in a service but also supports being run by a user, the binary should go in the user runnable binaries directory.

Binaries would be stored under UserBinaries/ and ServiceBinaries/.

Configuration

Configuration is a hard problem to solve in this layout because some programs want to read others’ configurations as well as their own. One solution would be to duplicate configuration files across directories, or perhaps symlink them. Another is to give programs access to configuration files within other Programs/ directories. I’ll leave this to later iterations of the layout since it is hard to find a satisfactory solution without seeing it in action.

Configuration would be stored under Config/.

Services

Services are an integral part of any system, so giving them their own directory is a must. All service files should go in this directory and NOT the directory for other data.

Services would be stored under Services/.

Other Data

Other data includes any non-user-readable files, such as prebuilt databases or extra information that a program needs. These should not be anything that the application expects the user to be able to edit, but does not exclude files that a user could edit. For example: if a program has config.toml and config.xml, and the program expects the user to edit config.toml and not config.xml, config.toml would go in Config/ and config.xml would go into Resources/.

Other data would be stored under Resources/

Persistent Data

Persistent data includes anything that should be expected to survive a system reset. This includes user configurations for applications under System/.

Persistent data would be stored only under the User/ directory, within Persist/, split into subdirectories per package.

Home Directory

This is a very opinionated decision. With this design, single-user desktop operating systems are specifically targeted. As such, anything utilizing multiple real user accounts are out of scope. This could be amended in the future with multiple User/ directories, but currently little thought has been put into this use-case.

The Home/ directory’s usage depends on whether it is under System/ or User/. Under System/, Home/ represents the home directory of the root account. Under User/, Home/ represents the home directory of the active user. For the root account, System/Home/ should be pre-populated, since at runtime it will not be modifiable.

Development Resources

Development resources, such as header files, libraries, or dynamic libraries should only be installed under User/. This is because System/ should consist solely of the base resources for the functionality of the system.

Development resources would be stored under Devel/. Headers would be stored under Devel/Headers/[Lang]/. Libraries would be stored under Devel/Libraries/[Lang]/. Dynamic libraries would be stored under Devel/DynLibraries/[Lang]/. The separation by language exists to explicitly portray what language a resource should be used by and to logically separate the resources for developer use.

The State Directory

State/ is separate from User/ or System/, including only the resources that represent drift from the vendor image, but are not specifically tied to the user. User/ should be able to be wiped without requiring also wiping State/.

Sub-Directories

The following are the directories included under State/. The full path will be excluded, so keep that in mind.

Mount Points

Mount points shall be included under sub-directories of Mount/. For example, the boot partition will always be mounted under Mount/Boot/.

Udev Managed Files

Anything managed by udev shall be located under Devices/.

Kernel Device Tree Mappings

Kernel device mappings shall be located under Objects/. (This is /sys/.)

Kernel State

The state of the currently running kernel shall be located under Status/. (This is /proc/.)

Cache Location for Running Programs

Each program will get its own directory under Cache/, created as temporary directories that will be lost on reboot. Applications should not store anything persistent in this directory.

Wrapping Up

Hopefully, we’ve drafted a logical filesystem layout for a UNIX-like operating system. But, there are absolutely some usecases or problems that have yet to be solved. I hope to iterate on this over time and clean it up to the point that it could be theoretically used by a real distribution. I would like to thank Gobo Linux for the original inspiration for designing a layout. That’s all for now!

Layout

/
┣ Vendor
┃ ┣ Packages
┃ ┃ ┣ package1
┃ ┃   ┣ Config
┃ ┃   ┣ ServiceBinaries
┃ ┃   ┣ UserBinaries
┃ ┃   ┣ Resources
┃ ┃   ┣ Services
┃ ┗ Home
┣ User
┃ ┣ Packages
┃ ┃  ┗ userpackage1
┃ ┃   ┣ Config
┃ ┃   ┣ ServiceBinaries
┃ ┃   ┣ UserBinaries
┃ ┃   ┣ Resources
┃ ┃   ┣ Services
┃ ┃   ┗ Devel
┃ ┃     ┣ Headers
┃ ┃     ┣ DynLibraries
┃ ┃     ┗ Libraries
┃ ┣ Home
┃ ┗ Persist
┃   ┣ package1
┃   ┗ userpackage1
┗ State
  ┣ Cache
  ┣ Devices
  ┣ Mount
  ┃ ┗ Boot
  ┣ Objects
  ┗ Status