diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/fcntl.c | 53 |
1 files changed, 27 insertions, 26 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c index 2e40799daad6..ac4f7db9f134 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c | |||
| @@ -63,31 +63,35 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) | |||
| 63 | return -EINVAL; | 63 | return -EINVAL; |
| 64 | 64 | ||
| 65 | spin_lock(&files->file_lock); | 65 | spin_lock(&files->file_lock); |
| 66 | if (!(file = fcheck(oldfd))) | ||
| 67 | goto out_unlock; | ||
| 68 | get_file(file); /* We are now finished with oldfd */ | ||
| 69 | |||
| 70 | err = expand_files(files, newfd); | 66 | err = expand_files(files, newfd); |
| 67 | file = fcheck(oldfd); | ||
| 68 | if (unlikely(!file)) | ||
| 69 | goto Ebadf; | ||
| 71 | if (unlikely(err < 0)) { | 70 | if (unlikely(err < 0)) { |
| 72 | if (err == -EMFILE) | 71 | if (err == -EMFILE) |
| 73 | err = -EBADF; | 72 | goto Ebadf; |
| 74 | goto out_fput; | 73 | goto out_unlock; |
| 75 | } | 74 | } |
| 76 | 75 | /* | |
| 77 | /* To avoid races with open() and dup(), we will mark the fd as | 76 | * We need to detect attempts to do dup2() over allocated but still |
| 78 | * in-use in the open-file bitmap throughout the entire dup2() | 77 | * not finished descriptor. NB: OpenBSD avoids that at the price of |
| 79 | * process. This is quite safe: do_close() uses the fd array | 78 | * extra work in their equivalent of fget() - they insert struct |
| 80 | * entry, not the bitmap, to decide what work needs to be | 79 | * file immediately after grabbing descriptor, mark it larval if |
| 81 | * done. --sct */ | 80 | * more work (e.g. actual opening) is needed and make sure that |
| 82 | /* Doesn't work. open() might be there first. --AV */ | 81 | * fget() treats larval files as absent. Potentially interesting, |
| 83 | 82 | * but while extra work in fget() is trivial, locking implications | |
| 84 | /* Yes. It's a race. In user space. Nothing sane to do */ | 83 | * and amount of surgery on open()-related paths in VFS are not. |
| 84 | * FreeBSD fails with -EBADF in the same situation, NetBSD "solution" | ||
| 85 | * deadlocks in rather amusing ways, AFAICS. All of that is out of | ||
| 86 | * scope of POSIX or SUS, since neither considers shared descriptor | ||
| 87 | * tables and this condition does not arise without those. | ||
| 88 | */ | ||
| 85 | err = -EBUSY; | 89 | err = -EBUSY; |
| 86 | fdt = files_fdtable(files); | 90 | fdt = files_fdtable(files); |
| 87 | tofree = fdt->fd[newfd]; | 91 | tofree = fdt->fd[newfd]; |
| 88 | if (!tofree && FD_ISSET(newfd, fdt->open_fds)) | 92 | if (!tofree && FD_ISSET(newfd, fdt->open_fds)) |
| 89 | goto out_fput; | 93 | goto out_unlock; |
| 90 | 94 | get_file(file); | |
| 91 | rcu_assign_pointer(fdt->fd[newfd], file); | 95 | rcu_assign_pointer(fdt->fd[newfd], file); |
| 92 | FD_SET(newfd, fdt->open_fds); | 96 | FD_SET(newfd, fdt->open_fds); |
| 93 | if (flags & O_CLOEXEC) | 97 | if (flags & O_CLOEXEC) |
| @@ -98,17 +102,14 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) | |||
| 98 | 102 | ||
| 99 | if (tofree) | 103 | if (tofree) |
| 100 | filp_close(tofree, files); | 104 | filp_close(tofree, files); |
| 101 | err = newfd; | ||
| 102 | out: | ||
| 103 | return err; | ||
| 104 | out_unlock: | ||
| 105 | spin_unlock(&files->file_lock); | ||
| 106 | goto out; | ||
| 107 | 105 | ||
| 108 | out_fput: | 106 | return newfd; |
| 107 | |||
| 108 | Ebadf: | ||
| 109 | err = -EBADF; | ||
| 110 | out_unlock: | ||
| 109 | spin_unlock(&files->file_lock); | 111 | spin_unlock(&files->file_lock); |
| 110 | fput(file); | 112 | return err; |
| 111 | goto out; | ||
| 112 | } | 113 | } |
| 113 | 114 | ||
| 114 | asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) | 115 | asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) |
