[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1109421: marked as done (unblock: rust-virtiofsd/1.13.2-1)



Your message dated Mon, 21 Jul 2025 21:30:40 +0000
with message-id <E1udy5o-00AMXs-2o@respighi.debian.org>
and subject line unblock rust-virtiofsd
has caused the Debian Bug report #1109421,
regarding unblock: rust-virtiofsd/1.13.2-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1109421: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1109421
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
X-Debbugs-Cc: rust-virtiofsd@packages.debian.org, pkg-qemu-devel@lists.alioth.debian.org
Control: affects -1 + src:rust-virtiofsd
User: release.debian.org@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 <mjt@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-maintainers@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:?}")
         }
     }
 }

--- End Message ---
--- Begin Message ---
Unblocked rust-virtiofsd.

--- End Message ---

Reply to: