This is still the header! Main site

We Are Doing Files Wrong

2021/01/29

... in which we discuss files, digress towards C64s and other weird kinds of files, and eventually come back to describing a new way of handling files on computers. Maybe it's not new... but maybe interesting to think about.

tl;dr: make directories and files literally the same thing! All file formats are broken; we must mount all the things; let's rewrite the world.

Where we currently stand

Back in the 90s, if you wanted to explain "computers" to people who never saw one, you'd surely get to the following fact in the first 5 minutes:

Documents, pictures etc are "files". You can put files in folders. You could even put folders in folders.

a screenshot of Norton Commander

This really didn't change a lot in the 2000s.

win98 folder tree

(... technically, the screenshot isn't from the 2000s, but the ability of GUIs to show what's actually happening has been steadily declining ever since. But... this is pretty much how people have been imagining file systems.)

Now, pedantic Linux / Unix people might say: but directories are files, too! In Linux, you can get a file descriptor pointing to a folder, and they do indeed behave fairly similarly on a file system level. However... as far as users and developers are concerned, the following is still true:

Files are blobs of bytes. Directories hold files and other directories. They're two distinct things.

As trivial as this might seem, this is not the only way this could be, and indeed, it's not the way things always were.

Counterexamples!

To begin with, "directories" weren't always infinitely hierarchical things. DOS 1.0, for example, didn't support subdirectories at all; you had a single catalog. The same goes for Commodore 64 disk catalogs.

"Sure", you might say, "computers in the past were simple and primitive; Progress has happened since, so by now we have the Most Full-Featured System Imaginable already. Especially if you consider symlinks and hard links."

Well, no. There were, for example, resource forks. On the "classic" Mac OS line (up to 9), you could store multiple binary blobs under a single file name, identified by a four-byte resource type, an id and (optinally) a name. They were used to store e.g. bitmaps, localized strings, even program code, apparently, sometimes. That much about our assumption of "files are a single binary blob".

However, things get even more weird once we go back somewhat more. Namely, Commodore 64 files are... even more alien from a modern point of view. To begin with, unlike with the PC, the 1541 disk drive was its own standalone intelligence, coming with yet another instance of the same CPU also found in the C64 itself. Thus, most of what we currently consider part of a file system (NTFS, ext3, etc.) driver was done by the drive itself.

The drive, and such the "file system", could handle several distinct kinds of files. "PRG" ones were for programs; "SEQ" and "USR" could only be used sequentially, while "REL" ones also had random access, coming with a concept of fixed-length "records": e.g. you create the file as one that contains records of 26 bytes, and then you seek to record number 10 (... see this example). This is not just a bunch of OS convenience functions covering up a binary blob: this is the Actual Way the OS talks to the disk drive (... which will then translate all this to cyliders and sectors; no one really has to think about a flat array of bytes in the entire process).

Of course, this is yet another example of a combination of "we didn't yet know better" and "we needed to optimize because computers were stupid". Fair point. A point I really wanted to get to. Since... I'm going to argue for the same thing in the next section.

The thesis

Why isn't every file also a directory?

Currently have files, that are binary blobs. We have directories that contain other things. Directories aren't binary blobs. Files cannot contain other files. And yet... why not???

If files were allowed to contain other files on the file system level, who would need "directories" anymore? They're just empty files with a bunch of sub-files, after all.

I'll argue that this would make life a lot easier from many perspectives.

... but why?

What's the point of sub-files? Why would you want to put a sub-thing into a JPEG file, for example?

Well... here is the second part of the thesis: most file formats come with ad-hoc, rigid, poorly expandable, application-specific implementation of a subdirectory tree.

