When the inode_file_handles option is set, try to generate a file handle
for new inodes instead of opening an O_PATH FD.
Being able to open these again will require CAP_DAC_READ_SEARCH, so the
description text tells the user they will also need to specify
-o modcaps=+dac_read_search.
Generating a file handle returns the mount ID it is valid for. Opening
it will require an FD instead. We have mount_fds to map an ID to an FD.
get_file_handle() fills the hash map by opening the file we have
generated a handle for. To verify that the resulting FD indeed
represents the handle's mount ID, we use statx(). Therefore, using file
handles requires statx() support.
Signed-off-by: Max Reitz <mre...@redhat.com>
---
tools/virtiofsd/helper.c | 3 +
tools/virtiofsd/passthrough_ll.c | 170 ++++++++++++++++++++++++--
tools/virtiofsd/passthrough_seccomp.c | 1 +
3 files changed, 165 insertions(+), 9 deletions(-)
diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c
index 5e98ed702b..954f8639e6 100644
--- a/tools/virtiofsd/helper.c
+++ b/tools/virtiofsd/helper.c
@@ -186,6 +186,9 @@ void fuse_cmdline_help(void)
" to virtiofsd from guest
applications.\n"
" default: no_allow_direct_io\n"
" -o announce_submounts Announce sub-mount points to the
guest\n"
+ " -o inode_file_handles Use file handles to reference
inodes\n"
+ " instead of O_PATH file
descriptors\n"
+ " (requires -o
modcaps=+dac_read_search)\n"
);
}
diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c
index 793d2c333e..d01f9d3a59 100644
--- a/tools/virtiofsd/passthrough_ll.c
+++ b/tools/virtiofsd/passthrough_ll.c
@@ -190,6 +190,7 @@ struct lo_data {
/* An O_PATH file descriptor to /proc/self/fd/ */
int proc_self_fd;
int user_killpriv_v2, killpriv_v2;
+ int inode_file_handles;
};
/**
@@ -244,6 +245,10 @@ static const struct fuse_opt lo_opts[] = {
{ "announce_submounts", offsetof(struct lo_data, announce_submounts), 1 },
{ "killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 1 },
{ "no_killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 0 },
+ { "inode_file_handles", offsetof(struct lo_data, inode_file_handles), 1 },
+ { "no_inode_file_handles",
+ offsetof(struct lo_data, inode_file_handles),
+ 0 },
FUSE_OPT_END
};
static bool use_syslog = false;
@@ -315,6 +320,108 @@ static int temp_fd_steal(TempFd *temp_fd)
}
}
+/**
+ * Generate a file handle for the given dirfd/name combination.
+ *
+ * If mount_fds does not yet contain an entry for the handle's mount
+ * ID, (re)open dirfd/name in O_RDONLY mode and add it to mount_fds
+ * as the FD for that mount ID. (That is the file that we have
+ * generated a handle for, so it should be representative for the
+ * mount ID. However, to be sure (and to rule out races), we use
+ * statx() to verify that our assumption is correct.)
+ */
+static struct lo_fhandle *get_file_handle(struct lo_data *lo,
+ int dirfd, const char *name)
+{
+ /* We need statx() to verify the mount ID */
+#if defined(CONFIG_STATX) && defined(STATX_MNT_ID)
+ struct lo_fhandle *fh;
+ int ret;
+
+ if (!lo->use_statx || !lo->inode_file_handles) {
+ return NULL;
+ }
+
+ fh = g_new0(struct lo_fhandle, 1);
+
+ fh->handle.handle_bytes = sizeof(fh->padding) - sizeof(fh->handle);
+ ret = name_to_handle_at(dirfd, name, &fh->handle, &fh->mount_id,
+ AT_EMPTY_PATH);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ if (pthread_rwlock_rdlock(&mount_fds_lock)) {
+ goto fail;
+ }
+ if (!g_hash_table_contains(mount_fds, GINT_TO_POINTER(fh->mount_id))) {
+ struct statx stx;
+ int fd;
+
+ pthread_rwlock_unlock(&mount_fds_lock);
+
+ if (name[0]) {
+ fd = openat(dirfd, name, O_RDONLY);