fhandle: expose u64 mount id to name_to_handle_at(2)

Now that we provide a unique 64-bit mount ID interface in statx(2), we
can now provide a race-free way for name_to_handle_at(2) to provide a
file handle and corresponding mount without needing to worry about
racing with /proc/mountinfo parsing or having to open a file just to do
statx(2).

While this is not necessary if you are using AT_EMPTY_PATH and don't
care about an extra statx(2) call, users that pass full paths into
name_to_handle_at(2) need to know which mount the file handle comes from
(to make sure they don't try to open_by_handle_at a file handle from a
different filesystem) and switching to AT_EMPTY_PATH would require
allocating a file for every name_to_handle_at(2) call, turning

  err = name_to_handle_at(-EBADF, "/foo/bar/baz", &handle, &mntid,
                          AT_HANDLE_MNT_ID_UNIQUE);

into

  int fd = openat(-EBADF, "/foo/bar/baz", O_PATH | O_CLOEXEC);
  err1 = name_to_handle_at(fd, "", &handle, &unused_mntid, AT_EMPTY_PATH);
  err2 = statx(fd, "", AT_EMPTY_PATH, STATX_MNT_ID_UNIQUE, &statxbuf);
  mntid = statxbuf.stx_mnt_id;
  close(fd);

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
Link: https://lore.kernel.org/r/20240828-exportfs-u64-mount-id-v3-2-10c2c4c16708@cyphar.com
Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Aleksa Sarai 2024-08-28 20:19:43 +10:00 committed by Christian Brauner
parent b4fef22c2f
commit 4356d575ef
3 changed files with 24 additions and 8 deletions

View File

@ -16,7 +16,8 @@
static long do_sys_name_to_handle(const struct path *path, static long do_sys_name_to_handle(const struct path *path,
struct file_handle __user *ufh, struct file_handle __user *ufh,
int __user *mnt_id, int fh_flags) void __user *mnt_id, bool unique_mntid,
int fh_flags)
{ {
long retval; long retval;
struct file_handle f_handle; struct file_handle f_handle;
@ -69,9 +70,19 @@ static long do_sys_name_to_handle(const struct path *path,
} else } else
retval = 0; retval = 0;
/* copy the mount id */ /* copy the mount id */
if (put_user(real_mount(path->mnt)->mnt_id, mnt_id) || if (unique_mntid) {
copy_to_user(ufh, handle, if (put_user(real_mount(path->mnt)->mnt_id_unique,
struct_size(handle, f_handle, handle_bytes))) (u64 __user *) mnt_id))
retval = -EFAULT;
} else {
if (put_user(real_mount(path->mnt)->mnt_id,
(int __user *) mnt_id))
retval = -EFAULT;
}
/* copy the handle */
if (retval != -EFAULT &&
copy_to_user(ufh, handle,
struct_size(handle, f_handle, handle_bytes)))
retval = -EFAULT; retval = -EFAULT;
kfree(handle); kfree(handle);
return retval; return retval;
@ -83,6 +94,7 @@ static long do_sys_name_to_handle(const struct path *path,
* @name: name that should be converted to handle. * @name: name that should be converted to handle.
* @handle: resulting file handle * @handle: resulting file handle
* @mnt_id: mount id of the file system containing the file * @mnt_id: mount id of the file system containing the file
* (u64 if AT_HANDLE_MNT_ID_UNIQUE, otherwise int)
* @flag: flag value to indicate whether to follow symlink or not * @flag: flag value to indicate whether to follow symlink or not
* and whether a decodable file handle is required. * and whether a decodable file handle is required.
* *
@ -92,7 +104,7 @@ static long do_sys_name_to_handle(const struct path *path,
* value required. * value required.
*/ */
SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
struct file_handle __user *, handle, int __user *, mnt_id, struct file_handle __user *, handle, void __user *, mnt_id,
int, flag) int, flag)
{ {
struct path path; struct path path;
@ -100,7 +112,8 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
int fh_flags; int fh_flags;
int err; int err;
if (flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | AT_HANDLE_FID)) if (flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | AT_HANDLE_FID |
AT_HANDLE_MNT_ID_UNIQUE))
return -EINVAL; return -EINVAL;
lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0; lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0;
@ -109,7 +122,9 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
lookup_flags |= LOOKUP_EMPTY; lookup_flags |= LOOKUP_EMPTY;
err = user_path_at(dfd, name, lookup_flags, &path); err = user_path_at(dfd, name, lookup_flags, &path);
if (!err) { if (!err) {
err = do_sys_name_to_handle(&path, handle, mnt_id, fh_flags); err = do_sys_name_to_handle(&path, handle, mnt_id,
flag & AT_HANDLE_MNT_ID_UNIQUE,
fh_flags);
path_put(&path); path_put(&path);
} }
return err; return err;

View File

@ -870,7 +870,7 @@ asmlinkage long sys_fanotify_mark(int fanotify_fd, unsigned int flags,
#endif #endif
asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name, asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name,
struct file_handle __user *handle, struct file_handle __user *handle,
int __user *mnt_id, int flag); void __user *mnt_id, int flag);
asmlinkage long sys_open_by_handle_at(int mountdirfd, asmlinkage long sys_open_by_handle_at(int mountdirfd,
struct file_handle __user *handle, struct file_handle __user *handle,
int flags); int flags);

View File

@ -152,6 +152,7 @@
#define AT_HANDLE_FID 0x200 /* File handle is needed to compare #define AT_HANDLE_FID 0x200 /* File handle is needed to compare
object identity and may not be object identity and may not be
usable with open_by_handle_at(2). */ usable with open_by_handle_at(2). */
#define AT_HANDLE_MNT_ID_UNIQUE 0x001 /* Return the u64 unique mount ID. */
#if defined(__KERNEL__) #if defined(__KERNEL__)
#define AT_GETATTR_NOSEC 0x80000000 #define AT_GETATTR_NOSEC 0x80000000