(... yes this sounds intentionally like Greenspun's tenth rule. The one about Common Lisp.)

Example time!

The list goes on and on. Actually, it's somewhat harder to find file formats that don't actually contain trees, or at least a flat list, of other blobs: text files and MP3s come to mind that don't really. Or raw audio.

Plus, there are some "formats" which aren't even really file formats: Mac OS (OSX) app packages are in fact directories, which show up as files on the GUI.

Returning to the question of "why we want files to have sub-files": because they mostly already do have them, and acknowledging this would help reduce all the chaos by a lot.

In an imaginary world...

... where files are indeed directories, how would things look like?

From an "user" perspective: not radically different. "Folders with documents in them" would still be a thing; it'd perhaps be easier to copy out an image out of a Word document. Otherwise: Mac OS bundles work well today; it'd be not that much different. From a "complexity" perspective though...

... imagine that most overcomplicated file formats just do not exist.

Why come up with an ad-hoc scheme of addressing resources, if you could just rely on the OS keeping them in order? Why embed resources into your binary if you could just add them as sub-files?

Remember: every file can have sub-files. If you start out as a binary blob but suddenly need a sub-file, you can add one. If you need to add a JPEG thumbnail to anything, you can specify that it should go into the "/thumbnail.jpg" subfile. And thereby we solved "thumbnails" for every kind of file, even those that our OS doesn't support. Done.

Also, if you ever have the temptation for embedding that JPEG into one of your blobs... well, remember that a JPEG file is an entire tree now. So you either turn it into a flat binary first (ZIP it up?), or... it's actually much simpler to you if you can keep it as a sub-file. So now your format has sub-files, too, further motivating everyone else to make use of them.

If this sounds horrible from a "tooling" perspective: it doesn't need to be. In this world, "attaching a file to an email" would imply all sub-files automatically. Or a configurable subset. Want to redact EXIF location? Well, you just don't send that "/location.txt" subfile of the JPEG. Since everything has subfiles now, all the tools would include them by default, with "just the binary blob only" being the weird special case (... analogous to using dd to extract a JPEG header today: doable, but why). File copy: copies subfiles by default. (Compare this with today's world: try declaring that your JPEG file is suddenly a directory... and good luck with getting everyone reading JPEG files onboard.)

Of course, we'd need wire formats for this: you can't send over a file tree over an Ethernet cable. But... can you really send over a binary blob either? (It's broken up into packets eventually, after all.) But all of this can be lower-level standards. "The Internet Wire Format for Trees" can equally apply to a JPEG file and a video, and would probably be an OS-level feature instead, perhaps transparently supplied by HTTP. E.g.

GET http://some.site.example.com/background.jpg

would get you the picture, subtrees and all, while

GET http://some.site.example.com/background.jpg/thumbnail.jpg

would supply the thumbnail, just because it's there in the actual file. Transparently. Imagine trying to do this with a current-world JPEG. Technically, you could fetch the header... figure out binary offsets... send a GET request to fetch part of the file... and, in the process, do so many round trips that "just fetch the entire thing" would've been simpler.

ZIP files wouldn't exist. Or... in fact, there would be ZIP "files": file-directories with some blobs compressed inside, with extra info attached on how to decompress them. No need for an extra catalog (think of the way gzip doesn't bundle files). You could even do compression as an OS service, since, suddenly, the OS would know the internal structure of files! And you don't need them for the other popular usage, "let's send multiple files in one go" either: if every file is multiple files, you could just create a file/directory, throw your documents in, and send that one (as a single file, no more complex than a single JPEG).

The way forward

Well, there is bad news:

... this is not how we've been doing "files" for the past... um... 30-40-50 years.

So, if someone writes an OS that works like this, it'd still need to deal with all the half-broken mini-file-systems that are our file formats.

Here is the good news though: who said we can't mount mini file systems? In fact, we could on-demand automount everything. The way you can browse into an iso image. (I guess you could even cd into it on a low level with a sufficiently sophisticated automounter.)

Of course, a lot of this would be read only. You can't just attach a thumbnail to, say, a random MP4 file, if your mini-file-system driver has no idea where to put thumbnails in MP4s (... because it's just not a thing in the format spec). You could definitely have an EXIF editor by loading up

our_image.jpg/exif.txt

in your favorite text editor.

Meanwhile, tools on this hypothetical new OS would use these old, flat-binary formats as mounted file systems (unlike current OSes, which open them directly as binaries). Meanwhile, new-OS-native file formats can just use a wire format (a bit similar to ZIP files, with an explicit catalog) to produce self-contained binary blob files to the rest of the world that still doesn't think of them as trees.

You could even use traditional-style file systems to emulate a sub-file one: you could just have a directory tree with every directory containing a file called "data". It'd be seriously ugly, with way too many directories around, but it'd work.

Summary

It is a historical accident that files can't have sub-files. This made everything worse, with all file formats trying to be their own miniature file systems that can do this. We can do better.

We can partially fix this by mounting them as file systems in OSes that do support sub-files. (I don't currently know of any that do, though.) We can't get all the nice things this way, but it's a viable upgrade route.

... comments welcome, either in email or on the (eventual) Mastodon post on Fosstodon.