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 | |
parent | 6a6d27de340c89c5323565b49f7851362619925d (diff) |
take purely descriptor-related stuff from fcntl.c to file.c
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/fcntl.c | 131 | ||||
-rw-r--r-- | fs/file.c | 132 | ||||
-rw-r--r-- | include/linux/file.h | 2 |
3 files changed, 137 insertions, 128 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c index 08e6af5c1b1f..40a5bfb72cca 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c | |||
@@ -26,127 +26,6 @@ | |||
26 | #include <asm/siginfo.h> | 26 | #include <asm/siginfo.h> |
27 | #include <asm/uaccess.h> | 27 | #include <asm/uaccess.h> |
28 | 28 | ||
29 | void set_close_on_exec(unsigned int fd, int flag) | ||
30 | { | ||
31 | struct files_struct *files = current->files; | ||
32 | struct fdtable *fdt; | ||
33 | spin_lock(&files->file_lock); | ||
34 | fdt = files_fdtable(files); | ||
35 | if (flag) | ||
36 | __set_close_on_exec(fd, fdt); | ||
37 | else | ||
38 | __clear_close_on_exec(fd, fdt); | ||
39 | spin_unlock(&files->file_lock); | ||
40 | } | ||
41 | |||
42 | static bool get_close_on_exec(unsigned int fd) | ||
43 | { | ||
44 | struct files_struct *files = current->files; | ||
45 | struct fdtable *fdt; | ||
46 | bool res; | ||
47 | rcu_read_lock(); | ||
48 | fdt = files_fdtable(files); | ||
49 | res = close_on_exec(fd, fdt); | ||
50 | rcu_read_unlock(); | ||
51 | return res; | ||
52 | } | ||
53 | |||
54 | SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags) | ||
55 | { | ||
56 | int err = -EBADF; | ||
57 | struct file * file, *tofree; | ||
58 | struct files_struct * files = current->files; | ||
59 | struct fdtable *fdt; | ||
60 | |||
61 | if ((flags & ~O_CLOEXEC) != 0) | ||
62 | return -EINVAL; | ||
63 | |||
64 | if (unlikely(oldfd == newfd)) | ||
65 | return -EINVAL; | ||
66 | |||
67 | if (newfd >= rlimit(RLIMIT_NOFILE)) | ||
68 | return -EMFILE; | ||
69 | |||
70 | spin_lock(&files->file_lock); | ||
71 | err = expand_files(files, newfd); | ||
72 | file = fcheck(oldfd); | ||
73 | if (unlikely(!file)) | ||
74 | goto Ebadf; | ||
75 | if (unlikely(err < 0)) { | ||
76 | if (err == -EMFILE) | ||
77 | goto Ebadf; | ||
78 | goto out_unlock; | ||
79 | } | ||
80 | /* | ||
81 | * We need to detect attempts to do dup2() over allocated but still | ||
82 | * not finished descriptor. NB: OpenBSD avoids that at the price of | ||
83 | * extra work in their equivalent of fget() - they insert struct | ||
84 | * file immediately after grabbing descriptor, mark it larval if | ||
85 | * more work (e.g. actual opening) is needed and make sure that | ||
86 | * fget() treats larval files as absent. Potentially interesting, | ||
87 | * but while extra work in fget() is trivial, locking implications | ||
88 | * and amount of surgery on open()-related paths in VFS are not. | ||
89 | * FreeBSD fails with -EBADF in the same situation, NetBSD "solution" | ||
90 | * deadlocks in rather amusing ways, AFAICS. All of that is out of | ||
91 | * scope of POSIX or SUS, since neither considers shared descriptor | ||
92 | * tables and this condition does not arise without those. | ||
93 | */ | ||
94 | err = -EBUSY; | ||
95 | fdt = files_fdtable(files); | ||
96 | tofree = fdt->fd[newfd]; | ||
97 | if (!tofree && fd_is_open(newfd, fdt)) | ||
98 | goto out_unlock; | ||
99 | get_file(file); | ||
100 | rcu_assign_pointer(fdt->fd[newfd], file); | ||
101 | __set_open_fd(newfd, fdt); | ||
102 | if (flags & O_CLOEXEC) | ||
103 | __set_close_on_exec(newfd, fdt); | ||
104 | else | ||
105 | __clear_close_on_exec(newfd, fdt); | ||
106 | spin_unlock(&files->file_lock); | ||
107 | |||
108 | if (tofree) | ||
109 | filp_close(tofree, files); | ||
110 | |||
111 | return newfd; | ||
112 | |||
113 | Ebadf: | ||
114 | err = -EBADF; | ||
115 | out_unlock: | ||
116 | spin_unlock(&files->file_lock); | ||
117 | return err; | ||
118 | } | ||
119 | |||
120 | SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd) | ||
121 | { | ||
122 | if (unlikely(newfd == oldfd)) { /* corner case */ | ||
123 | struct files_struct *files = current->files; | ||
124 | int retval = oldfd; | ||
125 | |||
126 | rcu_read_lock(); | ||
127 | if (!fcheck_files(files, oldfd)) | ||
128 | retval = -EBADF; | ||
129 | rcu_read_unlock(); | ||
130 | return retval; | ||
131 | } | ||
132 | return sys_dup3(oldfd, newfd, 0); | ||
133 | } | ||
134 | |||
135 | SYSCALL_DEFINE1(dup, unsigned int, fildes) | ||
136 | { | ||
137 | int ret = -EBADF; | ||
138 | struct file *file = fget_raw(fildes); | ||
139 | |||
140 | if (file) { | ||
141 | ret = get_unused_fd(); | ||
142 | if (ret >= 0) | ||
143 | fd_install(ret, file); | ||
144 | else | ||
145 | fput(file); | ||
146 | } | ||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME) | 29 | #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME) |
151 | 30 | ||
152 | static int setfl(int fd, struct file * filp, unsigned long arg) | 31 | static int setfl(int fd, struct file * filp, unsigned long arg) |
@@ -376,14 +255,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, | |||
376 | 255 | ||
377 | switch (cmd) { | 256 | switch (cmd) { |
378 | case F_DUPFD: | 257 | case F_DUPFD: |
258 | err = f_dupfd(arg, filp, 0); | ||
259 | break; | ||
379 | case F_DUPFD_CLOEXEC: | 260 | case F_DUPFD_CLOEXEC: |
380 | if (arg >= rlimit(RLIMIT_NOFILE)) | 261 | err = f_dupfd(arg, filp, FD_CLOEXEC); |
381 | break; | ||
382 | err = alloc_fd(arg, cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0); | ||
383 | if (err >= 0) { | ||
384 | get_file(filp); | ||
385 | fd_install(err, filp); | ||
386 | } | ||
387 | break; | 262 | break; |
388 | case F_GETFD: | 263 | case F_GETFD: |
389 | err = get_close_on_exec(fd) ? FD_CLOEXEC : 0; | 264 | err = get_close_on_exec(fd) ? FD_CLOEXEC : 0; |
@@ -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 | } | ||
diff --git a/include/linux/file.h b/include/linux/file.h index 86795ec6027d..da84fa0f4579 100644 --- a/include/linux/file.h +++ b/include/linux/file.h | |||
@@ -30,7 +30,9 @@ extern struct file *fget(unsigned int fd); | |||
30 | extern struct file *fget_light(unsigned int fd, int *fput_needed); | 30 | extern struct file *fget_light(unsigned int fd, int *fput_needed); |
31 | extern struct file *fget_raw(unsigned int fd); | 31 | extern struct file *fget_raw(unsigned int fd); |
32 | extern struct file *fget_raw_light(unsigned int fd, int *fput_needed); | 32 | extern struct file *fget_raw_light(unsigned int fd, int *fput_needed); |
33 | extern int f_dupfd(unsigned int from, struct file *file, unsigned flags); | ||
33 | extern void set_close_on_exec(unsigned int fd, int flag); | 34 | extern void set_close_on_exec(unsigned int fd, int flag); |
35 | extern bool get_close_on_exec(unsigned int fd); | ||
34 | extern void put_filp(struct file *); | 36 | extern void put_filp(struct file *); |
35 | extern int alloc_fd(unsigned start, unsigned flags); | 37 | extern int alloc_fd(unsigned start, unsigned flags); |
36 | extern int get_unused_fd_flags(unsigned flags); | 38 | extern int get_unused_fd_flags(unsigned flags); |