diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2012-08-21 12:11:46 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-09-26 21:09:57 -0400 |
commit | 8280d16172243702ed43432f826ca6130edb4086 (patch) | |
tree | 020dff359c5b717a110432159bcc3ec1acf6594e /fs/file.c | |
parent | fe17f22d7fd0e344ef6447238f799bb49f670c6f (diff) |
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>
Diffstat (limited to 'fs/file.c')
-rw-r--r-- | fs/file.c | 91 |
1 files changed, 62 insertions, 29 deletions
@@ -821,29 +821,12 @@ bool get_close_on_exec(unsigned int fd) | |||
821 | return res; | 821 | return res; |
822 | } | 822 | } |
823 | 823 | ||
824 | SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags) | 824 | static int do_dup2(struct files_struct *files, |
825 | struct file *file, unsigned fd, unsigned flags) | ||
825 | { | 826 | { |
826 | int err = -EBADF; | 827 | struct file *tofree; |
827 | struct file * file, *tofree; | ||
828 | struct files_struct * files = current->files; | ||
829 | struct fdtable *fdt; | 828 | struct fdtable *fdt; |
830 | 829 | ||
831 | if ((flags & ~O_CLOEXEC) != 0) | ||
832 | return -EINVAL; | ||
833 | |||
834 | if (newfd >= rlimit(RLIMIT_NOFILE)) | ||
835 | return -EMFILE; | ||
836 | |||
837 | spin_lock(&files->file_lock); | ||
838 | err = expand_files(files, newfd); | ||
839 | file = fcheck(oldfd); | ||
840 | if (unlikely(!file)) | ||
841 | goto Ebadf; | ||
842 | if (unlikely(err < 0)) { | ||
843 | if (err == -EMFILE) | ||
844 | goto Ebadf; | ||
845 | goto out_unlock; | ||
846 | } | ||
847 | /* | 830 | /* |
848 | * We need to detect attempts to do dup2() over allocated but still | 831 | * We need to detect attempts to do dup2() over allocated but still |
849 | * not finished descriptor. NB: OpenBSD avoids that at the price of | 832 | * 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) | |||
858 | * scope of POSIX or SUS, since neither considers shared descriptor | 841 | * scope of POSIX or SUS, since neither considers shared descriptor |
859 | * tables and this condition does not arise without those. | 842 | * tables and this condition does not arise without those. |
860 | */ | 843 | */ |
861 | err = -EBUSY; | ||
862 | fdt = files_fdtable(files); | 844 | fdt = files_fdtable(files); |
863 | tofree = fdt->fd[newfd]; | 845 | tofree = fdt->fd[fd]; |
864 | if (!tofree && fd_is_open(newfd, fdt)) | 846 | if (!tofree && fd_is_open(fd, fdt)) |
865 | goto out_unlock; | 847 | goto Ebusy; |
866 | get_file(file); | 848 | get_file(file); |
867 | rcu_assign_pointer(fdt->fd[newfd], file); | 849 | rcu_assign_pointer(fdt->fd[fd], file); |
868 | __set_open_fd(newfd, fdt); | 850 | __set_open_fd(fd, fdt); |
869 | if (flags & O_CLOEXEC) | 851 | if (flags & O_CLOEXEC) |
870 | __set_close_on_exec(newfd, fdt); | 852 | __set_close_on_exec(fd, fdt); |
871 | else | 853 | else |
872 | __clear_close_on_exec(newfd, fdt); | 854 | __clear_close_on_exec(fd, fdt); |
873 | spin_unlock(&files->file_lock); | 855 | spin_unlock(&files->file_lock); |
874 | 856 | ||
875 | if (tofree) | 857 | if (tofree) |
876 | filp_close(tofree, files); | 858 | filp_close(tofree, files); |
877 | 859 | ||
878 | return newfd; | 860 | return fd; |
861 | |||
862 | Ebusy: | ||
863 | spin_unlock(&files->file_lock); | ||
864 | return -EBUSY; | ||
865 | } | ||
866 | |||
867 | int replace_fd(unsigned fd, struct file *file, unsigned flags) | ||
868 | { | ||
869 | int err; | ||
870 | struct files_struct *files = current->files; | ||
871 | |||
872 | if (!file) | ||
873 | return __close_fd(files, fd); | ||
874 | |||
875 | if (fd >= rlimit(RLIMIT_NOFILE)) | ||
876 | return -EMFILE; | ||
877 | |||
878 | spin_lock(&files->file_lock); | ||
879 | err = expand_files(files, fd); | ||
880 | if (unlikely(err < 0)) | ||
881 | goto out_unlock; | ||
882 | return do_dup2(files, file, fd, flags); | ||
883 | |||
884 | out_unlock: | ||
885 | spin_unlock(&files->file_lock); | ||
886 | return err; | ||
887 | } | ||
888 | |||
889 | SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags) | ||
890 | { | ||
891 | int err = -EBADF; | ||
892 | struct file *file; | ||
893 | struct files_struct *files = current->files; | ||
894 | |||
895 | if ((flags & ~O_CLOEXEC) != 0) | ||
896 | return -EINVAL; | ||
897 | |||
898 | if (newfd >= rlimit(RLIMIT_NOFILE)) | ||
899 | return -EMFILE; | ||
900 | |||
901 | spin_lock(&files->file_lock); | ||
902 | err = expand_files(files, newfd); | ||
903 | file = fcheck(oldfd); | ||
904 | if (unlikely(!file)) | ||
905 | goto Ebadf; | ||
906 | if (unlikely(err < 0)) { | ||
907 | if (err == -EMFILE) | ||
908 | goto Ebadf; | ||
909 | goto out_unlock; | ||
910 | } | ||
911 | return do_dup2(files, file, newfd, flags); | ||
879 | 912 | ||
880 | Ebadf: | 913 | Ebadf: |
881 | err = -EBADF; | 914 | err = -EBADF; |