diff options
Diffstat (limited to 'fs/quota/quota.c')
-rw-r--r-- | fs/quota/quota.c | 735 |
1 files changed, 228 insertions, 507 deletions
diff --git a/fs/quota/quota.c b/fs/quota/quota.c index ee91e2756950..95388f9b7356 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c | |||
@@ -10,7 +10,6 @@ | |||
10 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
11 | #include <asm/current.h> | 11 | #include <asm/current.h> |
12 | #include <asm/uaccess.h> | 12 | #include <asm/uaccess.h> |
13 | #include <linux/compat.h> | ||
14 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
15 | #include <linux/security.h> | 14 | #include <linux/security.h> |
16 | #include <linux/syscalls.h> | 15 | #include <linux/syscalls.h> |
@@ -18,220 +17,205 @@ | |||
18 | #include <linux/capability.h> | 17 | #include <linux/capability.h> |
19 | #include <linux/quotaops.h> | 18 | #include <linux/quotaops.h> |
20 | #include <linux/types.h> | 19 | #include <linux/types.h> |
21 | #include <net/netlink.h> | 20 | #include <linux/writeback.h> |
22 | #include <net/genetlink.h> | ||
23 | 21 | ||
24 | /* Check validity of generic quotactl commands */ | 22 | static int check_quotactl_permission(struct super_block *sb, int type, int cmd, |
25 | static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, | 23 | qid_t id) |
26 | qid_t id) | ||
27 | { | 24 | { |
28 | if (type >= MAXQUOTAS) | ||
29 | return -EINVAL; | ||
30 | if (!sb && cmd != Q_SYNC) | ||
31 | return -ENODEV; | ||
32 | /* Is operation supported? */ | ||
33 | if (sb && !sb->s_qcop) | ||
34 | return -ENOSYS; | ||
35 | |||
36 | switch (cmd) { | 25 | switch (cmd) { |
37 | case Q_GETFMT: | 26 | /* these commands do not require any special privilegues */ |
38 | break; | 27 | case Q_GETFMT: |
39 | case Q_QUOTAON: | 28 | case Q_SYNC: |
40 | if (!sb->s_qcop->quota_on) | 29 | case Q_GETINFO: |
41 | return -ENOSYS; | 30 | case Q_XGETQSTAT: |
42 | break; | 31 | case Q_XQUOTASYNC: |
43 | case Q_QUOTAOFF: | 32 | break; |
44 | if (!sb->s_qcop->quota_off) | 33 | /* allow to query information for dquots we "own" */ |
45 | return -ENOSYS; | 34 | case Q_GETQUOTA: |
46 | break; | 35 | case Q_XGETQUOTA: |
47 | case Q_SETINFO: | 36 | if ((type == USRQUOTA && current_euid() == id) || |
48 | if (!sb->s_qcop->set_info) | 37 | (type == GRPQUOTA && in_egroup_p(id))) |
49 | return -ENOSYS; | ||
50 | break; | ||
51 | case Q_GETINFO: | ||
52 | if (!sb->s_qcop->get_info) | ||
53 | return -ENOSYS; | ||
54 | break; | ||
55 | case Q_SETQUOTA: | ||
56 | if (!sb->s_qcop->set_dqblk) | ||
57 | return -ENOSYS; | ||
58 | break; | ||
59 | case Q_GETQUOTA: | ||
60 | if (!sb->s_qcop->get_dqblk) | ||
61 | return -ENOSYS; | ||
62 | break; | ||
63 | case Q_SYNC: | ||
64 | if (sb && !sb->s_qcop->quota_sync) | ||
65 | return -ENOSYS; | ||
66 | break; | 38 | break; |
67 | default: | 39 | /*FALLTHROUGH*/ |
68 | return -EINVAL; | 40 | default: |
41 | if (!capable(CAP_SYS_ADMIN)) | ||
42 | return -EPERM; | ||
69 | } | 43 | } |
70 | 44 | ||
71 | /* Is quota turned on for commands which need it? */ | 45 | return security_quotactl(cmd, type, id, sb); |
72 | switch (cmd) { | 46 | } |
73 | case Q_GETFMT: | ||
74 | case Q_GETINFO: | ||
75 | case Q_SETINFO: | ||
76 | case Q_SETQUOTA: | ||
77 | case Q_GETQUOTA: | ||
78 | /* This is just an informative test so we are satisfied | ||
79 | * without the lock */ | ||
80 | if (!sb_has_quota_active(sb, type)) | ||
81 | return -ESRCH; | ||
82 | } | ||
83 | 47 | ||
84 | /* Check privileges */ | 48 | static int quota_sync_all(int type) |
85 | if (cmd == Q_GETQUOTA) { | 49 | { |
86 | if (((type == USRQUOTA && current_euid() != id) || | 50 | struct super_block *sb; |
87 | (type == GRPQUOTA && !in_egroup_p(id))) && | 51 | int ret; |
88 | !capable(CAP_SYS_ADMIN)) | 52 | |
89 | return -EPERM; | 53 | if (type >= MAXQUOTAS) |
54 | return -EINVAL; | ||
55 | ret = security_quotactl(Q_SYNC, type, 0, NULL); | ||
56 | if (ret) | ||
57 | return ret; | ||
58 | |||
59 | spin_lock(&sb_lock); | ||
60 | restart: | ||
61 | list_for_each_entry(sb, &super_blocks, s_list) { | ||
62 | if (!sb->s_qcop || !sb->s_qcop->quota_sync) | ||
63 | continue; | ||
64 | |||
65 | sb->s_count++; | ||
66 | spin_unlock(&sb_lock); | ||
67 | down_read(&sb->s_umount); | ||
68 | if (sb->s_root) | ||
69 | sb->s_qcop->quota_sync(sb, type, 1); | ||
70 | up_read(&sb->s_umount); | ||
71 | spin_lock(&sb_lock); | ||
72 | if (__put_super_and_need_restart(sb)) | ||
73 | goto restart; | ||
90 | } | 74 | } |
91 | else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO) | 75 | spin_unlock(&sb_lock); |
92 | if (!capable(CAP_SYS_ADMIN)) | ||
93 | return -EPERM; | ||
94 | 76 | ||
95 | return 0; | 77 | return 0; |
96 | } | 78 | } |
97 | 79 | ||
98 | /* Check validity of XFS Quota Manager commands */ | 80 | static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id, |
99 | static int xqm_quotactl_valid(struct super_block *sb, int type, int cmd, | 81 | void __user *addr) |
100 | qid_t id) | ||
101 | { | 82 | { |
102 | if (type >= XQM_MAXQUOTAS) | 83 | char *pathname; |
103 | return -EINVAL; | 84 | int ret = -ENOSYS; |
104 | if (!sb) | 85 | |
105 | return -ENODEV; | 86 | pathname = getname(addr); |
106 | if (!sb->s_qcop) | 87 | if (IS_ERR(pathname)) |
107 | return -ENOSYS; | 88 | return PTR_ERR(pathname); |
89 | if (sb->s_qcop->quota_on) | ||
90 | ret = sb->s_qcop->quota_on(sb, type, id, pathname, 0); | ||
91 | putname(pathname); | ||
92 | return ret; | ||
93 | } | ||
108 | 94 | ||
109 | switch (cmd) { | 95 | static int quota_getfmt(struct super_block *sb, int type, void __user *addr) |
110 | case Q_XQUOTAON: | 96 | { |
111 | case Q_XQUOTAOFF: | 97 | __u32 fmt; |
112 | case Q_XQUOTARM: | ||
113 | if (!sb->s_qcop->set_xstate) | ||
114 | return -ENOSYS; | ||
115 | break; | ||
116 | case Q_XGETQSTAT: | ||
117 | if (!sb->s_qcop->get_xstate) | ||
118 | return -ENOSYS; | ||
119 | break; | ||
120 | case Q_XSETQLIM: | ||
121 | if (!sb->s_qcop->set_xquota) | ||
122 | return -ENOSYS; | ||
123 | break; | ||
124 | case Q_XGETQUOTA: | ||
125 | if (!sb->s_qcop->get_xquota) | ||
126 | return -ENOSYS; | ||
127 | break; | ||
128 | case Q_XQUOTASYNC: | ||
129 | if (!sb->s_qcop->quota_sync) | ||
130 | return -ENOSYS; | ||
131 | break; | ||
132 | default: | ||
133 | return -EINVAL; | ||
134 | } | ||
135 | 98 | ||
136 | /* Check privileges */ | 99 | down_read(&sb_dqopt(sb)->dqptr_sem); |
137 | if (cmd == Q_XGETQUOTA) { | 100 | if (!sb_has_quota_active(sb, type)) { |
138 | if (((type == XQM_USRQUOTA && current_euid() != id) || | 101 | up_read(&sb_dqopt(sb)->dqptr_sem); |
139 | (type == XQM_GRPQUOTA && !in_egroup_p(id))) && | 102 | return -ESRCH; |
140 | !capable(CAP_SYS_ADMIN)) | ||
141 | return -EPERM; | ||
142 | } else if (cmd != Q_XGETQSTAT && cmd != Q_XQUOTASYNC) { | ||
143 | if (!capable(CAP_SYS_ADMIN)) | ||
144 | return -EPERM; | ||
145 | } | 103 | } |
104 | fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id; | ||
105 | up_read(&sb_dqopt(sb)->dqptr_sem); | ||
106 | if (copy_to_user(addr, &fmt, sizeof(fmt))) | ||
107 | return -EFAULT; | ||
108 | return 0; | ||
109 | } | ||
146 | 110 | ||
111 | static int quota_getinfo(struct super_block *sb, int type, void __user *addr) | ||
112 | { | ||
113 | struct if_dqinfo info; | ||
114 | int ret; | ||
115 | |||
116 | if (!sb_has_quota_active(sb, type)) | ||
117 | return -ESRCH; | ||
118 | if (!sb->s_qcop->get_info) | ||
119 | return -ENOSYS; | ||
120 | ret = sb->s_qcop->get_info(sb, type, &info); | ||
121 | if (!ret && copy_to_user(addr, &info, sizeof(info))) | ||
122 | return -EFAULT; | ||
123 | return ret; | ||
124 | } | ||
125 | |||
126 | static int quota_setinfo(struct super_block *sb, int type, void __user *addr) | ||
127 | { | ||
128 | struct if_dqinfo info; | ||
129 | |||
130 | if (copy_from_user(&info, addr, sizeof(info))) | ||
131 | return -EFAULT; | ||
132 | if (!sb_has_quota_active(sb, type)) | ||
133 | return -ESRCH; | ||
134 | if (!sb->s_qcop->set_info) | ||
135 | return -ENOSYS; | ||
136 | return sb->s_qcop->set_info(sb, type, &info); | ||
137 | } | ||
138 | |||
139 | static int quota_getquota(struct super_block *sb, int type, qid_t id, | ||
140 | void __user *addr) | ||
141 | { | ||
142 | struct if_dqblk idq; | ||
143 | int ret; | ||
144 | |||
145 | if (!sb_has_quota_active(sb, type)) | ||
146 | return -ESRCH; | ||
147 | if (!sb->s_qcop->get_dqblk) | ||
148 | return -ENOSYS; | ||
149 | ret = sb->s_qcop->get_dqblk(sb, type, id, &idq); | ||
150 | if (ret) | ||
151 | return ret; | ||
152 | if (copy_to_user(addr, &idq, sizeof(idq))) | ||
153 | return -EFAULT; | ||
147 | return 0; | 154 | return 0; |
148 | } | 155 | } |
149 | 156 | ||
150 | static int check_quotactl_valid(struct super_block *sb, int type, int cmd, | 157 | static int quota_setquota(struct super_block *sb, int type, qid_t id, |
151 | qid_t id) | 158 | void __user *addr) |
152 | { | 159 | { |
153 | int error; | 160 | struct if_dqblk idq; |
154 | 161 | ||
155 | if (XQM_COMMAND(cmd)) | 162 | if (copy_from_user(&idq, addr, sizeof(idq))) |
156 | error = xqm_quotactl_valid(sb, type, cmd, id); | 163 | return -EFAULT; |
157 | else | 164 | if (!sb_has_quota_active(sb, type)) |
158 | error = generic_quotactl_valid(sb, type, cmd, id); | 165 | return -ESRCH; |
159 | if (!error) | 166 | if (!sb->s_qcop->set_dqblk) |
160 | error = security_quotactl(cmd, type, id, sb); | 167 | return -ENOSYS; |
161 | return error; | 168 | return sb->s_qcop->set_dqblk(sb, type, id, &idq); |
162 | } | 169 | } |
163 | 170 | ||
164 | #ifdef CONFIG_QUOTA | 171 | static int quota_setxstate(struct super_block *sb, int cmd, void __user *addr) |
165 | void sync_quota_sb(struct super_block *sb, int type) | ||
166 | { | 172 | { |
167 | int cnt; | 173 | __u32 flags; |
168 | 174 | ||
169 | if (!sb->s_qcop->quota_sync) | 175 | if (copy_from_user(&flags, addr, sizeof(flags))) |
170 | return; | 176 | return -EFAULT; |
177 | if (!sb->s_qcop->set_xstate) | ||
178 | return -ENOSYS; | ||
179 | return sb->s_qcop->set_xstate(sb, flags, cmd); | ||
180 | } | ||
171 | 181 | ||
172 | sb->s_qcop->quota_sync(sb, type); | 182 | static int quota_getxstate(struct super_block *sb, void __user *addr) |
183 | { | ||
184 | struct fs_quota_stat fqs; | ||
185 | int ret; | ||
173 | 186 | ||
174 | if (sb_dqopt(sb)->flags & DQUOT_QUOTA_SYS_FILE) | 187 | if (!sb->s_qcop->get_xstate) |
175 | return; | 188 | return -ENOSYS; |
176 | /* This is not very clever (and fast) but currently I don't know about | 189 | ret = sb->s_qcop->get_xstate(sb, &fqs); |
177 | * any other simple way of getting quota data to disk and we must get | 190 | if (!ret && copy_to_user(addr, &fqs, sizeof(fqs))) |
178 | * them there for userspace to be visible... */ | 191 | return -EFAULT; |
179 | if (sb->s_op->sync_fs) | 192 | return ret; |
180 | sb->s_op->sync_fs(sb, 1); | 193 | } |
181 | sync_blockdev(sb->s_bdev); | ||
182 | 194 | ||
183 | /* | 195 | static int quota_setxquota(struct super_block *sb, int type, qid_t id, |
184 | * Now when everything is written we can discard the pagecache so | 196 | void __user *addr) |
185 | * that userspace sees the changes. | 197 | { |
186 | */ | 198 | struct fs_disk_quota fdq; |
187 | mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); | 199 | |
188 | for (cnt = 0; cnt < MAXQUOTAS; cnt++) { | 200 | if (copy_from_user(&fdq, addr, sizeof(fdq))) |
189 | if (type != -1 && cnt != type) | 201 | return -EFAULT; |
190 | continue; | 202 | if (!sb->s_qcop->set_xquota) |
191 | if (!sb_has_quota_active(sb, cnt)) | 203 | return -ENOSYS; |
192 | continue; | 204 | return sb->s_qcop->set_xquota(sb, type, id, &fdq); |
193 | mutex_lock_nested(&sb_dqopt(sb)->files[cnt]->i_mutex, | ||
194 | I_MUTEX_QUOTA); | ||
195 | truncate_inode_pages(&sb_dqopt(sb)->files[cnt]->i_data, 0); | ||
196 | mutex_unlock(&sb_dqopt(sb)->files[cnt]->i_mutex); | ||
197 | } | ||
198 | mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); | ||
199 | } | 205 | } |
200 | #endif | ||
201 | 206 | ||
202 | static void sync_dquots(int type) | 207 | static int quota_getxquota(struct super_block *sb, int type, qid_t id, |
208 | void __user *addr) | ||
203 | { | 209 | { |
204 | struct super_block *sb; | 210 | struct fs_disk_quota fdq; |
205 | int cnt; | 211 | int ret; |
206 | 212 | ||
207 | spin_lock(&sb_lock); | 213 | if (!sb->s_qcop->get_xquota) |
208 | restart: | 214 | return -ENOSYS; |
209 | list_for_each_entry(sb, &super_blocks, s_list) { | 215 | ret = sb->s_qcop->get_xquota(sb, type, id, &fdq); |
210 | /* This test just improves performance so it needn't be | 216 | if (!ret && copy_to_user(addr, &fdq, sizeof(fdq))) |
211 | * reliable... */ | 217 | return -EFAULT; |
212 | for (cnt = 0; cnt < MAXQUOTAS; cnt++) { | 218 | return ret; |
213 | if (type != -1 && type != cnt) | ||
214 | continue; | ||
215 | if (!sb_has_quota_active(sb, cnt)) | ||
216 | continue; | ||
217 | if (!info_dirty(&sb_dqopt(sb)->info[cnt]) && | ||
218 | list_empty(&sb_dqopt(sb)->info[cnt].dqi_dirty_list)) | ||
219 | continue; | ||
220 | break; | ||
221 | } | ||
222 | if (cnt == MAXQUOTAS) | ||
223 | continue; | ||
224 | sb->s_count++; | ||
225 | spin_unlock(&sb_lock); | ||
226 | down_read(&sb->s_umount); | ||
227 | if (sb->s_root) | ||
228 | sync_quota_sb(sb, type); | ||
229 | up_read(&sb->s_umount); | ||
230 | spin_lock(&sb_lock); | ||
231 | if (__put_super_and_need_restart(sb)) | ||
232 | goto restart; | ||
233 | } | ||
234 | spin_unlock(&sb_lock); | ||
235 | } | 219 | } |
236 | 220 | ||
237 | /* Copy parameters and call proper function */ | 221 | /* Copy parameters and call proper function */ |
@@ -240,117 +224,55 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, | |||
240 | { | 224 | { |
241 | int ret; | 225 | int ret; |
242 | 226 | ||
227 | if (type >= (XQM_COMMAND(cmd) ? XQM_MAXQUOTAS : MAXQUOTAS)) | ||
228 | return -EINVAL; | ||
229 | if (!sb->s_qcop) | ||
230 | return -ENOSYS; | ||
231 | |||
232 | ret = check_quotactl_permission(sb, type, cmd, id); | ||
233 | if (ret < 0) | ||
234 | return ret; | ||
235 | |||
243 | switch (cmd) { | 236 | switch (cmd) { |
244 | case Q_QUOTAON: { | 237 | case Q_QUOTAON: |
245 | char *pathname; | 238 | return quota_quotaon(sb, type, cmd, id, addr); |
246 | 239 | case Q_QUOTAOFF: | |
247 | pathname = getname(addr); | 240 | if (!sb->s_qcop->quota_off) |
248 | if (IS_ERR(pathname)) | 241 | return -ENOSYS; |
249 | return PTR_ERR(pathname); | 242 | return sb->s_qcop->quota_off(sb, type, 0); |
250 | ret = sb->s_qcop->quota_on(sb, type, id, pathname, 0); | 243 | case Q_GETFMT: |
251 | putname(pathname); | 244 | return quota_getfmt(sb, type, addr); |
252 | return ret; | 245 | case Q_GETINFO: |
253 | } | 246 | return quota_getinfo(sb, type, addr); |
254 | case Q_QUOTAOFF: | 247 | case Q_SETINFO: |
255 | return sb->s_qcop->quota_off(sb, type, 0); | 248 | return quota_setinfo(sb, type, addr); |
256 | 249 | case Q_GETQUOTA: | |
257 | case Q_GETFMT: { | 250 | return quota_getquota(sb, type, id, addr); |
258 | __u32 fmt; | 251 | case Q_SETQUOTA: |
259 | 252 | return quota_setquota(sb, type, id, addr); | |
260 | down_read(&sb_dqopt(sb)->dqptr_sem); | 253 | case Q_SYNC: |
261 | if (!sb_has_quota_active(sb, type)) { | 254 | if (!sb->s_qcop->quota_sync) |
262 | up_read(&sb_dqopt(sb)->dqptr_sem); | 255 | return -ENOSYS; |
263 | return -ESRCH; | 256 | return sb->s_qcop->quota_sync(sb, type, 1); |
264 | } | 257 | case Q_XQUOTAON: |
265 | fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id; | 258 | case Q_XQUOTAOFF: |
266 | up_read(&sb_dqopt(sb)->dqptr_sem); | 259 | case Q_XQUOTARM: |
267 | if (copy_to_user(addr, &fmt, sizeof(fmt))) | 260 | return quota_setxstate(sb, cmd, addr); |
268 | return -EFAULT; | 261 | case Q_XGETQSTAT: |
269 | return 0; | 262 | return quota_getxstate(sb, addr); |
270 | } | 263 | case Q_XSETQLIM: |
271 | case Q_GETINFO: { | 264 | return quota_setxquota(sb, type, id, addr); |
272 | struct if_dqinfo info; | 265 | case Q_XGETQUOTA: |
273 | 266 | return quota_getxquota(sb, type, id, addr); | |
274 | ret = sb->s_qcop->get_info(sb, type, &info); | 267 | case Q_XQUOTASYNC: |
275 | if (ret) | 268 | /* caller already holds s_umount */ |
276 | return ret; | 269 | if (sb->s_flags & MS_RDONLY) |
277 | if (copy_to_user(addr, &info, sizeof(info))) | 270 | return -EROFS; |
278 | return -EFAULT; | 271 | writeback_inodes_sb(sb); |
279 | return 0; | 272 | return 0; |
280 | } | 273 | default: |
281 | case Q_SETINFO: { | 274 | return -EINVAL; |
282 | struct if_dqinfo info; | ||
283 | |||
284 | if (copy_from_user(&info, addr, sizeof(info))) | ||
285 | return -EFAULT; | ||
286 | return sb->s_qcop->set_info(sb, type, &info); | ||
287 | } | ||
288 | case Q_GETQUOTA: { | ||
289 | struct if_dqblk idq; | ||
290 | |||
291 | ret = sb->s_qcop->get_dqblk(sb, type, id, &idq); | ||
292 | if (ret) | ||
293 | return ret; | ||
294 | if (copy_to_user(addr, &idq, sizeof(idq))) | ||
295 | return -EFAULT; | ||
296 | return 0; | ||
297 | } | ||
298 | case Q_SETQUOTA: { | ||
299 | struct if_dqblk idq; | ||
300 | |||
301 | if (copy_from_user(&idq, addr, sizeof(idq))) | ||
302 | return -EFAULT; | ||
303 | return sb->s_qcop->set_dqblk(sb, type, id, &idq); | ||
304 | } | ||
305 | case Q_SYNC: | ||
306 | if (sb) | ||
307 | sync_quota_sb(sb, type); | ||
308 | else | ||
309 | sync_dquots(type); | ||
310 | return 0; | ||
311 | |||
312 | case Q_XQUOTAON: | ||
313 | case Q_XQUOTAOFF: | ||
314 | case Q_XQUOTARM: { | ||
315 | __u32 flags; | ||
316 | |||
317 | if (copy_from_user(&flags, addr, sizeof(flags))) | ||
318 | return -EFAULT; | ||
319 | return sb->s_qcop->set_xstate(sb, flags, cmd); | ||
320 | } | ||
321 | case Q_XGETQSTAT: { | ||
322 | struct fs_quota_stat fqs; | ||
323 | |||
324 | if ((ret = sb->s_qcop->get_xstate(sb, &fqs))) | ||
325 | return ret; | ||
326 | if (copy_to_user(addr, &fqs, sizeof(fqs))) | ||
327 | return -EFAULT; | ||
328 | return 0; | ||
329 | } | ||
330 | case Q_XSETQLIM: { | ||
331 | struct fs_disk_quota fdq; | ||
332 | |||
333 | if (copy_from_user(&fdq, addr, sizeof(fdq))) | ||
334 | return -EFAULT; | ||
335 | return sb->s_qcop->set_xquota(sb, type, id, &fdq); | ||
336 | } | ||
337 | case Q_XGETQUOTA: { | ||
338 | struct fs_disk_quota fdq; | ||
339 | |||
340 | ret = sb->s_qcop->get_xquota(sb, type, id, &fdq); | ||
341 | if (ret) | ||
342 | return ret; | ||
343 | if (copy_to_user(addr, &fdq, sizeof(fdq))) | ||
344 | return -EFAULT; | ||
345 | return 0; | ||
346 | } | ||
347 | case Q_XQUOTASYNC: | ||
348 | return sb->s_qcop->quota_sync(sb, type); | ||
349 | /* We never reach here unless validity check is broken */ | ||
350 | default: | ||
351 | BUG(); | ||
352 | } | 275 | } |
353 | return 0; | ||
354 | } | 276 | } |
355 | 277 | ||
356 | /* | 278 | /* |
@@ -397,224 +319,23 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special, | |||
397 | cmds = cmd >> SUBCMDSHIFT; | 319 | cmds = cmd >> SUBCMDSHIFT; |
398 | type = cmd & SUBCMDMASK; | 320 | type = cmd & SUBCMDMASK; |
399 | 321 | ||
400 | if (cmds != Q_SYNC || special) { | 322 | /* |
401 | sb = quotactl_block(special); | 323 | * As a special case Q_SYNC can be called without a specific device. |
402 | if (IS_ERR(sb)) | 324 | * It will iterate all superblocks that have quota enabled and call |
403 | return PTR_ERR(sb); | 325 | * the sync action on each of them. |
326 | */ | ||
327 | if (!special) { | ||
328 | if (cmds == Q_SYNC) | ||
329 | return quota_sync_all(type); | ||
330 | return -ENODEV; | ||
404 | } | 331 | } |
405 | 332 | ||
406 | ret = check_quotactl_valid(sb, type, cmds, id); | 333 | sb = quotactl_block(special); |
407 | if (ret >= 0) | 334 | if (IS_ERR(sb)) |
408 | ret = do_quotactl(sb, type, cmds, id, addr); | 335 | return PTR_ERR(sb); |
409 | if (sb) | ||
410 | drop_super(sb); | ||
411 | 336 | ||
412 | return ret; | 337 | ret = do_quotactl(sb, type, cmds, id, addr); |
413 | } | ||
414 | |||
415 | #if defined(CONFIG_COMPAT_FOR_U64_ALIGNMENT) | ||
416 | /* | ||
417 | * This code works only for 32 bit quota tools over 64 bit OS (x86_64, ia64) | ||
418 | * and is necessary due to alignment problems. | ||
419 | */ | ||
420 | struct compat_if_dqblk { | ||
421 | compat_u64 dqb_bhardlimit; | ||
422 | compat_u64 dqb_bsoftlimit; | ||
423 | compat_u64 dqb_curspace; | ||
424 | compat_u64 dqb_ihardlimit; | ||
425 | compat_u64 dqb_isoftlimit; | ||
426 | compat_u64 dqb_curinodes; | ||
427 | compat_u64 dqb_btime; | ||
428 | compat_u64 dqb_itime; | ||
429 | compat_uint_t dqb_valid; | ||
430 | }; | ||
431 | |||
432 | /* XFS structures */ | ||
433 | struct compat_fs_qfilestat { | ||
434 | compat_u64 dqb_bhardlimit; | ||
435 | compat_u64 qfs_nblks; | ||
436 | compat_uint_t qfs_nextents; | ||
437 | }; | ||
438 | |||
439 | struct compat_fs_quota_stat { | ||
440 | __s8 qs_version; | ||
441 | __u16 qs_flags; | ||
442 | __s8 qs_pad; | ||
443 | struct compat_fs_qfilestat qs_uquota; | ||
444 | struct compat_fs_qfilestat qs_gquota; | ||
445 | compat_uint_t qs_incoredqs; | ||
446 | compat_int_t qs_btimelimit; | ||
447 | compat_int_t qs_itimelimit; | ||
448 | compat_int_t qs_rtbtimelimit; | ||
449 | __u16 qs_bwarnlimit; | ||
450 | __u16 qs_iwarnlimit; | ||
451 | }; | ||
452 | |||
453 | asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special, | ||
454 | qid_t id, void __user *addr) | ||
455 | { | ||
456 | unsigned int cmds; | ||
457 | struct if_dqblk __user *dqblk; | ||
458 | struct compat_if_dqblk __user *compat_dqblk; | ||
459 | struct fs_quota_stat __user *fsqstat; | ||
460 | struct compat_fs_quota_stat __user *compat_fsqstat; | ||
461 | compat_uint_t data; | ||
462 | u16 xdata; | ||
463 | long ret; | ||
464 | 338 | ||
465 | cmds = cmd >> SUBCMDSHIFT; | 339 | drop_super(sb); |
466 | |||
467 | switch (cmds) { | ||
468 | case Q_GETQUOTA: | ||
469 | dqblk = compat_alloc_user_space(sizeof(struct if_dqblk)); | ||
470 | compat_dqblk = addr; | ||
471 | ret = sys_quotactl(cmd, special, id, dqblk); | ||
472 | if (ret) | ||
473 | break; | ||
474 | if (copy_in_user(compat_dqblk, dqblk, sizeof(*compat_dqblk)) || | ||
475 | get_user(data, &dqblk->dqb_valid) || | ||
476 | put_user(data, &compat_dqblk->dqb_valid)) | ||
477 | ret = -EFAULT; | ||
478 | break; | ||
479 | case Q_SETQUOTA: | ||
480 | dqblk = compat_alloc_user_space(sizeof(struct if_dqblk)); | ||
481 | compat_dqblk = addr; | ||
482 | ret = -EFAULT; | ||
483 | if (copy_in_user(dqblk, compat_dqblk, sizeof(*compat_dqblk)) || | ||
484 | get_user(data, &compat_dqblk->dqb_valid) || | ||
485 | put_user(data, &dqblk->dqb_valid)) | ||
486 | break; | ||
487 | ret = sys_quotactl(cmd, special, id, dqblk); | ||
488 | break; | ||
489 | case Q_XGETQSTAT: | ||
490 | fsqstat = compat_alloc_user_space(sizeof(struct fs_quota_stat)); | ||
491 | compat_fsqstat = addr; | ||
492 | ret = sys_quotactl(cmd, special, id, fsqstat); | ||
493 | if (ret) | ||
494 | break; | ||
495 | ret = -EFAULT; | ||
496 | /* Copying qs_version, qs_flags, qs_pad */ | ||
497 | if (copy_in_user(compat_fsqstat, fsqstat, | ||
498 | offsetof(struct compat_fs_quota_stat, qs_uquota))) | ||
499 | break; | ||
500 | /* Copying qs_uquota */ | ||
501 | if (copy_in_user(&compat_fsqstat->qs_uquota, | ||
502 | &fsqstat->qs_uquota, | ||
503 | sizeof(compat_fsqstat->qs_uquota)) || | ||
504 | get_user(data, &fsqstat->qs_uquota.qfs_nextents) || | ||
505 | put_user(data, &compat_fsqstat->qs_uquota.qfs_nextents)) | ||
506 | break; | ||
507 | /* Copying qs_gquota */ | ||
508 | if (copy_in_user(&compat_fsqstat->qs_gquota, | ||
509 | &fsqstat->qs_gquota, | ||
510 | sizeof(compat_fsqstat->qs_gquota)) || | ||
511 | get_user(data, &fsqstat->qs_gquota.qfs_nextents) || | ||
512 | put_user(data, &compat_fsqstat->qs_gquota.qfs_nextents)) | ||
513 | break; | ||
514 | /* Copying the rest */ | ||
515 | if (copy_in_user(&compat_fsqstat->qs_incoredqs, | ||
516 | &fsqstat->qs_incoredqs, | ||
517 | sizeof(struct compat_fs_quota_stat) - | ||
518 | offsetof(struct compat_fs_quota_stat, qs_incoredqs)) || | ||
519 | get_user(xdata, &fsqstat->qs_iwarnlimit) || | ||
520 | put_user(xdata, &compat_fsqstat->qs_iwarnlimit)) | ||
521 | break; | ||
522 | ret = 0; | ||
523 | break; | ||
524 | default: | ||
525 | ret = sys_quotactl(cmd, special, id, addr); | ||
526 | } | ||
527 | return ret; | 340 | return ret; |
528 | } | 341 | } |
529 | #endif | ||
530 | |||
531 | |||
532 | #ifdef CONFIG_QUOTA_NETLINK_INTERFACE | ||
533 | |||
534 | /* Netlink family structure for quota */ | ||
535 | static struct genl_family quota_genl_family = { | ||
536 | .id = GENL_ID_GENERATE, | ||
537 | .hdrsize = 0, | ||
538 | .name = "VFS_DQUOT", | ||
539 | .version = 1, | ||
540 | .maxattr = QUOTA_NL_A_MAX, | ||
541 | }; | ||
542 | |||
543 | /** | ||
544 | * quota_send_warning - Send warning to userspace about exceeded quota | ||
545 | * @type: The quota type: USRQQUOTA, GRPQUOTA,... | ||
546 | * @id: The user or group id of the quota that was exceeded | ||
547 | * @dev: The device on which the fs is mounted (sb->s_dev) | ||
548 | * @warntype: The type of the warning: QUOTA_NL_... | ||
549 | * | ||
550 | * This can be used by filesystems (including those which don't use | ||
551 | * dquot) to send a message to userspace relating to quota limits. | ||
552 | * | ||
553 | */ | ||
554 | |||
555 | void quota_send_warning(short type, unsigned int id, dev_t dev, | ||
556 | const char warntype) | ||
557 | { | ||
558 | static atomic_t seq; | ||
559 | struct sk_buff *skb; | ||
560 | void *msg_head; | ||
561 | int ret; | ||
562 | int msg_size = 4 * nla_total_size(sizeof(u32)) + | ||
563 | 2 * nla_total_size(sizeof(u64)); | ||
564 | |||
565 | /* We have to allocate using GFP_NOFS as we are called from a | ||
566 | * filesystem performing write and thus further recursion into | ||
567 | * the fs to free some data could cause deadlocks. */ | ||
568 | skb = genlmsg_new(msg_size, GFP_NOFS); | ||
569 | if (!skb) { | ||
570 | printk(KERN_ERR | ||
571 | "VFS: Not enough memory to send quota warning.\n"); | ||
572 | return; | ||
573 | } | ||
574 | msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq), | ||
575 | "a_genl_family, 0, QUOTA_NL_C_WARNING); | ||
576 | if (!msg_head) { | ||
577 | printk(KERN_ERR | ||
578 | "VFS: Cannot store netlink header in quota warning.\n"); | ||
579 | goto err_out; | ||
580 | } | ||
581 | ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, type); | ||
582 | if (ret) | ||
583 | goto attr_err_out; | ||
584 | ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID, id); | ||
585 | if (ret) | ||
586 | goto attr_err_out; | ||
587 | ret = nla_put_u32(skb, QUOTA_NL_A_WARNING, warntype); | ||
588 | if (ret) | ||
589 | goto attr_err_out; | ||
590 | ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MAJOR, MAJOR(dev)); | ||
591 | if (ret) | ||
592 | goto attr_err_out; | ||
593 | ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MINOR, MINOR(dev)); | ||
594 | if (ret) | ||
595 | goto attr_err_out; | ||
596 | ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current_uid()); | ||
597 | if (ret) | ||
598 | goto attr_err_out; | ||
599 | genlmsg_end(skb, msg_head); | ||
600 | |||
601 | genlmsg_multicast(skb, 0, quota_genl_family.id, GFP_NOFS); | ||
602 | return; | ||
603 | attr_err_out: | ||
604 | printk(KERN_ERR "VFS: Not enough space to compose quota message!\n"); | ||
605 | err_out: | ||
606 | kfree_skb(skb); | ||
607 | } | ||
608 | EXPORT_SYMBOL(quota_send_warning); | ||
609 | |||
610 | static int __init quota_init(void) | ||
611 | { | ||
612 | if (genl_register_family("a_genl_family) != 0) | ||
613 | printk(KERN_ERR | ||
614 | "VFS: Failed to create quota netlink interface.\n"); | ||
615 | return 0; | ||
616 | }; | ||
617 | |||
618 | module_init(quota_init); | ||
619 | #endif | ||
620 | |||