mirror of https://github.com/torvalds/linux.git
Merge patch series "file: FD_{ADD,PREPARE}()"
Christian Brauner <brauner@kernel.org> says:
This now removes roughly double the code that it adds.
I've been playing with this to allow for moderately flexible usage of
the get_unused_fd_flags() + create file + fd_install() pattern that's
used quite extensively and requires cumbersome cleanup paths.
How callers allocate files is really heterogenous so it's not really
convenient to fold them into a single class. It's possibe to split them
into subclasses like for anon inodes. I think that's not necessarily
nice as well. This adds two primitives:
(1) FD_ADD() the simple cases a file is installed:
fd = FD_ADD(O_CLOEXEC, vfio_device_open_file(device));
if (fd < 0)
vfio_device_put_registration(device);
return fd;
(2) FD_PREPARE() that captures all the cases where access to fd or file
or additional work before publishing the fd is needed:
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);
I've converted all of the easy cases over to it and it gets rid of an
aweful lot of convoluted cleanup logic. There are a bunch of other cases
that can also be converted after a bit of massaging.
It's centered around a simple struct. FD_PREPARE() encapsulates all of
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 callers fdtable. If fd_publish() isn't called both are deallocated.
FD_ADD() is a shorthand that does the fd_publish() and never exposes the
struct to the caller. That's often the case when they don't need access
to anything after installing the fd.
It mandates a specific order namely that first we allocate the fd and
then instantiate the file. But that shouldn't be a problem. Nearly
everyone I've converted used this order anyway.
There's a bunch of additional cases where it would be easy to convert
them to this pattern. For example, the whole sync file stuff in dma
currently returns the containing structure of the file instead of the
file itself even though it's only used to allocate files. Changing that
would make it fall into the FD_PREPARE() pattern easily. I've not done
that work yet.
There's room for extending this in a way that wed'd have subclasses for
some particularly often use patterns but as I said I'm not even sure
that's worth it.
* patches from https://patch.msgid.link/20251123-work-fd-prepare-v4-0-b6efa1706cfd@kernel.org: (47 commits)
kvm: convert kvm_vcpu_ioctl_get_stats_fd() to FD_PREPARE()
kvm: convert kvm_arch_supports_gmem_init_shared() to FD_PREPARE()
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_PREPARE()
tty: convert ptm_open_peer() to FD_PREPARE()
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_PREPARE()
gpio: convert linehandle_create() to FD_PREPARE()
dma: port sw_sync_ioctl_create_fence() to FD_PREPARE()
pseries: port papr_rtas_setup_file_interface() to FD_PREPARE()
pseries: convert papr_platform_dump_create_handle() to FD_PREPARE()
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_PREPARE()
net/socket: convert sock_map_fd() to FD_PREPARE()
net/sctp: convert sctp_getsockopt_peeloff_common() to FD_PREPARE()
net/kcm: convert kcm_ioctl() to FD_PREPARE()
...
Link: https://patch.msgid.link/20251123-work-fd-prepare-v4-0-b6efa1706cfd@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
commit
0512bf9701
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
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) {
|
||||
err = fd;
|
||||
goto free_area;
|
||||
rtas_work_area_free(params->work_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(¶ms->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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
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 fd;
|
||||
|
||||
put_fd:
|
||||
put_unused_fd(fd);
|
||||
free_blob:
|
||||
papr_rtas_blob_free(blob);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
vfio_device_put_registration(device);
|
||||
return ret;
|
||||
fd = FD_ADD(O_CLOEXEC, vfio_device_open_file(device));
|
||||
if (fd < 0)
|
||||
vfio_device_put_registration(device);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int vfio_group_ioctl_get_status(struct vfio_group *group,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
struct path path __free(path_put) = {};
|
||||
int err;
|
||||
|
||||
fd = get_unused_fd_flags(O_CLOEXEC);
|
||||
if (likely(fd >= 0)) {
|
||||
struct file *filp;
|
||||
struct path path;
|
||||
err = find_autofs_mount(name, &path, test_by_dev, &devid);
|
||||
if (err)
|
||||
return 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 */
|
||||
|
|
|
|||
29
fs/eventfd.c
29
fs/eventfd.c
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
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 fdf.err;
|
||||
}
|
||||
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:
|
||||
ep_clear_and_put(ep);
|
||||
return error;
|
||||
ep->file = fd_prepare_file(fdf);
|
||||
return fd_publish(fdf);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(epoll_create1, int, flags)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
30
fs/fhandle.c
30
fs/fhandle.c
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
19
fs/file.c
19
fs/file.c
|
|
@ -1357,28 +1357,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);
|
||||
|
||||
|
|
|
|||
103
fs/namespace.c
103
fs/namespace.c
|
|
@ -3103,19 +3103,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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -4283,10 +4271,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;
|
||||
|
|
@ -4324,35 +4312,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
|
||||
|
|
@ -4362,38 +4351,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,
|
||||
|
|
@ -5035,19 +5013,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;
|
||||
|
|
@ -5063,12 +5039,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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
47
fs/nsfs.c
47
fs/nsfs.c
|
|
@ -108,7 +108,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 */
|
||||
|
|
@ -116,16 +115,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,
|
||||
|
|
@ -311,7 +301,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)
|
||||
|
|
@ -330,28 +319,18 @@ 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) {
|
||||
/*
|
||||
* If @uinfo is passed return all information about the
|
||||
* mount namespace as well.
|
||||
*/
|
||||
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);
|
||||
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.
|
||||
*/
|
||||
ret = copy_ns_info_to_user(to_mnt_ns(ns), uinfo, usize, &kinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = fd_publish(fdf);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -ENOTTY;
|
||||
|
|
|
|||
17
fs/open.c
17
fs/open.c
|
|
@ -1421,8 +1421,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))
|
||||
|
|
@ -1432,18 +1432,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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
29
fs/timerfd.c
29
fs/timerfd.c
|
|
@ -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,
|
||||
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;
|
||||
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 (ret >= 0)
|
||||
retain_and_null_ptr(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_timerfd_settime(int ufd, int flags,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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, args) \
|
||||
for (CLASS(_name, var)(args); \
|
||||
__guard_ptr(_name)(&var) || !__is_cond_ptr(_name); \
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
54
ipc/mqueue.c
54
ipc/mqueue.c
|
|
@ -892,14 +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 path path __free(path_put) = {};
|
||||
struct dentry *dentry;
|
||||
int ret;
|
||||
|
||||
dentry = lookup_noperm(&QSTR(name->name), mnt->mnt_root);
|
||||
if (IS_ERR(dentry))
|
||||
return ERR_CAST(dentry);
|
||||
|
||||
path.dentry = dentry;
|
||||
path.mnt = mntget(mnt);
|
||||
|
||||
ret = prepare_open(path.dentry, oflag, ro, mode, name, attr);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return dentry_open(&path, oflag, current_cred());
|
||||
}
|
||||
|
||||
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 fd;
|
||||
int ro;
|
||||
|
||||
audit_mq_open(oflag, mode, attr);
|
||||
|
|
@ -908,37 +929,12 @@ 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 */
|
||||
inode_lock(d_inode(root));
|
||||
path.dentry = lookup_noperm(&QSTR(name->name), root);
|
||||
if (IS_ERR(path.dentry)) {
|
||||
error = PTR_ERR(path.dentry);
|
||||
goto out_putfd;
|
||||
}
|
||||
path.mnt = mntget(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);
|
||||
}
|
||||
path_put(&path);
|
||||
out_putfd:
|
||||
if (error) {
|
||||
put_unused_fd(fd);
|
||||
fd = error;
|
||||
}
|
||||
fd = FD_ADD(O_CLOEXEC, mqueue_file_open(name, mnt, oflag, ro, mode, attr));
|
||||
inode_unlock(d_inode(root));
|
||||
if (!ro)
|
||||
mnt_drop_write(mnt);
|
||||
out_putname:
|
||||
putname(name);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
return err;
|
||||
|
||||
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,
|
||||
|
|
|
|||
29
mm/memfd.c
29
mm/memfd.c
|
|
@ -470,9 +470,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)
|
||||
|
|
@ -482,25 +482,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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,27 +106,25 @@ 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;
|
||||
|
||||
sock = req->hr_sk->sk_socket;
|
||||
fd = get_unused_fd_flags(O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
err = fd;
|
||||
goto out_complete;
|
||||
FD_PREPARE(fdf, O_CLOEXEC, sock->file);
|
||||
if (fdf.err) {
|
||||
err = fdf.err;
|
||||
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:
|
||||
handshake_complete(req, -EIO, NULL);
|
||||
out_status:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
34
net/socket.c
34
net/socket.c
|
|
@ -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);
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -3286,9 +3286,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;
|
||||
|
||||
|
|
@ -3298,18 +3295,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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue