vfs-6.19-rc1.fd_prepare.fs

-----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaSmOZwAKCRCRxhvAZXjc
 op0AAP4oNVJkFyvgKoPos5K2EGFB8M7merGhpYtsOoeg8UK6OwD/UySQErHsXQDR
 sUDDa5uFOhfrkcfM8REtAN4wF8p5qAc=
 =QgFD
 -----END PGP SIGNATURE-----

Merge tag 'vfs-6.19-rc1.fd_prepare.fs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull fd prepare updates from Christian Brauner:
 "This adds the FD_ADD() and FD_PREPARE() primitive. They simplify the
  common pattern of get_unused_fd_flags() + create file + fd_install()
  that is used extensively throughout the kernel and currently requires
  cumbersome cleanup paths.

  FD_ADD() - For simple cases where a file is installed immediately:

      fd = FD_ADD(O_CLOEXEC, vfio_device_open_file(device));
      if (fd < 0)
          vfio_device_put_registration(device);
      return fd;

  FD_PREPARE() - For cases requiring access to the fd or file, or
  additional work before publishing:

      FD_PREPARE(fdf, O_CLOEXEC, sync_file->file);
      if (fdf.err) {
          fput(sync_file->file);
          return fdf.err;
      }

      data.fence = fd_prepare_fd(fdf);
      if (copy_to_user((void __user *)arg, &data, sizeof(data)))
          return -EFAULT;

      return fd_publish(fdf);

  The primitives are centered around struct fd_prepare. FD_PREPARE()
  encapsulates all allocation and cleanup logic and must be followed by
  a call to fd_publish() which associates the fd with the file and
  installs it into the caller's fdtable. If fd_publish() isn't called,
  both are deallocated automatically. FD_ADD() is a shorthand that does
  fd_publish() immediately and never exposes the struct to the caller.

  I've implemented this in a way that it's compatible with the cleanup
  infrastructure while also being usable separately. IOW, it's centered
  around struct fd_prepare which is aliased to class_fd_prepare_t and so
  we can make use of all the basica guard infrastructure"

* tag 'vfs-6.19-rc1.fd_prepare.fs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (42 commits)
  io_uring: convert io_create_mock_file() to FD_PREPARE()
  file: convert replace_fd() to FD_PREPARE()
  vfio: convert vfio_group_ioctl_get_device_fd() to FD_ADD()
  tty: convert ptm_open_peer() to FD_ADD()
  ntsync: convert ntsync_obj_get_fd() to FD_PREPARE()
  media: convert media_request_alloc() to FD_PREPARE()
  hv: convert mshv_ioctl_create_partition() to FD_ADD()
  gpio: convert linehandle_create() to FD_PREPARE()
  pseries: port papr_rtas_setup_file_interface() to FD_ADD()
  pseries: convert papr_platform_dump_create_handle() to FD_ADD()
  spufs: convert spufs_gang_open() to FD_PREPARE()
  papr-hvpipe: convert papr_hvpipe_dev_create_handle() to FD_PREPARE()
  spufs: convert spufs_context_open() to FD_PREPARE()
  net/socket: convert __sys_accept4_file() to FD_ADD()
  net/socket: convert sock_map_fd() to FD_ADD()
  net/kcm: convert kcm_ioctl() to FD_PREPARE()
  net/handshake: convert handshake_nl_accept_doit() to FD_PREPARE()
  secretmem: convert memfd_secret() to FD_ADD()
  memfd: convert memfd_create() to FD_ADD()
  bpf: convert bpf_token_create() to FD_PREPARE()
  ...
This commit is contained in:
Linus Torvalds 2025-12-01 17:32:07 -08:00
commit 1b5dd29869
38 changed files with 506 additions and 873 deletions

View File

@ -267,22 +267,11 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
static int spufs_context_open(const struct path *path)
{
int ret;
struct file *filp;
ret = get_unused_fd_flags(0);
if (ret < 0)
return ret;
filp = dentry_open(path, O_RDONLY, current_cred());
if (IS_ERR(filp)) {
put_unused_fd(ret);
return PTR_ERR(filp);
}
filp->f_op = &spufs_context_fops;
fd_install(ret, filp);
return ret;
FD_PREPARE(fdf, 0, dentry_open(path, O_RDONLY, current_cred()));
if (fdf.err)
return fdf.err;
fd_prepare_file(fdf)->f_op = &spufs_context_fops;
return fd_publish(fdf);
}
static struct spu_context *
@ -508,26 +497,15 @@ static const struct file_operations spufs_gang_fops = {
static int spufs_gang_open(const struct path *path)
{
int ret;
struct file *filp;
ret = get_unused_fd_flags(0);
if (ret < 0)
return ret;
/*
* get references for dget and mntget, will be released
* in error path of *_open().
*/
filp = dentry_open(path, O_RDONLY, current_cred());
if (IS_ERR(filp)) {
put_unused_fd(ret);
return PTR_ERR(filp);
}
filp->f_op = &spufs_gang_fops;
fd_install(ret, filp);
return ret;
FD_PREPARE(fdf, 0, dentry_open(path, O_RDONLY, current_cred()));
if (fdf.err)
return fdf.err;
fd_prepare_file(fdf)->f_op = &spufs_gang_fops;
return fd_publish(fdf);
}
static int spufs_create_gang(struct inode *inode,

View File

@ -479,10 +479,7 @@ static const struct file_operations papr_hvpipe_handle_ops = {
static int papr_hvpipe_dev_create_handle(u32 srcID)
{
struct hvpipe_source_info *src_info;
struct file *file;
long err;
int fd;
struct hvpipe_source_info *src_info __free(kfree) = NULL;
spin_lock(&hvpipe_src_list_lock);
/*
@ -506,20 +503,13 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
src_info->tsk = current;
init_waitqueue_head(&src_info->recv_wqh);
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
if (fd < 0) {
err = fd;
goto free_buf;
}
file = anon_inode_getfile("[papr-hvpipe]",
&papr_hvpipe_handle_ops, (void *)src_info,
O_RDWR);
if (IS_ERR(file)) {
err = PTR_ERR(file);
goto free_fd;
}
FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
(void *)src_info, O_RDWR));
if (fdf.err)
return fdf.err;
retain_and_null_ptr(src_info);
spin_lock(&hvpipe_src_list_lock);
/*
* If two processes are executing ioctl() for the same
@ -528,22 +518,11 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
*/
if (hvpipe_find_source(srcID)) {
spin_unlock(&hvpipe_src_list_lock);
err = -EALREADY;
goto free_file;
return -EALREADY;
}
list_add(&src_info->list, &hvpipe_src_list);
spin_unlock(&hvpipe_src_list_lock);
fd_install(fd, file);
return fd;
free_file:
fput(file);
free_fd:
put_unused_fd(fd);
free_buf:
kfree(src_info);
return err;
return fd_publish(fdf);
}
/*

View File

@ -303,8 +303,6 @@ static long papr_platform_dump_create_handle(u64 dump_tag)
{
struct ibm_platform_dump_params *params;
u64 param_dump_tag;
struct file *file;
long err;
int fd;
/*
@ -334,34 +332,22 @@ static long papr_platform_dump_create_handle(u64 dump_tag)
params->dump_tag_lo = (u32)(dump_tag & 0x00000000ffffffffULL);
params->status = RTAS_IBM_PLATFORM_DUMP_START;
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
if (fd < 0) {
err = fd;
goto free_area;
}
file = anon_inode_getfile_fmode("[papr-platform-dump]",
fd = FD_ADD(O_RDONLY | O_CLOEXEC,
anon_inode_getfile_fmode("[papr-platform-dump]",
&papr_platform_dump_handle_ops,
(void *)params, O_RDONLY,
FMODE_LSEEK | FMODE_PREAD);
if (IS_ERR(file)) {
err = PTR_ERR(file);
goto put_fd;
FMODE_LSEEK | FMODE_PREAD));
if (fd < 0) {
rtas_work_area_free(params->work_area);
kfree(params);
return fd;
}
fd_install(fd, file);
list_add(&params->list, &platform_dump_list);
pr_info("%s (%d) initiated platform dump for dump tag %llu\n",
current->comm, current->pid, dump_tag);
return fd;
put_fd:
put_unused_fd(fd);
free_area:
rtas_work_area_free(params->work_area);
kfree(params);
return err;
}
/*

View File

@ -205,35 +205,18 @@ long papr_rtas_setup_file_interface(struct papr_rtas_sequence *seq,
char *name)
{
const struct papr_rtas_blob *blob;
struct file *file;
long ret;
int fd;
blob = papr_rtas_retrieve(seq);
if (IS_ERR(blob))
return PTR_ERR(blob);
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
if (fd < 0) {
ret = fd;
goto free_blob;
}
file = anon_inode_getfile_fmode(name, fops, (void *)blob,
O_RDONLY, FMODE_LSEEK | FMODE_PREAD);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto put_fd;
}
fd_install(fd, file);
return fd;
put_fd:
put_unused_fd(fd);
free_blob:
fd = FD_ADD(O_RDONLY | O_CLOEXEC,
anon_inode_getfile_fmode(name, fops, (void *)blob, O_RDONLY,
FMODE_LSEEK | FMODE_PREAD));
if (fd < 0)
papr_rtas_blob_free(blob);
return ret;
return fd;
}
/*

View File

@ -768,18 +768,10 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_export, "DMA_BUF");
*/
int dma_buf_fd(struct dma_buf *dmabuf, int flags)
{
int fd;
if (!dmabuf || !dmabuf->file)
return -EINVAL;
fd = get_unused_fd_flags(flags);
if (fd < 0)
return fd;
fd_install(fd, dmabuf->file);
return fd;
return FD_ADD(flags, dmabuf->file);
}
EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF");

View File

@ -298,12 +298,13 @@ static const struct file_operations linehandle_fileops = {
#endif
};
DEFINE_FREE(linehandle_free, struct linehandle_state *, if (!IS_ERR_OR_NULL(_T)) linehandle_free(_T))
static int linehandle_create(struct gpio_device *gdev, void __user *ip)
{
struct gpiohandle_request handlereq;
struct linehandle_state *lh;
struct file *file;
int fd, i, ret;
struct linehandle_state *lh __free(linehandle_free) = NULL;
int i, ret;
u32 lflags;
if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
@ -327,10 +328,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
lh->label = kstrndup(handlereq.consumer_label,
sizeof(handlereq.consumer_label) - 1,
GFP_KERNEL);
if (!lh->label) {
ret = -ENOMEM;
goto out_free_lh;
}
if (!lh->label)
return -ENOMEM;
}
lh->num_descs = handlereq.lines;
@ -340,20 +339,18 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
u32 offset = handlereq.lineoffsets[i];
struct gpio_desc *desc = gpio_device_get_desc(gdev, offset);
if (IS_ERR(desc)) {
ret = PTR_ERR(desc);
goto out_free_lh;
}
if (IS_ERR(desc))
return PTR_ERR(desc);
ret = gpiod_request_user(desc, lh->label);
if (ret)
goto out_free_lh;
return ret;
lh->descs[i] = desc;
linehandle_flags_to_desc_flags(handlereq.flags, &desc->flags);
ret = gpiod_set_transitory(desc, false);
if (ret < 0)
goto out_free_lh;
return ret;
/*
* Lines have to be requested explicitly for input
@ -364,11 +361,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
ret = gpiod_direction_output_nonotify(desc, val);
if (ret)
goto out_free_lh;
return ret;
} else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
ret = gpiod_direction_input_nonotify(desc);
if (ret)
goto out_free_lh;
return ret;
}
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
@ -377,44 +374,23 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
offset);
}
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
if (fd < 0) {
ret = fd;
goto out_free_lh;
}
FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
anon_inode_getfile("gpio-linehandle", &linehandle_fileops,
lh, O_RDONLY | O_CLOEXEC));
if (fdf.err)
return fdf.err;
retain_and_null_ptr(lh);
file = anon_inode_getfile("gpio-linehandle",
&linehandle_fileops,
lh,
O_RDONLY | O_CLOEXEC);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto out_put_unused_fd;
}
handlereq.fd = fd;
if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
/*
* fput() will trigger the release() callback, so do not go onto
* the regular error cleanup path here.
*/
fput(file);
put_unused_fd(fd);
handlereq.fd = fd_prepare_fd(fdf);
if (copy_to_user(ip, &handlereq, sizeof(handlereq)))
return -EFAULT;
}
fd_install(fd, file);
fd_publish(fdf);
dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
lh->num_descs);
return 0;
out_put_unused_fd:
put_unused_fd(fd);
out_free_lh:
linehandle_free(lh);
return ret;
}
#endif /* CONFIG_GPIO_CDEV_V1 */

