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:?}")
         }
     }
 }

Reply via email to