Filesystem code in AIF

In light of the work and discussions around supporting Nilfs2 and Btrfs on Arch Linux and its installer AIF,
I've shared some AIF filesystem code design insights and experiences on the arch-releng mailing list.
This is some hard to understand code. Partly because it's in bash (and I've needed to work around some limitations in bash),
partly because there is some complex logic going on.

I think it's very useful material for those who are interested (it can also help understanding the user aspect),
so I wanted to share an improved version here.
On a related topic: I proposed to do a session at Fosdem 2011/"distro miniconf" about simple (console based) installers for Linux,
and how multiple distributions could share efforts maintaining installation tools, because there are a lot of cross-distribution concerns
which are not trivial to get right (mostly filesystems, but I also think about clock adjustments, bootloaders, etc).
Already several distro's use the (or a fork of) the Arch installer, for example Pentoo,
but I think cooperation could be much better and more efficient.

Anyway:

my acronyms for this text:

  • LV = lvm2 Logical Volume
  • VG = lvm2 Volume Group
  • PV = lvm2 Physical Volume
  • DM = Device Mapper
  • BD = Block Device
  • FS = File System
  • DF = DeviceFile

"Normal" FS'es ("do something on the BD represented by DF /dev/foo, so that you can then call `mount /dev/foo $somedir`") are trivial to add to aif.
Basically you just need to tell aif the name of the filesystem, and which commands and arguments it needs to invoke to create it and add a label to it.
Nilfs2 falls in this category. So do ext2/3/4, xfs, jfs, reiserfs, etc. (Nilfs is now supported and new archiso testbuilds are available)

"Complex" FS'es (which yield a new DF for a DM BD, which can span multiple underlying BD's, etc) are more difficult, and I'll explain why.
Btrfs falls in this category. So do LVM, dm_crypt and softraid. In aif terminology anything you put on top of a BD is a FS. This is not always technically correct, but it's not far-fetched and avoids needless complication. For example softraid would be a FS you put on top of one or more BD's, and which itself yields a new BD on top of which you can put something else.
In the same way I call a VG a FS being applied on top of a PV BD, which itself results in a new BD which can host multiple LV FS'es, which in return yield new BD's which can host other FS'es. Currently AIF provides support for lvm and dm_crypt, but not softraid or Btrfs.

How aif works is this: it uses a "model" that represents how your DF/FS structure will look like.
I personally usually configure my hard disk like this:
a boot partition, and a partition on which i do dm_crypt, which results in a DM BD, which I make a PV, then put a VG on
it, which contains multiple LV's, one for swap, and two containing
some FS'es which get mounted as / and /home.

You can see that model on the bottom ($BLOCKDATA) of the example file "fancy-install-on-sda" for automatic installations, included with aif.

You might have noticed in the installer - if you do it interactively - how you first configure all
your filesystems in the dialog interface, but only after confirming, it
does all the required actions, step by step. Actually the dialog-based configuration helper will just generate a textfile in the same format as $BLOCKDATA and the processing code is the same for interactive and automatic installs.
Since in the config file for an automatic install you could define your FS's in abitrary order, aif
figures out the dependencies and processes things in the right order.
With the example given above aif will parse the text and will figure out the order of creation: first partition (obviously), then create the dm_crypt, then the PV, then the VG,
then the LV's, then the FS'es on those LV's)
then mount all mountpoints in the right order (first /, then /home and /boot)
On rollback (when user changes his mind, an error occured - usually because of misconfiguration like a too big LV) everything I just explained happens in reverse.
This allows users to tune their config (or try something completely different), without the installer breaking with errors like "This device is already marked as encrypted" or "VG already exists"