View File

@ -1870,8 +1870,6 @@ mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev)
struct hv_partition_creation_properties creation_properties = {};
union hv_partition_isolation_properties isolation_properties = {};
struct mshv_partition *partition;
struct file *file;
int fd;
long ret;
if (copy_from_user(&args, user_arg, sizeof(args)))
@ -1938,29 +1936,13 @@ mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev)
goto delete_partition;
ret = mshv_init_async_handler(partition);
if (ret)
goto remove_partition;
fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0) {
ret = fd;
goto remove_partition;
if (!ret) {
ret = FD_ADD(O_CLOEXEC, anon_inode_getfile("mshv_partition",
&mshv_partition_fops,
partition, O_RDWR));
if (ret >= 0)
return ret;
}
file = anon_inode_getfile("mshv_partition", &mshv_partition_fops,
partition, O_RDWR);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto put_fd;
}
fd_install(fd, file);
return fd;
put_fd:
put_unused_fd(fd);
remove_partition:
remove_partition(partition);
delete_partition:
hv_call_delete_partition(partition->pt_id);

View File

@ -282,8 +282,6 @@ EXPORT_SYMBOL_GPL(media_request_get_by_fd);
int media_request_alloc(struct media_device *mdev, int *alloc_fd)
{
struct media_request *req;
struct file *filp;
int fd;
int ret;
/* Either both are NULL or both are non-NULL */
@ -297,19 +295,6 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd)
if (!req)
return -ENOMEM;
fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0) {
ret = fd;
goto err_free_req;
}
filp = anon_inode_getfile("request", &request_fops, NULL, O_CLOEXEC);
if (IS_ERR(filp)) {
ret = PTR_ERR(filp);
goto err_put_fd;
}
filp->private_data = req;
req->mdev = mdev;
req->state = MEDIA_REQUEST_STATE_IDLE;
req->num_incomplete_objects = 0;
@ -320,19 +305,24 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd)
req->updating_count = 0;
req->access_count = 0;
*alloc_fd = fd;
FD_PREPARE(fdf, O_CLOEXEC,
anon_inode_getfile("request", &request_fops, NULL,
O_CLOEXEC));
if (fdf.err) {
ret = fdf.err;
goto err_free_req;
}
fd_prepare_file(fdf)->private_data = req;
*alloc_fd = fd_publish(fdf);
snprintf(req->debug_str, sizeof(req->debug_str), "%u:%d",
atomic_inc_return(&mdev->request_id), fd);
atomic_inc_return(&mdev->request_id), *alloc_fd);
dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
fd_install(fd, filp);
return 0;
err_put_fd:
put_unused_fd(fd);
err_free_req:
if (mdev->ops->req_free)
mdev->ops->req_free(req);

View File

@ -721,21 +721,12 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
static int ntsync_obj_get_fd(struct ntsync_obj *obj)
{
struct file *file;
int fd;
fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0)
return fd;
file = anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR);
if (IS_ERR(file)) {
put_unused_fd(fd);
return PTR_ERR(file);
}
obj->file = file;
fd_install(fd, file);
return fd;
FD_PREPARE(fdf, O_CLOEXEC,
anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR));
if (fdf.err)
return fdf.err;
obj->file = fd_prepare_file(fdf);
return fd_publish(fdf);
}
static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)

View File

