Package: release.debian.org Severity: normal X-Debbugs-Cc: rust-virtio...@packages.debian.org, pkg-qemu-de...@lists.alioth.debian.org Control: affects -1 + src:rust-virtiofsd User: release.debian....@packages.debian.org Usertags: unblock
Please unblock package rust-virtiofsd [ Reason ] There's a new upstream minor/bugfix release of virtiofsd, fixing a number of bugs/omissions in previous releases and adding some docs. In particular, it adds some docs about how to use virtiofsd as non-privileged user, which is a confusing topic for some users. [ Tests ] This release passes all debian automatic tests (there are quite some now), and my internal usage testing too, showing no regressions. [ Risks ] The changes in this (upstream) version are rather small and focused. I don't expect much risk in this case. [ Checklist ] [?] all changes are documented in the d/changelog [x] I reviewed all changes and I approve them [x] attach debdiff against the package in testing [ Other info ] Maybe I should've add this info to d/changelog directly (our rust tooling in debian fills d/changelog automatically). Here's the actual set of upstream changes: * v1.13.2: - https://gitlab.com/virtio-fs/virtiofsd/-/merge_requests/286 Call setgroups syscall directly - https://gitlab.com/virtio-fs/virtiofsd/-/merge_requests/285 README.md: expand section on non-privileged running - https://gitlab.com/virtio-fs/virtiofsd/-/merge_requests/282 Fix/silence new clippy warnings - https://gitlab.com/virtio-fs/virtiofsd/-/merge_requests/278 Fix new clippy warning(s)/CI - https://gitlab.com/virtio-fs/virtiofsd/-/merge_requests/277 Document root-less usage using util-linux's unshare - https://gitlab.com/virtio-fs/virtiofsd/-/merge_requests/262 Limit guest FD allocation * v1.13.1: - https://gitlab.com/virtio-fs/virtiofsd/-/merge_requests/275 Enable --xattr when --security-label is used - https://gitlab.com/virtio-fs/virtiofsd/-/merge_requests/274 seccomp: Allow tkill syscall - https://gitlab.com/virtio-fs/virtiofsd/-/merge_requests/273 CI: Disable clippy::manual-c-str-literals warning Unfortunately this is just in upstream commit messages, not in any README/NEWS files, and sort of difficult to find. Either way, the changes do look fine (each of them). If this package isn't unblocked before trixie release, I'll be filing a s-p-u bug request with this package (maybe after adding the missing d/changelog info from the above). unblock rust-virtiofsd/1.13.2-1 The debdiff follows. Thanks, /mjt diff -Nru rust-virtiofsd-1.13.0/.cargo_vcs_info.json rust-virtiofsd-1.13.2/.cargo_vcs_info.json --- rust-virtiofsd-1.13.0/.cargo_vcs_info.json 1970-01-01 03:00:01.000000000 +0300 +++ rust-virtiofsd-1.13.2/.cargo_vcs_info.json 1970-01-01 03:00:01.000000000 +0300 @@ -1,6 +1,6 @@ { "git": { - "sha1": "3bf77b7cfa42b23935968c757a4f22ed31bc35d6" + "sha1": "720245d0857e9837484488ff69c2f8c577466851" }, "path_in_vcs": "" } \ No newline at end of file diff -Nru rust-virtiofsd-1.13.0/Cargo.lock rust-virtiofsd-1.13.2/Cargo.lock --- rust-virtiofsd-1.13.0/Cargo.lock 1970-01-01 03:00:01.000000000 +0300 +++ rust-virtiofsd-1.13.2/Cargo.lock 1970-01-01 03:00:01.000000000 +0300 @@ -875,7 +875,7 @@ [[package]] name = "virtiofsd" -version = "1.13.0" +version = "1.13.2" dependencies = [ "bitflags 1.3.2", "btree-range-map", diff -Nru rust-virtiofsd-1.13.0/Cargo.toml rust-virtiofsd-1.13.2/Cargo.toml --- rust-virtiofsd-1.13.0/Cargo.toml 1970-01-01 03:00:01.000000000 +0300 +++ rust-virtiofsd-1.13.2/Cargo.toml 1970-01-01 03:00:01.000000000 +0300 @@ -12,10 +12,11 @@ [package] edition = "2018" name = "virtiofsd" -version = "1.13.0" +version = "1.13.2" authors = ["The Virtiofs Project Developers"] build = false exclude = [".gitlab-ci.yml"] +autolib = false autobins = false autoexamples = false autotests = false @@ -26,8 +27,14 @@ license = "Apache-2.0 AND BSD-3-Clause" repository = "https://gitlab.com/virtio-fs/virtiofsd" -[profile.release] -lto = true +[features] +default = ["seccomp"] +seccomp = ["dep:libseccomp-sys"] +xen = [ + "vhost-user-backend/xen", + "vhost/xen", + "vm-memory/xen", +] [lib] name = "virtiofsd" @@ -101,11 +108,5 @@ [dependencies.vmm-sys-util] version = "0.12.1" -[features] -default = ["seccomp"] -seccomp = ["dep:libseccomp-sys"] -xen = [ - "vhost-user-backend/xen", - "vhost/xen", - "vm-memory/xen", -] +[profile.release] +lto = true diff -Nru rust-virtiofsd-1.13.0/Cargo.toml.orig rust-virtiofsd-1.13.2/Cargo.toml.orig --- rust-virtiofsd-1.13.0/Cargo.toml.orig 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/Cargo.toml.orig 2006-07-24 05:21:28.000000000 +0400 @@ -1,7 +1,7 @@ [package] name = "virtiofsd" description = "A virtio-fs vhost-user device daemon" -version = "1.13.0" +version = "1.13.2" authors = ["The Virtiofs Project Developers"] edition = "2018" homepage = "https://virtio-fs.gitlab.io/" diff -Nru rust-virtiofsd-1.13.0/README.md rust-virtiofsd-1.13.2/README.md --- rust-virtiofsd-1.13.0/README.md 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/README.md 2006-07-24 05:21:28.000000000 +0400 @@ -145,7 +145,7 @@ ```shell --security-label ``` -Enable support for security label (SELinux). +Enable support for security label (SELinux). Implies --xattr. ```shell --preserve-noatime @@ -512,12 +512,27 @@ See [FAQ](#faq) for adding virtiofs config to an existing qemu command-line. ### Running as non-privileged user -When run without root, virtiofsd requires a user namespace (see `user_namespaces(7)`) -to be able to switch between arbitrary user/group IDs within the guest. -virtiofsd will fail in a user namespace where UIDs/GIDs have not been mapped -(i.e., `uid_map` and `gid_map` files have not been written). -There are many options to run virtiofsd inside a user namespace. -For instance: + +virtiofsd can be run as a non-privileged user without a sandbox. If +you take care to ensure the UID/GID used in the guest matches the +effective UID of virtiofsd on the host you will be able to mount your +host $HOME into the guest and use it transparently. + +If the UID/GID on the guest is different from the host but you only +expect a single user to be accessing files you can use +`--translate-uid` to map it to the host UID: + +```shell +--translate-uid=map:<guest UID>:<host UID>:1 +``` + +with a similar mapping for `--translate-gid`. + +If you want to support multiple user/group IDs within the guest, you +can use subordinate UIDs/GIDs (subuids/subgids) that virtiofsd can +then use despite not running as root. There are many options to employ +a user namespace to map those subuids/subgids for virtiofsd to use for +the guest, for instance: Let's assume the invoking UID and GID is 1000 and the content of both `/etc/subuid` and `/etc/subgid` are: @@ -534,6 +549,13 @@ host$ podman unshare -- virtiofsd --socket-path=/tmp/vfsd.sock --shared-dir /mnt \ --announce-submounts --sandbox chroot & ``` +Alternatively we can also achieve the same effect without Podman by relying on `unshare(1)` included in +`util-linux` which has the benefit that it should already be installed for most users. Use it like so: + +```shell +host$ unshare -r --map-auto -- virtiofsd --socket-path=/tmp/vfsd.sock --shared-dir /mnt \ + --announce-submounts --sandbox chroot & +``` Using `lxc-usernsexec(1)`, we could leave the invoking user outside the mapping, having the root user inside the user namespace mapped to the user and group 100000: diff -Nru rust-virtiofsd-1.13.0/debian/changelog rust-virtiofsd-1.13.2/debian/changelog --- rust-virtiofsd-1.13.0/debian/changelog 2025-01-31 14:10:04.000000000 +0300 +++ rust-virtiofsd-1.13.2/debian/changelog 2025-07-10 20:02:47.000000000 +0300 @@ -1,3 +1,9 @@ +rust-virtiofsd (1.13.2-1) unstable; urgency=medium + + * Package virtiofsd 1.13.2 from crates.io using debcargo 2.7.8 + + -- Michael Tokarev <m...@tls.msk.ru> Thu, 10 Jul 2025 20:02:47 +0300 + rust-virtiofsd (1.13.0-5) unstable; urgency=medium [ Luca Boccassi ] diff -Nru rust-virtiofsd-1.13.0/debian/control rust-virtiofsd-1.13.2/debian/control --- rust-virtiofsd-1.13.0/debian/control 2025-01-31 14:10:04.000000000 +0300 +++ rust-virtiofsd-1.13.2/debian/control 2025-07-10 20:02:47.000000000 +0300 @@ -3,7 +3,8 @@ Priority: optional Build-Depends: debhelper-compat (= 13), dh-sequence-cargo, - cargo:native, + architecture-is-64-bit +Build-Depends-Arch: cargo:native, rustc:native, libstd-rust-dev, librust-bitflags-1+default-dev (>= 1.2-~~), @@ -30,8 +31,7 @@ librust-vm-memory-0.16+backend-atomic-dev, librust-vm-memory-0.16+backend-mmap-dev, librust-vm-memory-0.16+default-dev, - librust-vmm-sys-util-0.12+default-dev (>= 0.12.1-~~), - architecture-is-64-bit + librust-vmm-sys-util-0.12+default-dev (>= 0.12.1-~~) Maintainer: Debian Rust Maintainers <pkg-rust-maintain...@alioth-lists.debian.net> Uploaders: Fabian Grünbichler <debian@fabian.gruenbichler.email>, @@ -88,16 +88,15 @@ librust-virtiofsd-1.13+default-dev (= ${binary:Version}), librust-virtiofsd-1.13+seccomp-dev (= ${binary:Version}), librust-virtiofsd-1.13+xen-dev (= ${binary:Version}), - librust-virtiofsd-1.13.0-dev (= ${binary:Version}), - librust-virtiofsd-1.13.0+default-dev (= ${binary:Version}), - librust-virtiofsd-1.13.0+seccomp-dev (= ${binary:Version}), - librust-virtiofsd-1.13.0+xen-dev (= ${binary:Version}) + librust-virtiofsd-1.13.2-dev (= ${binary:Version}), + librust-virtiofsd-1.13.2+default-dev (= ${binary:Version}), + librust-virtiofsd-1.13.2+seccomp-dev (= ${binary:Version}), + librust-virtiofsd-1.13.2+xen-dev (= ${binary:Version}) Description: Virtio-fs vhost-user device daemon - Rust source code Source code for Debianized Rust crate "virtiofsd" Package: virtiofsd Architecture: any -Multi-Arch: allowed Section: otherosfs Depends: ${misc:Depends}, diff -Nru rust-virtiofsd-1.13.0/debian/copyright rust-virtiofsd-1.13.2/debian/copyright --- rust-virtiofsd-1.13.0/debian/copyright 2025-01-31 14:10:04.000000000 +0300 +++ rust-virtiofsd-1.13.2/debian/copyright 2025-07-10 20:02:47.000000000 +0300 @@ -37,6 +37,7 @@ src/passthrough/device_state/serialization.rs src/passthrough/device_state/serialized.rs src/passthrough/file_handle.rs + src/passthrough/guest_fd_limit.rs src/passthrough/read_only.rs src/passthrough/stat.rs src/sandbox.rs diff -Nru rust-virtiofsd-1.13.0/debian/copyright.debcargo.hint rust-virtiofsd-1.13.2/debian/copyright.debcargo.hint --- rust-virtiofsd-1.13.0/debian/copyright.debcargo.hint 2025-01-31 14:10:04.000000000 +0300 +++ rust-virtiofsd-1.13.2/debian/copyright.debcargo.hint 2025-07-10 20:02:47.000000000 +0300 @@ -131,6 +131,13 @@ FIXME (overlay): These notices are extracted from files. Please review them before uploading to the archive. +Files: src/passthrough/guest_fd_limit.rs +Copyright: 2024 Red Hat, Inc. All rights reserved. +License: UNKNOWN-LICENSE; FIXME (overlay) +Comment: + FIXME (overlay): These notices are extracted from files. Please review them + before uploading to the archive. + Files: src/passthrough/mod.rs Copyright: 2019 The Chromium OS Authors. All rights reserved. License: UNKNOWN-LICENSE; FIXME (overlay) diff -Nru rust-virtiofsd-1.13.0/debian/tests/control rust-virtiofsd-1.13.2/debian/tests/control --- rust-virtiofsd-1.13.0/debian/tests/control 2025-01-31 14:10:04.000000000 +0300 +++ rust-virtiofsd-1.13.2/debian/tests/control 2025-07-10 20:02:47.000000000 +0300 @@ -1,24 +1,24 @@ -Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.0 --all-targets --all-features +Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.2 --all-targets --all-features Features: test-name=rust-virtiofsd:@ Depends: dh-cargo (>= 31), rustc, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.0 --all-targets +Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.2 --all-targets Features: test-name=librust-virtiofsd-dev:default Depends: dh-cargo (>= 31), rustc, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.0 --all-targets --no-default-features --features seccomp +Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.2 --all-targets --no-default-features --features seccomp Features: test-name=librust-virtiofsd-dev:seccomp Depends: dh-cargo (>= 31), rustc, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.0 --all-targets --no-default-features --features xen +Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.2 --all-targets --no-default-features --features xen Features: test-name=librust-virtiofsd-dev:xen Depends: dh-cargo (>= 31), rustc, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.0 --all-targets --no-default-features +Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.2 --all-targets --no-default-features Features: test-name=librust-virtiofsd-dev: Depends: dh-cargo (>= 31), rustc, @ Restrictions: allow-stderr, skip-not-installable diff -Nru rust-virtiofsd-1.13.0/debian/tests/control.debcargo.hint rust-virtiofsd-1.13.2/debian/tests/control.debcargo.hint --- rust-virtiofsd-1.13.0/debian/tests/control.debcargo.hint 2025-01-31 14:10:04.000000000 +0300 +++ rust-virtiofsd-1.13.2/debian/tests/control.debcargo.hint 2025-07-10 20:02:47.000000000 +0300 @@ -1,24 +1,24 @@ -Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.0 --all-targets --all-features +Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.2 --all-targets --all-features Features: test-name=rust-virtiofsd:@ Depends: dh-cargo (>= 31), rustc, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.0 --all-targets +Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.2 --all-targets Features: test-name=librust-virtiofsd-dev:default Depends: dh-cargo (>= 31), rustc, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.0 --all-targets --no-default-features --features seccomp +Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.2 --all-targets --no-default-features --features seccomp Features: test-name=librust-virtiofsd-dev:seccomp Depends: dh-cargo (>= 31), rustc, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.0 --all-targets --no-default-features --features xen +Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.2 --all-targets --no-default-features --features xen Features: test-name=librust-virtiofsd-dev:xen Depends: dh-cargo (>= 31), rustc, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.0 --all-targets --no-default-features +Test-Command: /usr/share/cargo/bin/cargo-auto-test virtiofsd 1.13.2 --all-targets --no-default-features Features: test-name=librust-virtiofsd-dev: Depends: dh-cargo (>= 31), rustc, @ Restrictions: allow-stderr, skip-not-installable diff -Nru rust-virtiofsd-1.13.0/src/idmap.rs rust-virtiofsd-1.13.2/src/idmap.rs --- rust-virtiofsd-1.13.0/src/idmap.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/idmap.rs 2006-07-24 05:21:28.000000000 +0400 @@ -26,7 +26,7 @@ f, "The map is empty or incorrect number of values are provided" ), - IdMapError::InvalidValue(err) => write!(f, "{}", err), + IdMapError::InvalidValue(err) => write!(f, "{err}"), } } } diff -Nru rust-virtiofsd-1.13.0/src/limits.rs rust-virtiofsd-1.13.2/src/limits.rs --- rust-virtiofsd-1.13.0/src/limits.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/limits.rs 2006-07-24 05:21:28.000000000 +0400 @@ -46,18 +46,19 @@ } } -pub fn setup_rlimit_nofile(nofile: Option<u64>) -> Result<(), String> { +/// Set the limit of open files to the given value, returning the actual limit. +pub fn setup_rlimit_nofile(nofile: Option<u64>) -> Result<u64, String> { let max_nofile = get_max_nofile()?; let rlimit { rlim_cur, rlim_max } = get_nofile_limits()?; let target_limit = if let Some(nofile) = nofile { if nofile == 0 { - return Ok(()); // '--rlimit-nofile=0' leaves the resource limit unchanged + return Ok(rlim_cur); // '--rlimit-nofile=0' leaves the resource limit unchanged } nofile } else { if DEFAULT_NOFILE <= rlim_cur { - return Ok(()); // the user has already setup the soft limit higher than the target + return Ok(rlim_cur); // the user has already setup the soft limit higher than the target } cmp::min(DEFAULT_NOFILE, max_nofile) }; @@ -66,21 +67,23 @@ return Err(format!("It cannot be increased above {max_nofile}")); } - if let Err(error) = setup_rlimit_nofile_to(target_limit) { + let new_limit = if let Err(error) = setup_rlimit_nofile_to(target_limit) { if nofile.is_some() { // Error attempting to setup user-supplied value return Err(error); } else { warn!( - "Failure when trying to set the limit to {}, \ - the hard limit ({}) of open file descriptors is used instead.", - target_limit, rlim_max + "Failure when trying to set the limit to {target_limit}, \ + the hard limit ({rlim_max}) of open file descriptors is used instead." ); setup_rlimit_nofile_to(rlim_max).map_err(|error| { format!("Cannot increase the soft limit to the hard limit: {error}") - })? + })?; + rlim_max } - } + } else { + target_limit + }; - Ok(()) + Ok(new_limit) } diff -Nru rust-virtiofsd-1.13.0/src/main.rs rust-virtiofsd-1.13.2/src/main.rs --- rust-virtiofsd-1.13.0/src/main.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/main.rs 2006-07-24 05:21:28.000000000 +0400 @@ -12,7 +12,7 @@ use std::str::FromStr; use std::sync::Arc; use std::time::Duration; -use std::{env, process}; +use std::{cmp, env, process}; use virtiofsd::idmap::{GidMap, UidMap}; use clap::{CommandFactory, Parser}; @@ -33,6 +33,23 @@ use virtiofsd::{limits, oslib, soft_idmap}; use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; +/// Maximum number of memory areas (slots) supported by the vhost-user-backend crate. +/// +/// The constant has the same name there, but is not exported. +const MAX_MEM_SLOTS: u64 = 509; + +/// How many file descriptors to reserve for internal use. +/// +/// The exact value has been chosen mostly arbitrarily, but we know we need one FD per shared +/// memory area, which is where the `MAX_MEM_SLOTS` comes from. +/// +/// Given how allocating FD towards the guest quota works, we also need to add one FD per thread in +/// our pool: FDs are created first, and only then accounted for. All threads must be able to +/// simultaneously create an FD and then have it be accounted for, so we need to make room for as +/// many additional FDs as there are threads in the pool. The thread pool size is set at runtime, +/// though, so cannot be taken into account here, but instead where this constant is used. +const INTERNAL_FD_RESERVE: u64 = MAX_MEM_SLOTS + 100; + type Result<T> = std::result::Result<T, Error>; fn parse_seccomp(src: &str) -> std::result::Result<SeccompAction, &'static str> { @@ -254,7 +271,7 @@ #[arg(short = 'f')] compat_foreground: bool, - /// Enable security label support. Expects SELinux xattr on file creation + /// Enable security label support (implies --xattr). Expects SELinux xattr on file creation /// from client and stores it in the newly created file. #[arg(long = "security-label")] security_label: bool, @@ -349,10 +366,10 @@ /// different ways: /// /// - abort: Whenever any error occurs, return a hard error to the vhost-user front-end (e.g. - /// QEMU), aborting migration. + /// QEMU), aborting migration. /// /// - guest-error: Let migration finish, but the guest will be unable to access any of the - /// affected inodes, receiving only errors. + /// affected inodes, receiving only errors. /// /// This parameter is ignored on the source side. #[arg(long = "migration-on-error", default_value = "abort")] @@ -514,7 +531,7 @@ if opt.syslog { if let Err(e) = syslog::init(syslog::Facility::LOG_USER, log_level, None) { set_default_logger(log_level); - warn!("can't enable syslog: {}", e); + warn!("can't enable syslog: {e}"); } } else { set_default_logger(log_level); @@ -530,7 +547,7 @@ let signals = vec![libc::SIGHUP, libc::SIGTERM]; for s in signals { if let Err(e) = signal::register_signal_handler(s, handle_signal) { - error!("Setting signal handlers: {}", e); + error!("Setting signal handlers: {e}"); process::exit(1); } } @@ -552,14 +569,11 @@ let (action, cap_name) = modcap.split_at(1); let cap_name = cap_name.to_uppercase(); if !matches!(action, "+" | "-") { - error!( - "invalid modcap action: expecting '+'|'-' but found '{}'", - action - ); + error!("invalid modcap action: expecting '+'|'-' but found '{action}'"); process::exit(1); } if let Err(error) = capng::name_to_capability(&cap_name) { - error!("invalid capability '{}': {}", &cap_name, error); + error!("invalid capability '{cap_name}': {error}"); process::exit(1); } @@ -595,10 +609,7 @@ if inode_file_handles != InodeFileHandlesMode::Never { let required_cap = "DAC_READ_SEARCH".to_owned(); if disabled_caps.contains(&required_cap) { - error!( - "can't disable {} when using --inode-file-handles={:?}", - &required_cap, inode_file_handles - ); + error!("can't disable {required_cap} when using --inode-file-handles={inode_file_handles:?}"); process::exit(1); } required_caps.insert(required_cap); @@ -612,11 +623,11 @@ capng::Type::PERMITTED | capng::Type::EFFECTIVE, required_caps.iter().map(String::as_str).collect(), ) { - error!("can't set up the child capabilities: {}", e); + error!("can't set up the child capabilities: {e}"); process::exit(1); } if let Err(e) = capng::apply(capng::Set::BOTH) { - error!("can't apply the child capabilities: {}", e); + error!("can't apply the child capabilities: {e}"); process::exit(1); } } @@ -684,7 +695,7 @@ } let xattrmap = opt.xattrmap.clone(); - let xattr = xattrmap.is_some() || opt.posix_acl || opt.xattr; + let xattr = xattrmap.is_some() || opt.posix_acl || opt.security_label || opt.xattr; let thread_pool_size = opt.thread_pool_size; let readdirplus = match opt.cache { CachePolicy::Never => false, @@ -738,12 +749,12 @@ let pid_file_name = socket.to_owned() + ".pid"; let pid_file_path = Path::new(pid_file_name.as_str()); let pid_file = write_pid_file(pid_file_path).unwrap_or_else(|error| { - error!("Error creating pid file '{}': {}", pid_file_name, error); + error!("Error creating pid file '{pid_file_name}': {error}"); process::exit(1); }); let listener = Listener::new(socket, true).unwrap_or_else(|error| { - error!("Error creating listener: {}", error); + error!("Error creating listener: {error}"); process::exit(1); }); @@ -771,11 +782,25 @@ } } - limits::setup_rlimit_nofile(opt.rlimit_nofile).unwrap_or_else(|error| { - error!("Error increasing number of open files: {}", error); + let fd_count_limit = limits::setup_rlimit_nofile(opt.rlimit_nofile).unwrap_or_else(|error| { + error!("Error increasing number of open files: {error}"); process::exit(1) }); + // Account for guest FDs that are created first and only accounted for then (see doc comment on + // `INTERNAL_FD_RESERVE` + let internal_fd_reserve = INTERNAL_FD_RESERVE + cmp::max(opt.thread_pool_size as u64, 1); + let guest_fd_limit = fd_count_limit.checked_sub(internal_fd_reserve).unwrap_or_else(|| { + error!("Maximum number of file descriptors too small: Limit is {fd_count_limit}, must be at least {internal_fd_reserve}"); + process::exit(1) + }); + + // Warn the user if there is a suspiciously low limit on the guest FD count that will make it + // hard to actually do something; the number of `128` is completely arbitrary. + if guest_fd_limit < 128 { + warn!("File descriptor count limit is very small, leaving only {guest_fd_limit} file descriptors for the guest"); + } + let mut sandbox = Sandbox::new( shared_dir.to_string(), opt.sandbox, @@ -783,14 +808,14 @@ opt.gid_map, ) .unwrap_or_else(|error| { - error!("Error creating sandbox: {}", error); + error!("Error creating sandbox: {error}"); process::exit(1) }); // Enter the sandbox, from this point the process will be isolated (or not) // as chosen in '--sandbox'. let listener = sandbox.enter(listener).unwrap_or_else(|error| { - error!("Error entering sandbox: {}", error); + error!("Error entering sandbox: {error}"); process::exit(1) }); @@ -820,6 +845,7 @@ migration_mode: opt.migration_mode, uid_map: Some(opt.translate_uid), gid_map: Some(opt.translate_gid), + guest_fd_limit, ..Default::default() }; @@ -864,7 +890,7 @@ .set_tag(tag) .build(fs) .unwrap_or_else(|error| { - error!("Error creating vhost-user backend: {}", error); + error!("Error creating vhost-user backend: {error}"); process::exit(1) }), ); @@ -879,7 +905,7 @@ info!("Waiting for vhost-user socket connection..."); if let Err(e) = daemon.start(listener) { - error!("Failed to start daemon: {:?}", e); + error!("Failed to start daemon: {e:?}"); process::exit(1); } @@ -888,7 +914,7 @@ if let Err(e) = daemon.wait() { match e { HandleRequest(Disconnected) => info!("Client disconnected, shutting down"), - _ => error!("Waiting for daemon failed: {:?}", e), + _ => error!("Waiting for daemon failed: {e:?}"), } } } diff -Nru rust-virtiofsd-1.13.0/src/oslib.rs rust-virtiofsd-1.13.2/src/oslib.rs --- rust-virtiofsd-1.13.0/src/oslib.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/oslib.rs 2006-07-24 05:21:28.000000000 +0400 @@ -286,17 +286,15 @@ Ok(CFileHandle { handle_bytes: sfh_bytes.len().try_into().map_err(|err| { other_io_error(format!( - "Handle size ({} bytes) too big: {}", + "Handle size ({} bytes) too big: {err}", sfh_bytes.len(), - err )) })?, #[allow(clippy::useless_conversion)] handle_type: sfh.handle_type().try_into().map_err(|err| { other_io_error(format!( - "Handle type (0x{:x}) too large: {}", + "Handle type (0x{:x}) too large: {err}", sfh.handle_type(), - err )) })?, f_handle, @@ -533,12 +531,14 @@ /// Set supplementary group pub fn setsupgroup(gid: HostGid) -> io::Result<()> { let gid_raw = gid.into_inner(); - check_retval(unsafe { libc::setgroups(1, &gid_raw) })?; + check_retval(unsafe { libc::syscall(libc::SYS_setgroups, 1, &gid_raw) })?; Ok(()) } /// Drop all supplementary groups pub fn dropsupgroups() -> io::Result<()> { - check_retval(unsafe { libc::setgroups(0, std::ptr::null()) })?; + check_retval(unsafe { + libc::syscall(libc::SYS_setgroups, 0, std::ptr::null::<libc::gid_t>()) + })?; Ok(()) } diff -Nru rust-virtiofsd-1.13.0/src/passthrough/credentials.rs rust-virtiofsd-1.13.2/src/passthrough/credentials.rs --- rust-virtiofsd-1.13.0/src/passthrough/credentials.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/passthrough/credentials.rs 2006-07-24 05:21:28.000000000 +0400 @@ -123,10 +123,7 @@ let cap = capng::name_to_capability(cap_name).map_err(|_| { let err = io::Error::last_os_error(); - error!( - "couldn't get the capability id for name {}: {:?}", - cap_name, err - ); + error!("couldn't get the capability id for name {cap_name}: {err:?}"); err })?; @@ -137,14 +134,11 @@ capability: cap, }]; capng::update(req).map_err(|e| { - error!("couldn't drop {} capability: {:?}", cap, e); + error!("couldn't drop {cap} capability: {e:?}"); einval() })?; capng::apply(Set::CAPS).map_err(|e| { - error!( - "couldn't apply capabilities after dropping {}: {:?}", - cap, e - ); + error!("couldn't apply capabilities after dropping {cap}: {e:?}"); einval() })?; Ok(Some(Self { cap })) diff -Nru rust-virtiofsd-1.13.0/src/passthrough/device_state/deserialization.rs rust-virtiofsd-1.13.2/src/passthrough/device_state/deserialization.rs --- rust-virtiofsd-1.13.0/src/passthrough/device_state/deserialization.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/passthrough/device_state/deserialization.rs 2006-07-24 05:21:28.000000000 +0400 @@ -25,7 +25,7 @@ use std::convert::{TryFrom, TryInto}; use std::io; use std::sync::atomic::{AtomicU64, Ordering}; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex}; impl TryFrom<Vec<u8>> for serialized::PassthroughFs { type Error = io::Error; @@ -360,11 +360,8 @@ io::Error::new( err.kind(), format!( - "Opening {}{}{}: {}", - pfd, + "Opening {pfd}{}{filename}: {err}", if pfd.ends_with('/') { "" } else { "/" }, - filename, - err ), ) })?; @@ -375,7 +372,7 @@ let file_or_handle = if let Some(h) = handle.as_ref() { FileOrHandle::Handle(fs.make_file_handle_openable(h)?) } else { - FileOrHandle::File(fd) + FileOrHandle::File(fs.guest_fds.allocate(fd)?) }; Ok(InodeData { @@ -404,7 +401,7 @@ match fs.cfg.migration_on_error { MigrationOnError::Abort => Err(err.context(format!("Inode {}", self.id))), MigrationOnError::GuestError => { - warn!("Invalid inode {} indexed: {}", self.id, err); + warn!("Invalid inode {} indexed: {err}", self.id); Ok(InodeData { inode: self.id, file_or_handle: FileOrHandle::Invalid(Arc::new(err)), @@ -427,8 +424,8 @@ // Disregard the mount ID, this may be a different host, so the mount ID may differ is_fh.require_equal_without_mount_id(ref_fh).map_err(|err| { other_io_error(format!( - "Inode {} is not the same inode as in the migration source: {}", - self.id, err + "Inode {} is not the same inode as in the migration source: {err}", + self.id )) }) } @@ -458,7 +455,7 @@ let st = statx(&fd, None).err_context(|| "stat")?; let file_or_handle = match fs.cfg.inode_file_handles { - InodeFileHandlesMode::Never => FileOrHandle::File(fd), + InodeFileHandlesMode::Never => FileOrHandle::File(fs.guest_fds.allocate(fd)?), InodeFileHandlesMode::Mandatory | InodeFileHandlesMode::Prefer => { FileOrHandle::Handle(ofh) } @@ -493,30 +490,32 @@ .open_file(flags, &fs.proc_self_fd) .and_then(|f| f.into_file()) { - Ok(f) => HandleDataFile::File(RwLock::new(f)), + Ok(f) => fs.guest_fds.allocate(f).map(Into::into), Err(err) => { let error_msg = if let Ok(path) = inode.get_path(&fs.proc_self_fd) { let p = path.as_c_str().to_string_lossy(); format!( - "Opening inode {} ({}) as handle {}: {}", - self.inode, p, self.id, err + "Opening inode {} ({p}) as handle {}: {err}", + self.inode, self.id ) } else { - format!( - "Opening inode {} as handle {}: {}", - self.inode, self.id, err - ) + format!("Opening inode {} as handle {}: {err}", self.inode, self.id) }; - let err = io::Error::new(err.kind(), error_msg); - match fs.cfg.migration_on_error { - MigrationOnError::Abort => return Err(err), - MigrationOnError::GuestError => { - warn!("Invalid handle {} is open in guest: {}", self.id, err); - HandleDataFile::Invalid(Arc::new(err)) - } - } + Err(io::Error::new(err.kind(), error_msg)) } }; + + let handle_data_file = match handle_data_file { + Ok(hdf) => hdf, + Err(err) => match fs.cfg.migration_on_error { + MigrationOnError::Abort => return Err(err), + MigrationOnError::GuestError => { + warn!("Invalid handle {} is open in guest: {err}", self.id); + HandleDataFile::Invalid(Arc::new(err)) + } + }, + }; + let migration_info = HandleMigrationInfo::OpenInode { flags }; (handle_data_file, migration_info) } diff -Nru rust-virtiofsd-1.13.0/src/passthrough/device_state/mod.rs rust-virtiofsd-1.13.2/src/passthrough/device_state/mod.rs --- rust-virtiofsd-1.13.0/src/passthrough/device_state/mod.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/passthrough/device_state/mod.rs 2006-07-24 05:21:28.000000000 +0400 @@ -7,9 +7,8 @@ * following submodules: * - serialized: Serialized data structures * - preserialization: Structures and functionality for preparing for migration (serialization), - * i.e. define and construct the precursors to the eventually serialized - * information that are stored alongside the associated inodes and handles they - * describe + * i.e. define and construct the precursors to the eventually serialized information that are + * stored alongside the associated inodes and handles they describe * - serialization: Functionality for serializing * - deserialization: Functionality for deserializing */ diff -Nru rust-virtiofsd-1.13.0/src/passthrough/device_state/preserialization/file_handles.rs rust-virtiofsd-1.13.2/src/passthrough/device_state/preserialization/file_handles.rs --- rust-virtiofsd-1.13.0/src/passthrough/device_state/preserialization/file_handles.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/passthrough/device_state/preserialization/file_handles.rs 2006-07-24 05:21:28.000000000 +0400 @@ -69,10 +69,9 @@ if let Err(err) = self.set_migration_info(&inode_data) { error!( - "Inode {} ({}): {}", + "Inode {} ({}): {err}", inode_data.inode, inode_data.identify(&self.fs.proc_self_fd), - err ); } } diff -Nru rust-virtiofsd-1.13.0/src/passthrough/device_state/preserialization/find_paths.rs rust-virtiofsd-1.13.2/src/passthrough/device_state/preserialization/find_paths.rs --- rust-virtiofsd-1.13.0/src/passthrough/device_state/preserialization/find_paths.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/passthrough/device_state/preserialization/find_paths.rs 2006-07-24 05:21:28.000000000 +0400 @@ -286,7 +286,7 @@ let file_or_handle = if let Some(h) = handle.as_ref() { FileOrHandle::Handle(self.fs.make_file_handle_openable(h)?) } else { - FileOrHandle::File(path_fd) + FileOrHandle::File(self.fs.guest_fds.allocate(path_fd)?) }; let mig_info = InodeMigrationInfo::new_internal( diff -Nru rust-virtiofsd-1.13.0/src/passthrough/device_state/preserialization/mod.rs rust-virtiofsd-1.13.2/src/passthrough/device_state/preserialization/mod.rs --- rust-virtiofsd-1.13.0/src/passthrough/device_state/preserialization/mod.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/passthrough/device_state/preserialization/mod.rs 2006-07-24 05:21:28.000000000 +0400 @@ -67,9 +67,8 @@ MigrationMode::FileHandles => { let handle = file_or_handle.try_into().err_context(|| { format!( - "(inode {})/{:?}: Failed to generate file handle", + "(inode {})/{filename:?}: Failed to generate file handle", parent_ref.get().inode, - filename, ) })?; file_handles::FileHandle::new(handle).into() diff -Nru rust-virtiofsd-1.13.0/src/passthrough/device_state/preserialization/proc_paths.rs rust-virtiofsd-1.13.2/src/passthrough/device_state/preserialization/proc_paths.rs --- rust-virtiofsd-1.13.0/src/passthrough/device_state/preserialization/proc_paths.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/passthrough/device_state/preserialization/proc_paths.rs 2006-07-24 05:21:28.000000000 +0400 @@ -224,11 +224,10 @@ * * In case of error, differentiate between: * - `Fallback(err)`: We failed to construct inode migration info for some number of inodes. - * However, we expect a different, more exhaustive method to find inodes’ - * paths (e.g. DFS through the shared directory) can succeed. In case of - * `Mode::Constructor`, the caller must fall back to such a different - * preserialization module (i.e. [`super::find_paths`]). In other modes, - * this should be treated the same as `Unrecoverable`. + * However, we expect a different, more exhaustive method to find inodes’ paths (e.g. DFS + * through the shared directory) can succeed. In case of `Mode::Constructor`, the caller + * must fall back to such a different preserialization module (i.e. [`super::find_paths`]). + * In other modes, this should be treated the same as `Unrecoverable`. * - `Unrecoverable(err)`: Hard error, falling back to a different method is not advised. */ fn run(self) -> Result<(), WrappedError> { diff -Nru rust-virtiofsd-1.13.0/src/passthrough/device_state/serialization.rs rust-virtiofsd-1.13.2/src/passthrough/device_state/serialization.rs --- rust-virtiofsd-1.13.0/src/passthrough/device_state/serialization.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/passthrough/device_state/serialization.rs 2006-07-24 05:21:28.000000000 +0400 @@ -58,8 +58,8 @@ .as_serialized(fs) .unwrap_or_else(|err| { warn!( - "Failed to serialize inode {} (st_dev={}, mnt_id={}, st_ino={}): {}; marking as invalid", - inode.inode, inode.ids.dev, inode.ids.mnt_id, inode.ids.ino, err + "Failed to serialize inode {} (st_dev={}, mnt_id={}, st_ino={}): {err}; marking as invalid", + inode.inode, inode.ids.dev, inode.ids.mnt_id, inode.ids.ino ); serialized::Inode { id: inode.inode, diff -Nru rust-virtiofsd-1.13.0/src/passthrough/file_handle.rs rust-virtiofsd-1.13.2/src/passthrough/file_handle.rs --- rust-virtiofsd-1.13.0/src/passthrough/file_handle.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/passthrough/file_handle.rs 2006-07-24 05:21:28.000000000 +0400 @@ -3,6 +3,7 @@ // found in the LICENSE file. use crate::oslib; +use crate::passthrough::guest_fd_limit::GuestFile; use crate::passthrough::mount_fd::{MPRResult, MountFd, MountFds}; use crate::passthrough::stat::MountId; use serde::{Deserialize, Serialize}; @@ -35,7 +36,7 @@ } pub enum FileOrHandle { - File(File), + File(GuestFile), Handle(OpenableFileHandle), // `io::Error` does not implement `Clone`, so without wrapping it in `Arc`, returning the error // anywhere would be impossible without consuming it diff -Nru rust-virtiofsd-1.13.0/src/passthrough/guest_fd_limit.rs rust-virtiofsd-1.13.2/src/passthrough/guest_fd_limit.rs --- rust-virtiofsd-1.13.0/src/passthrough/guest_fd_limit.rs 1970-01-01 03:00:00.000000000 +0300 +++ rust-virtiofsd-1.13.2/src/passthrough/guest_fd_limit.rs 2006-07-24 05:21:28.000000000 +0400 @@ -0,0 +1,111 @@ +// Copyright 2024 Red Hat, Inc. All rights reserved. +// +// SPDX-License-Identifier: (Apache-2.0 AND BSD-3-Clause) + +//! Allow limiting the number of file descriptors we allocate for the guest. +//! +//! Any process only has a limited number of file descriptor slots available for use, and besides +//! allocating FDs for the guest, virtiofsd also needs to be able to create file descriptors for +//! internal use. By limiting the number we will allocate for the guest, we can ensure there are +//! always free slots open for such internal use. + +use std::fs::File; +use std::io; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::Arc; + +/// Basically just a semaphore, but specifically for limiting guest FD use. +/// +/// Wraps plain `File`s to create `GuestFile`s that count against the guest FD limit until dropped. +pub(crate) struct GuestFdSemaphore { + /// Initial (overall) limit. + initial: u64, + + /// How many allocations are still available before exhausting the limit. + available: AtomicU64, + + /// Whether an error about no remaining FD slots has been logged. + /// + /// Further errors will then be suppressed. + error_logged: AtomicBool, +} + +/// Returned by `GuestFdSemaphore::allocate()`, will release the slot when dropped. +pub struct GuestFile { + /// Contained FD. + file: File, + + /// Semaphore reference. + sem: Arc<GuestFdSemaphore>, +} + +impl GuestFdSemaphore { + /// Create a new instance with the given `limit`. + pub fn new(limit: u64) -> Self { + GuestFdSemaphore { + initial: limit, + available: limit.into(), + error_logged: false.into(), + } + } + + /// Put the given file into a free slot. + /// + /// The slot is released by dropping the returned `GuestFile`. + pub fn allocate(self: &Arc<Self>, file: File) -> io::Result<GuestFile> { + self.available.fetch_update( + Ordering::Relaxed, + Ordering::Relaxed, + |previous| previous.checked_sub(1), + ).map_err(|_| { + if !self.error_logged.fetch_or(true, Ordering::Relaxed) { + error!( + "No more file descriptors available to the guest (0 available out of {} initially), \ + consider increasing the --rlimit-nofile value", + self.initial, + ); + } + + // Since this error is likely returned to the guest (and not logged), prefer an + // error with a reasonable errno number over a useful error message. + io::Error::from_raw_os_error(libc::ENFILE) + })?; + + Ok(GuestFile { + file, + sem: Arc::clone(self), + }) + } + + /// Release one slot. + /// + /// Do not use directly, just drop [`GuestFile`]. + fn release(&self) { + let increased_to = self + .available + .fetch_add(1, Ordering::Relaxed) + .checked_add(1) + .unwrap_or_else(|| panic!("FD semaphore overflow")); + debug_assert!(increased_to <= self.initial); + } +} + +impl GuestFile { + /// Get the inner file. + pub fn get_file(&self) -> &File { + &self.file + } +} + +impl AsRawFd for GuestFile { + fn as_raw_fd(&self) -> RawFd { + self.file.as_raw_fd() + } +} + +impl Drop for GuestFile { + fn drop(&mut self) { + self.sem.release(); + } +} diff -Nru rust-virtiofsd-1.13.0/src/passthrough/inode_store.rs rust-virtiofsd-1.13.2/src/passthrough/inode_store.rs --- rust-virtiofsd-1.13.0/src/passthrough/inode_store.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/passthrough/inode_store.rs 2006-07-24 05:21:28.000000000 +0400 @@ -132,7 +132,7 @@ /// Get an `O_PATH` file for this inode pub fn get_file(&'a self) -> io::Result<InodeFile<'a>> { match &self.file_or_handle { - FileOrHandle::File(f) => Ok(InodeFile::Ref(f)), + FileOrHandle::File(f) => Ok(InodeFile::Ref(f.get_file())), FileOrHandle::Handle(h) => { let file = h.open(libc::O_PATH)?; Ok(InodeFile::Owned(file)) @@ -206,8 +206,8 @@ _ => "unknown inode type", }; format!( - "[{}; mount_id={} device_id={} inode_id={}]", - mode, self.ids.mnt_id, self.ids.dev, self.ids.ino, + "[{mode}; mount_id={} device_id={} inode_id={}]", + self.ids.mnt_id, self.ids.dev, self.ids.ino, ) } } diff -Nru rust-virtiofsd-1.13.0/src/passthrough/mod.rs rust-virtiofsd-1.13.2/src/passthrough/mod.rs --- rust-virtiofsd-1.13.0/src/passthrough/mod.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/passthrough/mod.rs 2006-07-24 05:21:28.000000000 +0400 @@ -5,6 +5,7 @@ pub mod credentials; pub mod device_state; pub mod file_handle; +mod guest_fd_limit; pub mod inode_store; pub mod mount_fd; pub mod read_only; @@ -31,6 +32,7 @@ use crate::util::{other_io_error, ResultErrorContext}; use crate::{fuse, oslib}; use file_handle::{FileHandle, FileOrHandle, OpenableFileHandle}; +use guest_fd_limit::{GuestFdSemaphore, GuestFile}; use mount_fd::{MPRError, MountFds}; use stat::{statx, StatExt}; use std::borrow::Cow; @@ -53,7 +55,7 @@ type Handle = u64; enum HandleDataFile { - File(RwLock<File>), + File(RwLock<GuestFile>), // `io::Error` does not implement `Clone`, so without wrapping it in `Arc`, returning the error // anywhere would be impossible without consuming it Invalid(Arc<io::Error>), @@ -386,6 +388,12 @@ * Is `take()`n when `PassthroughFs` is created, i.e. `None` during runtime. */ pub gid_map: Option<Vec<soft_idmap::cmdline::IdMap>>, + + /// Number of file descriptors we can allocate for guest use. Limiting this ensures there is + /// always some room for file descriptors used and needed by virtiofsd internally. + /// + /// The default is `u64::MAX`. + pub guest_fd_limit: u64, } impl Default for Config { @@ -417,6 +425,7 @@ migration_mode: MigrationMode::FindPaths, uid_map: None, gid_map: None, + guest_fd_limit: u64::MAX, } } } @@ -439,6 +448,11 @@ handles: RwLock<BTreeMap<Handle, Arc<HandleData>>>, next_handle: AtomicU64, + // Represents a limit for the number of file descriptors we allow allocating for the guest. + // Having such a limit that is below the actual maximum number of file descriptors virtiofsd is + // allowed to use ensures that virtiofsd can always create file descriptors for internal use. + guest_fds: Arc<GuestFdSemaphore>, + // Maps mount IDs to an open FD on the respective ID for the purpose of open_by_handle_at(). // This is set when inode_file_handles is not never, since in the 'never' case, // open_by_handle_at() is not called. @@ -535,6 +549,7 @@ next_inode: AtomicU64::new(fuse::ROOT_ID + 1), handles: RwLock::new(BTreeMap::new()), next_handle: AtomicU64::new(0), + guest_fds: Arc::new(GuestFdSemaphore::new(cfg.guest_fd_limit)), mount_fds, proc_self_fd, root_fd, @@ -634,16 +649,16 @@ /// These are the possible return values: /// - `Ok(Some(_))`: Success, caller should use this file handle. /// - `Ok(None)`: No error, but no file handle is available. The caller should fall back to - /// using an `O_PATH` FD. + /// using an `O_PATH` FD. /// - `Err(_)`: An error occurred, the caller should return this to the guest. /// /// This function takes the chosen `self.cfg.inode_file_handles` mode into account: /// - `Never`: Always return `Ok(None)`. /// - `Prefer`: Return `Ok(None)` when file handles are not supported by this filesystem. - /// Otherwise, return either `Ok(Some(_))` or `Err(_)`, depending on whether a file - /// handle could be generated or not. + /// Otherwise, return either `Ok(Some(_))` or `Err(_)`, depending on whether a file handle + /// could be generated or not. /// - `Mandatory`: Never return `Ok(None)`. When the filesystem does not support file handles, - /// return an `Err(_)`. + /// return an `Err(_)`. /// /// When the filesystem does not support file handles, this is logged (as a warning in /// `Prefer` mode, and as an error in `Mandatory` mode) one time per filesystem. @@ -700,12 +715,12 @@ InodeFileHandlesMode::Never => unreachable!(), InodeFileHandlesMode::Prefer => { if !err.silent() { - warn!("{}", err); + warn!("{err}"); } } InodeFileHandlesMode::Mandatory => { if !err.silent() { - error!("{}", err); + error!("{err}"); } return Err(err.into_inner()); } @@ -722,7 +737,7 @@ }) .map_err(|e| { if !e.silent() { - error!("{}", e); + error!("{e}"); } e.into_inner() }) @@ -753,12 +768,12 @@ Err(e) => match self.cfg.inode_file_handles { InodeFileHandlesMode::Never => unreachable!(), InodeFileHandlesMode::Prefer => { - warn!("Failed to open file handle for the root node: {}", e); + warn!("Failed to open file handle for the root node: {e}"); warn!("File handles do not appear safe to use, disabling file handles altogether"); self.cfg.inode_file_handles = InodeFileHandlesMode::Never; } InodeFileHandlesMode::Mandatory => { - error!("Failed to open file handle for the root node: {}", e); + error!("Failed to open file handle for the root node: {e}"); error!("Refusing to use (mandatory) file handles, as they do not appear safe to use"); return Err(e); } @@ -862,7 +877,7 @@ let file_or_handle = if let Some(h) = handle.as_ref() { FileOrHandle::Handle(self.make_file_handle_openable(h)?) } else { - FileOrHandle::File(path_fd) + FileOrHandle::File(self.guest_fds.allocate(path_fd)?) }; let mig_info = if self.track_migration_info.load(Ordering::Relaxed) { @@ -943,7 +958,7 @@ let handle = self.next_handle.fetch_add(1, Ordering::Relaxed); let data = HandleData { inode, - file: file.into(), + file: self.guest_fds.allocate(file)?.into(), migration_info: HandleMigrationInfo::new(flags as i32), }; @@ -1263,7 +1278,7 @@ let file_or_handle = if let Some(h) = handle.as_ref() { FileOrHandle::Handle(self.make_file_handle_openable(h)?) } else { - FileOrHandle::File(path_fd) + FileOrHandle::File(self.guest_fds.allocate(path_fd)?) }; // Always keep the root node's migration info set (`InodeStore::clear_migration_info()` @@ -1764,7 +1779,7 @@ let handle = self.next_handle.fetch_add(1, Ordering::Relaxed); let data = HandleData { inode: entry.inode, - file: file.into(), + file: self.guest_fds.allocate(file)?.into(), migration_info: HandleMigrationInfo::new(flags as i32), }; @@ -1805,7 +1820,7 @@ // This is safe because read_from_file_at uses preadv64, so the underlying file descriptor // offset is not affected by this operation. let f = data.file.get()?.read().unwrap(); - w.read_from_file_at(&f, size as usize, offset) + w.read_from_file_at(f.get_file(), size as usize, offset) } fn write<R: ZeroCopyReader>( @@ -1847,7 +1862,7 @@ // write on the underlying file is performed in append mode. let is_append = flags & libc::O_APPEND as u32 != 0; let flags = (!delayed_write && is_append).then_some(oslib::WritevFlags::RWF_APPEND); - r.write_to_file_at(&f, size as usize, offset, flags) + r.write_to_file_at(f.get_file(), size as usize, offset, flags) } } @@ -2650,7 +2665,7 @@ let file = self.open_inode(inode, libc::O_RDONLY | libc::O_NOFOLLOW)?; let raw_fd = file.as_raw_fd(); - debug!("syncfs: inode={}, mount_fd={}", inode, raw_fd); + debug!("syncfs: inode={inode}, mount_fd={raw_fd}"); let ret = unsafe { libc::syncfs(raw_fd) }; if ret != 0 { // Thread-safe, because errno is stored in thread-local storage. @@ -2662,7 +2677,7 @@ } impl HandleDataFile { - fn get(&self) -> io::Result<&'_ RwLock<File>> { + fn get(&self) -> io::Result<&'_ RwLock<GuestFile>> { match self { HandleDataFile::File(file) => Ok(file), HandleDataFile::Invalid(err) => Err(io::Error::new( @@ -2673,8 +2688,8 @@ } } -impl From<File> for HandleDataFile { - fn from(file: File) -> Self { +impl From<GuestFile> for HandleDataFile { + fn from(file: GuestFile) -> Self { HandleDataFile::File(RwLock::new(file)) } } diff -Nru rust-virtiofsd-1.13.0/src/passthrough/mount_fd.rs rust-virtiofsd-1.13.2/src/passthrough/mount_fd.rs --- rust-virtiofsd-1.13.0/src/passthrough/mount_fd.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/passthrough/mount_fd.rs 2006-07-24 05:21:28.000000000 +0400 @@ -191,7 +191,7 @@ /// Add a prefix to the description #[must_use] pub fn prefix(self, s: String) -> Self { - let new_desc = format!("{}: {}", s, self.description); + let new_desc = format!("{s}: {}", self.description); self.set_desc(new_desc) } @@ -234,18 +234,16 @@ match (self.fs_mount_id, &self.fs_mount_root) { (None, None) => write!(f, "{}", self.description), - (Some(id), None) => write!(f, "Filesystem with mount ID {}: {}", id, self.description), + (Some(id), None) => write!(f, "Filesystem with mount ID {id}: {}", self.description), - (None, Some(root)) => write!( - f, - "Filesystem mounted on \"{}\": {}", - root, self.description - ), + (None, Some(root)) => { + write!(f, "Filesystem mounted on \"{root}\": {}", self.description) + } (Some(id), Some(root)) => write!( f, - "Filesystem mounted on \"{}\" (mount ID: {}): {}", - root, id, self.description + "Filesystem mounted on \"{root}\" (mount ID: {id}): {}", + self.description ), } } @@ -319,8 +317,8 @@ return Err(self .error_for(mount_id, io::Error::from_raw_os_error(libc::EIO)) .set_desc(format!( - "Mount point's ({}) mount ID ({}) does not match expected value ({})", - mount_point, stx.mnt_id, mount_id + "Mount point's ({mount_point}) mount ID ({}) does not match expected value ({mount_id})", + stx.mnt_id ))); } @@ -357,8 +355,7 @@ mount_fd } else { debug!( - "Creating MountFd: mount_id={}, mount_fd={}", - mount_id, + "Creating MountFd: mount_id={mount_id}, mount_fd={}", file.as_raw_fd(), ); let mount_fd = Arc::new(MountFd { diff -Nru rust-virtiofsd-1.13.0/src/sandbox.rs rust-virtiofsd-1.13.2/src/sandbox.rs --- rust-virtiofsd-1.13.0/src/sandbox.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/sandbox.rs 2006-07-24 05:21:28.000000000 +0400 @@ -447,7 +447,7 @@ // other end of the pipe. drop(x_reader); drop(y_writer); - error!("sandbox: couldn't setup id mappings: {}", error); + error!("sandbox: couldn't setup id mappings: {error}"); process::exit(1); }; } @@ -489,11 +489,11 @@ // Set the process inside the user namespace as root let mut ret = unsafe { libc::setresuid(0, 0, 0) }; if ret != 0 { - warn!("Couldn't set the process uid as root: {}", ret); + warn!("Couldn't set the process uid as root: {ret}"); } ret = unsafe { libc::setresgid(0, 0, 0) }; if ret != 0 { - warn!("Couldn't set the process gid as root: {}", ret); + warn!("Couldn't set the process gid as root: {ret}"); } let child = util::sfork().map_err(Error::Fork)?; diff -Nru rust-virtiofsd-1.13.0/src/seccomp.rs rust-virtiofsd-1.13.2/src/seccomp.rs --- rust-virtiofsd-1.13.0/src/seccomp.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/seccomp.rs 2006-07-24 05:21:28.000000000 +0400 @@ -186,6 +186,9 @@ allow_syscall!(ctx, libc::SYS_syncfs); #[cfg(target_arch = "x86_64")] allow_syscall!(ctx, libc::SYS_time); // Rarely needed, except on static builds + allow_syscall!(ctx, libc::SYS_tkill); // Deprecated in favour of tgkill, but older OSes may + // still be using it, and we should avoid crashing on + // those cases. allow_syscall!(ctx, libc::SYS_tgkill); allow_syscall!(ctx, libc::SYS_umask); #[cfg(any( diff -Nru rust-virtiofsd-1.13.0/src/server.rs rust-virtiofsd-1.13.2/src/server.rs --- rust-virtiofsd-1.13.0/src/server.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/server.rs 2006-07-24 05:21:28.000000000 +0400 @@ -112,8 +112,8 @@ if let Ok(opcode) = Opcode::try_from(in_header.opcode) { debug!( - "Received request: opcode={:?} ({}), inode={}, unique={}, pid={}", - opcode, in_header.opcode, in_header.nodeid, in_header.unique, in_header.pid + "Received request: opcode={opcode:?} ({}), inode={}, unique={}, pid={}", + in_header.opcode, in_header.nodeid, in_header.unique, in_header.pid ); match opcode { Opcode::Lookup => self.lookup(in_header, r, w), @@ -598,7 +598,7 @@ unique: in_header.unique, }; - debug!("Replying OK, header: {:?}", out); + debug!("Replying OK, header: {out:?}"); w.write_all(out.as_slice()).map_err(Error::EncodeMessage)?; Ok(out.len as usize) } @@ -889,7 +889,7 @@ }; if major < KERNEL_VERSION { - error!("Unsupported fuse protocol version: {}.{}", major, minor); + error!("Unsupported fuse protocol version: {major}.{minor}"); return reply_error( io::Error::from_raw_os_error(libc::EPROTO), in_header.unique, @@ -909,10 +909,7 @@ } if minor < MIN_KERNEL_MINOR_VERSION { - error!( - "Unsupported fuse protocol minor version: {}.{}", - major, minor - ); + error!("Unsupported fuse protocol minor version: {major}.{minor}"); return reply_error( io::Error::from_raw_os_error(libc::EPROTO), in_header.unique, @@ -1491,7 +1488,7 @@ unique, }; - debug!("Replying OK, header: {:?}", out); + debug!("Replying OK, header: {out:?}"); w.write_all(out.as_slice()).map_err(Error::EncodeMessage)?; w.flush().map_err(Error::FlushMessage)?; Ok(out.len as usize) @@ -1519,7 +1516,7 @@ unique, }; - debug!("Replying OK, header: {:?}", header); + debug!("Replying OK, header: {header:?}"); w.write_all(header.as_slice()) .map_err(Error::EncodeMessage)?; @@ -1743,7 +1740,7 @@ secctx_received = true; extensions.secctx = parse_security_context(nr_secctx, current_extension_bytes)?; - debug!("Extension received: {} SecCtx", nr_secctx); + debug!("Extension received: {nr_secctx} SecCtx"); } ExtType::SupGroups => { if !options.contains(FsOptions::CREATE_SUPP_GROUP) || extensions.sup_gid.is_some() { diff -Nru rust-virtiofsd-1.13.0/src/soft_idmap/mod.rs rust-virtiofsd-1.13.2/src/soft_idmap/mod.rs --- rust-virtiofsd-1.13.0/src/soft_idmap/mod.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/soft_idmap/mod.rs 2006-07-24 05:21:28.000000000 +0400 @@ -218,15 +218,14 @@ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { MapEntry::Squash { from, to } => { - write!(f, "squash [{}, {}) to {}", from.start, from.end, to) + write!(f, "squash [{}, {}) to {to}", from.start, from.end) } MapEntry::Range { from, to_base } => { write!( f, - "map [{}, {}) to [{}, {})", + "map [{}, {}) to [{to_base}, {})", from.start, from.end, - to_base, *to_base + (from.end - from.start) ) } diff -Nru rust-virtiofsd-1.13.0/src/util.rs rust-virtiofsd-1.13.2/src/util.rs --- rust-virtiofsd-1.13.0/src/util.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/util.rs 2006-07-24 05:21:28.000000000 +0400 @@ -110,7 +110,7 @@ capng::clear(capng::Set::BOTH); if let Err(e) = capng::apply(capng::Set::BOTH) { // Don't exit the process here since we already have a child. - error!("warning: can't apply the parent capabilities: {}", e); + error!("warning: can't apply the parent capabilities: {e}"); } let mut status = 0; @@ -124,10 +124,10 @@ libc::WEXITSTATUS(status) } else if libc::WIFSIGNALED(status) { let signal = libc::WTERMSIG(status); - error!("Child process terminated by signal {}", signal); + error!("Child process terminated by signal {signal}"); -signal } else { - error!("Unexpected waitpid status: {:#X}", status); + error!("Unexpected waitpid status: {status:#X}"); libc::EXIT_FAILURE }; @@ -163,6 +163,8 @@ /// Same as `io::Error::other()`, but the respective io_error_other feature has only been /// stabilized in Rust 1.74.0, which is too new for our intended targets. pub fn other_io_error<E: Into<Box<dyn std::error::Error + Send + Sync>>>(err: E) -> io::Error { + #[allow(unknown_lints)] + #[allow(clippy::io_other_error)] io::Error::new(io::ErrorKind::Other, err) } diff -Nru rust-virtiofsd-1.13.0/src/vhost_user.rs rust-virtiofsd-1.13.2/src/vhost_user.rs --- rust-virtiofsd-1.13.0/src/vhost_user.rs 2006-07-24 05:21:28.000000000 +0400 +++ rust-virtiofsd-1.13.2/src/vhost_user.rs 2006-07-24 05:21:28.000000000 +0400 @@ -459,9 +459,9 @@ } fn features(&self) -> u64 { - 1 << VIRTIO_F_VERSION_1 - | 1 << VIRTIO_RING_F_INDIRECT_DESC - | 1 << VIRTIO_RING_F_EVENT_IDX + (1 << VIRTIO_F_VERSION_1) + | (1 << VIRTIO_RING_F_INDIRECT_DESC) + | (1 << VIRTIO_RING_F_EVENT_IDX) | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() | VhostUserVirtioFeatures::LOG_ALL.bits() } @@ -623,7 +623,7 @@ if phase != VhostTransferStatePhase::STOPPED { return Err(io::Error::new( io::ErrorKind::Unsupported, - format!("Transfer in phase {:?} is not supported", phase), + format!("Transfer in phase {phase:?} is not supported"), )); } @@ -658,9 +658,9 @@ server.prepare_serialization(Arc::new(AtomicBool::new(false))); } - server.serialize(file).map_err(|e| { - io::Error::new(e.kind(), format!("Failed to save state: {}", e)) - }) + server + .serialize(file) + .map_err(|e| io::Error::new(e.kind(), format!("Failed to save state: {e}"))) }) } @@ -673,9 +673,9 @@ } thread::spawn(move || { - server.deserialize_and_apply(file).map_err(|e| { - io::Error::new(e.kind(), format!("Failed to load state: {}", e)) - }) + server + .deserialize_and_apply(file) + .map_err(|e| io::Error::new(e.kind(), format!("Failed to load state: {e}"))) }) } }; @@ -712,7 +712,7 @@ .kill_evt .write(1); if let Err(e) = result { - error!("Error shutting down worker thread: {:?}", e) + error!("Error shutting down worker thread: {e:?}") } } }