|
|
|
@ -821,29 +821,12 @@ bool get_close_on_exec(unsigned int fd) |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags) |
|
|
|
|
static int do_dup2(struct files_struct *files, |
|
|
|
|
struct file *file, unsigned fd, unsigned flags) |
|
|
|
|
{ |
|
|
|
|
int err = -EBADF; |
|
|
|
|
struct file * file, *tofree; |
|
|
|
|
struct files_struct * files = current->files; |
|
|
|
|
struct file *tofree; |
|
|
|
|
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 |
|
|
|
@ -858,24 +841,74 @@ SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags) |
|
|
|
|
* 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; |
|
|
|
|
tofree = fdt->fd[fd]; |
|
|
|
|
if (!tofree && fd_is_open(fd, fdt)) |
|
|
|
|
goto Ebusy; |
|
|
|
|
get_file(file); |
|
|
|
|
rcu_assign_pointer(fdt->fd[newfd], file); |
|
|
|
|
__set_open_fd(newfd, fdt); |
|
|
|
|
rcu_assign_pointer(fdt->fd[fd], file); |
|
|
|
|
__set_open_fd(fd, fdt); |
|
|
|
|
if (flags & O_CLOEXEC) |
|
|
|
|
__set_close_on_exec(newfd, fdt); |
|
|
|
|
__set_close_on_exec(fd, fdt); |
|
|
|
|
else |
|
|
|
|
__clear_close_on_exec(newfd, fdt); |
|
|
|
|
__clear_close_on_exec(fd, fdt); |
|
|
|
|
spin_unlock(&files->file_lock); |
|
|
|
|
|
|
|
|
|
if (tofree) |
|
|
|
|
filp_close(tofree, files); |
|
|
|
|
|
|
|
|
|
return newfd; |
|
|
|
|
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) |
|
|
|
|
{ |
|
|
|
|
int err = -EBADF; |
|
|
|
|
struct file *file; |
|
|
|
|
struct files_struct *files = current->files; |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
return do_dup2(files, file, newfd, flags); |
|
|
|
|
|
|
|
|
|
Ebadf: |
|
|
|
|
err = -EBADF; |
|
|
|
|