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) static int spufs_context_open(const struct path *path)
{ {
int ret; FD_PREPARE(fdf, 0, dentry_open(path, O_RDONLY, current_cred()));
struct file *filp; if (fdf.err)
return fdf.err;
ret = get_unused_fd_flags(0); fd_prepare_file(fdf)->f_op = &spufs_context_fops;
if (ret < 0) return fd_publish(fdf);
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;
} }
static struct spu_context * 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) 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 * get references for dget and mntget, will be released
* in error path of *_open(). * in error path of *_open().
*/ */
filp = dentry_open(path, O_RDONLY, current_cred()); FD_PREPARE(fdf, 0, dentry_open(path, O_RDONLY, current_cred()));
if (IS_ERR(filp)) { if (fdf.err)
put_unused_fd(ret); return fdf.err;
return PTR_ERR(filp); fd_prepare_file(fdf)->f_op = &spufs_gang_fops;
} return fd_publish(fdf);
filp->f_op = &spufs_gang_fops;
fd_install(ret, filp);
return ret;
} }
static int spufs_create_gang(struct inode *inode, 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) static int papr_hvpipe_dev_create_handle(u32 srcID)
{ {
struct hvpipe_source_info *src_info; struct hvpipe_source_info *src_info __free(kfree) = NULL;
struct file *file;
long err;
int fd;
spin_lock(&hvpipe_src_list_lock); spin_lock(&hvpipe_src_list_lock);
/* /*
@ -506,20 +503,13 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
src_info->tsk = current; src_info->tsk = current;
init_waitqueue_head(&src_info->recv_wqh); init_waitqueue_head(&src_info->recv_wqh);
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
if (fd < 0) { anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
err = fd; (void *)src_info, O_RDWR));
goto free_buf; if (fdf.err)
} return fdf.err;
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;
}
retain_and_null_ptr(src_info);
spin_lock(&hvpipe_src_list_lock); spin_lock(&hvpipe_src_list_lock);
/* /*
* If two processes are executing ioctl() for the same * 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)) { if (hvpipe_find_source(srcID)) {
spin_unlock(&hvpipe_src_list_lock); spin_unlock(&hvpipe_src_list_lock);
err = -EALREADY; return -EALREADY;
goto free_file;
} }
list_add(&src_info->list, &hvpipe_src_list); list_add(&src_info->list, &hvpipe_src_list);
spin_unlock(&hvpipe_src_list_lock); spin_unlock(&hvpipe_src_list_lock);
return fd_publish(fdf);
fd_install(fd, file);
return fd;
free_file:
fput(file);
free_fd:
put_unused_fd(fd);
free_buf:
kfree(src_info);
return err;
} }
/* /*

View File

@ -303,8 +303,6 @@ static long papr_platform_dump_create_handle(u64 dump_tag)
{ {
struct ibm_platform_dump_params *params; struct ibm_platform_dump_params *params;
u64 param_dump_tag; u64 param_dump_tag;
struct file *file;
long err;
int fd; 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->dump_tag_lo = (u32)(dump_tag & 0x00000000ffffffffULL);
params->status = RTAS_IBM_PLATFORM_DUMP_START; params->status = RTAS_IBM_PLATFORM_DUMP_START;
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); 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 (fd < 0) { if (fd < 0) {
err = fd; rtas_work_area_free(params->work_area);
goto free_area; kfree(params);
return fd;
} }
file = 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;
}
fd_install(fd, file);
list_add(&params->list, &platform_dump_list); list_add(&params->list, &platform_dump_list);
pr_info("%s (%d) initiated platform dump for dump tag %llu\n", pr_info("%s (%d) initiated platform dump for dump tag %llu\n",
current->comm, current->pid, dump_tag); current->comm, current->pid, dump_tag);
return fd; 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) char *name)
{ {
const struct papr_rtas_blob *blob; const struct papr_rtas_blob *blob;
struct file *file;
long ret;
int fd; int fd;
blob = papr_rtas_retrieve(seq); blob = papr_rtas_retrieve(seq);
if (IS_ERR(blob)) if (IS_ERR(blob))
return PTR_ERR(blob); return PTR_ERR(blob);
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); fd = FD_ADD(O_RDONLY | O_CLOEXEC,
if (fd < 0) { anon_inode_getfile_fmode(name, fops, (void *)blob, O_RDONLY,
ret = fd; FMODE_LSEEK | FMODE_PREAD));
goto free_blob; if (fd < 0)
} papr_rtas_blob_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; return fd;
put_fd:
put_unused_fd(fd);
free_blob:
papr_rtas_blob_free(blob);
return ret;
} }
/* /*

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 dma_buf_fd(struct dma_buf *dmabuf, int flags)
{ {
int fd;
if (!dmabuf || !dmabuf->file) if (!dmabuf || !dmabuf->file)
return -EINVAL; return -EINVAL;
fd = get_unused_fd_flags(flags); return FD_ADD(flags, dmabuf->file);
if (fd < 0)
return fd;
fd_install(fd, dmabuf->file);
return fd;
} }
EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF"); EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF");

View File

@ -298,12 +298,13 @@ static const struct file_operations linehandle_fileops = {
#endif #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) static int linehandle_create(struct gpio_device *gdev, void __user *ip)
{ {
struct gpiohandle_request handlereq; struct gpiohandle_request handlereq;
struct linehandle_state *lh; struct linehandle_state *lh __free(linehandle_free) = NULL;
struct file *file; int i, ret;
int fd, i, ret;
u32 lflags; u32 lflags;
if (copy_from_user(&handlereq, ip, sizeof(handlereq))) 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, lh->label = kstrndup(handlereq.consumer_label,
sizeof(handlereq.consumer_label) - 1, sizeof(handlereq.consumer_label) - 1,
GFP_KERNEL); GFP_KERNEL);
if (!lh->label) { if (!lh->label)
ret = -ENOMEM; return -ENOMEM;
goto out_free_lh;
}
} }
lh->num_descs = handlereq.lines; 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]; u32 offset = handlereq.lineoffsets[i];
struct gpio_desc *desc = gpio_device_get_desc(gdev, offset); struct gpio_desc *desc = gpio_device_get_desc(gdev, offset);
if (IS_ERR(desc)) { if (IS_ERR(desc))
ret = PTR_ERR(desc); return PTR_ERR(desc);
goto out_free_lh;
}
ret = gpiod_request_user(desc, lh->label); ret = gpiod_request_user(desc, lh->label);
if (ret) if (ret)
goto out_free_lh; return ret;
lh->descs[i] = desc; lh->descs[i] = desc;
linehandle_flags_to_desc_flags(handlereq.flags, &desc->flags); linehandle_flags_to_desc_flags(handlereq.flags, &desc->flags);
ret = gpiod_set_transitory(desc, false); ret = gpiod_set_transitory(desc, false);
if (ret < 0) if (ret < 0)
goto out_free_lh; return ret;
/* /*
* Lines have to be requested explicitly for input * 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); ret = gpiod_direction_output_nonotify(desc, val);
if (ret) if (ret)
goto out_free_lh; return ret;
} else if (lflags & GPIOHANDLE_REQUEST_INPUT) { } else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
ret = gpiod_direction_input_nonotify(desc); ret = gpiod_direction_input_nonotify(desc);
if (ret) if (ret)
goto out_free_lh; return ret;
} }
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); 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); offset);
} }
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
if (fd < 0) { anon_inode_getfile("gpio-linehandle", &linehandle_fileops,
ret = fd; lh, O_RDONLY | O_CLOEXEC));
goto out_free_lh; if (fdf.err)
} return fdf.err;
retain_and_null_ptr(lh);
file = anon_inode_getfile("gpio-linehandle", handlereq.fd = fd_prepare_fd(fdf);
&linehandle_fileops, if (copy_to_user(ip, &handlereq, sizeof(handlereq)))
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);
return -EFAULT; return -EFAULT;
}
fd_install(fd, file); fd_publish(fdf);
dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
lh->num_descs); lh->num_descs);
return 0; return 0;
out_put_unused_fd:
put_unused_fd(fd);
out_free_lh:
linehandle_free(lh);
return ret;
} }
#endif /* CONFIG_GPIO_CDEV_V1 */ #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 = {}; struct hv_partition_creation_properties creation_properties = {};
union hv_partition_isolation_properties isolation_properties = {}; union hv_partition_isolation_properties isolation_properties = {};
struct mshv_partition *partition; struct mshv_partition *partition;
struct file *file;
int fd;
long ret; long ret;
if (copy_from_user(&args, user_arg, sizeof(args))) 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; goto delete_partition;
ret = mshv_init_async_handler(partition); ret = mshv_init_async_handler(partition);
if (ret) if (!ret) {
goto remove_partition; ret = FD_ADD(O_CLOEXEC, anon_inode_getfile("mshv_partition",
&mshv_partition_fops,
fd = get_unused_fd_flags(O_CLOEXEC); partition, O_RDWR));
if (fd < 0) { if (ret >= 0)
ret = fd; return ret;
goto remove_partition;
} }
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); remove_partition(partition);
delete_partition: delete_partition:
hv_call_delete_partition(partition->pt_id); 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) int media_request_alloc(struct media_device *mdev, int *alloc_fd)
{ {
struct media_request *req; struct media_request *req;
struct file *filp;
int fd;
int ret; int ret;
/* Either both are NULL or both are non-NULL */ /* 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) if (!req)
return -ENOMEM; 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->mdev = mdev;
req->state = MEDIA_REQUEST_STATE_IDLE; req->state = MEDIA_REQUEST_STATE_IDLE;
req->num_incomplete_objects = 0; 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->updating_count = 0;
req->access_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", 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); dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
fd_install(fd, filp);
return 0; return 0;
err_put_fd:
put_unused_fd(fd);
err_free_req: err_free_req:
if (mdev->ops->req_free) if (mdev->ops->req_free)
mdev->ops->req_free(req); 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) static int ntsync_obj_get_fd(struct ntsync_obj *obj)
{ {
struct file *file; FD_PREPARE(fdf, O_CLOEXEC,
int fd; anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR));
if (fdf.err)
fd = get_unused_fd_flags(O_CLOEXEC); return fdf.err;
if (fd < 0) obj->file = fd_prepare_file(fdf);
return fd; return fd_publish(fdf);
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;
} }
static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp) 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 #ifdef CONFIG_UNIX98_PTYS
static struct cdev ptmx_cdev; 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 * ptm_open_peer - open the peer of a pty
* @master: the open struct file of the ptmx device node * @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 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) if (tty->driver != ptm_driver)
return -EIO; return -EIO;
fd = get_unused_fd_flags(flags); return FD_ADD(flags, ptm_open_peer_file(master, tty, 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;
} }
static int pty_unix98_ioctl(struct tty_struct *tty, 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) char __user *arg)
{ {
struct vfio_device *device; struct vfio_device *device;
struct file *filep;
char *buf; char *buf;
int fdno; int fd;
int ret;
buf = strndup_user(arg, PAGE_SIZE); buf = strndup_user(arg, PAGE_SIZE);
if (IS_ERR(buf)) if (IS_ERR(buf))
@ -313,26 +311,10 @@ static int vfio_group_ioctl_get_device_fd(struct vfio_group *group,
if (IS_ERR(device)) if (IS_ERR(device))
return PTR_ERR(device); return PTR_ERR(device);
fdno = get_unused_fd_flags(O_CLOEXEC); fd = FD_ADD(O_CLOEXEC, vfio_device_open_file(device));
if (fdno < 0) { if (fd < 0)
ret = fdno; vfio_device_put_registration(device);
goto err_put_device; return fd;
}
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:
vfio_device_put_registration(device);
return ret;
} }
static int vfio_group_ioctl_get_status(struct vfio_group *group, 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, const struct inode *context_inode,
bool make_inode) bool make_inode)
{ {
int error, fd; return FD_ADD(flags, __anon_inode_getfile(name, fops, priv, flags,
struct file *file; context_inode, make_inode));
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;
} }
/** /**

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) static int autofs_dev_ioctl_open_mountpoint(const char *name, dev_t devid)
{ {
int err, fd; struct path path __free(path_put) = {};
int err;
fd = get_unused_fd_flags(O_CLOEXEC); err = find_autofs_mount(name, &path, test_by_dev, &devid);
if (likely(fd >= 0)) { if (err)
struct file *filp; return err;
struct path path;
err = find_autofs_mount(name, &path, test_by_dev, &devid); return FD_ADD(O_CLOEXEC, dentry_open(&path, O_RDONLY, current_cred()));
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;
} }
/* Open a file descriptor on an autofs mount point */ /* 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) static int do_eventfd(unsigned int count, int flags)
{ {
struct eventfd_ctx *ctx; struct eventfd_ctx *ctx __free(kfree) = NULL;
struct file *file;
int fd;
/* Check the EFD_* constants for consistency. */ /* Check the EFD_* constants for consistency. */
BUILD_BUG_ON(EFD_CLOEXEC != O_CLOEXEC); 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); init_waitqueue_head(&ctx->wqh);
ctx->count = count; ctx->count = count;
ctx->flags = flags; ctx->flags = flags;
ctx->id = ida_alloc(&eventfd_ida, GFP_KERNEL);
flags &= EFD_SHARED_FCNTL_FLAGS; flags &= EFD_SHARED_FCNTL_FLAGS;
flags |= O_RDWR; flags |= O_RDWR;
fd = get_unused_fd_flags(flags);
if (fd < 0)
goto err;
file = anon_inode_getfile_fmode("[eventfd]", &eventfd_fops, FD_PREPARE(fdf, flags,
ctx, flags, FMODE_NOWAIT); anon_inode_getfile_fmode("[eventfd]", &eventfd_fops, ctx,
if (IS_ERR(file)) { flags, FMODE_NOWAIT));
put_unused_fd(fd); if (fdf.err)
fd = PTR_ERR(file); return fdf.err;
goto err;
} ctx->id = ida_alloc(&eventfd_ida, GFP_KERNEL);
fd_install(fd, file); retain_and_null_ptr(ctx);
return fd; return fd_publish(fdf);
err:
eventfd_free_ctx(ctx);
return fd;
} }
SYSCALL_DEFINE2(eventfd2, unsigned int, count, int, flags) 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) static int do_epoll_create(int flags)
{ {
int error, fd; int error;
struct eventpoll *ep = NULL; struct eventpoll *ep;
struct file *file;
/* Check the EPOLL_* constant for consistency. */ /* Check the EPOLL_* constant for consistency. */
BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC); 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, * Creates all the items needed to setup an eventpoll file. That is,
* a file structure and a free file descriptor. * a file structure and a free file descriptor.
*/ */
fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC)); FD_PREPARE(fdf, O_RDWR | (flags & O_CLOEXEC),
if (fd < 0) { anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep,
error = fd; O_RDWR | (flags & O_CLOEXEC)));
goto out_free_ep; if (fdf.err) {
ep_clear_and_put(ep);
return fdf.err;
} }
file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep, ep->file = fd_prepare_file(fdf);
O_RDWR | (flags & O_CLOEXEC)); return fd_publish(fdf);
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:
ep_clear_and_put(ep);
return error;
} }
SYSCALL_DEFINE1(epoll_create1, int, flags) 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. */ /* Pass the opened binary to the interpreter. */
if (bprm->have_execfd) { if (bprm->have_execfd) {
retval = get_unused_fd_flags(0); retval = FD_ADD(0, bprm->executable);
if (retval < 0) if (retval < 0)
goto out_unlock; goto out_unlock;
fd_install(retval, bprm->executable);
bprm->executable = NULL; bprm->executable = NULL;
bprm->execfd = retval; bprm->execfd = retval;
} }

