take purely descriptor-related stuff from fcntl.c to file.c
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
6a6d27de34
commit
fe17f22d7f
131
fs/fcntl.c
131
fs/fcntl.c
|
@ -26,127 +26,6 @@
|
||||||
#include <asm/siginfo.h>
|
#include <asm/siginfo.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
|
|
||||||
void set_close_on_exec(unsigned int fd, int flag)
|
|
||||||
{
|
|
||||||
struct files_struct *files = current->files;
|
|
||||||
struct fdtable *fdt;
|
|
||||||
spin_lock(&files->file_lock);
|
|
||||||
fdt = files_fdtable(files);
|
|
||||||
if (flag)
|
|
||||||
__set_close_on_exec(fd, fdt);
|
|
||||||
else
|
|
||||||
__clear_close_on_exec(fd, fdt);
|
|
||||||
spin_unlock(&files->file_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool get_close_on_exec(unsigned int fd)
|
|
||||||
{
|
|
||||||
struct files_struct *files = current->files;
|
|
||||||
struct fdtable *fdt;
|
|
||||||
bool res;
|
|
||||||
rcu_read_lock();
|
|
||||||
fdt = files_fdtable(files);
|
|
||||||
res = close_on_exec(fd, fdt);
|
|
||||||
rcu_read_unlock();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags)
|
|
||||||
{
|
|
||||||
int err = -EBADF;
|
|
||||||
struct file * file, *tofree;
|
|
||||||
struct files_struct * files = current->files;
|
|
||||||
struct fdtable *fdt;
|
|
||||||
|
|
||||||
if ((flags & ~O_CLOEXEC) != 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (unlikely(oldfd == newfd))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (newfd >= rlimit(RLIMIT_NOFILE))
|
|
||||||
return -EMFILE;
|
|
||||||
|
|
||||||
spin_lock(&files->file_lock);
|
|
||||||
err = expand_files(files, newfd);
|
|
||||||
file = fcheck(oldfd);
|
|
||||||
if (unlikely(!file))
|
|
||||||
goto Ebadf;
|
|
||||||
if (unlikely(err < 0)) {
|
|
||||||
if (err == -EMFILE)
|
|
||||||
goto Ebadf;
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
err = -EBADF;
|
|
||||||
out_unlock:
|
|
||||||
spin_unlock(&files->file_lock);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd)
|
|
||||||
{
|
|
||||||
if (unlikely(newfd == oldfd)) { /* corner case */
|
|
||||||
struct files_struct *files = current->files;
|
|
||||||
int retval = oldfd;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
if (!fcheck_files(files, oldfd))
|
|
||||||
retval = -EBADF;
|
|
||||||
rcu_read_unlock();
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
return sys_dup3(oldfd, newfd, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SYSCALL_DEFINE1(dup, unsigned int, fildes)
|
|
||||||
{
|
|
||||||
int ret = -EBADF;
|
|
||||||
struct file *file = fget_raw(fildes);
|
|
||||||
|
|
||||||
if (file) {
|
|
||||||
ret = get_unused_fd();
|
|
||||||
if (ret >= 0)
|
|
||||||
fd_install(ret, file);
|
|
||||||
else
|
|
||||||
fput(file);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME)
|
#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME)
|
||||||
|
|
||||||
static int setfl(int fd, struct file * filp, unsigned long arg)
|
static int setfl(int fd, struct file * filp, unsigned long arg)
|
||||||
|
@ -376,14 +255,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case F_DUPFD:
|
case F_DUPFD:
|
||||||
|
err = f_dupfd(arg, filp, 0);
|
||||||
|
break;
|
||||||
case F_DUPFD_CLOEXEC:
|
case F_DUPFD_CLOEXEC:
|
||||||
if (arg >= rlimit(RLIMIT_NOFILE))
|
err = f_dupfd(arg, filp, FD_CLOEXEC);
|
||||||
break;
|
|
||||||
err = alloc_fd(arg, cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0);
|
|
||||||
if (err >= 0) {
|
|
||||||
get_file(filp);
|
|
||||||
fd_install(err, filp);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case F_GETFD:
|
case F_GETFD:
|
||||||
err = get_close_on_exec(fd) ? FD_CLOEXEC : 0;
|
err = get_close_on_exec(fd) ? FD_CLOEXEC : 0;
|
||||||
|
|
132
fs/file.c
132
fs/file.c
|
@ -6,6 +6,7 @@
|
||||||
* Manage the dynamic fd arrays in the process files_struct.
|
* Manage the dynamic fd arrays in the process files_struct.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/syscalls.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
|
@ -794,3 +795,134 @@ struct file *fget_raw_light(unsigned int fd, int *fput_needed)
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_close_on_exec(unsigned int fd, int flag)
|
||||||
|
{
|
||||||
|
struct files_struct *files = current->files;
|
||||||
|
struct fdtable *fdt;
|
||||||
|
spin_lock(&files->file_lock);
|
||||||
|
fdt = files_fdtable(files);
|
||||||
|
if (flag)
|
||||||
|
__set_close_on_exec(fd, fdt);
|
||||||
|
else
|
||||||
|
__clear_close_on_exec(fd, fdt);
|
||||||
|
spin_unlock(&files->file_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_close_on_exec(unsigned int fd)
|
||||||
|
{
|
||||||
|
struct files_struct *files = current->files;
|
||||||
|
struct fdtable *fdt;
|
||||||
|
bool res;
|
||||||
|
rcu_read_lock();
|
||||||
|
fdt = files_fdtable(files);
|
||||||
|
res = close_on_exec(fd, fdt);
|
||||||
|
rcu_read_unlock();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags)
|
||||||
|
{
|
||||||
|
int err = -EBADF;
|
||||||
|
struct file * file, *tofree;
|
||||||
|
struct files_struct * files = current->files;
|
||||||
|
struct fdtable *fdt;
|
||||||
|
|
||||||
|
if ((flags & ~O_CLOEXEC) != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (newfd >= rlimit(RLIMIT_NOFILE))
|
||||||
|
return -EMFILE;
|
||||||
|
|
||||||
|
spin_lock(&files->file_lock);
|
||||||
|
err = expand_files(files, newfd);
|
||||||
|
file = fcheck(oldfd);
|
||||||
|
if (unlikely(!file))
|
||||||
|
goto Ebadf;
|
||||||
|
if (unlikely(err < 0)) {
|
||||||
|
if (err == -EMFILE)
|
||||||
|
goto Ebadf;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 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:
|
||||||
|
err = -EBADF;
|
||||||
|
out_unlock:
|
||||||
|
spin_unlock(&files->file_lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd)
|
||||||
|
{
|
||||||
|
if (unlikely(newfd == oldfd)) { /* corner case */
|
||||||
|
struct files_struct *files = current->files;
|
||||||
|
int retval = oldfd;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
if (!fcheck_files(files, oldfd))
|
||||||
|
retval = -EBADF;
|
||||||
|
rcu_read_unlock();
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
return sys_dup3(oldfd, newfd, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SYSCALL_DEFINE1(dup, unsigned int, fildes)
|
||||||
|
{
|
||||||
|
int ret = -EBADF;
|
||||||
|
struct file *file = fget_raw(fildes);
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
ret = get_unused_fd();
|
||||||
|
if (ret >= 0)
|
||||||
|
fd_install(ret, file);
|
||||||
|
else
|
||||||
|
fput(file);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int f_dupfd(unsigned int from, struct file *file, unsigned flags)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
if (from >= rlimit(RLIMIT_NOFILE))
|
||||||
|
return -EINVAL;
|
||||||
|
err = alloc_fd(from, flags);
|
||||||
|
if (err >= 0) {
|
||||||
|
get_file(file);
|
||||||
|
fd_install(err, file);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
|
@ -30,7 +30,9 @@ extern struct file *fget(unsigned int fd);
|
||||||
extern struct file *fget_light(unsigned int fd, int *fput_needed);
|
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 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 void put_filp(struct file *);
|
extern void put_filp(struct file *);
|
||||||
extern int alloc_fd(unsigned start, unsigned flags);
|
extern int alloc_fd(unsigned start, unsigned flags);
|
||||||
extern int get_unused_fd_flags(unsigned flags);
|
extern int get_unused_fd_flags(unsigned flags);
|
||||||
|
|
Loading…
Reference in New Issue