diff options
Diffstat (limited to 'fs/ioctl.c')
-rw-r--r-- | fs/ioctl.c | 223 |
1 files changed, 130 insertions, 93 deletions
diff --git a/fs/ioctl.c b/fs/ioctl.c index c2a773e8620b..683002fefa55 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c | |||
@@ -12,12 +12,24 @@ | |||
12 | #include <linux/fs.h> | 12 | #include <linux/fs.h> |
13 | #include <linux/security.h> | 13 | #include <linux/security.h> |
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <linux/uaccess.h> | ||
15 | 16 | ||
16 | #include <asm/uaccess.h> | ||
17 | #include <asm/ioctls.h> | 17 | #include <asm/ioctls.h> |
18 | 18 | ||
19 | static long do_ioctl(struct file *filp, unsigned int cmd, | 19 | /** |
20 | unsigned long arg) | 20 | * vfs_ioctl - call filesystem specific ioctl methods |
21 | * @filp: [in] open file to invoke ioctl method on | ||
22 | * @cmd: [in] ioctl command to execute | ||
23 | * @arg: [in/out] command-specific argument for ioctl | ||
24 | * | ||
25 | * Invokes filesystem specific ->unlocked_ioctl, if one exists; otherwise | ||
26 | * invokes * filesystem specific ->ioctl method. If neither method exists, | ||
27 | * returns -ENOTTY. | ||
28 | * | ||
29 | * Returns 0 on success, -errno on error. | ||
30 | */ | ||
31 | long vfs_ioctl(struct file *filp, unsigned int cmd, | ||
32 | unsigned long arg) | ||
21 | { | 33 | { |
22 | int error = -ENOTTY; | 34 | int error = -ENOTTY; |
23 | 35 | ||
@@ -40,123 +52,148 @@ static long do_ioctl(struct file *filp, unsigned int cmd, | |||
40 | return error; | 52 | return error; |
41 | } | 53 | } |
42 | 54 | ||
55 | static int ioctl_fibmap(struct file *filp, int __user *p) | ||
56 | { | ||
57 | struct address_space *mapping = filp->f_mapping; | ||
58 | int res, block; | ||
59 | |||
60 | /* do we support this mess? */ | ||
61 | if (!mapping->a_ops->bmap) | ||
62 | return -EINVAL; | ||
63 | if (!capable(CAP_SYS_RAWIO)) | ||
64 | return -EPERM; | ||
65 | res = get_user(block, p); | ||
66 | if (res) | ||
67 | return res; | ||
68 | lock_kernel(); | ||
69 | res = mapping->a_ops->bmap(mapping, block); | ||
70 | unlock_kernel(); | ||
71 | return put_user(res, p); | ||
72 | } | ||
73 | |||
43 | static int file_ioctl(struct file *filp, unsigned int cmd, | 74 | static int file_ioctl(struct file *filp, unsigned int cmd, |
44 | unsigned long arg) | 75 | unsigned long arg) |
45 | { | 76 | { |
46 | int error; | 77 | struct inode *inode = filp->f_path.dentry->d_inode; |
47 | int block; | ||
48 | struct inode * inode = filp->f_path.dentry->d_inode; | ||
49 | int __user *p = (int __user *)arg; | 78 | int __user *p = (int __user *)arg; |
50 | 79 | ||
51 | switch (cmd) { | 80 | switch (cmd) { |
52 | case FIBMAP: | 81 | case FIBMAP: |
53 | { | 82 | return ioctl_fibmap(filp, p); |
54 | struct address_space *mapping = filp->f_mapping; | 83 | case FIGETBSZ: |
55 | int res; | 84 | return put_user(inode->i_sb->s_blocksize, p); |
56 | /* do we support this mess? */ | 85 | case FIONREAD: |
57 | if (!mapping->a_ops->bmap) | 86 | return put_user(i_size_read(inode) - filp->f_pos, p); |
58 | return -EINVAL; | 87 | } |
59 | if (!capable(CAP_SYS_RAWIO)) | ||
60 | return -EPERM; | ||
61 | if ((error = get_user(block, p)) != 0) | ||
62 | return error; | ||
63 | 88 | ||
89 | return vfs_ioctl(filp, cmd, arg); | ||
90 | } | ||
91 | |||
92 | static int ioctl_fionbio(struct file *filp, int __user *argp) | ||
93 | { | ||
94 | unsigned int flag; | ||
95 | int on, error; | ||
96 | |||
97 | error = get_user(on, argp); | ||
98 | if (error) | ||
99 | return error; | ||
100 | flag = O_NONBLOCK; | ||
101 | #ifdef __sparc__ | ||
102 | /* SunOS compatibility item. */ | ||
103 | if (O_NONBLOCK != O_NDELAY) | ||
104 | flag |= O_NDELAY; | ||
105 | #endif | ||
106 | if (on) | ||
107 | filp->f_flags |= flag; | ||
108 | else | ||
109 | filp->f_flags &= ~flag; | ||
110 | return error; | ||
111 | } | ||
112 | |||
113 | static int ioctl_fioasync(unsigned int fd, struct file *filp, | ||
114 | int __user *argp) | ||
115 | { | ||
116 | unsigned int flag; | ||
117 | int on, error; | ||
118 | |||
119 | error = get_user(on, argp); | ||
120 | if (error) | ||
121 | return error; | ||
122 | flag = on ? FASYNC : 0; | ||
123 | |||
124 | /* Did FASYNC state change ? */ | ||
125 | if ((flag ^ filp->f_flags) & FASYNC) { | ||
126 | if (filp->f_op && filp->f_op->fasync) { | ||
64 | lock_kernel(); | 127 | lock_kernel(); |
65 | res = mapping->a_ops->bmap(mapping, block); | 128 | error = filp->f_op->fasync(fd, filp, on); |
66 | unlock_kernel(); | 129 | unlock_kernel(); |
67 | return put_user(res, p); | 130 | } else |
68 | } | 131 | error = -ENOTTY; |
69 | case FIGETBSZ: | ||
70 | return put_user(inode->i_sb->s_blocksize, p); | ||
71 | case FIONREAD: | ||
72 | return put_user(i_size_read(inode) - filp->f_pos, p); | ||
73 | } | 132 | } |
133 | if (error) | ||
134 | return error; | ||
74 | 135 | ||
75 | return do_ioctl(filp, cmd, arg); | 136 | if (on) |
137 | filp->f_flags |= FASYNC; | ||
138 | else | ||
139 | filp->f_flags &= ~FASYNC; | ||
140 | return error; | ||
76 | } | 141 | } |
77 | 142 | ||
78 | /* | 143 | /* |
79 | * When you add any new common ioctls to the switches above and below | 144 | * When you add any new common ioctls to the switches above and below |
80 | * please update compat_sys_ioctl() too. | 145 | * please update compat_sys_ioctl() too. |
81 | * | 146 | * |
82 | * vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d. | 147 | * do_vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d. |
83 | * It's just a simple helper for sys_ioctl and compat_sys_ioctl. | 148 | * It's just a simple helper for sys_ioctl and compat_sys_ioctl. |
84 | */ | 149 | */ |
85 | int vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, unsigned long arg) | 150 | int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, |
151 | unsigned long arg) | ||
86 | { | 152 | { |
87 | unsigned int flag; | 153 | int error = 0; |
88 | int on, error = 0; | 154 | int __user *argp = (int __user *)arg; |
89 | 155 | ||
90 | switch (cmd) { | 156 | switch (cmd) { |
91 | case FIOCLEX: | 157 | case FIOCLEX: |
92 | set_close_on_exec(fd, 1); | 158 | set_close_on_exec(fd, 1); |
93 | break; | 159 | break; |
94 | 160 | ||
95 | case FIONCLEX: | 161 | case FIONCLEX: |
96 | set_close_on_exec(fd, 0); | 162 | set_close_on_exec(fd, 0); |
97 | break; | 163 | break; |
98 | 164 | ||
99 | case FIONBIO: | 165 | case FIONBIO: |
100 | if ((error = get_user(on, (int __user *)arg)) != 0) | 166 | error = ioctl_fionbio(filp, argp); |
101 | break; | 167 | break; |
102 | flag = O_NONBLOCK; | 168 | |
103 | #ifdef __sparc__ | 169 | case FIOASYNC: |
104 | /* SunOS compatibility item. */ | 170 | error = ioctl_fioasync(fd, filp, argp); |
105 | if(O_NONBLOCK != O_NDELAY) | 171 | break; |
106 | flag |= O_NDELAY; | 172 | |
107 | #endif | 173 | case FIOQSIZE: |
108 | if (on) | 174 | if (S_ISDIR(filp->f_path.dentry->d_inode->i_mode) || |
109 | filp->f_flags |= flag; | 175 | S_ISREG(filp->f_path.dentry->d_inode->i_mode) || |
110 | else | 176 | S_ISLNK(filp->f_path.dentry->d_inode->i_mode)) { |
111 | filp->f_flags &= ~flag; | 177 | loff_t res = |
112 | break; | 178 | inode_get_bytes(filp->f_path.dentry->d_inode); |
113 | 179 | error = copy_to_user((loff_t __user *)arg, &res, | |
114 | case FIOASYNC: | 180 | sizeof(res)) ? -EFAULT : 0; |
115 | if ((error = get_user(on, (int __user *)arg)) != 0) | 181 | } else |
116 | break; | 182 | error = -ENOTTY; |
117 | flag = on ? FASYNC : 0; | 183 | break; |
118 | 184 | default: | |
119 | /* Did FASYNC state change ? */ | 185 | if (S_ISREG(filp->f_path.dentry->d_inode->i_mode)) |
120 | if ((flag ^ filp->f_flags) & FASYNC) { | 186 | error = file_ioctl(filp, cmd, arg); |
121 | if (filp->f_op && filp->f_op->fasync) { | 187 | else |
122 | lock_kernel(); | 188 | error = vfs_ioctl(filp, cmd, arg); |
123 | error = filp->f_op->fasync(fd, filp, on); | 189 | break; |
124 | unlock_kernel(); | ||
125 | } | ||
126 | else error = -ENOTTY; | ||
127 | } | ||
128 | if (error != 0) | ||
129 | break; | ||
130 | |||
131 | if (on) | ||
132 | filp->f_flags |= FASYNC; | ||
133 | else | ||
134 | filp->f_flags &= ~FASYNC; | ||
135 | break; | ||
136 | |||
137 | case FIOQSIZE: | ||
138 | if (S_ISDIR(filp->f_path.dentry->d_inode->i_mode) || | ||
139 | S_ISREG(filp->f_path.dentry->d_inode->i_mode) || | ||
140 | S_ISLNK(filp->f_path.dentry->d_inode->i_mode)) { | ||
141 | loff_t res = inode_get_bytes(filp->f_path.dentry->d_inode); | ||
142 | error = copy_to_user((loff_t __user *)arg, &res, sizeof(res)) ? -EFAULT : 0; | ||
143 | } | ||
144 | else | ||
145 | error = -ENOTTY; | ||
146 | break; | ||
147 | default: | ||
148 | if (S_ISREG(filp->f_path.dentry->d_inode->i_mode)) | ||
149 | error = file_ioctl(filp, cmd, arg); | ||
150 | else | ||
151 | error = do_ioctl(filp, cmd, arg); | ||
152 | break; | ||
153 | } | 190 | } |
154 | return error; | 191 | return error; |
155 | } | 192 | } |
156 | 193 | ||
157 | asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) | 194 | asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) |
158 | { | 195 | { |
159 | struct file * filp; | 196 | struct file *filp; |
160 | int error = -EBADF; | 197 | int error = -EBADF; |
161 | int fput_needed; | 198 | int fput_needed; |
162 | 199 | ||
@@ -168,7 +205,7 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) | |||
168 | if (error) | 205 | if (error) |
169 | goto out_fput; | 206 | goto out_fput; |
170 | 207 | ||
171 | error = vfs_ioctl(filp, fd, cmd, arg); | 208 | error = do_vfs_ioctl(filp, fd, cmd, arg); |
172 | out_fput: | 209 | out_fput: |
173 | fput_light(filp, fput_needed); | 210 | fput_light(filp, fput_needed); |
174 | out: | 211 | out: |