-
Notifications
You must be signed in to change notification settings - Fork 25
WIP: add native rust-based initramfs generator, init and qemu wrapper #258
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ddiss
wants to merge
212
commits into
rapido-linux:master
Choose a base branch
from
ddiss:rs_wip
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Crosvm's rust argument library is very small and simple, while still providing helpful functionality. It will be consumed by dracut-cpio in a subsequent commit. The unmodified, BSD licensed argument.rs source is lifted as-is from https://chromium.googlesource.com/chromiumos/platform/crosvm (release-R92-13982.B b6ae6517aeef9ae1e3a39c55b52f9ac6de8edb31). The one-line crosvm.rs wrapper is needed to ensure that crosvm::argument imports continue to work. Signed-off-by: David Disseldorp <ddiss@suse.de>
dracut-cpio is a minimal cpio archive creation utility written in Rust. It provides support for a minimal set of features needed to create performant and space-efficient initramfs archives: - "newc" archive format only - reproducible; inode numbers, uid/gid and mtime can be explicitly set - data segment copy-on-write reflinks + using Rust io::copy()'s native copy_file_range() support[1] + optional archive data segment alignment for optimal reflink use[2] - hardlink support - comprehensive tests asserting GNU cpio binary output compatibility 1. Rust io::copy() copy_file_range() rust-lang/rust#75272 2. Data segment alignment We're bending the newc spec a bit to inject zeros after the file path to provide data segment alignment. These zeros are accounted for in the namesize, but some applications may only expect a single zero-terminator (and 4 byte alignment). GNU cpio and Linux initramfs handle this fine as long as PATH_MAX isn't exceeded. Signed-off-by: David Disseldorp <ddiss@suse.de>
This is a workaround for GRUB2's Btrfs implementation, which doesn't correctly handle gaps between extents. A fix has already been proposed upstream via https://lists.gnu.org/archive/html/grub-devel/2021-10/msg00206.html Given that this bug is severe, it makes sense to include this minimal workaround. Signed-off-by: David Disseldorp <ddiss@suse.de>
This will be used for future device major/minor testing. Convert the current fifo test to use it. Signed-off-by: David Disseldorp <ddiss@suse.de>
This tests dracut-cpio's handling of rmajor / rminor values compared to GNU cpio. The test requires root, due to mknod invocation for block device node creation. Signed-off-by: David Disseldorp <ddiss@suse.de>
dev_t -> major/minor number mapping is more complicated than the incorrect major=(dev_t >> 8) minor=(dev_t & 0xff) mapping that we currently perform. Fix mapping to match Linux / glibc behaviour. Fixes: dracutdevs/dracut#1695 Reported-by: Ethan Wu <ethanwu10@gmail.com> Signed-off-by: David Disseldorp <ddiss@suse.de>
The previous dracut-cpio commits were applied verbatim from patches generated from the dracut source via: git format-patch 94fc50262f5e6c28d92782dc231fbb6c61855954^..94fc50262f5e6c28d92782dc231fbb6c61855954 git format-patch a9c67046431ccf5fd4f4c16c890695df388f0d38^..a9c67046431ccf5fd4f4c16c890695df388f0d38 git format-patch 0af11c5ea5018a3e1049a2207a9a671049651876^..0af11c5ea5018a3e1049a2207a9a671049651876 git format-patch 80e70f76d92b1a1c8e5cd10a06b70ef3f97d0899^..80e70f76d92b1a1c8e5cd10a06b70ef3f97d0899 git format-patch 8bd7ddf8197c14532cf05edac3203d08798af6f2^..8bd7ddf8197c14532cf05edac3203d08798af6f2 git format-patch acc629abb0d7a26f692f99e5a9cf8c8401bc6a86^..acc629abb0d7a26f692f99e5a9cf8c8401bc6a86 This change moves the nested src/dracut-cpio/ Cargo project to the root, with source in src/main.rs . third_party is also moved under src. Signed-off-by: David Disseldorp <ddiss@suse.de>
warning: call to `.clone()` on a reference in this situation does
nothing
--> src/main.rs:289:27
|
289 | let mut outpath = path.clone();
| ^^^^^^^^
|
= note: the type `Path` does not implement `Clone`, so calling
`clone` on `&Path` copies the reference, which does not do anything and
can be removed
= note: `#[warn(noop_method_call)]` on by default
help: remove this redundant call
|
289 - let mut outpath = path.clone();
289 + let mut outpath = path;
Signed-off-by: David Disseldorp <ddiss@suse.de>
dracut-cpio unit tests compare binary archive output with that of GNU
cpio, for the same set of input files. A recent change to upstream GNU
cpio, commit 6a94d5e ("New option --ignore-dirnlink"), causes some tests
to fail.
The failure is due to GNU cpio `--reproducible` now hardcoding directory
nlink values to 2, instead of using the st_nlink value reported by
stat().
Fix the unit tests by dropping the GNU cpio `--reproducible` alias
parameter, and instead specify `--ignore-devno --renumber-inodes`
explicitly, matching pre-6a94d5e GNU cpio `--reproducible` behaviour.
This fix has also been submitted to upstream dracut-ng.
Signed-off-by: David Disseldorp <ddiss@suse.de>
The 'std::' namespace prefix is unnecessary so drop it. Signed-off-by: David Disseldorp <ddiss@suse.de>
In preparation for reusing the core cpio archiving library code for rapido. Signed-off-by: David Disseldorp <ddiss@suse.de>
The newly bundled dracut-cpio source is GPL-2.0 licensed, with and additional BSD licensed third_party crosvm argument parsing library. People should rely on the per-file license headers as an indicator. Signed-off-by: David Disseldorp <ddiss@suse.de>
The compiler warns that it's unused for the normal build. Signed-off-by: David Disseldorp <ddiss@suse.de>
Obtained from a https://github.com/cole14/rust-elf clone via: $ git archive --format=tgz -o rust-elf-v0.8.0.tgz v0.8.0 $ sha256sum rust-elf-v0.8.0.tgz 082a203a8b47a94cff85d055b03852dc69291c0d4c51d82d7b43c65fdd2a9188 The v0.8.0 tag references c4d5222a34a97e113f863f80399284767d725e28 . Signed-off-by: David Disseldorp <ddiss@suse.de>
Use rust-elf to recursively walk through all ELF dependencies for a
given binary.
Plenty of missing bits:
- hardcoded input: only looks for 'ls' dependencies
- directory and kernel modules handling
- cpio archive generation
- lacks proper error handling
- is written by someone who doesn't know idiomatic rust (me)
- not sure I want to learn it; heavy abstraction and explicit
lifetimes scare me
Signed-off-by: David Disseldorp <ddiss@suse.de>
`cargo test` runs tests in parallel by default, unless the --test-threads=1 parameter is provided. This causes the dracut-cpio unit tests to fail due to their working directory changes. Add a mutex and hold it over the course of the changed directory, so that the --test-threads=1 parameter is no longer needed. Suggested-by: Benjamin Drung <bdrung@posteo.de> Fixes: dracut-ng/dracut-ng#1702 Signed-off-by: David Disseldorp <ddiss@suse.de>
Drop the hardcoded 'ls' binary and allow users to provide a space separated install list, e.g. ./rapido-cut --install "ls bash" my.initramfs The actual cpio archive generation isn't hooked up yet. Signed-off-by: David Disseldorp <ddiss@suse.de>
Archive entries are written out as the files are found, so ideally we can reuse any file handle we have around from the elf parsing. Next steps will be to use the rust-elf file-seek API, so that we're not buffering the entire file data. Signed-off-by: David Disseldorp <ddiss@suse.de>
rust-elf provides a seeking file API via the "std" feature, so use it instead of buffering the entire file for parsing. Signed-off-by: David Disseldorp <ddiss@suse.de>
We're archiving paths immediately as they're found, so we shouldn't need to track this separately, at least not for now. Signed-off-by: David Disseldorp <ddiss@suse.de>
Perform the stat in the caller instead, so that rapido-cut can avoid a double stat. Signed-off-by: David Disseldorp <ddiss@suse.de>
This should be squashed with the previous commit to avoid breaking bisect. Signed-off-by: David Disseldorp <ddiss@suse.de>
The main reason to split this from archive_path is to support pre-opened file descriptors. E.g. For rapido-cut this will allow us to reuse the elf_deps fd instead of closing and reopening to write file data to the cpio archive. Signed-off-by: David Disseldorp <ddiss@suse.de>
Reuse the elf_deps fd for it. Signed-off-by: David Disseldorp <ddiss@suse.de>
Callers must now use archive_file for files and archive_path for anything else, to allow for open-fd reuse when writing file data. Convert dracut-cpio and hardlink callers.
A typo that I made in the initial dracut-cpio implementation sees the stat()-reported major number used for both major and minor number in the cpio archive. Device major / minor numbers (as opposed to rmajor / rminor numbers) are mostly ignored by initramfs, with the exception of hardlink association. I've not seen any bug reports from users hitting this in the wild, but theoretically cross-device archives could carry incorrectly colliding major/minor/inode triplets which erroneously trigger initramfs hardlink handling. Signed-off-by: David Disseldorp <ddiss@suse.de>
initramfs / cpio allow for the tracking of hardlinks for nlink >= 2 entries using a combination of the inode, device major and minor numbers. dracut-cpio uses unique inode numbers within an archive via the global state.ino counter. Device major/minor numbers are also renumbered, with each unique source device obtaining a major/minor number mapped from the index within dev_seen()/DevState array. With archive-unique inode numbers, device major/minor mapping is unnecessary. This change sees dracut-cpio behave the same as GNU cpio --ignore-devno, where archive device major/minor numbers are hardcoded to zero. Hardlink tracking is simplified, replacing per-device HardlinkState arrays with a global state.hls array. A hash could be used for faster source inode+dev -> archive HardlinkState mapping, but the extra size and complexity isn't worth it IMO, given that hardlinks should be rare. Signed-off-by: David Disseldorp <ddiss@suse.de>
Inode numbers are unique (for non-hardlinks) within the archive, so device ID mapping is unnecessary. Confirm that dracut-cpio behaves like GNU cpio --ignore-devno. Check this by archiving the /tmp directory alongside a working-directory nested file; despite differing source device IDs, the archived major/minor numbers should be zero. The test is skipped if stat(/tmp) fails, or working-directory and /tmp device ids match. Signed-off-by: David Disseldorp <ddiss@suse.de>
This is currently all my work (at SUSE), including dracut-cpio. Using (GPL-2.0 OR GPL-3.0) instead of GPL-2.0 only makes the rust codebase a little more compatible with other projects, while remaining copyleft. Signed-off-by: David Disseldorp <ddiss@suse.de>
Likely only useful for rapido.rs, but I've split it out as a separate project at https://github.com/ddiss/kv-conf . This source corresponds to commit 1f8d1448c613f87ed681a928c61b686914273b6e. Signed-off-by: David Disseldorp <ddiss@suse.de>
Ubuntu 24.04.3 LTS can run rust-based rapido using the distro kernel. However, simple-example isn't an option due to a lack of zram, so use simple-network instead. Signed-off-by: David Disseldorp <ddiss@suse.de>
Signed-off-by: David Disseldorp <ddiss@suse.de>
It appears to be present on the Github VM, so see whether we can use it. Some additional changes split out the cut / boot tests, so that we can inspect the initramfs image before boot. Signed-off-by: David Disseldorp <ddiss@suse.de>
Signed-off-by: David Disseldorp <ddiss@suse.de>
On Ubuntu, initramfs-tools-core carries an lsinitramfs script, which is a wrapper for unmkinitramfs --list, which is a wrapper for GNU cpio. Signed-off-by: David Disseldorp <ddiss@suse.de>
Use something a little shorter. I've left the src/bin/kmod/main.rs example code as-is. Signed-off-by: David Disseldorp <ddiss@suse.de>
Pass the mutable line buffer to kv_process, so that it can clear any
processed data and retain unprocessed multi-line portions. A (now
non-mut) map still needs to be provided for ${} variable substitution.
This should put us in a better position to support callback based kv
parsing or (perhaps?) iter().
Signed-off-by: David Disseldorp <ddiss@suse.de>
Signed-off-by: David Disseldorp <ddiss@suse.de>
Like bash, we should return an error if we hit EOF before the closing quote of a (multi-line assumed) key/val pair. Also add an extra test for comment parsing, where unquoted # without a preceding space should be handled as part of the value string. Signed-off-by: David Disseldorp <ddiss@suse.de>
All errors are InvalidInput here, so it's simpler if we just return a Result<..., &'static str>. Signed-off-by: David Disseldorp <ddiss@suse.de>
All errors are InvalidInput here, so it's simpler if we just return a Result<..., &'static str>. We might want to change over the kv_conf_process[_append] return types in future too, but Err mapping in callers would probably make it a bit ugly. Signed-off-by: David Disseldorp <ddiss@suse.de>
This function is the same as kv_conf_process_append() except variable lookup is done from @varmap, while kv entries are stored separately in @Map. It will (hopefully) be useful for rapido-cut manifest file support. To avoid too much duplication, the rdr.read_line() call and error handling is moved into a separate helper. Signed-off-by: David Disseldorp <ddiss@suse.de>
In preparation for reusing some of these parameters for manifest files. Signed-off-by: David Disseldorp <ddiss@suse.de>
Manifest files carry key=value entries, where "key" can only be one of:
install, try_install, include, kmods, autorun
These keys map directly to the corresponding rapido-cut command line
parameter; parsing logic for manifest keys and cli parameters is shared.
Manifest file values may include rapido.conf keys as variables, e.g.
include="${VM_NET_CONF} /net"
Signed-off-by: David Disseldorp <ddiss@suse.de>
Signed-off-by: David Disseldorp <ddiss@suse.de>
The new manifest/net.fest file carries all of the dependencies that were
automatically added via --net.
Pros:
- drop a workload specific parameter
- easy to change at runtime instead of build-time only
Cons:
- slight cut slowdown expected, due to kv-conf file parsing
- confusing:
- no net specific autorun; it's handed by rapido-init
- rapido-vm's rapido-rsc parsing optimization seems more fragile with
this, as control over the order of archiving is more difficult
Given the cons above, this change may be reverted in future (for net
*only* - I think generic manifest support should stay).
Signed-off-by: David Disseldorp <ddiss@suse.de>
When determining VM cpu/mem/net resources, rapido-vm breaks cpio traversal when moving past /rapido-rsc/* paths as an optimization. Therefore we need to ensure that these paths are grouped together. Do this by prepending any include destination path that starts with "/rapido-rsc" to the data.items list. Sangeetha helpfully pointed out that Path::starts_with() works fine even if multiple path separators are used. One subtle change in behaviour here is that rapido-rsc paths will be ordered in the reverse of where they're specified, which makes a difference for resource parameter override. E.g: --include "X /rapido-rsc/mem/4G" --include "X /rapido-rsc/mem/5G" Prior to this change, rapido-vm would boot with 5G while now it'll boot with 4G. This could be easily fixed by using a state.rsc_insert_off variable but I don't think it's necessary for now. One further note regarding /rapido-rsc/net subdirectories: these are discovered as part of gather_archive_data() directory traversal and won't be prepended, but that doesn't matter; rapido-vm only checks for /rapido-rsc/net parent path presence. Signed-off-by: David Disseldorp <ddiss@suse.de>
s.parse::<u32>() is called twice. Not sure whether the compiler optimizes out the second call, but cleaning it up is quicker than checking. Add a couple of extra rsc parsing test cases to ensure double-unit mem values don't slip through. Signed-off-by: David Disseldorp <ddiss@suse.de>
network kmods are now covered by the net.fest manifest file, so change rapido-init to explicitly add virtio_net and af_packet to the modprobe list if needed. Signed-off-by: David Disseldorp <ddiss@suse.de>
kcli_parse() is fed the entire /proc/cmdline buffer, and only splits on
b' ' match. Ignore any newline to ensure that it doesn't get picked up
by e.g. the hostname (via vm_num).
Also, sprinkle some lifetime syntax based on following compiler
suggestion:
--> src/bin/rapido-init.rs:71:25
|
71 | fn kcli_parse(kcmdline: &[u8]) -> io::Result<KcliArgs> {
| ^^^^^ ^^^^^^^^ the same lifetime is hidden here
| |
| the lifetime is elided here
|
= help: the same lifetime is referred to in inconsistent ways, making the signature confusing
= note: `#[warn(mismatched_lifetime_syntaxes)]` on by default
help: use `'_` for type paths
|
71 | fn kcli_parse(kcmdline: &[u8]) -> io::Result<KcliArgs<'_>> {
| ++++
I *think* we might be able to avoid this lifetime syntax by moving
(instead of borrowing) @kcmdline for the function call, but I don't know
how to do that without an extra type to wrap the array <shrug>.
Signed-off-by: David Disseldorp <ddiss@suse.de>
Check the hosts stty size when booting and pass to the VM via rapido.stty=<rows>,<columns>. This allows us to avoid using the xterm provided resize command for TIOCSWINSZ. Link: rapido-linux#259 Signed-off-by: David Disseldorp <ddiss@suse.de>
For install binaries, we currently only check for elf dependencies if any execute mode bits are set. This is not the case for libraries, where mode flags are ignored. In preparation for combining bin and lib gathering, drop the exec mode flag check and collect elf dependencies unconditionally. I don't expect performance to take much of a hit from this change, as we have a file handle ready for archiving. Besides, include / data gathering can be used for non-elf types. It may make sense to revert this change if support for recursive directory tree --install support is added. E.g. consider xfstests where some ELF binaries are scattered amongst a large number of non-ELF files, where many (but non-all) non-ELF files don't carry the exec mode flag. Signed-off-by: David Disseldorp <ddiss@suse.de>
This should allow us to more easily combine lib and bin gathering. Search path determination is moved from the caller into path_stat, and based on the GatherEnt type. Signed-off-by: David Disseldorp <ddiss@suse.de>
These functions have always been very similar. With separate Lib and Bin (Name) GatherEnt items it's a relatively straightforward merge. Signed-off-by: David Disseldorp <ddiss@suse.de>
gather_archive_elfs() is now the only caller. Signed-off-by: David Disseldorp <ddiss@suse.de>
archive_path already handles zero-length files fine, so fallback to it if md.len is zero. Signed-off-by: David Disseldorp <ddiss@suse.de>
The caller ignores all errors, so move error messaging closer to where the errors occur and relocate some comments too. Signed-off-by: David Disseldorp <ddiss@suse.de>
Commit 88fdb59 ("rapido-cut: drop exec mode req for bin elf_deps()") removed this optimization, but the new merged lib/bin gathering functionality makes it easy to add back cleanly. Signed-off-by: David Disseldorp <ddiss@suse.de>
Manifests are never added as NameTry types. Signed-off-by: David Disseldorp <ddiss@suse.de>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Sangeetha and I have been working on some changes to replace Dracut with
a rust-based initramfs / cpio image generator. The main reasons for this
are:
locally available
modules, and kernel / user dependency gathering for cpio
and is mostly written in bash
The rewrite builds on my previous dracut-cpio implementation and adds:
parsing (thanks @thackara !)
scripts
It's otherwise kept as minimal as possible, with rust-elf and the std
library the only major external dependencies. One single-file crosvm
argument parser is also bundled.
The preliminary benchmark results look good, particularly for initramfs
image generation (cut):
To try these changes yourself, check out this branch and run:
You may need to change your rapido.conf file a little if you
use env variables or shell callouts.
I'm flagging this as WIP, as there are still a few things to do:
in the working directory
I don't expect to convert remaining cut scripts before merge. Dracut and
rust based functionality should be able to live side by side, although
rapido.conf parsing is much less flexible in rust: no invocations,
currently no env var expansion, variables must be wrapped in {}.