I choose this model-based approach initially because I wanted to get rid
of the ugly, hacky original installer code, but still provide a lot of
control through the nice dialog interfaces. And interfaces that work fast (not causing you to wait between every step because it's creating the FS you just defined).
Perhaps most importantly, since my main goal was automautic installs where you could just specify how you wanted your
FS/BD structure to look like (not a series of commands), relatively little extra code was needed. (this is also what separates AIF from FAI&debian installer. On Debian the interactive and automatic installers are different projects. On Arch they are just different extensions for the same codebase)

Advantages:

  • provides some abstraction, it's trivial to support for new (simple) filesystems.
  • makes dialog-based "configurator" easier, because we can share code with the automated installer
  • the descriptive style of the config makes it easier (although the actual format could be further improved).
    definitely easier then running/writing a series of commands (resp. interactive, automatic install).
    if we would make users run/write a series of commands, we also couldn't support automatic rollbacks in case of failures.

Disadvantages:

  • the more control you want to give users, the more you're just putting
    effort in wrapping commandline arguments in fancy dialog interfaces
    (although there is also a textbox where you can enter which ever
    additional arguments you want, so this is a compromise)
  • pretty hard to implement fancier filesystems, you usually need to
    take the common use cases. (see next point)
  • bash datastructures are very limited. it's not easy to translate this model in a
    datastructure. If in "FS on top of a BD" the FS is a child, and the BD a parent, it would be like a tree,
    but each node could have multiple children and multiple parents, i.e. a graph.
    For now I choose to ignore the "multiple parents" clause.
    Currently the only implication in aif is that you cannot have a VG which consumes multiple PV's.
    This is rare enough to justify this simplification, but still quite some code is needed to update and parse text files
    to mimic the datastructure, although I do consider using a specific
    optimized text format and an external tool to update/query the data. (yaml?)
    (See FS 15640).
    Multiple children do need to be supported (a VG can have multiple LV's. line 34 in the example file)
    Implementing Softraid in this model means I need to support multiple parents, i.e. work with a graph structure.
    For Btrfs, the LVM-like restrictiction (no multiple physical volumes for the same FS) might be an acceptable compromise
  • users cannot do their own stuff outside aif and expect to see the
    results inside aif. If the installer would execute everything in realtime,
    you could detect changes being made to the system (probably not easy though)
  • since changes don't happen in realtime, the model needs to update itself to represent how
    the actual state would look like. For example: if you just added a dm_crypt FS on a BD, we must create
    a new entry /dev/mapper/$label in the model
  • for PV's, you need a way to differentiate in the menu between the
    real BD (the one on which you say you want to put a PV FS) and the actual PV (on which you can put VG FS's),
    so aif generates an entry with the same name as the DF, but with a '+' appended to the file.
    (see the example file if unclear). Although this problem is not specific to the actual model approach,
    it's more caused by my desire to present a "global interface" to the user, and not a series of wizards and dialogs
  • Rollbacks are cool, but requires some hard to maintain code, and I doubt they are used often.
    (also this is not really specific about the model approach taken, but still worth mentioning)

Because of all this, I have sweared quite a bit over the last few
years, wondering if bash really was a good choice. But using an external tool to work with a text-base graph/tree datastructure (where nodes can have some properties) will probably remove the big nuisance. Also, Because bash v4 has associative arrays (finally), I can clean up a bunch of other code as well.

I've pondered whether we should just let users do everything on the commandline (like Gentoo), or provide a minimal layer of
abstraction, like provide some scripts which they can modify that setup
a system in a certain way. (for example, basically a series of mkfs; mount;pacman; calls)
Another alternative would be to just make the user choose between a series of "common setups" and make them answer some questions for the choosen setup.
This is how the old installer did it, and afaik archboot still does it, but it doesn't scale well with more and more possibilities.
Actually, aif still contains the optional "autoprepare" method, which is in fact a simple wizard for a simple setup.

I guess it's a tradeoff between making it easy for users and not overloading the brain of people who want to hack on the installer.
The approach which afaik has always been taken by Arch is to make the installer hold the hands of the user.
When creating aif, I've choosen to stick to that concept. And frankly I still like the idea. It may be hard to maintain,
but as a user you can finish all your installs and have a lot of options, but still use very minimal keystrokes (and mental energy).

Like mentioned earlier, softraid hasn't been implemented yet in aif, nor btrfs.
I would need to know the most common/recommended use cases, and figure out the best way to implement them.
Btrfs might be relatively easy, if i can implement it like i did lvm. But it seems like a great FS and I don't want to provide only-half-assed support for it.
Either way, refactoring the datastructure is something that will needs to be done at some point, especially for softraid.

I hope this article was a bit understandable. And if you have any advice, please share :)
The actual blockdevices-filesystems library of AIF is here:
https://github.com/Dieterbe/aif/blob/2ee11ef334983fc68352364025ba5a97d795b95e/src/core/libs/lib-blockdevices-filesystems.sh
And the menu's are here:
https://github.com/Dieterbe/aif/blob/2ee11ef334983fc68352364025ba5a97d795b95e/src/core/libs/lib-ui-interactive.sh" (the most interesting functions are interactive_filesystems for the main FS menu, and interactive_filesystem which handles definition of a specific FS)

Add comment