There were some subtle bugs when handling symlinks to directories, and
other usrmerge bits. Rework the symlink handling to deal with this
properly and produce a sane ramdisk on usrmerge systems.
Signed-off-by: Caleb Connolly <caleb@connolly.tech>
Co-authored-by: Clayton Craft <clayton@craftyguy.net>
Found by fieldalignment:
archive.go:46:14: struct with 88 pointer bytes could be 56
archive.go:66:18: struct with 24 pointer bytes could be 16
The first one probably doesn't matter that much, there's only like 2 of
those objects that are instantiated at runtime. However, there are many
ArchiveItems (hundreds or more depending on the archives compositions)
I went with a simpler implementation that uses Go compression packages
to do the work. The downside of this is that the compression Level is a
bit weird to set, since most libraries discourage setting the numeric
compression level directly.
This is configured by setting `deviceinfo_initfs_compression`, the value
it expects is a string in the form: `FORMAT[:LEVEL]`, where `[:LEVEL]`
is optional. Actually setting the variable at all is optional... if
nothing is specified, or it can't parse the format/level from the string
value, it defaults to using gzip with the "default" level for the
package (which tries to mirror gzip's default, or something).
The level can be one of `default`, `fast`, `best`.
To configure gzip with the fastest compression (so, bigger size): deviceinfo_initfs_compression="gzip:fast"`
To configure zstd with the most compression: `deviceinfo_initfs_compression="zstd:best"`
To configure zstd with default compression: `deviceinfo_initfs_compression="zstd"` (or `deviceinfo_initfs_compression="zstd:default"`)
In this case, `gzip:default` is assumed: deviceinfo_initfs_compression="bananas:mmmm"`
There's really not a great way to map individual levels to each
compression library, so this just adds a new type that will invoke the
three relevant levels for each library used. This could be improved in
the future.
This replaces the parallel gzip with the boring gzip from Go's standard
lib. The main motivations for doing this are:
1) Reduce runtime memory requirements
2) shed some external dependencies
There's obviously a trade-off with compression speed/time (as seen
below), but I feel like it's a worthwhile trade-off.
Note that there's likely very little impact to boot performance wrt
extracting these archives, the compression levels are similar.
Measured on a Shift 6mq, which is a very fast phone...
** compress/gzip:
User time (seconds): 1.81
System time (seconds): 0.38
Percent of CPU this job got: 104%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:02.09
Maximum resident set size (kbytes): 62024
-rw-r--r-- 1 clayton clayton 6.1M Sep 20 17:20 initramfs
-rw-r--r-- 1 clayton clayton 2.5M Sep 20 17:20 initramfs-extra
** pgzip:
User time (seconds): 1.19
System time (seconds): 0.48
Percent of CPU this job got: 159%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:01.05
Maximum resident set size (kbytes): 109952
-rw-r--r-- 1 clayton clayton 6.8M Sep 20 17:20 initramfs
-rw-r--r-- 1 clayton clayton 2.8M Sep 20 17:20 initramfs-extra
inspired by: https://gitlab.com/postmarketOS/pmaports/-/issues/1704