diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2012-08-21 11:48:11 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-09-26 21:09:57 -0400 |
commit | fe17f22d7fd0e344ef6447238f799bb49f670c6f (patch) | |
tree | 793facbd992c674e55790793ffa912927ae7a766 /fs/file.c | |
parent | 6a6d27de340c89c5323565b49f7851362619925d (diff) |
take purely descriptor-related stuff from fcntl.c to file.c
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/file.c')
-rw-r--r-- | fs/file.c | 132 |
1 files changed, 132 insertions, 0 deletions
@@ -6,6 +6,7 @@ | |||
6 | * Manage the dynamic fd arrays in the process files_struct. | 6 | * Manage the dynamic fd arrays in the process files_struct. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/syscalls.h> | ||
9 | #include <linux/export.h> | 10 | #include <linux/export.h> |
10 | #include <linux/fs.h> | 11 | #include <linux/fs.h> |
11 | #include <linux/mm.h> | 12 | #include <linux/mm.h> |
@@ -794,3 +795,134 @@ struct file *fget_raw_light(unsigned int fd, int *fput_needed) | |||
794 | 795 | ||
795 | return file; | 796 | return file; |
796 | } | 797 | } |
798 | |||
799 | void set_close_on_exec(unsigned int fd, int flag) | ||
800 | { | ||
801 | struct files_struct *files = current->files; | ||
802 | struct fdtable *fdt; | ||
803 | spin_lock(&files->file_lock); | ||
804 | fdt = files_fdtable(files); | ||
805 | if (flag) | ||
806 | __set_close_on_exec(fd, fdt); | ||
807 | else | ||
808 | __clear_close_on_exec(fd, fdt); | ||
809 | spin_unlock(&files->file_lock); | ||
810 | } | ||
811 | |||
812 | bool get_close_on_exec(unsigned int fd) | ||
813 | { | ||
814 | struct files_struct *files = current->files; | ||
815 | struct fdtable *fdt; | ||
816 | bool res; | ||
817 | rcu_read_lock(); | ||
818 | fdt = files_fdtable(files); | ||
819 | res = close_on_exec(fd, fdt); | ||
820 | rcu_read_unlock(); | ||
821 | return res; | ||
822 | } | ||
823 | |||
824 | SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags) | ||
825 | { | ||
826 | int err = -EBADF; | ||
827 | struct file * file, *tofree; | ||
828 | struct files_struct * files = current->files; | ||
829 | struct fdtable *fdt; | ||
830 | |||
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 | /* | ||
848 | * We need to detect attempts to do dup2() over allocated but still | ||
849 | * not finished descriptor. NB: OpenBSD avoids that at the price of | ||
850 | * extra work in their equivalent of fget() - they insert struct | ||
851 | * file immediately after grabbing descriptor, mark it larval if | ||
852 | * more work (e.g. actual opening) is needed and make sure that | ||
853 | * fget() treats larval files as absent. Potentially interesting, | ||
854 | * but while extra work in fget() is trivial, locking implications | ||
855 | * and amount of surgery on open()-related paths in VFS are not. | ||
856 | * FreeBSD fails with -EBADF in the same situation, NetBSD "solution" | ||
857 | * deadlocks in rather amusing ways, AFAICS. All of that is out of | ||
858 | * scope of POSIX or SUS, since neither considers shared descriptor | ||
859 | * tables and this condition does not arise without those. | ||
860 | */ | ||
861 | err = -EBUSY; | ||
862 | fdt = files_fdtable(files); | ||
863 | tofree = fdt->fd[newfd]; | ||
864 | if (!tofree && fd_is_open(newfd, fdt)) | ||
865 | goto out_unlock; | ||
866 | get_file(file); | ||
867 | rcu_assign_pointer(fdt->fd[newfd], file); | ||
868 | __set_open_fd(newfd, fdt); | ||
869 | if (flags & O_CLOEXEC) | ||
870 | __set_close_on_exec(newfd, fdt); | ||
871 | else | ||
872 | __clear_close_on_exec(newfd, fdt); | ||
873 | spin_unlock(&files->file_lock); | ||
874 | |||
875 | if (tofree) | ||
876 | filp_close(tofree, files); | ||
877 | |||
878 | return newfd; | ||
879 | |||
880 | Ebadf: | ||
881 | err = -EBADF; | ||
882 | out_unlock: | ||
883 | spin_unlock(&files->file_lock); | ||
884 | return err; | ||
885 | } | ||
886 | |||
887 | SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd) | ||
888 | { | ||
889 | if (unlikely(newfd == oldfd)) { /* corner case */ | ||
890 | struct files_struct *files = current->files; | ||
891 | int retval = oldfd; | ||
892 | |||
893 | rcu_read_lock(); | ||
894 | if (!fcheck_files(files, oldfd)) | ||
895 | retval = -EBADF; | ||
896 | rcu_read_unlock(); | ||
897 | return retval; | ||
898 | } | ||
899 | return sys_dup3(oldfd, newfd, 0); | ||
900 | } | ||
901 | |||
902 | SYSCALL_DEFINE1(dup, unsigned int, fildes) | ||
903 | { | ||
904 | int ret = -EBADF; | ||
905 | struct file *file = fget_raw(fildes); | ||
906 | |||
907 | if (file) { | ||
908 | ret = get_unused_fd(); | ||
909 | if (ret >= 0) | ||
910 | fd_install(ret, file); | ||
911 | else | ||
912 | fput(file); | ||
913 | } | ||
914 | return ret; | ||
915 | } | ||
916 | |||
917 | int f_dupfd(unsigned int from, struct file *file, unsigned flags) | ||
918 | { | ||
919 | int err; | ||
920 | if (from >= rlimit(RLIMIT_NOFILE)) | ||
921 | return -EINVAL; | ||
922 | err = alloc_fd(from, flags); | ||
923 | if (err >= 0) { | ||
924 | get_file(file); | ||
925 | fd_install(err, file); | ||
926 | } | ||
927 | return err; | ||
928 | } | ||