@ -589,6 +589,23 @@ static inline void legacy_pty_init(void) { }
#ifdef CONFIG_UNIX98_PTYS
static struct cdev ptmx_cdev;
static struct file *ptm_open_peer_file(struct file *master,
struct tty_struct *tty, int flags)
{
struct path path;
struct file *file;
/* Compute the slave's path */
path.mnt = devpts_mntget(master, tty->driver_data);
if (IS_ERR(path.mnt))
return ERR_CAST(path.mnt);
path.dentry = tty->link->driver_data;
file = dentry_open(&path, flags, current_cred());
mntput(path.mnt);
return file;
}
/**
* ptm_open_peer - open the peer of a pty
* @master: the open struct file of the ptmx device node
@ -601,42 +618,10 @@ static struct cdev ptmx_cdev;
*/
int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags)
{
int fd;
struct file *filp;
int retval = -EINVAL;
struct path path;
if (tty->driver != ptm_driver)
return -EIO;
fd = get_unused_fd_flags(flags);
if (fd < 0) {
retval = fd;
goto err;
}
/* Compute the slave's path */
path.mnt = devpts_mntget(master, tty->driver_data);
if (IS_ERR(path.mnt)) {
retval = PTR_ERR(path.mnt);
goto err_put;
}
path.dentry = tty->link->driver_data;
filp = dentry_open(&path, flags, current_cred());
mntput(path.mnt);
if (IS_ERR(filp)) {
retval = PTR_ERR(filp);
goto err_put;
}
fd_install(fd, filp);
return fd;
err_put:
put_unused_fd(fd);
err:
return retval;
return FD_ADD(flags, ptm_open_peer_file(master, tty, flags));
}
static int pty_unix98_ioctl(struct tty_struct *tty,

View File

@ -299,10 +299,8 @@ static int vfio_group_ioctl_get_device_fd(struct vfio_group *group,
char __user *arg)
{
struct vfio_device *device;
struct file *filep;
char *buf;
int fdno;
int ret;
int fd;
buf = strndup_user(arg, PAGE_SIZE);
if (IS_ERR(buf))
@ -313,26 +311,10 @@ static int vfio_group_ioctl_get_device_fd(struct vfio_group *group,
if (IS_ERR(device))
return PTR_ERR(device);
fdno = get_unused_fd_flags(O_CLOEXEC);
if (fdno < 0) {
ret = fdno;
goto err_put_device;
}
filep = vfio_device_open_file(device);
if (IS_ERR(filep)) {
ret = PTR_ERR(filep);
goto err_put_fdno;
}
fd_install(fdno, filep);
return fdno;
err_put_fdno:
put_unused_fd(fdno);
err_put_device:
fd = FD_ADD(O_CLOEXEC, vfio_device_open_file(device));
if (fd < 0)
vfio_device_put_registration(device);
return ret;
return fd;
}
static int vfio_group_ioctl_get_status(struct vfio_group *group,

View File

@ -280,27 +280,8 @@ static int __anon_inode_getfd(const char *name,
const struct inode *context_inode,
bool make_inode)
{
int error, fd;
struct file *file;
error = get_unused_fd_flags(flags);
if (error < 0)
return error;
fd = error;
file = __anon_inode_getfile(name, fops, priv, flags, context_inode,
make_inode);
if (IS_ERR(file)) {
error = PTR_ERR(file);
goto err_put_unused_fd;
}
fd_install(fd, file);
return fd;
err_put_unused_fd:
put_unused_fd(fd);
return error;
return FD_ADD(flags, __anon_inode_getfile(name, fops, priv, flags,
context_inode, make_inode));
}
/**

View File

@ -231,32 +231,14 @@ static int test_by_type(const struct path *path, void *p)
*/
static int autofs_dev_ioctl_open_mountpoint(const char *name, dev_t devid)
{
int err, fd;
fd = get_unused_fd_flags(O_CLOEXEC);
if (likely(fd >= 0)) {
struct file *filp;
struct path path;
struct path path __free(path_put) = {};
int err;
err = find_autofs_mount(name, &path, test_by_dev, &devid);
if (err)
goto out;
filp = dentry_open(&path, O_RDONLY, current_cred());
path_put(&path);
if (IS_ERR(filp)) {
err = PTR_ERR(filp);
goto out;
}
fd_install(fd, filp);
}
return fd;
out:
put_unused_fd(fd);
return err;
return FD_ADD(O_CLOEXEC, dentry_open(&path, O_RDONLY, current_cred()));
}
/* Open a file descriptor on an autofs mount point */

View File

@ -378,9 +378,7 @@ EXPORT_SYMBOL_GPL(eventfd_ctx_fileget);
static int do_eventfd(unsigned int count, int flags)
{
struct eventfd_ctx *ctx;
struct file *file;
int fd;
struct eventfd_ctx *ctx __free(kfree) = NULL;
/* Check the EFD_* constants for consistency. */
BUILD_BUG_ON(EFD_CLOEXEC != O_CLOEXEC);
@ -398,26 +396,19 @@ static int do_eventfd(unsigned int count, int flags)
init_waitqueue_head(&ctx->wqh);
ctx->count = count;
ctx->flags = flags;
ctx->id = ida_alloc(&eventfd_ida, GFP_KERNEL);
flags &= EFD_SHARED_FCNTL_FLAGS;
flags |= O_RDWR;
fd = get_unused_fd_flags(flags);
if (fd < 0)
goto err;
file = anon_inode_getfile_fmode("[eventfd]", &eventfd_fops,
ctx, flags, FMODE_NOWAIT);
if (IS_ERR(file)) {
put_unused_fd(fd);
fd = PTR_ERR(file);
goto err;
}
fd_install(fd, file);
return fd;
err:
eventfd_free_ctx(ctx);
return fd;
FD_PREPARE(fdf, flags,
anon_inode_getfile_fmode("[eventfd]", &eventfd_fops, ctx,
flags, FMODE_NOWAIT));
if (fdf.err)
return fdf.err;
ctx->id = ida_alloc(&eventfd_ida, GFP_KERNEL);
retain_and_null_ptr(ctx);
return fd_publish(fdf);
}
SYSCALL_DEFINE2(eventfd2, unsigned int, count, int, flags)

View File

@ -2165,9 +2165,8 @@ static void clear_tfile_check_list(void)
*/
static int do_epoll_create(int flags)
{
int error, fd;
struct eventpoll *ep = NULL;
struct file *file;
int error;
struct eventpoll *ep;
/* Check the EPOLL_* constant for consistency. */
BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC);
@ -2184,26 +2183,15 @@ static int do_epoll_create(int flags)
* Creates all the items needed to setup an eventpoll file. That is,
* a file structure and a free file descriptor.
*/
fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC));
if (fd < 0) {
error = fd;
goto out_free_ep;
}
file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep,
O_RDWR | (flags & O_CLOEXEC));
if (IS_ERR(file)) {
error = PTR_ERR(file);
goto out_free_fd;
}
ep->file = file;
fd_install(fd, file);
return fd;
out_free_fd:
put_unused_fd(fd);
out_free_ep:
FD_PREPARE(fdf, O_RDWR | (flags & O_CLOEXEC),
anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep,
O_RDWR | (flags & O_CLOEXEC)));
if (fdf.err) {
ep_clear_and_put(ep);
return error;
return fdf.err;
}
ep->file = fd_prepare_file(fdf);
return fd_publish(fdf);
}
SYSCALL_DEFINE1(epoll_create1, int, flags)

View File

@ -1280,10 +1280,9 @@ int begin_new_exec(struct linux_binprm * bprm)
/* Pass the opened binary to the interpreter. */
if (bprm->have_execfd) {
retval = get_unused_fd_flags(0);
retval = FD_ADD(0, bprm->executable);
if (retval < 0)
goto out_unlock;
fd_install(retval, bprm->executable);
bprm->executable = NULL;
bprm->execfd = retval;
}

View File

