Commit 1b7e190b authored by Al Viro's avatar Al Viro

[PATCH] clean dup2() up a bit

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 1027abe8
...@@ -63,31 +63,35 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) ...@@ -63,31 +63,35 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
return -EINVAL; return -EINVAL;
spin_lock(&files->file_lock); spin_lock(&files->file_lock);
if (!(file = fcheck(oldfd)))
goto out_unlock;
get_file(file); /* We are now finished with oldfd */
err = expand_files(files, newfd); err = expand_files(files, newfd);
file = fcheck(oldfd);
if (unlikely(!file))
goto Ebadf;
if (unlikely(err < 0)) { if (unlikely(err < 0)) {
if (err == -EMFILE) if (err == -EMFILE)
err = -EBADF; goto Ebadf;
goto out_fput; goto out_unlock;
} }
/*
/* To avoid races with open() and dup(), we will mark the fd as * We need to detect attempts to do dup2() over allocated but still
* in-use in the open-file bitmap throughout the entire dup2() * not finished descriptor. NB: OpenBSD avoids that at the price of
* process. This is quite safe: do_close() uses the fd array * extra work in their equivalent of fget() - they insert struct
* entry, not the bitmap, to decide what work needs to be * file immediately after grabbing descriptor, mark it larval if
* done. --sct */ * more work (e.g. actual opening) is needed and make sure that
/* Doesn't work. open() might be there first. --AV */ * fget() treats larval files as absent. Potentially interesting,
* but while extra work in fget() is trivial, locking implications
/* Yes. It's a race. In user space. Nothing sane to do */ * 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; err = -EBUSY;
fdt = files_fdtable(files); fdt = files_fdtable(files);
tofree = fdt->fd[newfd]; tofree = fdt->fd[newfd];
if (!tofree && FD_ISSET(newfd, fdt->open_fds)) if (!tofree && FD_ISSET(newfd, fdt->open_fds))
goto out_fput; goto out_unlock;
get_file(file);
rcu_assign_pointer(fdt->fd[newfd], file); rcu_assign_pointer(fdt->fd[newfd], file);
FD_SET(newfd, fdt->open_fds); FD_SET(newfd, fdt->open_fds);
if (flags & O_CLOEXEC) if (flags & O_CLOEXEC)
...@@ -98,17 +102,14 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) ...@@ -98,17 +102,14 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
if (tofree) if (tofree)
filp_close(tofree, files); filp_close(tofree, files);
err = newfd;
out:
return err;
out_unlock:
spin_unlock(&files->file_lock);
goto out;
out_fput: return newfd;
Ebadf:
err = -EBADF;
out_unlock:
spin_unlock(&files->file_lock); spin_unlock(&files->file_lock);
fput(file); return err;
goto out;
} }
asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment