Contents of /alx-src/tags/kernel26-2.6.12-alx-r9/fs/quota.c
Parent Directory
|
Revision Log
Revision 630 -
(show annotations)
(download)
Wed Mar 4 11:03:09 2009 UTC (15 years, 3 months ago) by niro
File MIME type: text/plain
File size: 9028 byte(s)
Wed Mar 4 11:03:09 2009 UTC (15 years, 3 months ago) by niro
File MIME type: text/plain
File size: 9028 byte(s)
Tag kernel26-2.6.12-alx-r9
1 | /* |
2 | * Quota code necessary even when VFS quota support is not compiled |
3 | * into the kernel. The interesting stuff is over in dquot.c, here |
4 | * we have symbols for initial quotactl(2) handling, the sysctl(2) |
5 | * variables, etc - things needed even when quota support disabled. |
6 | */ |
7 | |
8 | #include <linux/fs.h> |
9 | #include <linux/namei.h> |
10 | #include <linux/slab.h> |
11 | #include <asm/current.h> |
12 | #include <asm/uaccess.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/smp_lock.h> |
15 | #include <linux/security.h> |
16 | #include <linux/syscalls.h> |
17 | #include <linux/buffer_head.h> |
18 | |
19 | /* Check validity of generic quotactl commands */ |
20 | static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id) |
21 | { |
22 | if (type >= MAXQUOTAS) |
23 | return -EINVAL; |
24 | if (!sb && cmd != Q_SYNC) |
25 | return -ENODEV; |
26 | /* Is operation supported? */ |
27 | if (sb && !sb->s_qcop) |
28 | return -ENOSYS; |
29 | |
30 | switch (cmd) { |
31 | case Q_GETFMT: |
32 | break; |
33 | case Q_QUOTAON: |
34 | if (!sb->s_qcop->quota_on) |
35 | return -ENOSYS; |
36 | break; |
37 | case Q_QUOTAOFF: |
38 | if (!sb->s_qcop->quota_off) |
39 | return -ENOSYS; |
40 | break; |
41 | case Q_SETINFO: |
42 | if (!sb->s_qcop->set_info) |
43 | return -ENOSYS; |
44 | break; |
45 | case Q_GETINFO: |
46 | if (!sb->s_qcop->get_info) |
47 | return -ENOSYS; |
48 | break; |
49 | case Q_SETQUOTA: |
50 | if (!sb->s_qcop->set_dqblk) |
51 | return -ENOSYS; |
52 | break; |
53 | case Q_GETQUOTA: |
54 | if (!sb->s_qcop->get_dqblk) |
55 | return -ENOSYS; |
56 | break; |
57 | case Q_SYNC: |
58 | if (sb && !sb->s_qcop->quota_sync) |
59 | return -ENOSYS; |
60 | break; |
61 | default: |
62 | return -EINVAL; |
63 | } |
64 | |
65 | /* Is quota turned on for commands which need it? */ |
66 | switch (cmd) { |
67 | case Q_GETFMT: |
68 | case Q_GETINFO: |
69 | case Q_QUOTAOFF: |
70 | case Q_SETINFO: |
71 | case Q_SETQUOTA: |
72 | case Q_GETQUOTA: |
73 | /* This is just informative test so we are satisfied without a lock */ |
74 | if (!sb_has_quota_enabled(sb, type)) |
75 | return -ESRCH; |
76 | } |
77 | |
78 | /* Check privileges */ |
79 | if (cmd == Q_GETQUOTA) { |
80 | if (((type == USRQUOTA && current->euid != id) || |
81 | (type == GRPQUOTA && !in_egroup_p(id))) && |
82 | !capable(CAP_SYS_ADMIN)) |
83 | return -EPERM; |
84 | } |
85 | else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO) |
86 | if (!capable(CAP_SYS_ADMIN)) |
87 | return -EPERM; |
88 | |
89 | return 0; |
90 | } |
91 | |
92 | /* Check validity of XFS Quota Manager commands */ |
93 | static int xqm_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id) |
94 | { |
95 | if (type >= XQM_MAXQUOTAS) |
96 | return -EINVAL; |
97 | if (!sb) |
98 | return -ENODEV; |
99 | if (!sb->s_qcop) |
100 | return -ENOSYS; |
101 | |
102 | switch (cmd) { |
103 | case Q_XQUOTAON: |
104 | case Q_XQUOTAOFF: |
105 | case Q_XQUOTARM: |
106 | if (!sb->s_qcop->set_xstate) |
107 | return -ENOSYS; |
108 | break; |
109 | case Q_XGETQSTAT: |
110 | if (!sb->s_qcop->get_xstate) |
111 | return -ENOSYS; |
112 | break; |
113 | case Q_XSETQLIM: |
114 | if (!sb->s_qcop->set_xquota) |
115 | return -ENOSYS; |
116 | break; |
117 | case Q_XGETQUOTA: |
118 | if (!sb->s_qcop->get_xquota) |
119 | return -ENOSYS; |
120 | break; |
121 | default: |
122 | return -EINVAL; |
123 | } |
124 | |
125 | /* Check privileges */ |
126 | if (cmd == Q_XGETQUOTA) { |
127 | if (((type == XQM_USRQUOTA && current->euid != id) || |
128 | (type == XQM_GRPQUOTA && !in_egroup_p(id))) && |
129 | !capable(CAP_SYS_ADMIN)) |
130 | return -EPERM; |
131 | } else if (cmd != Q_XGETQSTAT) { |
132 | if (!capable(CAP_SYS_ADMIN)) |
133 | return -EPERM; |
134 | } |
135 | |
136 | return 0; |
137 | } |
138 | |
139 | static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id) |
140 | { |
141 | int error; |
142 | |
143 | if (XQM_COMMAND(cmd)) |
144 | error = xqm_quotactl_valid(sb, type, cmd, id); |
145 | else |
146 | error = generic_quotactl_valid(sb, type, cmd, id); |
147 | if (!error) |
148 | error = security_quotactl(cmd, type, id, sb); |
149 | return error; |
150 | } |
151 | |
152 | static struct super_block *get_super_to_sync(int type) |
153 | { |
154 | struct list_head *head; |
155 | int cnt, dirty; |
156 | |
157 | restart: |
158 | spin_lock(&sb_lock); |
159 | list_for_each(head, &super_blocks) { |
160 | struct super_block *sb = list_entry(head, struct super_block, s_list); |
161 | |
162 | /* This test just improves performance so it needn't be reliable... */ |
163 | for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++) |
164 | if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, cnt) |
165 | && info_any_dirty(&sb_dqopt(sb)->info[cnt])) |
166 | dirty = 1; |
167 | if (!dirty) |
168 | continue; |
169 | sb->s_count++; |
170 | spin_unlock(&sb_lock); |
171 | down_read(&sb->s_umount); |
172 | if (!sb->s_root) { |
173 | drop_super(sb); |
174 | goto restart; |
175 | } |
176 | return sb; |
177 | } |
178 | spin_unlock(&sb_lock); |
179 | return NULL; |
180 | } |
181 | |
182 | static void quota_sync_sb(struct super_block *sb, int type) |
183 | { |
184 | int cnt; |
185 | struct inode *discard[MAXQUOTAS]; |
186 | |
187 | sb->s_qcop->quota_sync(sb, type); |
188 | /* This is not very clever (and fast) but currently I don't know about |
189 | * any other simple way of getting quota data to disk and we must get |
190 | * them there for userspace to be visible... */ |
191 | if (sb->s_op->sync_fs) |
192 | sb->s_op->sync_fs(sb, 1); |
193 | sync_blockdev(sb->s_bdev); |
194 | |
195 | /* Now when everything is written we can discard the pagecache so |
196 | * that userspace sees the changes. We need i_sem and so we could |
197 | * not do it inside dqonoff_sem. Moreover we need to be carefull |
198 | * about races with quotaoff() (that is the reason why we have own |
199 | * reference to inode). */ |
200 | down(&sb_dqopt(sb)->dqonoff_sem); |
201 | for (cnt = 0; cnt < MAXQUOTAS; cnt++) { |
202 | discard[cnt] = NULL; |
203 | if (type != -1 && cnt != type) |
204 | continue; |
205 | if (!sb_has_quota_enabled(sb, cnt)) |
206 | continue; |
207 | discard[cnt] = igrab(sb_dqopt(sb)->files[cnt]); |
208 | } |
209 | up(&sb_dqopt(sb)->dqonoff_sem); |
210 | for (cnt = 0; cnt < MAXQUOTAS; cnt++) { |
211 | if (discard[cnt]) { |
212 | down(&discard[cnt]->i_sem); |
213 | truncate_inode_pages(&discard[cnt]->i_data, 0); |
214 | up(&discard[cnt]->i_sem); |
215 | iput(discard[cnt]); |
216 | } |
217 | } |
218 | } |
219 | |
220 | void sync_dquots(struct super_block *sb, int type) |
221 | { |
222 | if (sb) { |
223 | if (sb->s_qcop->quota_sync) |
224 | quota_sync_sb(sb, type); |
225 | } |
226 | else { |
227 | while ((sb = get_super_to_sync(type)) != NULL) { |
228 | if (sb->s_qcop->quota_sync) |
229 | quota_sync_sb(sb, type); |
230 | drop_super(sb); |
231 | } |
232 | } |
233 | } |
234 | |
235 | /* Copy parameters and call proper function */ |
236 | static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void __user *addr) |
237 | { |
238 | int ret; |
239 | |
240 | switch (cmd) { |
241 | case Q_QUOTAON: { |
242 | char *pathname; |
243 | |
244 | if (IS_ERR(pathname = getname(addr))) |
245 | return PTR_ERR(pathname); |
246 | ret = sb->s_qcop->quota_on(sb, type, id, pathname); |
247 | putname(pathname); |
248 | return ret; |
249 | } |
250 | case Q_QUOTAOFF: |
251 | return sb->s_qcop->quota_off(sb, type); |
252 | |
253 | case Q_GETFMT: { |
254 | __u32 fmt; |
255 | |
256 | down_read(&sb_dqopt(sb)->dqptr_sem); |
257 | if (!sb_has_quota_enabled(sb, type)) { |
258 | up_read(&sb_dqopt(sb)->dqptr_sem); |
259 | return -ESRCH; |
260 | } |
261 | fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id; |
262 | up_read(&sb_dqopt(sb)->dqptr_sem); |
263 | if (copy_to_user(addr, &fmt, sizeof(fmt))) |
264 | return -EFAULT; |
265 | return 0; |
266 | } |
267 | case Q_GETINFO: { |
268 | struct if_dqinfo info; |
269 | |
270 | if ((ret = sb->s_qcop->get_info(sb, type, &info))) |
271 | return ret; |
272 | if (copy_to_user(addr, &info, sizeof(info))) |
273 | return -EFAULT; |
274 | return 0; |
275 | } |
276 | case Q_SETINFO: { |
277 | struct if_dqinfo info; |
278 | |
279 | if (copy_from_user(&info, addr, sizeof(info))) |
280 | return -EFAULT; |
281 | return sb->s_qcop->set_info(sb, type, &info); |
282 | } |
283 | case Q_GETQUOTA: { |
284 | struct if_dqblk idq; |
285 | |
286 | if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq))) |
287 | return ret; |
288 | if (copy_to_user(addr, &idq, sizeof(idq))) |
289 | return -EFAULT; |
290 | return 0; |
291 | } |
292 | case Q_SETQUOTA: { |
293 | struct if_dqblk idq; |
294 | |
295 | if (copy_from_user(&idq, addr, sizeof(idq))) |
296 | return -EFAULT; |
297 | return sb->s_qcop->set_dqblk(sb, type, id, &idq); |
298 | } |
299 | case Q_SYNC: |
300 | sync_dquots(sb, type); |
301 | return 0; |
302 | |
303 | case Q_XQUOTAON: |
304 | case Q_XQUOTAOFF: |
305 | case Q_XQUOTARM: { |
306 | __u32 flags; |
307 | |
308 | if (copy_from_user(&flags, addr, sizeof(flags))) |
309 | return -EFAULT; |
310 | return sb->s_qcop->set_xstate(sb, flags, cmd); |
311 | } |
312 | case Q_XGETQSTAT: { |
313 | struct fs_quota_stat fqs; |
314 | |
315 | if ((ret = sb->s_qcop->get_xstate(sb, &fqs))) |
316 | return ret; |
317 | if (copy_to_user(addr, &fqs, sizeof(fqs))) |
318 | return -EFAULT; |
319 | return 0; |
320 | } |
321 | case Q_XSETQLIM: { |
322 | struct fs_disk_quota fdq; |
323 | |
324 | if (copy_from_user(&fdq, addr, sizeof(fdq))) |
325 | return -EFAULT; |
326 | return sb->s_qcop->set_xquota(sb, type, id, &fdq); |
327 | } |
328 | case Q_XGETQUOTA: { |
329 | struct fs_disk_quota fdq; |
330 | |
331 | if ((ret = sb->s_qcop->get_xquota(sb, type, id, &fdq))) |
332 | return ret; |
333 | if (copy_to_user(addr, &fdq, sizeof(fdq))) |
334 | return -EFAULT; |
335 | return 0; |
336 | } |
337 | /* We never reach here unless validity check is broken */ |
338 | default: |
339 | BUG(); |
340 | } |
341 | return 0; |
342 | } |
343 | |
344 | /* |
345 | * This is the system call interface. This communicates with |
346 | * the user-level programs. Currently this only supports diskquota |
347 | * calls. Maybe we need to add the process quotas etc. in the future, |
348 | * but we probably should use rlimits for that. |
349 | */ |
350 | asmlinkage long sys_quotactl(unsigned int cmd, const char __user *special, qid_t id, void __user *addr) |
351 | { |
352 | uint cmds, type; |
353 | struct super_block *sb = NULL; |
354 | struct block_device *bdev; |
355 | char *tmp; |
356 | int ret; |
357 | |
358 | cmds = cmd >> SUBCMDSHIFT; |
359 | type = cmd & SUBCMDMASK; |
360 | |
361 | if (cmds != Q_SYNC || special) { |
362 | tmp = getname(special); |
363 | if (IS_ERR(tmp)) |
364 | return PTR_ERR(tmp); |
365 | bdev = lookup_bdev(tmp); |
366 | putname(tmp); |
367 | if (IS_ERR(bdev)) |
368 | return PTR_ERR(bdev); |
369 | sb = get_super(bdev); |
370 | bdput(bdev); |
371 | if (!sb) |
372 | return -ENODEV; |
373 | } |
374 | |
375 | ret = check_quotactl_valid(sb, type, cmds, id); |
376 | if (ret >= 0) |
377 | ret = do_quotactl(sb, type, cmds, id, addr); |
378 | if (sb) |
379 | drop_super(sb); |
380 | |
381 | return ret; |
382 | } |