@ -404,32 +404,28 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
return retval;
}
static struct file *file_open_handle(struct path *path, int open_flag)
{
const struct export_operations *eops;
eops = path->mnt->mnt_sb->s_export_op;
if (eops->open)
return eops->open(path, open_flag);
return file_open_root(path, "", open_flag, 0);
}
static long do_handle_open(int mountdirfd, struct file_handle __user *ufh,
int open_flag)
{
long retval = 0;
long retval;
struct path path __free(path_put) = {};
struct file *file;
const struct export_operations *eops;
retval = handle_to_path(mountdirfd, ufh, &path, open_flag);
if (retval)
return retval;
CLASS(get_unused_fd, fd)(open_flag);
if (fd < 0)
return fd;
eops = path.mnt->mnt_sb->s_export_op;
if (eops->open)
file = eops->open(&path, open_flag);
else
file = file_open_root(&path, "", open_flag, 0);
if (IS_ERR(file))
return PTR_ERR(file);
fd_install(fd, file);
return take_fd(fd);
return FD_ADD(open_flag, file_open_handle(&path, open_flag));
}
/**

View File

@ -1380,28 +1380,25 @@ int replace_fd(unsigned fd, struct file *file, unsigned flags)
*/
int receive_fd(struct file *file, int __user *ufd, unsigned int o_flags)
{
int new_fd;
int error;
error = security_file_receive(file);
if (error)
return error;
new_fd = get_unused_fd_flags(o_flags);
if (new_fd < 0)
return new_fd;
FD_PREPARE(fdf, o_flags, file);
if (fdf.err)
return fdf.err;
get_file(file);
if (ufd) {
error = put_user(new_fd, ufd);
if (error) {
put_unused_fd(new_fd);
error = put_user(fd_prepare_fd(fdf), ufd);
if (error)
return error;
}
}
fd_install(new_fd, get_file(file));
__receive_sock(file);
return new_fd;
__receive_sock(fd_prepare_file(fdf));
return fd_publish(fdf);
}
EXPORT_SYMBOL_GPL(receive_fd);

View File

@ -3100,19 +3100,7 @@ static struct file *vfs_open_tree(int dfd, const char __user *filename, unsigned
SYSCALL_DEFINE3(open_tree, int, dfd, const char __user *, filename, unsigned, flags)
{
int fd;
struct file *file __free(fput) = NULL;
file = vfs_open_tree(dfd, filename, flags);
if (IS_ERR(file))
return PTR_ERR(file);
fd = get_unused_fd_flags(flags & O_CLOEXEC);
if (fd < 0)
return fd;
fd_install(fd, no_free_ptr(file));
return fd;
return FD_ADD(flags, vfs_open_tree(dfd, filename, flags));
}
/*
@ -4281,10 +4269,10 @@ static unsigned int attr_flags_to_mnt_flags(u64 attr_flags)
SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
unsigned int, attr_flags)
{
struct path new_path __free(path_put) = {};
struct mnt_namespace *ns;
struct fs_context *fc;
struct file *file;
struct path newmount;
struct vfsmount *new_mnt;
struct mount *mnt;
unsigned int mnt_flags = 0;
long ret;
@ -4322,35 +4310,36 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
fc = fd_file(f)->private_data;
ret = mutex_lock_interruptible(&fc->uapi_mutex);
if (ret < 0)
ACQUIRE(mutex_intr, uapi_mutex)(&fc->uapi_mutex);
ret = ACQUIRE_ERR(mutex_intr, &uapi_mutex);
if (ret)
return ret;
/* There must be a valid superblock or we can't mount it */
ret = -EINVAL;
if (!fc->root)
goto err_unlock;
return ret;
ret = -EPERM;
if (mount_too_revealing(fc->root->d_sb, &mnt_flags)) {
errorfcp(fc, "VFS", "Mount too revealing");
goto err_unlock;
return ret;
}
ret = -EBUSY;
if (fc->phase != FS_CONTEXT_AWAITING_MOUNT)
goto err_unlock;
return ret;
if (fc->sb_flags & SB_MANDLOCK)
warn_mandlock();
newmount.mnt = vfs_create_mount(fc);
if (IS_ERR(newmount.mnt)) {
ret = PTR_ERR(newmount.mnt);
goto err_unlock;
}
newmount.dentry = dget(fc->root);
newmount.mnt->mnt_flags = mnt_flags;
new_mnt = vfs_create_mount(fc);
if (IS_ERR(new_mnt))
return PTR_ERR(new_mnt);
new_mnt->mnt_flags = mnt_flags;
new_path.dentry = dget(fc->root);
new_path.mnt = new_mnt;
/* We've done the mount bit - now move the file context into more or
* less the same state as if we'd done an fspick(). We don't want to
@ -4360,38 +4349,27 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
vfs_clean_context(fc);
ns = alloc_mnt_ns(current->nsproxy->mnt_ns->user_ns, true);
if (IS_ERR(ns)) {
ret = PTR_ERR(ns);
goto err_path;
}
mnt = real_mount(newmount.mnt);
if (IS_ERR(ns))
return PTR_ERR(ns);
mnt = real_mount(new_path.mnt);
ns->root = mnt;
ns->nr_mounts = 1;
mnt_add_to_ns(ns, mnt);
mntget(newmount.mnt);
mntget(new_path.mnt);
/* Attach to an apparent O_PATH fd with a note that we need to unmount
* it, not just simply put it.
*/
file = dentry_open(&newmount, O_PATH, fc->cred);
if (IS_ERR(file)) {
dissolve_on_fput(newmount.mnt);
ret = PTR_ERR(file);
goto err_path;
FD_PREPARE(fdf, (flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0,
dentry_open(&new_path, O_PATH, fc->cred));
if (fdf.err) {
dissolve_on_fput(new_path.mnt);
return fdf.err;
}
file->f_mode |= FMODE_NEED_UNMOUNT;
ret = get_unused_fd_flags((flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0);
if (ret >= 0)
fd_install(ret, file);
else
fput(file);
err_path:
path_put(&newmount);
err_unlock:
mutex_unlock(&fc->uapi_mutex);
return ret;
/*
* Attach to an apparent O_PATH fd with a note that we
* need to unmount it, not just simply put it.
*/
fd_prepare_file(fdf)->f_mode |= FMODE_NEED_UNMOUNT;
return fd_publish(fdf);
}
static inline int vfs_move_mount(const struct path *from_path,
@ -5033,19 +5011,17 @@ SYSCALL_DEFINE5(open_tree_attr, int, dfd, const char __user *, filename,
unsigned, flags, struct mount_attr __user *, uattr,
size_t, usize)
{
struct file __free(fput) *file = NULL;
int fd;
if (!uattr && usize)
return -EINVAL;
file = vfs_open_tree(dfd, filename, flags);
if (IS_ERR(file))
return PTR_ERR(file);
FD_PREPARE(fdf, flags, vfs_open_tree(dfd, filename, flags));
if (fdf.err)
return fdf.err;
if (uattr) {
int ret;
struct mount_kattr kattr = {};
struct file *file = fd_prepare_file(fdf);
int ret;
if (flags & OPEN_TREE_CLONE)
kattr.kflags = MOUNT_KATTR_IDMAP_REPLACE;
@ -5061,12 +5037,7 @@ SYSCALL_DEFINE5(open_tree_attr, int, dfd, const char __user *, filename,
return ret;
}
fd = get_unused_fd_flags(flags & O_CLOEXEC);
if (fd < 0)
return fd;
fd_install(fd, no_free_ptr(file));
return fd;
return fd_publish(fdf);
}
int show_path(struct seq_file *m, struct dentry *root)

View File

@ -1597,16 +1597,20 @@ static struct hlist_head *fanotify_alloc_merge_hash(void)
return hash;
}
DEFINE_CLASS(fsnotify_group,
struct fsnotify_group *,
if (!IS_ERR_OR_NULL(_T)) fsnotify_destroy_group(_T),
fsnotify_alloc_group(ops, flags),
const struct fsnotify_ops *ops, int flags)
/* fanotify syscalls */
SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
{
struct user_namespace *user_ns = current_user_ns();
struct fsnotify_group *group;
int f_flags, fd;
unsigned int fid_mode = flags & FANOTIFY_FID_BITS;
unsigned int class = flags & FANOTIFY_CLASS_BITS;
unsigned int internal_flags = 0;
struct file *file;
pr_debug("%s: flags=%x event_f_flags=%x\n",
__func__, flags, event_f_flags);
@ -1690,36 +1694,29 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
if (flags & FAN_NONBLOCK)
f_flags |= O_NONBLOCK;
/* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */
group = fsnotify_alloc_group(&fanotify_fsnotify_ops,
CLASS(fsnotify_group, group)(&fanotify_fsnotify_ops,
FSNOTIFY_GROUP_USER);
if (IS_ERR(group)) {
/* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */
if (IS_ERR(group))
return PTR_ERR(group);
}
/* Enforce groups limits per user in all containing user ns */
group->fanotify_data.ucounts = inc_ucount(user_ns, current_euid(),
UCOUNT_FANOTIFY_GROUPS);
if (!group->fanotify_data.ucounts) {
fd = -EMFILE;
goto out_destroy_group;
}
if (!group->fanotify_data.ucounts)
return -EMFILE;
group->fanotify_data.flags = flags | internal_flags;
group->memcg = get_mem_cgroup_from_mm(current->mm);
group->user_ns = get_user_ns(user_ns);
group->fanotify_data.merge_hash = fanotify_alloc_merge_hash();
if (!group->fanotify_data.merge_hash) {
fd = -ENOMEM;
goto out_destroy_group;
}
if (!group->fanotify_data.merge_hash)
return -ENOMEM;
group->overflow_event = fanotify_alloc_overflow_event();
if (unlikely(!group->overflow_event)) {
fd = -ENOMEM;
goto out_destroy_group;
}
if (unlikely(!group->overflow_event))
return -ENOMEM;
if (force_o_largefile())
event_f_flags |= O_LARGEFILE;
@ -1738,8 +1735,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
group->priority = FSNOTIFY_PRIO_PRE_CONTENT;
break;
default:
fd = -EINVAL;
goto out_destroy_group;
return -EINVAL;
}
BUILD_BUG_ON(!(FANOTIFY_ADMIN_INIT_FLAGS & FAN_UNLIMITED_QUEUE));
@ -1750,27 +1746,15 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
}
if (flags & FAN_ENABLE_AUDIT) {
fd = -EPERM;
if (!capable(CAP_AUDIT_WRITE))
goto out_destroy_group;
return -EPERM;
}
fd = get_unused_fd_flags(f_flags);
if (fd < 0)
goto out_destroy_group;
file = anon_inode_getfile_fmode("[fanotify]", &fanotify_fops, group,
f_flags, FMODE_NONOTIFY);
if (IS_ERR(file)) {
put_unused_fd(fd);
fd = PTR_ERR(file);
goto out_destroy_group;
}
fd_install(fd, file);
return fd;
out_destroy_group:
fsnotify_destroy_group(group);
fd = FD_ADD(f_flags,
anon_inode_getfile_fmode("[fanotify]", &fanotify_fops,
group, f_flags, FMODE_NONOTIFY));
if (fd >= 0)
retain_and_null_ptr(group);
return fd;
}

View File

@ -110,7 +110,6 @@ int ns_get_path(struct path *path, struct task_struct *task,
int open_namespace(struct ns_common *ns)
{
struct path path __free(path_put) = {};
struct file *f;
int err;
/* call first to consume reference */
@ -118,16 +117,7 @@ int open_namespace(struct ns_common *ns)
if (err < 0)
return err;
CLASS(get_unused_fd, fd)(O_CLOEXEC);
if (fd < 0)
return fd;
f = dentry_open(&path, O_RDONLY, current_cred());
if (IS_ERR(f))
return PTR_ERR(f);
fd_install(fd, f);
return take_fd(fd);
return FD_ADD(O_CLOEXEC, dentry_open(&path, O_RDONLY, current_cred()));
}
int open_related_ns(struct ns_common *ns,
@ -313,7 +303,6 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl,
struct mnt_ns_info kinfo = {};
struct mnt_ns_info __user *uinfo = (struct mnt_ns_info __user *)arg;
struct path path __free(path_put) = {};
struct file *f __free(fput) = NULL;
size_t usize = _IOC_SIZE(ioctl);
if (ns->ns_type != CLONE_NEWNS)
@ -332,15 +321,9 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl,
if (ret)
return ret;
CLASS(get_unused_fd, fd)(O_CLOEXEC);
if (fd < 0)
return fd;
f = dentry_open(&path, O_RDONLY, current_cred());
if (IS_ERR(f))
return PTR_ERR(f);
if (uinfo) {
FD_PREPARE(fdf, O_CLOEXEC, dentry_open(&path, O_RDONLY, current_cred()));
if (fdf.err)
return fdf.err;
/*
* If @uinfo is passed return all information about the
* mount namespace as well.
@ -348,12 +331,8 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl,
ret = copy_ns_info_to_user(to_mnt_ns(ns), uinfo, usize, &kinfo);
if (ret)
return ret;
}
/* Transfer reference of @f to caller's fdtable. */
fd_install(fd, no_free_ptr(f));
/* File descriptor is live so hand it off to the caller. */
return take_fd(fd);
ret = fd_publish(fdf);
break;
}
default:
ret = -ENOTTY;

View File

@ -1416,8 +1416,8 @@ static int do_sys_openat2(int dfd, const char __user *filename,
struct open_how *how)
{
struct open_flags op;
struct filename *tmp;
int err, fd;
struct filename *tmp __free(putname) = NULL;
int err;
err = build_open_flags(how, &op);
if (unlikely(err))
@ -1427,18 +1427,7 @@ static int do_sys_openat2(int dfd, const char __user *filename,
if (IS_ERR(tmp))
return PTR_ERR(tmp);
fd = get_unused_fd_flags(how->flags);
if (likely(fd >= 0)) {
struct file *f = do_filp_open(dfd, tmp, &op);
if (IS_ERR(f)) {
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fd_install(fd, f);
}
}
putname(tmp);
return fd;
return FD_ADD(how->flags, do_filp_open(dfd, tmp, &op));
}
int do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)

View File

@ -250,8 +250,6 @@ static const struct file_operations signalfd_fops = {
static int do_signalfd4(int ufd, sigset_t *mask, int flags)
{
struct signalfd_ctx *ctx;
/* Check the SFD_* constants for consistency. */
BUILD_BUG_ON(SFD_CLOEXEC != O_CLOEXEC);
BUILD_BUG_ON(SFD_NONBLOCK != O_NONBLOCK);
@ -263,7 +261,8 @@ static int do_signalfd4(int ufd, sigset_t *mask, int flags)
signotset(mask);
if (ufd == -1) {
struct file *file;
int fd;
struct signalfd_ctx *ctx __free(kfree) = NULL;
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@ -271,22 +270,16 @@ static int do_signalfd4(int ufd, sigset_t *mask, int flags)
ctx->sigmask = *mask;
ufd = get_unused_fd_flags(flags & O_CLOEXEC);
if (ufd < 0) {
kfree(ctx);
return ufd;
}
file = anon_inode_getfile_fmode("[signalfd]", &signalfd_fops,
ctx, O_RDWR | (flags & O_NONBLOCK),
FMODE_NOWAIT);
if (IS_ERR(file)) {
put_unused_fd(ufd);
kfree(ctx);
return PTR_ERR(file);
}
fd_install(ufd, file);
fd = FD_ADD(flags & O_CLOEXEC,
anon_inode_getfile_fmode(
"[signalfd]", &signalfd_fops, ctx,
O_RDWR | (flags & O_NONBLOCK), FMODE_NOWAIT));
if (fd >= 0)
retain_and_null_ptr(ctx);
return fd;
} else {
struct signalfd_ctx *ctx;
CLASS(fd, f)(ufd);
if (fd_empty(f))
return -EBADF;

View File

@ -393,9 +393,8 @@ static const struct file_operations timerfd_fops = {
SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
{
int ufd;
struct timerfd_ctx *ctx;
struct file *file;
struct timerfd_ctx *ctx __free(kfree) = NULL;
int ret;
/* Check the TFD_* constants for consistency. */
BUILD_BUG_ON(TFD_CLOEXEC != O_CLOEXEC);
@ -432,23 +431,13 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
ctx->moffs = ktime_mono_to_real(0);
ufd = get_unused_fd_flags(flags & TFD_SHARED_FCNTL_FLAGS);
if (ufd < 0) {
kfree(ctx);
return ufd;
}
file = anon_inode_getfile_fmode("[timerfd]", &timerfd_fops, ctx,
ret = FD_ADD(flags & TFD_SHARED_FCNTL_FLAGS,
anon_inode_getfile_fmode("[timerfd]", &timerfd_fops, ctx,
O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS),
FMODE_NOWAIT);
if (IS_ERR(file)) {
put_unused_fd(ufd);
kfree(ctx);
return PTR_ERR(file);
}
fd_install(ufd, file);
return ufd;
FMODE_NOWAIT));
if (ret >= 0)
retain_and_null_ptr(ctx);
return ret;
}
static int do_timerfd_settime(int ufd, int flags,

View File

@ -2111,9 +2111,7 @@ static void init_once_userfaultfd_ctx(void *mem)
static int new_userfaultfd(int flags)
{
struct userfaultfd_ctx *ctx;
struct file *file;
int fd;
struct userfaultfd_ctx *ctx __free(kfree) = NULL;
VM_WARN_ON_ONCE(!current->mm);
@ -2135,26 +2133,18 @@ static int new_userfaultfd(int flags)
atomic_set(&ctx->mmap_changing, 0);
ctx->mm = current->mm;
fd = get_unused_fd_flags(flags & UFFD_SHARED_FCNTL_FLAGS);
if (fd < 0)
goto err_out;
FD_PREPARE(fdf, flags & UFFD_SHARED_FCNTL_FLAGS,
anon_inode_create_getfile("[userfaultfd]", &userfaultfd_fops, ctx,
O_RDONLY | (flags & UFFD_SHARED_FCNTL_FLAGS),
NULL));
if (fdf.err)
return fdf.err;
/* Create a new inode so that the LSM can block the creation. */
file = anon_inode_create_getfile("[userfaultfd]", &userfaultfd_fops, ctx,
O_RDONLY | (flags & UFFD_SHARED_FCNTL_FLAGS), NULL);
if (IS_ERR(file)) {
put_unused_fd(fd);
fd = PTR_ERR(file);
goto err_out;
}
/* prevent the mm struct to be freed */
mmgrab(ctx->mm);
file->f_mode |= FMODE_NOWAIT;
fd_install(fd, file);
return fd;
err_out:
kmem_cache_free(userfaultfd_ctx_cachep, ctx);
return fd;
fd_prepare_file(fdf)->f_mode |= FMODE_NOWAIT;
retain_and_null_ptr(ctx);
return fd_publish(fdf);
}
static inline bool userfaultfd_syscall_allowed(int flags)

View File

@ -233,14 +233,11 @@ xfs_open_by_handle(
xfs_fsop_handlereq_t *hreq)
{
const struct cred *cred = current_cred();
int error;
int fd;
int permflag;
struct file *filp;
struct inode *inode;
struct dentry *dentry;
fmode_t fmode;
struct path path;
struct path path __free(path_put) = {};
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@ -249,12 +246,11 @@ xfs_open_by_handle(
if (IS_ERR(dentry))
return PTR_ERR(dentry);
inode = d_inode(dentry);
path.dentry = dentry;
/* Restrict xfs_open_by_handle to directories & regular files. */
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
error = -EPERM;
goto out_dput;
}
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
return -EPERM;
#if BITS_PER_LONG != 32
hreq->oflags |= O_LARGEFILE;
@ -263,48 +259,30 @@ xfs_open_by_handle(
permflag = hreq->oflags;
fmode = OPEN_FMODE(permflag);
if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&
(fmode & FMODE_WRITE) && IS_APPEND(inode)) {
error = -EPERM;
goto out_dput;
}
(fmode & FMODE_WRITE) && IS_APPEND(inode))
return -EPERM;
if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
error = -EPERM;
goto out_dput;
}
if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode))
return -EPERM;
/* Can't write directories. */
if (S_ISDIR(inode->i_mode) && (fmode & FMODE_WRITE)) {
error = -EISDIR;
goto out_dput;
}
if (S_ISDIR(inode->i_mode) && (fmode & FMODE_WRITE))
return -EISDIR;
fd = get_unused_fd_flags(0);
if (fd < 0) {
error = fd;
goto out_dput;
}
path.mnt = mntget(parfilp->f_path.mnt);
path.mnt = parfilp->f_path.mnt;
path.dentry = dentry;
filp = dentry_open(&path, hreq->oflags, cred);
dput(dentry);
if (IS_ERR(filp)) {
put_unused_fd(fd);
return PTR_ERR(filp);
}
FD_PREPARE(fdf, 0, dentry_open(&path, hreq->oflags, cred));
if (fdf.err)
return fdf.err;
if (S_ISREG(inode->i_mode)) {
struct file *filp = fd_prepare_file(fdf);
filp->f_flags |= O_NOATIME;
filp->f_mode |= FMODE_NOCMTIME;
}
fd_install(fd, filp);
return fd;
out_dput:
dput(dentry);
return error;
return fd_publish(fdf);
}
int

View File

@ -261,6 +261,10 @@ const volatile void * __must_check_fn(const volatile void *val)
* CLASS(name, var)(args...):
* declare the variable @var as an instance of the named class
*
* CLASS_INIT(name, var, init_expr):
* declare the variable @var as an instance of the named class with
* custom initialization expression.
*
* Ex.
*
* DEFINE_CLASS(fdget, struct fd, fdput(_T), fdget(fd), int fd)
@ -290,6 +294,9 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
class_##_name##_t var __cleanup(class_##_name##_destructor) = \
class_##_name##_constructor
#define CLASS_INIT(_name, _var, _init_expr) \
class_##_name##_t _var __cleanup(class_##_name##_destructor) = (_init_expr)
#define __scoped_class(_name, var, _label, args...) \
for (CLASS(_name, var)(args); ; ({ goto _label; })) \
if (0) { \

View File

@ -127,4 +127,130 @@ extern void __fput_sync(struct file *);
extern unsigned int sysctl_nr_open_min, sysctl_nr_open_max;
/*
* fd_prepare: Combined fd + file allocation cleanup class.
* @err: Error code to indicate if allocation succeeded.
* @__fd: Allocated fd (may not be accessed directly)
* @__file: Allocated struct file pointer (may not be accessed directly)
*
* Allocates an fd and a file together. On error paths, automatically cleans
* up whichever resource was successfully allocated. Allows flexible file
* allocation with different functions per usage.
*
* Do not use directly.
*/
struct fd_prepare {
s32 err;
s32 __fd; /* do not access directly */
struct file *__file; /* do not access directly */
};
/* Typedef for fd_prepare cleanup guards. */
typedef struct fd_prepare class_fd_prepare_t;
/*
* Accessors for fd_prepare class members.
* _Generic() is used for zero-cost type safety.
*/
#define fd_prepare_fd(_fdf) \
(_Generic((_fdf), struct fd_prepare: (_fdf).__fd))
#define fd_prepare_file(_fdf) \
(_Generic((_fdf), struct fd_prepare: (_fdf).__file))
/* Do not use directly. */
static inline void class_fd_prepare_destructor(const struct fd_prepare *fdf)
{
if (unlikely(fdf->err)) {
if (likely(fdf->__fd >= 0))
put_unused_fd(fdf->__fd);
if (unlikely(!IS_ERR_OR_NULL(fdf->__file)))
fput(fdf->__file);
}
}
/* Do not use directly. */
static inline int class_fd_prepare_lock_err(const struct fd_prepare *fdf)
{
if (unlikely(fdf->err))
return fdf->err;
if (unlikely(fdf->__fd < 0))
return fdf->__fd;
if (unlikely(IS_ERR(fdf->__file)))
return PTR_ERR(fdf->__file);
if (unlikely(!fdf->__file))
return -ENOMEM;
return 0;
}
/*
* __FD_PREPARE_INIT - Helper to initialize fd_prepare class.
* @_fd_flags: flags for get_unused_fd_flags()
* @_file_owned: expression that returns struct file *
*
* Returns a struct fd_prepare with fd, file, and err set.
* If fd allocation fails, fd will be negative and err will be set. If
* fd succeeds but file_init_expr fails, file will be ERR_PTR and err
* will be set. The err field is the single source of truth for error
* checking.
*/
#define __FD_PREPARE_INIT(_fd_flags, _file_owned) \
({ \
struct fd_prepare fdf = { \
.__fd = get_unused_fd_flags((_fd_flags)), \
}; \
if (likely(fdf.__fd >= 0)) \
fdf.__file = (_file_owned); \
fdf.err = ACQUIRE_ERR(fd_prepare, &fdf); \
fdf; \
})
/*
* FD_PREPARE - Macro to declare and initialize an fd_prepare variable.
*
* Declares and initializes an fd_prepare variable with automatic
* cleanup. No separate scope required - cleanup happens when variable
* goes out of scope.
*
* @_fdf: name of struct fd_prepare variable to define
* @_fd_flags: flags for get_unused_fd_flags()
* @_file_owned: struct file to take ownership of (can be expression)
*/
#define FD_PREPARE(_fdf, _fd_flags, _file_owned) \
CLASS_INIT(fd_prepare, _fdf, __FD_PREPARE_INIT(_fd_flags, _file_owned))
/*
* fd_publish - Publish prepared fd and file to the fd table.
* @_fdf: struct fd_prepare variable
*/
#define fd_publish(_fdf) \
({ \
struct fd_prepare *fdp = &(_fdf); \
VFS_WARN_ON_ONCE(fdp->err); \
VFS_WARN_ON_ONCE(fdp->__fd < 0); \
VFS_WARN_ON_ONCE(IS_ERR_OR_NULL(fdp->__file)); \
fd_install(fdp->__fd, fdp->__file); \
fdp->__fd; \
})
/* Do not use directly. */
#define __FD_ADD(_fdf, _fd_flags, _file_owned) \
({ \
FD_PREPARE(_fdf, _fd_flags, _file_owned); \
s32 ret = _fdf.err; \
if (likely(!ret)) \
ret = fd_publish(_fdf); \
ret; \
})
/*
* FD_ADD - Allocate and install an fd and file in one step.
* @_fd_flags: flags for get_unused_fd_flags()
* @_file_owned: struct file to take ownership of
*
* Returns the allocated fd number, or negative error code on failure.
*/
#define FD_ADD(_fd_flags, _file_owned) \
__FD_ADD(__UNIQUE_ID(fd_prepare), _fd_flags, _file_owned)
#endif /* __LINUX_FILE_H */

View File

@ -211,10 +211,9 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag
const struct file_operations *fops = &io_mock_fops;
const struct io_uring_sqe *sqe = cmd->sqe;
struct io_uring_mock_create mc, __user *uarg;
struct io_mock_file *mf = NULL;
struct file *file = NULL;
struct file *file;
struct io_mock_file *mf __free(kfree) = NULL;
size_t uarg_size;
int fd = -1, ret;
/*
* It's a testing only driver that allows exercising edge cases
@ -246,10 +245,6 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag
if (!mf)
return -ENOMEM;
ret = fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC);
if (fd < 0)
goto fail;
init_waitqueue_head(&mf->poll_wq);
mf->size = mc.file_size;
mf->rw_delay_ns = mc.rw_delay_ns;
@ -258,33 +253,25 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag
mf->pollable = true;
}
file = anon_inode_create_getfile("[io_uring_mock]", fops,
mf, O_RDWR | O_CLOEXEC, NULL);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto fail;
}
FD_PREPARE(fdf, O_RDWR | O_CLOEXEC,
anon_inode_create_getfile("[io_uring_mock]", fops, mf,
O_RDWR | O_CLOEXEC, NULL));
if (fdf.err)
return fdf.err;
file->f_mode |= FMODE_READ | FMODE_CAN_READ |
FMODE_WRITE | FMODE_CAN_WRITE |
FMODE_LSEEK;
retain_and_null_ptr(mf);
file = fd_prepare_file(fdf);
file->f_mode |= FMODE_READ | FMODE_CAN_READ | FMODE_WRITE |
FMODE_CAN_WRITE | FMODE_LSEEK;
if (mc.flags & IORING_MOCK_CREATE_F_SUPPORT_NOWAIT)
file->f_mode |= FMODE_NOWAIT;
mc.out_fd = fd;
if (copy_to_user(uarg, &mc, uarg_size)) {
fput(file);
ret = -EFAULT;
goto fail;
}
mc.out_fd = fd_prepare_fd(fdf);
if (copy_to_user(uarg, &mc, uarg_size))
return -EFAULT;
fd_install(fd, file);
fd_publish(fdf);
return 0;
fail:
if (fd >= 0)
put_unused_fd(fd);
kfree(mf);
return ret;
}
static int io_probe_mock(struct io_uring_cmd *cmd)

View File

@ -892,15 +892,35 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro,
return inode_permission(&nop_mnt_idmap, d_inode(dentry), acc);
}
static struct file *mqueue_file_open(struct filename *name,
struct vfsmount *mnt, int oflag, bool ro,
umode_t mode, struct mq_attr *attr)
{
struct dentry *dentry;
struct file *file;
int ret;
dentry = start_creating_noperm(mnt->mnt_root, &QSTR(name->name));
if (IS_ERR(dentry))
return ERR_CAST(dentry);
ret = prepare_open(dentry, oflag, ro, mode, name, attr);
file = ERR_PTR(ret);
if (!ret) {
const struct path path = { .mnt = mnt, .dentry = dentry };
file = dentry_open(&path, oflag, current_cred());
}
end_creating(dentry);
return file;
}
static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
struct mq_attr *attr)
{
struct filename *name __free(putname) = NULL;;
struct vfsmount *mnt = current->nsproxy->ipc_ns->mq_mnt;
struct dentry *root = mnt->mnt_root;
struct filename *name;
struct path path;
int fd, error;
int ro;
int fd, ro;
audit_mq_open(oflag, mode, attr);
@ -908,35 +928,10 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
if (IS_ERR(name))
return PTR_ERR(name);
fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0)
goto out_putname;
ro = mnt_want_write(mnt); /* we'll drop it in any case */
path.dentry = start_creating_noperm(root, &QSTR(name->name));
if (IS_ERR(path.dentry)) {
error = PTR_ERR(path.dentry);
goto out_putfd;
}
path.mnt = mnt;
error = prepare_open(path.dentry, oflag, ro, mode, name, attr);
if (!error) {
struct file *file = dentry_open(&path, oflag, current_cred());
if (!IS_ERR(file))
fd_install(fd, file);
else
error = PTR_ERR(file);
}
out_putfd:
if (error) {
put_unused_fd(fd);
fd = error;
}
end_creating(path.dentry);
fd = FD_ADD(O_CLOEXEC, mqueue_file_open(name, mnt, oflag, ro, mode, attr));
if (!ro)
mnt_drop_write(mnt);
out_putname:
putname(name);
return fd;
}

View File

@ -634,37 +634,24 @@ static int prepare_seq_file(struct file *file, struct bpf_iter_link *link)
int bpf_iter_new_fd(struct bpf_link *link)
{
struct bpf_iter_link *iter_link;
struct file *file;
unsigned int flags;
int err, fd;
int err;
if (link->ops != &bpf_iter_link_lops)
return -EINVAL;
flags = O_RDONLY | O_CLOEXEC;
fd = get_unused_fd_flags(flags);
if (fd < 0)
return fd;
file = anon_inode_getfile("bpf_iter", &bpf_iter_fops, NULL, flags);
if (IS_ERR(file)) {
err = PTR_ERR(file);
goto free_fd;
}
FD_PREPARE(fdf, flags, anon_inode_getfile("bpf_iter", &bpf_iter_fops, NULL, flags));
if (fdf.err)
return fdf.err;
iter_link = container_of(link, struct bpf_iter_link, link);
err = prepare_seq_file(file, iter_link);
err = prepare_seq_file(fd_prepare_file(fdf), iter_link);
if (err)
goto free_file;
return err; /* Automatic cleanup handles fput */
fd_install(fd, file);
return fd;
free_file:
fput(file);
free_fd:
put_unused_fd(fd);
return err;
return fd_publish(fdf);
}
struct bpf_prog *bpf_iter_get_info(struct bpf_iter_meta *meta, bool in_stop)

View File

@ -110,16 +110,15 @@ const struct file_operations bpf_token_fops = {
int bpf_token_create(union bpf_attr *attr)
{
struct bpf_token *token __free(kfree) = NULL;
struct bpf_mount_opts *mnt_opts;
struct bpf_token *token = NULL;
struct user_namespace *userns;
struct inode *inode;
struct file *file;
CLASS(fd, f)(attr->token_create.bpffs_fd);
struct path path;
struct super_block *sb;
umode_t mode;
int err, fd;
int err;
if (fd_empty(f))
return -EBADF;
@ -166,23 +165,20 @@ int bpf_token_create(union bpf_attr *attr)
inode->i_fop = &bpf_token_fops;
clear_nlink(inode); /* make sure it is unlinked */
file = alloc_file_pseudo(inode, path.mnt, BPF_TOKEN_INODE_NAME, O_RDWR, &bpf_token_fops);
if (IS_ERR(file)) {
iput(inode);
return PTR_ERR(file);
}
FD_PREPARE(fdf, O_CLOEXEC,
alloc_file_pseudo(inode, path.mnt, BPF_TOKEN_INODE_NAME,
O_RDWR, &bpf_token_fops));
if (fdf.err)
return fdf.err;
token = kzalloc(sizeof(*token), GFP_USER);
if (!token) {
err = -ENOMEM;
goto out_file;
}
if (!token)
return -ENOMEM;
atomic64_set(&token->refcnt, 1);
/* remember bpffs owning userns for future ns_capable() checks */
token->userns = get_user_ns(userns);
/* remember bpffs owning userns for future ns_capable() checks. */
token->userns = userns;
token->allowed_cmds = mnt_opts->delegate_cmds;
token->allowed_maps = mnt_opts->delegate_maps;
token->allowed_progs = mnt_opts->delegate_progs;
@ -190,24 +186,11 @@ int bpf_token_create(union bpf_attr *attr)
err = security_bpf_token_create(token, attr, &path);
if (err)
goto out_token;
fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0) {
err = fd;
goto out_token;
}
file->private_data = token;
fd_install(fd, file);
return fd;
out_token:
bpf_token_free(token);
out_file:
fput(file);
return err;
get_user_ns(token->userns);
fd_prepare_file(fdf)->private_data = no_free_ptr(token);
return fd_publish(fdf);
}
int bpf_token_get_info_by_fd(struct bpf_token *token,

View File

@ -497,9 +497,9 @@ SYSCALL_DEFINE2(memfd_create,
const char __user *, uname,
unsigned int, flags)
{
struct file *file;
int fd, error;
char *name;
char *name __free(kfree) = NULL;
unsigned int fd_flags;
int error;
error = sanitize_flags(&flags);
if (error < 0)
@ -509,25 +509,6 @@ SYSCALL_DEFINE2(memfd_create,
if (IS_ERR(name))
return PTR_ERR(name);
fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);
if (fd < 0) {
error = fd;
goto err_free_name;
}
file = alloc_file(name, flags);
if (IS_ERR(file)) {
error = PTR_ERR(file);
goto err_free_fd;
}
fd_install(fd, file);
kfree(name);
return fd;
err_free_fd:
put_unused_fd(fd);
err_free_name:
kfree(name);
return error;
fd_flags = (flags & MFD_CLOEXEC) ? O_CLOEXEC : 0;
return FD_ADD(fd_flags, alloc_file(name, flags));
}

View File

@ -224,9 +224,6 @@ static struct file *secretmem_file_create(unsigned long flags)
SYSCALL_DEFINE1(memfd_secret, unsigned int, flags)
{
struct file *file;
int fd, err;
/* make sure local flags do not confict with global fcntl.h */
BUILD_BUG_ON(SECRETMEM_FLAGS_MASK & O_CLOEXEC);
@ -238,22 +235,7 @@ SYSCALL_DEFINE1(memfd_secret, unsigned int, flags)
if (atomic_read(&secretmem_users) < 0)
return -ENFILE;
fd = get_unused_fd_flags(flags & O_CLOEXEC);
if (fd < 0)
return fd;
file = secretmem_file_create(flags);
if (IS_ERR(file)) {
err = PTR_ERR(file);
goto err_put_fd;
}
fd_install(fd, file);
return fd;
err_put_fd:
put_unused_fd(fd);
return err;
return FD_ADD(flags & O_CLOEXEC, secretmem_file_create(flags));
}
static int secretmem_init_fs_context(struct fs_context *fc)

View File

@ -93,7 +93,7 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info)
struct handshake_net *hn = handshake_pernet(net);
struct handshake_req *req = NULL;
struct socket *sock;
int class, fd, err;
int class, err;
err = -EOPNOTSUPP;
if (!hn)
@ -106,26 +106,24 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info)
err = -EAGAIN;
req = handshake_req_next(hn, class);
if (!req)
goto out_status;
if (req) {
sock = req->hr_sk->sk_socket;
fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0) {
err = fd;
FD_PREPARE(fdf, O_CLOEXEC, sock->file);
if (fdf.err) {
err = fdf.err;
goto out_complete;
}
err = req->hr_proto->hp_accept(req, info, fd);
if (err) {
put_unused_fd(fd);
goto out_complete;
}
get_file(sock->file); /* FD_PREPARE() consumes a reference. */
err = req->hr_proto->hp_accept(req, info, fd_prepare_fd(fdf));
if (err)
goto out_complete; /* Automatic cleanup handles fput */
fd_install(fd, get_file(sock->file));
trace_handshake_cmd_accept(net, req, req->hr_sk, fd);
trace_handshake_cmd_accept(net, req, req->hr_sk, fd_prepare_fd(fdf));
fd_publish(fdf);
return 0;
}
out_complete:
handshake_complete(req, -EIO, NULL);

View File

@ -1560,24 +1560,16 @@ static int kcm_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
}
case SIOCKCMCLONE: {
struct kcm_clone info;
struct file *file;
info.fd = get_unused_fd_flags(0);
if (unlikely(info.fd < 0))
return info.fd;
FD_PREPARE(fdf, 0, kcm_clone(sock));
if (fdf.err)
return fdf.err;
file = kcm_clone(sock);
if (IS_ERR(file)) {
put_unused_fd(info.fd);
return PTR_ERR(file);
}
if (copy_to_user((void __user *)arg, &info,
sizeof(info))) {
put_unused_fd(info.fd);
fput(file);
info.fd = fd_prepare_fd(fdf);
if (copy_to_user((void __user *)arg, &info, sizeof(info)))
return -EFAULT;
}
fd_install(info.fd, file);
fd_publish(fdf);
err = 0;
break;
}

View File

@ -503,21 +503,12 @@ EXPORT_SYMBOL(sock_alloc_file);
static int sock_map_fd(struct socket *sock, int flags)
{
struct file *newfile;
int fd = get_unused_fd_flags(flags);
if (unlikely(fd < 0)) {
int fd;
fd = FD_ADD(flags, sock_alloc_file(sock, flags, NULL));
if (fd < 0)
sock_release(sock);
return fd;
}
newfile = sock_alloc_file(sock, flags, NULL);
if (!IS_ERR(newfile)) {
fd_install(fd, newfile);
return fd;
}
put_unused_fd(fd);
return PTR_ERR(newfile);
}
/**
@ -2012,8 +2003,6 @@ static int __sys_accept4_file(struct file *file, struct sockaddr __user *upeer_s
int __user *upeer_addrlen, int flags)
{
struct proto_accept_arg arg = { };
struct file *newfile;
int newfd;
if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
return -EINVAL;
@ -2021,18 +2010,7 @@ static int __sys_accept4_file(struct file *file, struct sockaddr __user *upeer_s
if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
newfd = get_unused_fd_flags(flags);
if (unlikely(newfd < 0))
return newfd;
newfile = do_accept(file, &arg, upeer_sockaddr, upeer_addrlen,
flags);
if (IS_ERR(newfile)) {
put_unused_fd(newfd);
return PTR_ERR(newfile);
}
fd_install(newfd, newfile);
return newfd;
return FD_ADD(flags, do_accept(file, &arg, upeer_sockaddr, upeer_addrlen, flags));
}
/*

View File

@ -3276,9 +3276,6 @@ EXPORT_SYMBOL_GPL(unix_outq_len);
static int unix_open_file(struct sock *sk)
{
struct file *f;
int fd;
if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
@ -3288,18 +3285,7 @@ static int unix_open_file(struct sock *sk)
if (!unix_sk(sk)->path.dentry)
return -ENOENT;
fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0)
return fd;
f = dentry_open(&unix_sk(sk)->path, O_PATH, current_cred());
if (IS_ERR(f)) {
put_unused_fd(fd);
return PTR_ERR(f);
}
fd_install(fd, f);
return fd;
return FD_ADD(O_CLOEXEC, dentry_open(&unix_sk(sk)->path, O_PATH, current_cred()));
}
static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)