From 135da1bde3942a92a0e8fb530c7cb58e8f793fab Mon Sep 17 00:00:00 2001
From: LoveSy <shana@zju.edu.cn>
Date: Wed, 19 Jul 2023 22:11:10 +0800
Subject: [PATCH 1/3] Add new mount api syscall for linux_raw

---
 src/backend/linux_raw/conv.rs        |  49 +++++++
 src/backend/linux_raw/fs/syscalls.rs | 199 +++++++++++++++++++++++++++
 src/backend/linux_raw/fs/types.rs    | 186 +++++++++++++++++++++++++
 3 files changed, 434 insertions(+)

diff --git a/src/backend/linux_raw/conv.rs b/src/backend/linux_raw/conv.rs
index 05d040204..43242455c 100644
--- a/src/backend/linux_raw/conv.rs
+++ b/src/backend/linux_raw/conv.rs
@@ -450,6 +450,55 @@ pub(crate) mod fs {
             c_uint(flags.bits())
         }
     }
+
+    impl<'a, Num: ArgNumber> From<crate::fs::FsConfigCmd> for ArgReg<'a, Num> {
+        #[inline]
+        fn from(cmd: crate::fs::FsConfigCmd) -> Self {
+            c_uint(cmd as c::c_uint)
+        }
+    }
+
+    impl<'a, Num: ArgNumber> From<crate::backend::fs::types::FsOpenFlags> for ArgReg<'a, Num> {
+        #[inline]
+        fn from(flags: crate::backend::fs::types::FsOpenFlags) -> Self {
+            c_uint(flags.bits())
+        }
+    }
+
+    impl<'a, Num: ArgNumber> From<crate::backend::fs::types::FsMountFlags> for ArgReg<'a, Num> {
+        #[inline]
+        fn from(flags: crate::backend::fs::types::FsMountFlags) -> Self {
+            c_uint(flags.bits())
+        }
+    }
+
+    impl<'a, Num: ArgNumber> From<crate::backend::fs::types::MountAttrFlags> for ArgReg<'a, Num> {
+        #[inline]
+        fn from(flags: crate::backend::fs::types::MountAttrFlags) -> Self {
+            c_uint(flags.bits())
+        }
+    }
+
+    impl<'a, Num: ArgNumber> From<crate::backend::fs::types::OpenTreeFlags> for ArgReg<'a, Num> {
+        #[inline]
+        fn from(flags: crate::backend::fs::types::OpenTreeFlags) -> Self {
+            c_uint(flags.bits())
+        }
+    }
+
+    impl<'a, Num: ArgNumber> From<crate::backend::fs::types::FsPickFlags> for ArgReg<'a, Num> {
+        #[inline]
+        fn from(flags: crate::backend::fs::types::FsPickFlags) -> Self {
+            c_uint(flags.bits())
+        }
+    }
+
+    impl<'a, Num: ArgNumber> From<crate::backend::fs::types::MoveMountFlags> for ArgReg<'a, Num> {
+        #[inline]
+        fn from(flags: crate::backend::fs::types::MoveMountFlags) -> Self {
+            c_uint(flags.bits())
+        }
+    }
 }
 
 impl<'a, Num: ArgNumber> From<crate::io::FdFlags> for ArgReg<'a, Num> {
diff --git a/src/backend/linux_raw/fs/syscalls.rs b/src/backend/linux_raw/fs/syscalls.rs
index 837fb0a76..8d8b76c7a 100644
--- a/src/backend/linux_raw/fs/syscalls.rs
+++ b/src/backend/linux_raw/fs/syscalls.rs
@@ -1436,6 +1436,205 @@ pub(crate) fn mount(
     }
 }
 
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn fsopen(fs_name: &CStr, flags: super::types::FsOpenFlags) -> io::Result<OwnedFd> {
+    unsafe { ret_owned_fd(syscall_readonly!(__NR_fsopen, fs_name, flags)) }
+}
+
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn fsmount(
+    fs_fd: BorrowedFd<'_>,
+    flags: super::types::FsMountFlags,
+    attr_flags: super::types::MountAttrFlags,
+) -> io::Result<()> {
+    unsafe { ret(syscall_readonly!(__NR_fsmount, fs_fd, flags, attr_flags)) }
+}
+
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn move_mount(
+    from_dfd: BorrowedFd<'_>,
+    from_pathname: &CStr,
+    to_dfd: BorrowedFd<'_>,
+    to_pathname: &CStr,
+    flags: super::types::MoveMountFlags,
+) -> io::Result<()> {
+    unsafe {
+        ret(syscall_readonly!(
+            __NR_move_mount,
+            from_dfd,
+            from_pathname,
+            to_dfd,
+            to_pathname,
+            flags
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn open_tree(
+    dfd: BorrowedFd<'_>,
+    filename: &CStr,
+    flags: super::types::OpenTreeFlags,
+) -> io::Result<OwnedFd> {
+    unsafe { ret_owned_fd(syscall_readonly!(__NR_open_tree, dfd, filename, flags)) }
+}
+
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn fspick(
+    dfd: BorrowedFd<'_>,
+    path: &CStr,
+    flags: super::types::FsPickFlags,
+) -> io::Result<OwnedFd> {
+    unsafe { ret_owned_fd(syscall_readonly!(__NR_fspick, dfd, path, flags)) }
+}
+
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn fsconfig_set_flag(fs_fd: BorrowedFd<'_>, key: &CStr) -> io::Result<()> {
+    unsafe {
+        ret(syscall_readonly!(
+            __NR_fsconfig,
+            fs_fd,
+            super::types::FsConfigCmd::SetFlag,
+            key,
+            zero(),
+            zero()
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn fsconfig_set_string(
+    fs_fd: BorrowedFd<'_>,
+    key: &CStr,
+    value: &CStr,
+) -> io::Result<()> {
+    unsafe {
+        ret(syscall_readonly!(
+            __NR_fsconfig,
+            fs_fd,
+            super::types::FsConfigCmd::SetString,
+            key,
+            value,
+            zero()
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn fsconfig_set_binary(
+    fs_fd: BorrowedFd<'_>,
+    key: &CStr,
+    value: &[u8],
+) -> io::Result<()> {
+    let (value_addr, value_len) = slice(value);
+    unsafe {
+        ret(syscall_readonly!(
+            __NR_fsconfig,
+            fs_fd,
+            super::types::FsConfigCmd::SetBinary,
+            key,
+            value_addr,
+            value_len
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn fsconfig_set_fd(
+    fs_fd: BorrowedFd<'_>,
+    key: &CStr,
+    fd: BorrowedFd<'_>,
+) -> io::Result<()> {
+    unsafe {
+        ret(syscall_readonly!(
+            __NR_fsconfig,
+            fs_fd,
+            super::types::FsConfigCmd::SetFd,
+            key,
+            zero(),
+            fd
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn fsconfig_set_path(
+    fs_fd: BorrowedFd<'_>,
+    key: &CStr,
+    path: &CStr,
+    fd: BorrowedFd<'_>,
+) -> io::Result<()> {
+    unsafe {
+        ret(syscall_readonly!(
+            __NR_fsconfig,
+            fs_fd,
+            super::types::FsConfigCmd::SetPath,
+            key,
+            path,
+            fd
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn fsconfig_set_path_empty(
+    fs_fd: BorrowedFd<'_>,
+    key: &CStr,
+    fd: BorrowedFd<'_>,
+) -> io::Result<()> {
+    unsafe {
+        ret(syscall_readonly!(
+            __NR_fsconfig,
+            fs_fd,
+            super::types::FsConfigCmd::SetPathEmpty,
+            key,
+            cstr!(""),
+            fd
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn fsconfig_create(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
+    unsafe {
+        ret(syscall_readonly!(
+            __NR_fsconfig,
+            fs_fd,
+            super::types::FsConfigCmd::Create,
+            zero(),
+            zero(),
+            zero()
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[inline]
+pub(crate) fn fsconfig_reconfigure(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
+    unsafe {
+        ret(syscall_readonly!(
+            __NR_fsconfig,
+            fs_fd,
+            super::types::FsConfigCmd::Reconfigure,
+            zero(),
+            zero(),
+            zero()
+        ))
+    }
+}
+
 #[inline]
 pub(crate) fn unmount(target: &CStr, flags: super::types::UnmountFlags) -> io::Result<()> {
     unsafe { ret(syscall_readonly!(__NR_umount2, target, flags)) }
diff --git a/src/backend/linux_raw/fs/types.rs b/src/backend/linux_raw/fs/types.rs
index df61cf721..00fb2b370 100644
--- a/src/backend/linux_raw/fs/types.rs
+++ b/src/backend/linux_raw/fs/types.rs
@@ -392,6 +392,37 @@ pub enum Advice {
     DontNeed = linux_raw_sys::general::POSIX_FADV_DONTNEED,
 }
 
+/// `FSCONFIG_*` constants for use with [`fsconfig`].
+///
+/// [`fsconfig`]: crate::fs::fsconfig
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[repr(u32)]
+pub enum FsConfigCmd {
+    /// `FSCONFIG_SET_FLAG`
+    SetFlag = linux_raw_sys::general::fsconfig_command::FSCONFIG_SET_FLAG as u32,
+
+    /// `FSCONFIG_SET_STRING`
+    SetString = linux_raw_sys::general::fsconfig_command::FSCONFIG_SET_STRING as u32,
+
+    /// `FSCONFIG_SET_BINARY`
+    SetBinary = linux_raw_sys::general::fsconfig_command::FSCONFIG_SET_BINARY as u32,
+
+    /// `FSCONFIG_SET_PATH`
+    SetPath = linux_raw_sys::general::fsconfig_command::FSCONFIG_SET_PATH as u32,
+
+    /// `FSCONFIG_SET_PATH_EMPTY`
+    SetPathEmpty = linux_raw_sys::general::fsconfig_command::FSCONFIG_SET_PATH_EMPTY as u32,
+
+    /// `FSCONFIG_SET_FD`
+    SetFd = linux_raw_sys::general::fsconfig_command::FSCONFIG_SET_FD as u32,
+
+    /// `FSCONFIG_CMD_CREATE`
+    Create = linux_raw_sys::general::fsconfig_command::FSCONFIG_CMD_CREATE as u32,
+
+    /// `FSCONFIG_CMD_RECONFIGURE`
+    Reconfigure = linux_raw_sys::general::fsconfig_command::FSCONFIG_CMD_RECONFIGURE as u32,
+}
+
 bitflags! {
     /// `MFD_*` constants for use with [`memfd_create`].
     ///
@@ -753,6 +784,161 @@ bitflags! {
     }
 }
 
+bitflags! {
+    /// `FSOPEN_*` constants for use with [`fsopen`].
+    ///
+    /// [`fsopen`]: crate::fs::fsopen
+    #[repr(transparent)]
+    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+    pub struct FsOpenFlags: c::c_uint {
+
+        /// `FSOPEN_CLOEXEC`
+        const FSOPEN_CLOEXEC = linux_raw_sys::general::FSOPEN_CLOEXEC;
+    }
+}
+
+bitflags! {
+    /// `FSMOUNT_*` constants for use with [`fsmount`].
+    ///
+    /// [`fsmount`]: crate::fs::fsmount
+    #[repr(transparent)]
+    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+    pub struct FsMountFlags: c::c_uint {
+        /// `FSMOUNT_CLOEXEC`
+        const FSMOUNT_CLOEXEC = linux_raw_sys::general::FSMOUNT_CLOEXEC;
+    }
+}
+
+bitflags! {
+    /// `MOUNT_ATTR_*` constants for use with [`fsmount`].
+    ///
+    /// [`fsmount`]: crate::fs::fsmount
+    #[repr(transparent)]
+    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+    pub struct MountAttrFlags: c::c_uint {
+        /// `MOUNT_ATTR_RDONLY`
+        const MOUNT_ATTR_RDONLY = linux_raw_sys::general::MOUNT_ATTR_RDONLY;
+
+        /// `MOUNT_ATTR_NOSUID`
+        const MOUNT_ATTR_NOSUID = linux_raw_sys::general::MOUNT_ATTR_NOSUID;
+
+        /// `MOUNT_ATTR_NODEV`
+        const MOUNT_ATTR_NODEV = linux_raw_sys::general::MOUNT_ATTR_NODEV;
+
+        /// `MOUNT_ATTR_NOEXEC`
+        const MOUNT_ATTR_NOEXEC = linux_raw_sys::general::MOUNT_ATTR_NOEXEC;
+
+        /// `MOUNT_ATTR__ATIME`
+        const MOUNT_ATTR__ATIME = linux_raw_sys::general::MOUNT_ATTR__ATIME;
+
+        /// `MOUNT_ATTR_RELATIME`
+        const MOUNT_ATTR_RELATIME = linux_raw_sys::general::MOUNT_ATTR_RELATIME;
+
+        /// `MOUNT_ATTR_NOATIME`
+        const MOUNT_ATTR_NOATIME = linux_raw_sys::general::MOUNT_ATTR_NOATIME;
+
+        /// `MOUNT_ATTR_STRICTATIME`
+        const MOUNT_ATTR_STRICTATIME = linux_raw_sys::general::MOUNT_ATTR_STRICTATIME;
+
+        /// `MOUNT_ATTR_NODIRATIME`
+        const MOUNT_ATTR_NODIRATIME = linux_raw_sys::general::MOUNT_ATTR_NODIRATIME;
+
+        /// `MOUNT_ATTR_NOUSER`
+        const MOUNT_ATTR_IDMAP = linux_raw_sys::general::MOUNT_ATTR_IDMAP;
+
+        /// `MOUNT_ATTR__ATIME_FLAGS`
+        const MOUNT_ATTR_NOSYMFOLLOW = linux_raw_sys::general::MOUNT_ATTR_NOSYMFOLLOW;
+
+        /// `MOUNT_ATTR__ATIME_FLAGS`
+        const MOUNT_ATTR_SIZE_VER0 = linux_raw_sys::general::MOUNT_ATTR_SIZE_VER0;
+    }
+}
+
+bitflags! {
+    /// `MOVE_MOUNT_*` constants for use with [`move_mount`].
+    ///
+    /// [`move_mount`]: crate::fs::move_mount
+    #[repr(transparent)]
+    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+    pub struct MoveMountFlags: c::c_uint {
+        /// `MOVE_MOUNT_F_EMPTY_PATH`
+        const MOVE_MOUNT_F_SYMLINKS = linux_raw_sys::general::MOVE_MOUNT_F_SYMLINKS;
+
+        /// `MOVE_MOUNT_F_AUTOMOUNTS`
+        const MOVE_MOUNT_F_AUTOMOUNTS = linux_raw_sys::general::MOVE_MOUNT_F_AUTOMOUNTS;
+
+        /// `MOVE_MOUNT_F_EMPTY_PATH`
+        const MOVE_MOUNT_F_EMPTY_PATH = linux_raw_sys::general::MOVE_MOUNT_F_EMPTY_PATH;
+
+        /// `MOVE_MOUNT_T_SYMLINKS`
+        const MOVE_MOUNT_T_SYMLINKS = linux_raw_sys::general::MOVE_MOUNT_T_SYMLINKS;
+
+        /// `MOVE_MOUNT_T_AUTOMOUNTS`
+        const MOVE_MOUNT_T_AUTOMOUNTS = linux_raw_sys::general::MOVE_MOUNT_T_AUTOMOUNTS;
+
+        /// `MOVE_MOUNT_T_EMPTY_PATH`
+        const MOVE_MOUNT_T_EMPTY_PATH = linux_raw_sys::general::MOVE_MOUNT_T_EMPTY_PATH;
+
+        /// `MOVE_MOUNT__MASK`
+        const MOVE_MOUNT_SET_GROUP = linux_raw_sys::general::MOVE_MOUNT_SET_GROUP;
+
+        // TODO: add when linux 6.5 is released
+        // /// `MOVE_MOUNT_BENEATH`
+        // const MOVE_MOUNT_BENEATH = linux_raw_sys::general::MOVE_MOUNT_BENEATH;
+
+        /// `MOVE_MOUNT__MASK`
+        const MOVE_MOUNT__MASK = linux_raw_sys::general::MOVE_MOUNT__MASK;
+    }
+}
+
+bitflags! {
+    /// `OPENTREE_*` constants for use with [`open_tree`].
+    ///
+    /// [`open_tree`]: crate::fs::open_tree
+    #[repr(transparent)]
+    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+    pub struct OpenTreeFlags: c::c_uint {
+        /// `OPENTREE_CLONE`
+        const OPEN_TREE_CLONE = linux_raw_sys::general::OPEN_TREE_CLONE;
+
+        /// `OPENTREE_CLOEXEC`
+        const OPEN_TREE_CLOEXEC = linux_raw_sys::general::OPEN_TREE_CLOEXEC;
+
+        /// `AT_EMPTY_PATH`
+        const AT_EMPTY_PATH = linux_raw_sys::general::AT_EMPTY_PATH;
+
+        /// `AT_NO_AUTOMOUNT`
+        const AT_NO_AUTOMOUNT = linux_raw_sys::general::AT_NO_AUTOMOUNT;
+
+        /// `AT_RECURSIVE`
+        const AT_RECURSIVE = linux_raw_sys::general::AT_RECURSIVE;
+
+        /// `AT_SYMLINK_NOFOLLOW`
+        const AT_SYMLINK_NOFOLLOW = linux_raw_sys::general::AT_SYMLINK_NOFOLLOW;
+    }
+}
+
+bitflags! {
+    /// `FSPICK_*` constants for use with [`fspick`].
+    ///
+    /// [`fspick`]: crate::fs::fspick
+    #[repr(transparent)]
+    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+    pub struct FsPickFlags: c::c_uint {
+        /// `FSPICK_CLOEXEC`
+        const FSPICK_CLOEXEC = linux_raw_sys::general::FSPICK_CLOEXEC;
+
+        /// `FSPICK_SYMLINK_NOFOLLOW`
+        const FSPICK_SYMLINK_NOFOLLOW = linux_raw_sys::general::FSPICK_SYMLINK_NOFOLLOW;
+
+        /// `FSPICK_NO_AUTOMOUNT`
+        const FSPICK_NO_AUTOMOUNT = linux_raw_sys::general::FSPICK_NO_AUTOMOUNT;
+
+        /// `FSPICK_EMPTY_PATH`
+        const FSPICK_EMPTY_PATH = linux_raw_sys::general::FSPICK_EMPTY_PATH;
+    }
+}
+
 bitflags! {
     /// `MS_*` constants for use with [`change_mount`].
     ///

From 48bc8c20440888755419991979c8db73f72d579f Mon Sep 17 00:00:00 2001
From: LoveSy <shana@zju.edu.cn>
Date: Wed, 19 Jul 2023 22:11:18 +0800
Subject: [PATCH 2/3] Add new mount api syscall for libc of linux

---
 src/backend/libc/fs/syscalls.rs | 238 ++++++++++++++++++++++++++++++++
 src/backend/libc/fs/types.rs    | 193 ++++++++++++++++++++++++++
 2 files changed, 431 insertions(+)

diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs
index fefd4f3af..c38b4b6bf 100644
--- a/src/backend/libc/fs/syscalls.rs
+++ b/src/backend/libc/fs/syscalls.rs
@@ -2087,6 +2087,244 @@ pub(crate) fn unmount(target: &CStr, flags: super::types::UnmountFlags) -> io::R
     unsafe { ret(c::umount2(target.as_ptr(), bitflags_bits!(flags))) }
 }
 
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn fsopen(fs_name: &CStr, flags: super::types::FsOpenFlags) -> io::Result<OwnedFd> {
+    syscall! {
+        fn fsopen(
+            fs_name: *const c::c_char,
+            flags: c::c_uint
+        ) via SYS_fsopen -> c::c_int
+    }
+    unsafe { ret_owned_fd(fsopen(c_str(fs_name), flags.bits())) }
+}
+
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn fsmount(
+    fs_fd: BorrowedFd<'_>,
+    flags: super::types::FsMountFlags,
+    attr_flags: super::types::MountAttrFlags,
+) -> io::Result<()> {
+    syscall! {
+        fn fsmount(
+            fs_fd: c::c_int,
+            flags: c::c_uint,
+            attr_flags: c::c_uint
+        ) via SYS_fsmount -> c::c_int
+    }
+    unsafe { ret(fsmount(borrowed_fd(fs_fd), flags.bits(), attr_flags.bits())) }
+}
+
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn move_mount(
+    from_dfd: BorrowedFd<'_>,
+    from_pathname: &CStr,
+    to_dfd: BorrowedFd<'_>,
+    to_pathname: &CStr,
+    flags: super::types::MoveMountFlags,
+) -> io::Result<()> {
+    syscall! {
+        fn move_mount(
+            from_dfd: c::c_int,
+            from_pathname: *const c::c_char,
+            to_dfd: c::c_int,
+            to_pathname: *const c::c_char,
+            flags: c::c_uint
+        ) via SYS_move_mount -> c::c_int
+    }
+    unsafe {
+        ret(move_mount(
+            borrowed_fd(from_dfd),
+            c_str(from_pathname),
+            borrowed_fd(to_dfd),
+            c_str(to_pathname),
+            flags.bits(),
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn open_tree(
+    dfd: BorrowedFd<'_>,
+    filename: &CStr,
+    flags: super::types::OpenTreeFlags,
+) -> io::Result<OwnedFd> {
+    syscall! {
+        fn open_tree(
+            dfd: c::c_int,
+            filename: *const c::c_char,
+            flags: c::c_uint
+        ) via SYS_open_tree -> c::c_int
+    }
+
+    unsafe { ret_owned_fd(open_tree(borrowed_fd(dfd), c_str(filename), flags.bits())) }
+}
+
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn fspick(
+    dfd: BorrowedFd<'_>,
+    path: &CStr,
+    flags: super::types::FsPickFlags,
+) -> io::Result<OwnedFd> {
+    syscall! {
+        fn fspick(
+            dfd: c::c_int,
+            path: *const c::c_char,
+            flags: c::c_uint
+        ) via SYS_fspick -> c::c_int
+    }
+
+    unsafe { ret_owned_fd(fspick(borrowed_fd(dfd), c_str(path), flags.bits())) }
+}
+
+#[cfg(linux_kernel)]
+syscall! {
+    fn fsconfig(
+        fs_fd: c::c_int,
+        cmd: c::c_uint,
+        key: *const c::c_char,
+        val: *const c::c_char,
+        aux: c::c_int
+    ) via SYS_fsconfig -> c::c_int
+}
+
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn fsconfig_set_flag(fs_fd: BorrowedFd<'_>, key: &CStr) -> io::Result<()> {
+    unsafe {
+        ret(fsconfig(
+            borrowed_fd(fs_fd),
+            super::types::FsConfigCmd::SetFlag as _,
+            c_str(key),
+            null(),
+            0,
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn fsconfig_set_string(
+    fs_fd: BorrowedFd<'_>,
+    key: &CStr,
+    value: &CStr,
+) -> io::Result<()> {
+    unsafe {
+        ret(fsconfig(
+            borrowed_fd(fs_fd),
+            super::types::FsConfigCmd::SetString as _,
+            c_str(key),
+            c_str(value),
+            0,
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn fsconfig_set_binary(
+    fs_fd: BorrowedFd<'_>,
+    key: &CStr,
+    value: &[u8],
+) -> io::Result<()> {
+    unsafe {
+        ret(fsconfig(
+            borrowed_fd(fs_fd),
+            super::types::FsConfigCmd::SetBinary as _,
+            c_str(key),
+            value.as_ptr().cast(),
+            value.len().try_into().map_err(|_| io::Errno::OVERFLOW)?,
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn fsconfig_set_fd(
+    fs_fd: BorrowedFd<'_>,
+    key: &CStr,
+    fd: BorrowedFd<'_>,
+) -> io::Result<()> {
+    unsafe {
+        ret(fsconfig(
+            borrowed_fd(fs_fd),
+            super::types::FsConfigCmd::SetFd as _,
+            c_str(key),
+            null(),
+            borrowed_fd(fd),
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn fsconfig_set_path(
+    fs_fd: BorrowedFd<'_>,
+    key: &CStr,
+    path: &CStr,
+    fd: BorrowedFd<'_>,
+) -> io::Result<()> {
+    unsafe {
+        ret(fsconfig(
+            borrowed_fd(fs_fd),
+            super::types::FsConfigCmd::SetPath as _,
+            c_str(key),
+            c_str(path),
+            borrowed_fd(fd),
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn fsconfig_set_path_empty(
+    fs_fd: BorrowedFd<'_>,
+    key: &CStr,
+    fd: BorrowedFd<'_>,
+) -> io::Result<()> {
+    unsafe {
+        ret(fsconfig(
+            borrowed_fd(fs_fd),
+            super::types::FsConfigCmd::SetPathEmpty as _,
+            c_str(key),
+            c_str(cstr!("")),
+            borrowed_fd(fd),
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn fsconfig_create(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
+    unsafe {
+        ret(fsconfig(
+            borrowed_fd(fs_fd),
+            super::types::FsConfigCmd::Create as _,
+            null(),
+            null(),
+            0,
+        ))
+    }
+}
+
+#[allow(dead_code)]
+#[cfg(linux_kernel)]
+pub(crate) fn fsconfig_reconfigure(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
+    unsafe {
+        ret(fsconfig(
+            borrowed_fd(fs_fd),
+            super::types::FsConfigCmd::Reconfigure as _,
+            null(),
+            null(),
+            0,
+        ))
+    }
+}
+
 #[cfg(any(apple, linux_kernel))]
 pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result<usize> {
     let value_ptr = value.as_mut_ptr();
diff --git a/src/backend/libc/fs/types.rs b/src/backend/libc/fs/types.rs
index 29aba5e2c..8a3e372d2 100644
--- a/src/backend/libc/fs/types.rs
+++ b/src/backend/libc/fs/types.rs
@@ -554,6 +554,38 @@ pub enum Advice {
     DontNeed = c::POSIX_FADV_DONTNEED as c::c_uint,
 }
 
+/// `FSCONFIG_*` constants for use with [`fsconfig`].
+///
+/// [`fsconfig`]: crate::fs::fsconfig
+#[cfg(linux_kernel)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[repr(u32)]
+pub enum FsConfigCmd {
+    /// `FSCONFIG_SET_FLAG`
+    SetFlag = 0,
+
+    /// `FSCONFIG_SET_STRING`
+    SetString = 1,
+
+    /// `FSCONFIG_SET_BINARY`
+    SetBinary = 2,
+
+    /// `FSCONFIG_SET_PATH`
+    SetPath = 3,
+
+    /// `FSCONFIG_SET_PATH_EMPTY`
+    SetPathEmpty = 4,
+
+    /// `FSCONFIG_SET_FD`
+    SetFd = 5,
+
+    /// `FSCONFIG_CMD_CREATE`
+    Create = 6,
+
+    /// `FSCONFIG_CMD_RECONFIGURE`
+    Reconfigure = 7,
+}
+
 #[cfg(any(linux_kernel, target_os = "freebsd"))]
 bitflags! {
     /// `MFD_*` constants for use with [`memfd_create`].
@@ -1162,6 +1194,167 @@ bitflags! {
     }
 }
 
+#[cfg(linux_kernel)]
+bitflags! {
+    /// `FSOPEN_*` constants for use with [`fsopen`].
+    ///
+    /// [`fsopen`]: crate::fs::fsopen
+    #[repr(transparent)]
+    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+    pub struct FsOpenFlags: c::c_uint {
+
+        /// `FSOPEN_CLOEXEC`
+        const FSOPEN_CLOEXEC = 0x00000001;
+    }
+}
+
+#[cfg(linux_kernel)]
+bitflags! {
+    /// `FSMOUNT_*` constants for use with [`fsmount`].
+    ///
+    /// [`fsmount`]: crate::fs::fsmount
+    #[repr(transparent)]
+    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+    pub struct FsMountFlags: c::c_uint {
+        /// `FSMOUNT_CLOEXEC`
+        const FSMOUNT_CLOEXEC = 0x00000001;
+    }
+}
+
+#[cfg(linux_kernel)]
+bitflags! {
+    /// `MOUNT_ATTR_*` constants for use with [`fsmount`].
+    ///
+    /// [`fsmount`]: crate::fs::fsmount
+    #[repr(transparent)]
+    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+    pub struct MountAttrFlags: c::c_uint {
+        /// `MOUNT_ATTR_RDONLY`
+        const MOUNT_ATTR_RDONLY = 0x00000001;
+
+        /// `MOUNT_ATTR_NOSUID`
+        const MOUNT_ATTR_NOSUID = 0x00000002;
+
+        /// `MOUNT_ATTR_NODEV`
+        const MOUNT_ATTR_NODEV = 0x00000004;
+
+        /// `MOUNT_ATTR_NOEXEC`
+        const MOUNT_ATTR_NOEXEC = 0x00000008;
+
+        /// `MOUNT_ATTR__ATIME`
+        const MOUNT_ATTR__ATIME = 0x00000070;
+
+        /// `MOUNT_ATTR_RELATIME`
+        const MOUNT_ATTR_RELATIME = 0x00000000;
+
+        /// `MOUNT_ATTR_NOATIME`
+        const MOUNT_ATTR_NOATIME = 0x00000010;
+
+        /// `MOUNT_ATTR_STRICTATIME`
+        const MOUNT_ATTR_STRICTATIME = 0x00000020;
+
+        /// `MOUNT_ATTR_NODIRATIME`
+        const MOUNT_ATTR_NODIRATIME = 0x00000080;
+
+        /// `MOUNT_ATTR_NOUSER`
+        const MOUNT_ATTR_IDMAP = 0x00100000;
+
+        /// `MOUNT_ATTR__ATIME_FLAGS`
+        const MOUNT_ATTR_NOSYMFOLLOW = 0x00200000;
+
+        /// `MOUNT_ATTR__ATIME_FLAGS`
+        const MOUNT_ATTR_SIZE_VER0 = 32;
+    }
+}
+
+#[cfg(linux_kernel)]
+bitflags! {
+    /// `MOVE_MOUNT_*` constants for use with [`move_mount`].
+    ///
+    /// [`move_mount`]: crate::fs::move_mount
+    #[repr(transparent)]
+    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+    pub struct MoveMountFlags: c::c_uint {
+        /// `MOVE_MOUNT_F_EMPTY_PATH`
+        const MOVE_MOUNT_F_SYMLINKS = 0x00000001;
+
+        /// `MOVE_MOUNT_F_AUTOMOUNTS`
+        const MOVE_MOUNT_F_AUTOMOUNTS = 0x00000002;
+
+        /// `MOVE_MOUNT_F_EMPTY_PATH`
+        const MOVE_MOUNT_F_EMPTY_PATH = 0x00000004;
+
+        /// `MOVE_MOUNT_T_SYMLINKS`
+        const MOVE_MOUNT_T_SYMLINKS = 0x00000010;
+
+        /// `MOVE_MOUNT_T_AUTOMOUNTS`
+        const MOVE_MOUNT_T_AUTOMOUNTS = 0x00000020;
+
+        /// `MOVE_MOUNT_T_EMPTY_PATH`
+        const MOVE_MOUNT_T_EMPTY_PATH = 0x00000040;
+
+        /// `MOVE_MOUNT__MASK`
+        const MOVE_MOUNT_SET_GROUP = 0x00000100;
+
+        // TODO: add when linux 6.5 is released
+        // /// `MOVE_MOUNT_BENEATH`
+        // const MOVE_MOUNT_BENEATH = 0x00000200;
+
+        /// `MOVE_MOUNT__MASK`
+        const MOVE_MOUNT__MASK = 0x00000377;
+    }
+}
+
+#[cfg(linux_kernel)]
+bitflags! {
+    /// `OPENTREE_*` constants for use with [`open_tree`].
+    ///
+    /// [`open_tree`]: crate::fs::open_tree
+    #[repr(transparent)]
+    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+    pub struct OpenTreeFlags: c::c_uint {
+        /// `OPENTREE_CLONE`
+        const OPEN_TREE_CLONE = 1;
+
+        /// `OPENTREE_CLOEXEC`
+        const OPEN_TREE_CLOEXEC = c::O_CLOEXEC as c::c_uint;
+
+        /// `AT_EMPTY_PATH`
+        const AT_EMPTY_PATH = c::AT_EMPTY_PATH as c::c_uint;
+
+        /// `AT_NO_AUTOMOUNT`
+        const AT_NO_AUTOMOUNT = c::AT_NO_AUTOMOUNT as c::c_uint;
+
+        /// `AT_RECURSIVE`
+        const AT_RECURSIVE = c::AT_RECURSIVE as c::c_uint;
+
+        /// `AT_SYMLINK_NOFOLLOW`
+        const AT_SYMLINK_NOFOLLOW = c::AT_SYMLINK_NOFOLLOW as c::c_uint;
+    }
+}
+
+#[cfg(linux_kernel)]
+bitflags! {
+    /// `FSPICK_*` constants for use with [`fspick`].
+    ///
+    /// [`fspick`]: crate::fs::fspick
+    #[repr(transparent)]
+    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+    pub struct FsPickFlags: c::c_uint {
+        /// `FSPICK_CLOEXEC`
+        const FSPICK_CLOEXEC = 0x00000001;
+
+        /// `FSPICK_SYMLINK_NOFOLLOW`
+        const FSPICK_SYMLINK_NOFOLLOW = 0x00000002;
+
+        /// `FSPICK_NO_AUTOMOUNT`
+        const FSPICK_NO_AUTOMOUNT = 0x00000004;
+
+        /// `FSPICK_EMPTY_PATH`
+        const FSPICK_EMPTY_PATH = 0x00000008;
+    }
+}
+
 #[cfg(linux_kernel)]
 bitflags! {
     /// `MS_*` constants for use with [`change_mount`].

From 2bac50739feca034429daebd6029f9b8970c15a4 Mon Sep 17 00:00:00 2001
From: LoveSy <shana@zju.edu.cn>
Date: Wed, 19 Jul 2023 23:03:48 +0800
Subject: [PATCH 3/3] Add new mount api syscall for linux

---
 src/backend/libc/fs/syscalls.rs      |  13 ---
 src/backend/linux_raw/fs/syscalls.rs |  13 ---
 src/fs/mount.rs                      | 145 ++++++++++++++++++++++++++-
 3 files changed, 144 insertions(+), 27 deletions(-)

diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs
index c38b4b6bf..817d89c19 100644
--- a/src/backend/libc/fs/syscalls.rs
+++ b/src/backend/libc/fs/syscalls.rs
@@ -2087,7 +2087,6 @@ pub(crate) fn unmount(target: &CStr, flags: super::types::UnmountFlags) -> io::R
     unsafe { ret(c::umount2(target.as_ptr(), bitflags_bits!(flags))) }
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn fsopen(fs_name: &CStr, flags: super::types::FsOpenFlags) -> io::Result<OwnedFd> {
     syscall! {
@@ -2099,7 +2098,6 @@ pub(crate) fn fsopen(fs_name: &CStr, flags: super::types::FsOpenFlags) -> io::Re
     unsafe { ret_owned_fd(fsopen(c_str(fs_name), flags.bits())) }
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn fsmount(
     fs_fd: BorrowedFd<'_>,
@@ -2116,7 +2114,6 @@ pub(crate) fn fsmount(
     unsafe { ret(fsmount(borrowed_fd(fs_fd), flags.bits(), attr_flags.bits())) }
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn move_mount(
     from_dfd: BorrowedFd<'_>,
@@ -2145,7 +2142,6 @@ pub(crate) fn move_mount(
     }
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn open_tree(
     dfd: BorrowedFd<'_>,
@@ -2163,7 +2159,6 @@ pub(crate) fn open_tree(
     unsafe { ret_owned_fd(open_tree(borrowed_fd(dfd), c_str(filename), flags.bits())) }
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn fspick(
     dfd: BorrowedFd<'_>,
@@ -2192,7 +2187,6 @@ syscall! {
     ) via SYS_fsconfig -> c::c_int
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn fsconfig_set_flag(fs_fd: BorrowedFd<'_>, key: &CStr) -> io::Result<()> {
     unsafe {
@@ -2206,7 +2200,6 @@ pub(crate) fn fsconfig_set_flag(fs_fd: BorrowedFd<'_>, key: &CStr) -> io::Result
     }
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn fsconfig_set_string(
     fs_fd: BorrowedFd<'_>,
@@ -2224,7 +2217,6 @@ pub(crate) fn fsconfig_set_string(
     }
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn fsconfig_set_binary(
     fs_fd: BorrowedFd<'_>,
@@ -2242,7 +2234,6 @@ pub(crate) fn fsconfig_set_binary(
     }
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn fsconfig_set_fd(
     fs_fd: BorrowedFd<'_>,
@@ -2260,7 +2251,6 @@ pub(crate) fn fsconfig_set_fd(
     }
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn fsconfig_set_path(
     fs_fd: BorrowedFd<'_>,
@@ -2279,7 +2269,6 @@ pub(crate) fn fsconfig_set_path(
     }
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn fsconfig_set_path_empty(
     fs_fd: BorrowedFd<'_>,
@@ -2297,7 +2286,6 @@ pub(crate) fn fsconfig_set_path_empty(
     }
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn fsconfig_create(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
     unsafe {
@@ -2311,7 +2299,6 @@ pub(crate) fn fsconfig_create(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
     }
 }
 
-#[allow(dead_code)]
 #[cfg(linux_kernel)]
 pub(crate) fn fsconfig_reconfigure(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
     unsafe {
diff --git a/src/backend/linux_raw/fs/syscalls.rs b/src/backend/linux_raw/fs/syscalls.rs
index 8d8b76c7a..425c0efc2 100644
--- a/src/backend/linux_raw/fs/syscalls.rs
+++ b/src/backend/linux_raw/fs/syscalls.rs
@@ -1436,13 +1436,11 @@ pub(crate) fn mount(
     }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn fsopen(fs_name: &CStr, flags: super::types::FsOpenFlags) -> io::Result<OwnedFd> {
     unsafe { ret_owned_fd(syscall_readonly!(__NR_fsopen, fs_name, flags)) }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn fsmount(
     fs_fd: BorrowedFd<'_>,
@@ -1452,7 +1450,6 @@ pub(crate) fn fsmount(
     unsafe { ret(syscall_readonly!(__NR_fsmount, fs_fd, flags, attr_flags)) }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn move_mount(
     from_dfd: BorrowedFd<'_>,
@@ -1473,7 +1470,6 @@ pub(crate) fn move_mount(
     }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn open_tree(
     dfd: BorrowedFd<'_>,
@@ -1483,7 +1479,6 @@ pub(crate) fn open_tree(
     unsafe { ret_owned_fd(syscall_readonly!(__NR_open_tree, dfd, filename, flags)) }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn fspick(
     dfd: BorrowedFd<'_>,
@@ -1493,7 +1488,6 @@ pub(crate) fn fspick(
     unsafe { ret_owned_fd(syscall_readonly!(__NR_fspick, dfd, path, flags)) }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn fsconfig_set_flag(fs_fd: BorrowedFd<'_>, key: &CStr) -> io::Result<()> {
     unsafe {
@@ -1508,7 +1502,6 @@ pub(crate) fn fsconfig_set_flag(fs_fd: BorrowedFd<'_>, key: &CStr) -> io::Result
     }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn fsconfig_set_string(
     fs_fd: BorrowedFd<'_>,
@@ -1527,7 +1520,6 @@ pub(crate) fn fsconfig_set_string(
     }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn fsconfig_set_binary(
     fs_fd: BorrowedFd<'_>,
@@ -1547,7 +1539,6 @@ pub(crate) fn fsconfig_set_binary(
     }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn fsconfig_set_fd(
     fs_fd: BorrowedFd<'_>,
@@ -1566,7 +1557,6 @@ pub(crate) fn fsconfig_set_fd(
     }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn fsconfig_set_path(
     fs_fd: BorrowedFd<'_>,
@@ -1586,7 +1576,6 @@ pub(crate) fn fsconfig_set_path(
     }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn fsconfig_set_path_empty(
     fs_fd: BorrowedFd<'_>,
@@ -1605,7 +1594,6 @@ pub(crate) fn fsconfig_set_path_empty(
     }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn fsconfig_create(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
     unsafe {
@@ -1620,7 +1608,6 @@ pub(crate) fn fsconfig_create(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
     }
 }
 
-#[allow(dead_code)]
 #[inline]
 pub(crate) fn fsconfig_reconfigure(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
     unsafe {
diff --git a/src/fs/mount.rs b/src/fs/mount.rs
index 1439b1f32..7cf7b2e9b 100644
--- a/src/fs/mount.rs
+++ b/src/fs/mount.rs
@@ -1,8 +1,10 @@
 //! Linux `mount`.
 
 use crate::backend::fs::types::{
-    InternalMountFlags, MountFlags, MountFlagsArg, MountPropagationFlags, UnmountFlags,
+    FsMountFlags, FsOpenFlags, FsPickFlags, InternalMountFlags, MountAttrFlags, MountFlags,
+    MountFlagsArg, MountPropagationFlags, MoveMountFlags, OpenTreeFlags, UnmountFlags,
 };
+use crate::fd::{BorrowedFd, OwnedFd};
 use crate::{backend, io, path};
 
 /// `mount(source, target, filesystemtype, mountflags, data)`
@@ -131,6 +133,10 @@ pub fn change_mount<Target: path::Arg>(
 
 /// `mount(source, target, NULL, MS_MOVE, NULL)`
 ///
+/// This is not the same as the `move_mount` syscall. If you want to use that,
+/// use [`move_mount_syscall`] instead.
+/// Its name will be changed in the next semver bump to avoid confusion.
+///
 /// # References
 ///  - [Linux]
 ///
@@ -160,7 +166,144 @@ pub fn move_mount<Source: path::Arg, Target: path::Arg>(
 ///  - [Linux]
 ///
 /// [Linux]: https://man7.org/linux/man-pages/man2/umount.2.html
+#[inline]
 #[doc(alias = "umount", alias = "umount2")]
 pub fn unmount<Target: path::Arg>(target: Target, flags: UnmountFlags) -> io::Result<()> {
     target.into_with_c_str(|target| backend::fs::syscalls::unmount(target, flags))
 }
+
+/// `fsopen(fs_name, flags)`
+#[inline]
+pub fn fsopen<Fs: path::Arg>(fs_name: Fs, flags: FsOpenFlags) -> io::Result<OwnedFd> {
+    fs_name.into_with_c_str(|fs_name| backend::fs::syscalls::fsopen(fs_name, flags))
+}
+
+/// `fsmount(fs_fd, flags, attr_flags)`
+#[inline]
+pub fn fsmount(
+    fs_fd: BorrowedFd<'_>,
+    flags: FsMountFlags,
+    attr_flags: MountAttrFlags,
+) -> io::Result<()> {
+    backend::fs::syscalls::fsmount(fs_fd, flags, attr_flags)
+}
+
+/// `move_mount(from_dfd, from_pathname, to_dfd, to_pathname, flags)`
+/// This is the `move_mount` syscall, and it will be renamed to `move_mount`
+/// in the next semver bump.
+#[inline]
+#[doc(alias = "move_mount")]
+pub fn move_mount_syscall<From: path::Arg, To: path::Arg>(
+    from_dfd: BorrowedFd<'_>,
+    from_pathname: From,
+    to_dfd: BorrowedFd<'_>,
+    to_pathname: To,
+    flags: MoveMountFlags,
+) -> io::Result<()> {
+    from_pathname.into_with_c_str(|from_pathname| {
+        to_pathname.into_with_c_str(|to_pathname| {
+            backend::fs::syscalls::move_mount(from_dfd, from_pathname, to_dfd, to_pathname, flags)
+        })
+    })
+}
+
+/// `open_tree(dfd, filename, flags)`
+#[inline]
+pub fn open_tree<Path: path::Arg>(
+    dfd: BorrowedFd<'_>,
+    filename: Path,
+    flags: OpenTreeFlags,
+) -> io::Result<OwnedFd> {
+    filename.into_with_c_str(|filename| backend::fs::syscalls::open_tree(dfd, filename, flags))
+}
+
+/// `fspick(dfd, path, flags)`
+#[inline]
+pub fn fspick<Path: path::Arg>(
+    dfd: BorrowedFd<'_>,
+    path: Path,
+    flags: FsPickFlags,
+) -> io::Result<OwnedFd> {
+    path.into_with_c_str(|path| backend::fs::syscalls::fspick(dfd, path, flags))
+}
+
+/// `fsconfig(fs_fd, FSCONFIG_SET_FLAG, key, NULL, 0)`
+#[inline]
+#[doc(alias = "fsconfig")]
+pub fn fsconfig_set_flag<Key: path::Arg>(fs_fd: BorrowedFd<'_>, key: Key) -> io::Result<()> {
+    key.into_with_c_str(|key| backend::fs::syscalls::fsconfig_set_flag(fs_fd, key))
+}
+
+/// `fsconfig(fs_fd, FSCONFIG_SET_STRING, key, value, 0)`
+#[inline]
+#[doc(alias = "fsconfig")]
+pub fn fsconfig_set_string<Key: path::Arg, Value: path::Arg>(
+    fs_fd: BorrowedFd<'_>,
+    key: Key,
+    value: Value,
+) -> io::Result<()> {
+    key.into_with_c_str(|key| {
+        value.into_with_c_str(|value| backend::fs::syscalls::fsconfig_set_string(fs_fd, key, value))
+    })
+}
+
+/// `fsconfig(fs_fd, FSCONFIG_SET_BINARY, key, value, value.len())`
+#[inline]
+#[doc(alias = "fsconfig")]
+pub fn fsconfig_set_binary<Key: path::Arg>(
+    fs_fd: BorrowedFd<'_>,
+    key: Key,
+    value: &[u8],
+) -> io::Result<()> {
+    key.into_with_c_str(|key| backend::fs::syscalls::fsconfig_set_binary(fs_fd, key, value))
+}
+
+/// `fsconfig(fs_fd, FSCONFIG_SET_PATH, key, path, fd)`
+#[inline]
+#[doc(alias = "fsconfig")]
+pub fn fsconfig_set_path<Key: path::Arg, Path: path::Arg>(
+    fs_fd: BorrowedFd<'_>,
+    key: Key,
+    path: Path,
+    fd: BorrowedFd<'_>,
+) -> io::Result<()> {
+    key.into_with_c_str(|key| {
+        path.into_with_c_str(|path| backend::fs::syscalls::fsconfig_set_path(fs_fd, key, path, fd))
+    })
+}
+
+/// `fsconfig(fs_fd, FSCONFIG_SET_PATH_EMPTY, key, "", fd)`
+#[inline]
+#[doc(alias = "fsconfig")]
+pub fn fsconfig_set_path_empty<Key: path::Arg>(
+    fs_fd: BorrowedFd<'_>,
+    key: Key,
+    fd: BorrowedFd<'_>,
+) -> io::Result<()> {
+    key.into_with_c_str(|key| backend::fs::syscalls::fsconfig_set_path_empty(fs_fd, key, fd))
+}
+
+/// `fsconfig(fs_fd, FSCONFIG_SET_FD, key, NULL, fd)`
+#[inline]
+#[doc(alias = "fsconfig")]
+pub fn fsconfig_set_fd<Key: path::Arg>(
+    fs_fd: BorrowedFd<'_>,
+    key: Key,
+    fd: BorrowedFd<'_>,
+) -> io::Result<()> {
+    key.into_with_c_str(|key| backend::fs::syscalls::fsconfig_set_fd(fs_fd, key, fd))
+}
+
+/// `fsconfig(fs_fd, FSCONFIG_CMD_CREATE, key, NULL, 0)`
+#[inline]
+#[doc(alias = "fsconfig")]
+pub fn fsconfig_create(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
+    backend::fs::syscalls::fsconfig_create(fs_fd)
+}
+
+/// `fsconfig(fs_fd, FSCONFIG_CMD_RECONFIGURE, key, NULL, 0)`
+#[inline]
+#[doc(alias = "fsconfig")]
+pub fn fsconfig_reconfigure(fs_fd: BorrowedFd<'_>) -> io::Result<()> {
+    backend::fs::syscalls::fsconfig_reconfigure(fs_fd)
+}