Back to projects
Thumbnail for (rs)mkswap

(rs)mkswap

mkswap is a simple utility for initializing block devices and files for use as swap space for the Linux kernel. Rather, it should be a simple utility. In reality, there are various pitfalls, edge cases and mysteries that await in
the shadows. Learning them from scratch while also learning Rust was a challenge, but I live to tell the tale. This is how it began:

Phase 1. A C program in Rust's clothing.

I had unsafe blocks littered everywhere, void pointers flying around and used C methods even for those features that Rust had a far better interface for. This arose from the fact that I had a basic grasp of C, which in this case was more of a weakness than a strength, as Rust's memory management and functional features eluded me in my purely imperative and unsafe mind.

Phase 2. The beginnings of understanding

Big-endian vs Little-endian. In the year 2025 I hadn't even heard of BE computing, though I had heard of network byte order while doing HTTP stuff (apparently network byte order, which is big endian, is in part a relic of when BE was much more common, and x86 hadn't yet conquered the world). Realistically I knew that less than 0.01% of people would use the feature, since it is only useful if you are initializing a swap area for a machine with a different endianness than the host's native byte order. But if someone needs to do that for their IBM mainframe or some obscure ARM mini computer, who am I to judge. They should be able to swap too.

I also had to learn a little about padding and struct packing, since they are used by the main data structure, the swap header. There are also some differences between Rust and C here which are conveniently resolved by the #[repr(C)] macro.

Phase 3. Despair

The swapon syscall seemingly has only one real error, which is EINVAL, "invalid argument." Great. Which argument? How is it invalid? That was my job to find out and considering I was dealing with a page where a single off-byte could make it fail, it wasn't an easy one. I made at least 5 C programs to calculate byte offsets and other details and used many more hours to iron out outliers.

Phase 4. Redemption

Slowly but surely learning basic idiomatic Rust techniques, from traits to proper error handling. Some terms for very common things (e.g. interfaces) are intriguingly reinvented, which can make it difficult since many of Rust's actually different things take much more time to learn (e.g. lifetimes, type system generally). However, after grasping how Rust wants you to do things I found myself thoroughly enjoying the experience.
Fortunately rsmkswap has an extremely simple runtime, typically lasting only milliseconds with a single main flow of execution. I am very happy not to have had to worry about lifetimes or async or multithreading, since
it let me focus on learning about endianness, struct padding, filesystem compatibility, file holes, and all the other beautiful foundational computing concepts that mkswap taught me. It wasn't easy or fast, and not always fun, but it did leave me with a strange satisfaction to be able to use my own tool to create a swap file or disk, even if I use btrfs.

rsmkwap_demo.gif 366 KB
Here's a demo of building and using rsmkswap to create and activate a swapfile on Arch Linux (btw) running btrfs. The 'C' flag in the lsattr output indicates that the file is marked with the NOCOW attribute, so btrfs' copy-on-write mechanism doesn't apply to the swapfile. 

When CoW is enabled, the filesystem never directly overwrites a file with new data, instead writing the modifications to a different region on the disk, only updating the file's metadata once the write is successfully completed. This is great for data integrity, since the original data is not gone, and only the pointers to blocks on the disk change.
However, a swapfile is directly written to by the kernel's memory or swap subsystem, which doesn't keep track or support much of the magic that btrfs does. It is also written to rapidly and with various mumbojumbo from program memory. These various issues could lead to data corruption due to the kernel not keeping up with where the swap data actually gets written and terrible performance even if it did.
Visit project