View File

@ -404,32 +404,28 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
return retval; 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, static long do_handle_open(int mountdirfd, struct file_handle __user *ufh,
int open_flag) int open_flag)
{ {
long retval = 0; long retval;
struct path path __free(path_put) = {}; struct path path __free(path_put) = {};
struct file *file;
const struct export_operations *eops;
retval = handle_to_path(mountdirfd, ufh, &path, open_flag); retval = handle_to_path(mountdirfd, ufh, &path, open_flag);
if (retval) if (retval)
return retval; return retval;
CLASS(get_unused_fd, fd)(open_flag); return FD_ADD(open_flag, file_open_handle(&path, 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);
} }
/** /**

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 receive_fd(struct file *file, int __user *ufd, unsigned int o_flags)
{ {
int new_fd;
int error; int error;
error = security_file_receive(file); error = security_file_receive(file);
if (error) if (error)
return error; return error;
new_fd = get_unused_fd_flags(o_flags); FD_PREPARE(fdf, o_flags, file);
if (new_fd < 0) if (fdf.err)
return new_fd; return fdf.err;
get_file(file);
if (ufd) { if (ufd) {
error = put_user(new_fd, ufd); error = put_user(fd_prepare_fd(fdf), ufd);
if (error) { if (error)
put_unused_fd(new_fd);
return error; return error;
}
} }
fd_install(new_fd, get_file(file)); __receive_sock(fd_prepare_file(fdf));
__receive_sock(file); return fd_publish(fdf);
return new_fd;
} }
EXPORT_SYMBOL_GPL(receive_fd); 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) SYSCALL_DEFINE3(open_tree, int, dfd, const char __user *, filename, unsigned, flags)
{ {
int fd; return FD_ADD(flags, vfs_open_tree(dfd, filename, flags));
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;
} }
/* /*
@ -4281,10 +4269,10 @@ static unsigned int attr_flags_to_mnt_flags(u64 attr_flags)
SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
unsigned int, attr_flags) unsigned int, attr_flags)
{ {
struct path new_path __free(path_put) = {};
struct mnt_namespace *ns; struct mnt_namespace *ns;
struct fs_context *fc; struct fs_context *fc;
struct file *file; struct vfsmount *new_mnt;
struct path newmount;
struct mount *mnt; struct mount *mnt;
unsigned int mnt_flags = 0; unsigned int mnt_flags = 0;
long ret; long ret;
@ -4322,35 +4310,36 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
fc = fd_file(f)->private_data; fc = fd_file(f)->private_data;
ret = mutex_lock_interruptible(&fc->uapi_mutex); ACQUIRE(mutex_intr, uapi_mutex)(&fc->uapi_mutex);
if (ret < 0) ret = ACQUIRE_ERR(mutex_intr, &uapi_mutex);
if (ret)
return ret; return ret;
/* There must be a valid superblock or we can't mount it */ /* There must be a valid superblock or we can't mount it */
ret = -EINVAL; ret = -EINVAL;
if (!fc->root) if (!fc->root)
goto err_unlock; return ret;
ret = -EPERM; ret = -EPERM;
if (mount_too_revealing(fc->root->d_sb, &mnt_flags)) { if (mount_too_revealing(fc->root->d_sb, &mnt_flags)) {
errorfcp(fc, "VFS", "Mount too revealing"); errorfcp(fc, "VFS", "Mount too revealing");
goto err_unlock; return ret;
} }
ret = -EBUSY; ret = -EBUSY;
if (fc->phase != FS_CONTEXT_AWAITING_MOUNT) if (fc->phase != FS_CONTEXT_AWAITING_MOUNT)
goto err_unlock; return ret;
if (fc->sb_flags & SB_MANDLOCK) if (fc->sb_flags & SB_MANDLOCK)
warn_mandlock(); warn_mandlock();
newmount.mnt = vfs_create_mount(fc); new_mnt = vfs_create_mount(fc);
if (IS_ERR(newmount.mnt)) { if (IS_ERR(new_mnt))
ret = PTR_ERR(newmount.mnt); return PTR_ERR(new_mnt);
goto err_unlock; new_mnt->mnt_flags = mnt_flags;
}
newmount.dentry = dget(fc->root); new_path.dentry = dget(fc->root);
newmount.mnt->mnt_flags = mnt_flags; new_path.mnt = new_mnt;
/* We've done the mount bit - now move the file context into more or /* 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 * 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); vfs_clean_context(fc);
ns = alloc_mnt_ns(current->nsproxy->mnt_ns->user_ns, true); ns = alloc_mnt_ns(current->nsproxy->mnt_ns->user_ns, true);
if (IS_ERR(ns)) { if (IS_ERR(ns))
ret = PTR_ERR(ns); return PTR_ERR(ns);
goto err_path; mnt = real_mount(new_path.mnt);
}
mnt = real_mount(newmount.mnt);
ns->root = mnt; ns->root = mnt;
ns->nr_mounts = 1; ns->nr_mounts = 1;
mnt_add_to_ns(ns, mnt); 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 FD_PREPARE(fdf, (flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0,
* it, not just simply put it. dentry_open(&new_path, O_PATH, fc->cred));
*/ if (fdf.err) {
file = dentry_open(&newmount, O_PATH, fc->cred); dissolve_on_fput(new_path.mnt);
if (IS_ERR(file)) { return fdf.err;
dissolve_on_fput(newmount.mnt);
ret = PTR_ERR(file);
goto err_path;
} }
file->f_mode |= FMODE_NEED_UNMOUNT;
ret = get_unused_fd_flags((flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0); /*
if (ret >= 0) * Attach to an apparent O_PATH fd with a note that we
fd_install(ret, file); * need to unmount it, not just simply put it.
else */
fput(file); fd_prepare_file(fdf)->f_mode |= FMODE_NEED_UNMOUNT;
return fd_publish(fdf);
err_path:
path_put(&newmount);
err_unlock:
mutex_unlock(&fc->uapi_mutex);
return ret;
} }
static inline int vfs_move_mount(const struct path *from_path, 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, unsigned, flags, struct mount_attr __user *, uattr,
size_t, usize) size_t, usize)
{ {
struct file __free(fput) *file = NULL;
int fd;
if (!uattr && usize) if (!uattr && usize)
return -EINVAL; return -EINVAL;
file = vfs_open_tree(dfd, filename, flags); FD_PREPARE(fdf, flags, vfs_open_tree(dfd, filename, flags));
if (IS_ERR(file)) if (fdf.err)
return PTR_ERR(file); return fdf.err;
if (uattr) { if (uattr) {
int ret;
struct mount_kattr kattr = {}; struct mount_kattr kattr = {};
struct file *file = fd_prepare_file(fdf);
int ret;
if (flags & OPEN_TREE_CLONE) if (flags & OPEN_TREE_CLONE)
kattr.kflags = MOUNT_KATTR_IDMAP_REPLACE; kattr.kflags = MOUNT_KATTR_IDMAP_REPLACE;
@ -5061,12 +5037,7 @@ SYSCALL_DEFINE5(open_tree_attr, int, dfd, const char __user *, filename,
return ret; return ret;
} }
fd = get_unused_fd_flags(flags & O_CLOEXEC); return fd_publish(fdf);
if (fd < 0)
return fd;
fd_install(fd, no_free_ptr(file));
return fd;
} }
int show_path(struct seq_file *m, struct dentry *root) 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; 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 */ /* fanotify syscalls */
SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
{ {
struct user_namespace *user_ns = current_user_ns(); struct user_namespace *user_ns = current_user_ns();
struct fsnotify_group *group;
int f_flags, fd; int f_flags, fd;
unsigned int fid_mode = flags & FANOTIFY_FID_BITS; unsigned int fid_mode = flags & FANOTIFY_FID_BITS;
unsigned int class = flags & FANOTIFY_CLASS_BITS; unsigned int class = flags & FANOTIFY_CLASS_BITS;
unsigned int internal_flags = 0; unsigned int internal_flags = 0;
struct file *file;
pr_debug("%s: flags=%x event_f_flags=%x\n", pr_debug("%s: flags=%x event_f_flags=%x\n",
__func__, flags, event_f_flags); __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) if (flags & FAN_NONBLOCK)
f_flags |= O_NONBLOCK; f_flags |= O_NONBLOCK;
/* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */ CLASS(fsnotify_group, group)(&fanotify_fsnotify_ops,
group = fsnotify_alloc_group(&fanotify_fsnotify_ops,
FSNOTIFY_GROUP_USER); 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); return PTR_ERR(group);
}
/* Enforce groups limits per user in all containing user ns */ /* Enforce groups limits per user in all containing user ns */
group->fanotify_data.ucounts = inc_ucount(user_ns, current_euid(), group->fanotify_data.ucounts = inc_ucount(user_ns, current_euid(),
UCOUNT_FANOTIFY_GROUPS); UCOUNT_FANOTIFY_GROUPS);
if (!group->fanotify_data.ucounts) { if (!group->fanotify_data.ucounts)
fd = -EMFILE; return -EMFILE;
goto out_destroy_group;
}
group->fanotify_data.flags = flags | internal_flags; group->fanotify_data.flags = flags | internal_flags;
group->memcg = get_mem_cgroup_from_mm(current->mm); group->memcg = get_mem_cgroup_from_mm(current->mm);
group->user_ns = get_user_ns(user_ns); group->user_ns = get_user_ns(user_ns);
group->fanotify_data.merge_hash = fanotify_alloc_merge_hash(); group->fanotify_data.merge_hash = fanotify_alloc_merge_hash();
if (!group->fanotify_data.merge_hash) { if (!group->fanotify_data.merge_hash)
fd = -ENOMEM; return -ENOMEM;
goto out_destroy_group;
}
group->overflow_event = fanotify_alloc_overflow_event(); group->overflow_event = fanotify_alloc_overflow_event();
if (unlikely(!group->overflow_event)) { if (unlikely(!group->overflow_event))
fd = -ENOMEM; return -ENOMEM;
goto out_destroy_group;
}
if (force_o_largefile()) if (force_o_largefile())
event_f_flags |= 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; group->priority = FSNOTIFY_PRIO_PRE_CONTENT;
break; break;
default: default:
fd = -EINVAL; return -EINVAL;
goto out_destroy_group;
} }
BUILD_BUG_ON(!(FANOTIFY_ADMIN_INIT_FLAGS & FAN_UNLIMITED_QUEUE)); 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) { if (flags & FAN_ENABLE_AUDIT) {
fd = -EPERM;
if (!capable(CAP_AUDIT_WRITE)) if (!capable(CAP_AUDIT_WRITE))
goto out_destroy_group; return -EPERM;
} }
fd = get_unused_fd_flags(f_flags); fd = FD_ADD(f_flags,
if (fd < 0) anon_inode_getfile_fmode("[fanotify]", &fanotify_fops,
goto out_destroy_group; group, f_flags, FMODE_NONOTIFY));
if (fd >= 0)
file = anon_inode_getfile_fmode("[fanotify]", &fanotify_fops, group, retain_and_null_ptr(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);
return fd; 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) int open_namespace(struct ns_common *ns)
{ {
struct path path __free(path_put) = {}; struct path path __free(path_put) = {};
struct file *f;
int err; int err;
/* call first to consume reference */ /* call first to consume reference */
@ -118,16 +117,7 @@ int open_namespace(struct ns_common *ns)
if (err < 0) if (err < 0)
return err; return err;
CLASS(get_unused_fd, fd)(O_CLOEXEC); return FD_ADD(O_CLOEXEC, dentry_open(&path, O_RDONLY, current_cred()));
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);
} }
int open_related_ns(struct ns_common *ns, 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 kinfo = {};
struct mnt_ns_info __user *uinfo = (struct mnt_ns_info __user *)arg; struct mnt_ns_info __user *uinfo = (struct mnt_ns_info __user *)arg;
struct path path __free(path_put) = {}; struct path path __free(path_put) = {};
struct file *f __free(fput) = NULL;
size_t usize = _IOC_SIZE(ioctl); size_t usize = _IOC_SIZE(ioctl);
if (ns->ns_type != CLONE_NEWNS) if (ns->ns_type != CLONE_NEWNS)
@ -332,28 +321,18 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl,
if (ret) if (ret)
return ret; return ret;
CLASS(get_unused_fd, fd)(O_CLOEXEC); FD_PREPARE(fdf, O_CLOEXEC, dentry_open(&path, O_RDONLY, current_cred()));
if (fd < 0) if (fdf.err)
return fd; return fdf.err;
/*
f = dentry_open(&path, O_RDONLY, current_cred()); * If @uinfo is passed return all information about the
if (IS_ERR(f)) * mount namespace as well.
return PTR_ERR(f); */
ret = copy_ns_info_to_user(to_mnt_ns(ns), uinfo, usize, &kinfo);
if (uinfo) { if (ret)
/* return ret;
* If @uinfo is passed return all information about the ret = fd_publish(fdf);
* mount namespace as well. break;
*/
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);
} }
default: default:
ret = -ENOTTY; 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_how *how)
{ {
struct open_flags op; struct open_flags op;
struct filename *tmp; struct filename *tmp __free(putname) = NULL;
int err, fd; int err;
err = build_open_flags(how, &op); err = build_open_flags(how, &op);
if (unlikely(err)) if (unlikely(err))
@ -1427,18 +1427,7 @@ static int do_sys_openat2(int dfd, const char __user *filename,
if (IS_ERR(tmp)) if (IS_ERR(tmp))
return PTR_ERR(tmp); return PTR_ERR(tmp);
fd = get_unused_fd_flags(how->flags); return FD_ADD(how->flags, do_filp_open(dfd, tmp, &op));
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;
} }
int do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) 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) static int do_signalfd4(int ufd, sigset_t *mask, int flags)
{ {
struct signalfd_ctx *ctx;
/* Check the SFD_* constants for consistency. */ /* Check the SFD_* constants for consistency. */
BUILD_BUG_ON(SFD_CLOEXEC != O_CLOEXEC); BUILD_BUG_ON(SFD_CLOEXEC != O_CLOEXEC);
BUILD_BUG_ON(SFD_NONBLOCK != O_NONBLOCK); BUILD_BUG_ON(SFD_NONBLOCK != O_NONBLOCK);
@ -263,7 +261,8 @@ static int do_signalfd4(int ufd, sigset_t *mask, int flags)
signotset(mask); signotset(mask);
if (ufd == -1) { if (ufd == -1) {
struct file *file; int fd;
struct signalfd_ctx *ctx __free(kfree) = NULL;
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) if (!ctx)
@ -271,22 +270,16 @@ static int do_signalfd4(int ufd, sigset_t *mask, int flags)
ctx->sigmask = *mask; ctx->sigmask = *mask;
ufd = get_unused_fd_flags(flags & O_CLOEXEC); fd = FD_ADD(flags & O_CLOEXEC,
if (ufd < 0) { anon_inode_getfile_fmode(
kfree(ctx); "[signalfd]", &signalfd_fops, ctx,
return ufd; O_RDWR | (flags & O_NONBLOCK), FMODE_NOWAIT));
} if (fd >= 0)
retain_and_null_ptr(ctx);
file = anon_inode_getfile_fmode("[signalfd]", &signalfd_fops, return fd;
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);
} else { } else {
struct signalfd_ctx *ctx;
CLASS(fd, f)(ufd); CLASS(fd, f)(ufd);
if (fd_empty(f)) if (fd_empty(f))
return -EBADF; return -EBADF;

View File

@ -393,9 +393,8 @@ static const struct file_operations timerfd_fops = {
SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
{ {
int ufd; struct timerfd_ctx *ctx __free(kfree) = NULL;
struct timerfd_ctx *ctx; int ret;
struct file *file;
/* Check the TFD_* constants for consistency. */ /* Check the TFD_* constants for consistency. */
BUILD_BUG_ON(TFD_CLOEXEC != O_CLOEXEC); 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); ctx->moffs = ktime_mono_to_real(0);
ufd = get_unused_fd_flags(flags & TFD_SHARED_FCNTL_FLAGS); ret = FD_ADD(flags & TFD_SHARED_FCNTL_FLAGS,
if (ufd < 0) { anon_inode_getfile_fmode("[timerfd]", &timerfd_fops, ctx,
kfree(ctx); O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS),
return ufd; FMODE_NOWAIT));
} if (ret >= 0)
retain_and_null_ptr(ctx);
file = anon_inode_getfile_fmode("[timerfd]", &timerfd_fops, ctx, return ret;
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;
} }
static int do_timerfd_settime(int ufd, int flags, 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) static int new_userfaultfd(int flags)
{ {
struct userfaultfd_ctx *ctx; struct userfaultfd_ctx *ctx __free(kfree) = NULL;
struct file *file;
int fd;
VM_WARN_ON_ONCE(!current->mm); VM_WARN_ON_ONCE(!current->mm);
@ -2135,26 +2133,18 @@ static int new_userfaultfd(int flags)
atomic_set(&ctx->mmap_changing, 0); atomic_set(&ctx->mmap_changing, 0);
ctx->mm = current->mm; ctx->mm = current->mm;
fd = get_unused_fd_flags(flags & UFFD_SHARED_FCNTL_FLAGS); FD_PREPARE(fdf, flags & UFFD_SHARED_FCNTL_FLAGS,
if (fd < 0) anon_inode_create_getfile("[userfaultfd]", &userfaultfd_fops, ctx,
goto err_out; 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 */ /* prevent the mm struct to be freed */
mmgrab(ctx->mm); mmgrab(ctx->mm);
file->f_mode |= FMODE_NOWAIT; fd_prepare_file(fdf)->f_mode |= FMODE_NOWAIT;
fd_install(fd, file); retain_and_null_ptr(ctx);
return fd; return fd_publish(fdf);
err_out:
kmem_cache_free(userfaultfd_ctx_cachep, ctx);
return fd;
} }
static inline bool userfaultfd_syscall_allowed(int flags) static inline bool userfaultfd_syscall_allowed(int flags)

View File

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

View File

@ -261,6 +261,10 @@ const volatile void * __must_check_fn(const volatile void *val)
* CLASS(name, var)(args...): * CLASS(name, var)(args...):
* declare the variable @var as an instance of the named class * 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. * Ex.
* *
* DEFINE_CLASS(fdget, struct fd, fdput(_T), fdget(fd), int fd) * 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##_t var __cleanup(class_##_name##_destructor) = \
class_##_name##_constructor 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...) \ #define __scoped_class(_name, var, _label, args...) \
for (CLASS(_name, var)(args); ; ({ goto _label; })) \ for (CLASS(_name, var)(args); ; ({ goto _label; })) \
if (0) { \ 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; 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 */ #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 file_operations *fops = &io_mock_fops;
const struct io_uring_sqe *sqe = cmd->sqe; const struct io_uring_sqe *sqe = cmd->sqe;
struct io_uring_mock_create mc, __user *uarg; struct io_uring_mock_create mc, __user *uarg;
struct io_mock_file *mf = NULL; struct file *file;
struct file *file = NULL; struct io_mock_file *mf __free(kfree) = NULL;
size_t uarg_size; size_t uarg_size;
int fd = -1, ret;
/* /*
* It's a testing only driver that allows exercising edge cases * 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) if (!mf)
return -ENOMEM; return -ENOMEM;
ret = fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC);
if (fd < 0)
goto fail;
init_waitqueue_head(&mf->poll_wq); init_waitqueue_head(&mf->poll_wq);
mf->size = mc.file_size; mf->size = mc.file_size;
mf->rw_delay_ns = mc.rw_delay_ns; 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; mf->pollable = true;
} }
file = anon_inode_create_getfile("[io_uring_mock]", fops, FD_PREPARE(fdf, O_RDWR | O_CLOEXEC,
mf, O_RDWR | O_CLOEXEC, NULL); anon_inode_create_getfile("[io_uring_mock]", fops, mf,
if (IS_ERR(file)) { O_RDWR | O_CLOEXEC, NULL));
ret = PTR_ERR(file); if (fdf.err)
goto fail; return fdf.err;
}
file->f_mode |= FMODE_READ | FMODE_CAN_READ | retain_and_null_ptr(mf);
FMODE_WRITE | FMODE_CAN_WRITE | file = fd_prepare_file(fdf);
FMODE_LSEEK; file->f_mode |= FMODE_READ | FMODE_CAN_READ | FMODE_WRITE |
FMODE_CAN_WRITE | FMODE_LSEEK;
if (mc.flags & IORING_MOCK_CREATE_F_SUPPORT_NOWAIT) if (mc.flags & IORING_MOCK_CREATE_F_SUPPORT_NOWAIT)
file->f_mode |= FMODE_NOWAIT; file->f_mode |= FMODE_NOWAIT;
mc.out_fd = fd; mc.out_fd = fd_prepare_fd(fdf);
if (copy_to_user(uarg, &mc, uarg_size)) { if (copy_to_user(uarg, &mc, uarg_size))
fput(file); return -EFAULT;
ret = -EFAULT;
goto fail;
}
fd_install(fd, file); fd_publish(fdf);
return 0; return 0;
fail:
if (fd >= 0)
put_unused_fd(fd);
kfree(mf);
return ret;
} }
static int io_probe_mock(struct io_uring_cmd *cmd) 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); 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, static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
struct mq_attr *attr) struct mq_attr *attr)
{ {
struct filename *name __free(putname) = NULL;;
struct vfsmount *mnt = current->nsproxy->ipc_ns->mq_mnt; struct vfsmount *mnt = current->nsproxy->ipc_ns->mq_mnt;
struct dentry *root = mnt->mnt_root; int fd, ro;
struct filename *name;
struct path path;
int fd, error;
int ro;
audit_mq_open(oflag, mode, attr); 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)) if (IS_ERR(name))
return PTR_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 */ ro = mnt_want_write(mnt); /* we'll drop it in any case */
path.dentry = start_creating_noperm(root, &QSTR(name->name)); fd = FD_ADD(O_CLOEXEC, mqueue_file_open(name, mnt, oflag, ro, mode, attr));
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);
if (!ro) if (!ro)
mnt_drop_write(mnt); mnt_drop_write(mnt);
out_putname:
putname(name);
return fd; 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) int bpf_iter_new_fd(struct bpf_link *link)
{ {
struct bpf_iter_link *iter_link; struct bpf_iter_link *iter_link;
struct file *file;
unsigned int flags; unsigned int flags;
int err, fd; int err;
if (link->ops != &bpf_iter_link_lops) if (link->ops != &bpf_iter_link_lops)
return -EINVAL; return -EINVAL;
flags = O_RDONLY | O_CLOEXEC; 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); FD_PREPARE(fdf, flags, anon_inode_getfile("bpf_iter", &bpf_iter_fops, NULL, flags));
if (IS_ERR(file)) { if (fdf.err)
err = PTR_ERR(file); return fdf.err;
goto free_fd;
}
iter_link = container_of(link, struct bpf_iter_link, link); 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) if (err)
goto free_file; return err; /* Automatic cleanup handles fput */
fd_install(fd, file); return fd_publish(fdf);
return fd;
free_file:
fput(file);
free_fd:
put_unused_fd(fd);
return err;
} }
struct bpf_prog *bpf_iter_get_info(struct bpf_iter_meta *meta, bool in_stop) 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) int bpf_token_create(union bpf_attr *attr)
{ {
struct bpf_token *token __free(kfree) = NULL;
struct bpf_mount_opts *mnt_opts; struct bpf_mount_opts *mnt_opts;
struct bpf_token *token = NULL;
struct user_namespace *userns; struct user_namespace *userns;
struct inode *inode; struct inode *inode;
struct file *file;
CLASS(fd, f)(attr->token_create.bpffs_fd); CLASS(fd, f)(attr->token_create.bpffs_fd);
struct path path; struct path path;
struct super_block *sb; struct super_block *sb;
umode_t mode; umode_t mode;
int err, fd; int err;
if (fd_empty(f)) if (fd_empty(f))
return -EBADF; return -EBADF;
@ -166,23 +165,20 @@ int bpf_token_create(union bpf_attr *attr)
inode->i_fop = &bpf_token_fops; inode->i_fop = &bpf_token_fops;
clear_nlink(inode); /* make sure it is unlinked */ clear_nlink(inode); /* make sure it is unlinked */
file = alloc_file_pseudo(inode, path.mnt, BPF_TOKEN_INODE_NAME, O_RDWR, &bpf_token_fops); FD_PREPARE(fdf, O_CLOEXEC,
if (IS_ERR(file)) { alloc_file_pseudo(inode, path.mnt, BPF_TOKEN_INODE_NAME,
iput(inode); O_RDWR, &bpf_token_fops));
return PTR_ERR(file); if (fdf.err)
} return fdf.err;
token = kzalloc(sizeof(*token), GFP_USER); token = kzalloc(sizeof(*token), GFP_USER);
if (!token) { if (!token)
err = -ENOMEM; return -ENOMEM;
goto out_file;
}
atomic64_set(&token->refcnt, 1); atomic64_set(&token->refcnt, 1);
/* remember bpffs owning userns for future ns_capable() checks */ /* remember bpffs owning userns for future ns_capable() checks. */
token->userns = get_user_ns(userns); token->userns = userns;
token->allowed_cmds = mnt_opts->delegate_cmds; token->allowed_cmds = mnt_opts->delegate_cmds;
token->allowed_maps = mnt_opts->delegate_maps; token->allowed_maps = mnt_opts->delegate_maps;
token->allowed_progs = mnt_opts->delegate_progs; 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); err = security_bpf_token_create(token, attr, &path);
if (err) if (err)
goto out_token; return err;
fd = get_unused_fd_flags(O_CLOEXEC); get_user_ns(token->userns);
if (fd < 0) { fd_prepare_file(fdf)->private_data = no_free_ptr(token);
err = fd; return fd_publish(fdf);
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;
} }
int bpf_token_get_info_by_fd(struct bpf_token *token, 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, const char __user *, uname,
unsigned int, flags) unsigned int, flags)
{ {
struct file *file; char *name __free(kfree) = NULL;
int fd, error; unsigned int fd_flags;
char *name; int error;
error = sanitize_flags(&flags); error = sanitize_flags(&flags);
if (error < 0) if (error < 0)
@ -509,25 +509,6 @@ SYSCALL_DEFINE2(memfd_create,
if (IS_ERR(name)) if (IS_ERR(name))
return PTR_ERR(name); return PTR_ERR(name);
fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0); fd_flags = (flags & MFD_CLOEXEC) ? O_CLOEXEC : 0;
if (fd < 0) { return FD_ADD(fd_flags, alloc_file(name, flags));
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;
} }

View File

@ -224,9 +224,6 @@ static struct file *secretmem_file_create(unsigned long flags)
SYSCALL_DEFINE1(memfd_secret, unsigned int, 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 */ /* make sure local flags do not confict with global fcntl.h */
BUILD_BUG_ON(SECRETMEM_FLAGS_MASK & O_CLOEXEC); 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) if (atomic_read(&secretmem_users) < 0)
return -ENFILE; return -ENFILE;
fd = get_unused_fd_flags(flags & O_CLOEXEC); return FD_ADD(flags & O_CLOEXEC, secretmem_file_create(flags));
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;
} }
static int secretmem_init_fs_context(struct fs_context *fc) 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_net *hn = handshake_pernet(net);
struct handshake_req *req = NULL; struct handshake_req *req = NULL;
struct socket *sock; struct socket *sock;
int class, fd, err; int class, err;
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
if (!hn) if (!hn)
@ -106,27 +106,25 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info)
err = -EAGAIN; err = -EAGAIN;
req = handshake_req_next(hn, class); req = handshake_req_next(hn, class);
if (!req) if (req) {
goto out_status; sock = req->hr_sk->sk_socket;
sock = req->hr_sk->sk_socket; FD_PREPARE(fdf, O_CLOEXEC, sock->file);
fd = get_unused_fd_flags(O_CLOEXEC); if (fdf.err) {
if (fd < 0) { err = fdf.err;
err = fd; goto out_complete;
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 */
trace_handshake_cmd_accept(net, req, req->hr_sk, fd_prepare_fd(fdf));
fd_publish(fdf);
return 0;
} }
err = req->hr_proto->hp_accept(req, info, fd);
if (err) {
put_unused_fd(fd);
goto out_complete;
}
fd_install(fd, get_file(sock->file));
trace_handshake_cmd_accept(net, req, req->hr_sk, fd);
return 0;
out_complete: out_complete:
handshake_complete(req, -EIO, NULL); handshake_complete(req, -EIO, NULL);
out_status: out_status:

View File

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

View File

@ -503,21 +503,12 @@ EXPORT_SYMBOL(sock_alloc_file);
static int sock_map_fd(struct socket *sock, int flags) static int sock_map_fd(struct socket *sock, int flags)
{ {
struct file *newfile; int fd;
int fd = get_unused_fd_flags(flags);
if (unlikely(fd < 0)) { fd = FD_ADD(flags, sock_alloc_file(sock, flags, NULL));
if (fd < 0)
sock_release(sock); sock_release(sock);
return fd; 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) int __user *upeer_addrlen, int flags)
{ {
struct proto_accept_arg arg = { }; struct proto_accept_arg arg = { };
struct file *newfile;
int newfd;
if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
return -EINVAL; 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)) if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
newfd = get_unused_fd_flags(flags); return FD_ADD(flags, do_accept(file, &arg, upeer_sockaddr, upeer_addrlen, 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;
} }
/* /*

View File

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