mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
new helper: replace_fd()
analog of dup2(), except that it takes struct file * as source. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
fe17f22d7f
commit
8280d16172
11
fs/exec.c
11
fs/exec.c
@ -2041,23 +2041,14 @@ static void wait_for_dump_helpers(struct file *file)
|
|||||||
static int umh_pipe_setup(struct subprocess_info *info, struct cred *new)
|
static int umh_pipe_setup(struct subprocess_info *info, struct cred *new)
|
||||||
{
|
{
|
||||||
struct file *files[2];
|
struct file *files[2];
|
||||||
struct fdtable *fdt;
|
|
||||||
struct coredump_params *cp = (struct coredump_params *)info->data;
|
struct coredump_params *cp = (struct coredump_params *)info->data;
|
||||||
struct files_struct *cf = current->files;
|
|
||||||
int err = create_pipe_files(files, 0);
|
int err = create_pipe_files(files, 0);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
cp->file = files[1];
|
cp->file = files[1];
|
||||||
|
|
||||||
sys_close(0);
|
replace_fd(0, files[0], 0);
|
||||||
fd_install(0, files[0]);
|
|
||||||
spin_lock(&cf->file_lock);
|
|
||||||
fdt = files_fdtable(cf);
|
|
||||||
__set_open_fd(0, fdt);
|
|
||||||
__clear_close_on_exec(0, fdt);
|
|
||||||
spin_unlock(&cf->file_lock);
|
|
||||||
|
|
||||||
/* and disallow core files too */
|
/* and disallow core files too */
|
||||||
current->signal->rlim[RLIMIT_CORE] = (struct rlimit){1, 1};
|
current->signal->rlim[RLIMIT_CORE] = (struct rlimit){1, 1};
|
||||||
|
|
||||||
|
103
fs/file.c
103
fs/file.c
@ -821,12 +821,76 @@ bool get_close_on_exec(unsigned int fd)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int do_dup2(struct files_struct *files,
|
||||||
|
struct file *file, unsigned fd, unsigned flags)
|
||||||
|
{
|
||||||
|
struct file *tofree;
|
||||||
|
struct fdtable *fdt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to detect attempts to do dup2() over allocated but still
|
||||||
|
* not finished descriptor. NB: OpenBSD avoids that at the price of
|
||||||
|
* extra work in their equivalent of fget() - they insert struct
|
||||||
|
* file immediately after grabbing descriptor, mark it larval if
|
||||||
|
* more work (e.g. actual opening) is needed and make sure that
|
||||||
|
* fget() treats larval files as absent. Potentially interesting,
|
||||||
|
* but while extra work in fget() is trivial, locking implications
|
||||||
|
* and amount of surgery on open()-related paths in VFS are not.
|
||||||
|
* FreeBSD fails with -EBADF in the same situation, NetBSD "solution"
|
||||||
|
* deadlocks in rather amusing ways, AFAICS. All of that is out of
|
||||||
|
* scope of POSIX or SUS, since neither considers shared descriptor
|
||||||
|
* tables and this condition does not arise without those.
|
||||||
|
*/
|
||||||
|
fdt = files_fdtable(files);
|
||||||
|
tofree = fdt->fd[fd];
|
||||||
|
if (!tofree && fd_is_open(fd, fdt))
|
||||||
|
goto Ebusy;
|
||||||
|
get_file(file);
|
||||||
|
rcu_assign_pointer(fdt->fd[fd], file);
|
||||||
|
__set_open_fd(fd, fdt);
|
||||||
|
if (flags & O_CLOEXEC)
|
||||||
|
__set_close_on_exec(fd, fdt);
|
||||||
|
else
|
||||||
|
__clear_close_on_exec(fd, fdt);
|
||||||
|
spin_unlock(&files->file_lock);
|
||||||
|
|
||||||
|
if (tofree)
|
||||||
|
filp_close(tofree, files);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
Ebusy:
|
||||||
|
spin_unlock(&files->file_lock);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int replace_fd(unsigned fd, struct file *file, unsigned flags)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct files_struct *files = current->files;
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
return __close_fd(files, fd);
|
||||||
|
|
||||||
|
if (fd >= rlimit(RLIMIT_NOFILE))
|
||||||
|
return -EMFILE;
|
||||||
|
|
||||||
|
spin_lock(&files->file_lock);
|
||||||
|
err = expand_files(files, fd);
|
||||||
|
if (unlikely(err < 0))
|
||||||
|
goto out_unlock;
|
||||||
|
return do_dup2(files, file, fd, flags);
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
spin_unlock(&files->file_lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags)
|
SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags)
|
||||||
{
|
{
|
||||||
int err = -EBADF;
|
int err = -EBADF;
|
||||||
struct file * file, *tofree;
|
struct file *file;
|
||||||
struct files_struct * files = current->files;
|
struct files_struct *files = current->files;
|
||||||
struct fdtable *fdt;
|
|
||||||
|
|
||||||
if ((flags & ~O_CLOEXEC) != 0)
|
if ((flags & ~O_CLOEXEC) != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -844,38 +908,7 @@ SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags)
|
|||||||
goto Ebadf;
|
goto Ebadf;
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
/*
|
return do_dup2(files, file, newfd, flags);
|
||||||
* We need to detect attempts to do dup2() over allocated but still
|
|
||||||
* not finished descriptor. NB: OpenBSD avoids that at the price of
|
|
||||||
* extra work in their equivalent of fget() - they insert struct
|
|
||||||
* file immediately after grabbing descriptor, mark it larval if
|
|
||||||
* more work (e.g. actual opening) is needed and make sure that
|
|
||||||
* fget() treats larval files as absent. Potentially interesting,
|
|
||||||
* but while extra work in fget() is trivial, locking implications
|
|
||||||
* and amount of surgery on open()-related paths in VFS are not.
|
|
||||||
* FreeBSD fails with -EBADF in the same situation, NetBSD "solution"
|
|
||||||
* deadlocks in rather amusing ways, AFAICS. All of that is out of
|
|
||||||
* scope of POSIX or SUS, since neither considers shared descriptor
|
|
||||||
* tables and this condition does not arise without those.
|
|
||||||
*/
|
|
||||||
err = -EBUSY;
|
|
||||||
fdt = files_fdtable(files);
|
|
||||||
tofree = fdt->fd[newfd];
|
|
||||||
if (!tofree && fd_is_open(newfd, fdt))
|
|
||||||
goto out_unlock;
|
|
||||||
get_file(file);
|
|
||||||
rcu_assign_pointer(fdt->fd[newfd], file);
|
|
||||||
__set_open_fd(newfd, fdt);
|
|
||||||
if (flags & O_CLOEXEC)
|
|
||||||
__set_close_on_exec(newfd, fdt);
|
|
||||||
else
|
|
||||||
__clear_close_on_exec(newfd, fdt);
|
|
||||||
spin_unlock(&files->file_lock);
|
|
||||||
|
|
||||||
if (tofree)
|
|
||||||
filp_close(tofree, files);
|
|
||||||
|
|
||||||
return newfd;
|
|
||||||
|
|
||||||
Ebadf:
|
Ebadf:
|
||||||
err = -EBADF;
|
err = -EBADF;
|
||||||
|
@ -31,6 +31,7 @@ extern struct file *fget_light(unsigned int fd, int *fput_needed);
|
|||||||
extern struct file *fget_raw(unsigned int fd);
|
extern struct file *fget_raw(unsigned int fd);
|
||||||
extern struct file *fget_raw_light(unsigned int fd, int *fput_needed);
|
extern struct file *fget_raw_light(unsigned int fd, int *fput_needed);
|
||||||
extern int f_dupfd(unsigned int from, struct file *file, unsigned flags);
|
extern int f_dupfd(unsigned int from, struct file *file, unsigned flags);
|
||||||
|
extern int replace_fd(unsigned fd, struct file *file, unsigned flags);
|
||||||
extern void set_close_on_exec(unsigned int fd, int flag);
|
extern void set_close_on_exec(unsigned int fd, int flag);
|
||||||
extern bool get_close_on_exec(unsigned int fd);
|
extern bool get_close_on_exec(unsigned int fd);
|
||||||
extern void put_filp(struct file *);
|
extern void put_filp(struct file *);
|
||||||
|
Loading…
Reference in New Issue
Block a user