Blame view

fs/quota/quota.c 9.27 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * Quota code necessary even when VFS quota support is not compiled
   * into the kernel.  The interesting stuff is over in dquot.c, here
   * we have symbols for initial quotactl(2) handling, the sysctl(2)
   * variables, etc - things needed even when quota support disabled.
   */
  
  #include <linux/fs.h>
  #include <linux/namei.h>
  #include <linux/slab.h>
  #include <asm/current.h>
  #include <asm/uaccess.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
  #include <linux/security.h>
  #include <linux/syscalls.h>
16f7e0fe2   Randy Dunlap   [PATCH] capable/c...
16
  #include <linux/capability.h>
be586bab8   Adrian Bunk   [PATCH] quota: sm...
17
  #include <linux/quotaops.h>
b716395e2   Vasily Tarasov   diskquota: 32bit ...
18
  #include <linux/types.h>
8c4e4acd6   Christoph Hellwig   quota: clean up Q...
19
  #include <linux/writeback.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20

c988afb5f   Christoph Hellwig   quota: simplify p...
21
22
  static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
  				     qid_t id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
  {
c988afb5f   Christoph Hellwig   quota: simplify p...
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  	switch (cmd) {
  	/* these commands do not require any special privilegues */
  	case Q_GETFMT:
  	case Q_SYNC:
  	case Q_GETINFO:
  	case Q_XGETQSTAT:
  	case Q_XQUOTASYNC:
  		break;
  	/* allow to query information for dquots we "own" */
  	case Q_GETQUOTA:
  	case Q_XGETQUOTA:
  		if ((type == USRQUOTA && current_euid() == id) ||
  		    (type == GRPQUOTA && in_egroup_p(id)))
  			break;
  		/*FALLTHROUGH*/
  	default:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
  		if (!capable(CAP_SYS_ADMIN))
  			return -EPERM;
  	}
c988afb5f   Christoph Hellwig   quota: simplify p...
43
  	return security_quotactl(cmd, type, id, sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
  }
01a05b337   Al Viro   new helper: itera...
45
46
47
48
49
  static void quota_sync_one(struct super_block *sb, void *arg)
  {
  	if (sb->s_qcop && sb->s_qcop->quota_sync)
  		sb->s_qcop->quota_sync(sb, *(int *)arg, 1);
  }
6ae09575b   Christoph Hellwig   quota: special ca...
50
  static int quota_sync_all(int type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
  {
6ae09575b   Christoph Hellwig   quota: special ca...
52
53
54
55
56
  	int ret;
  
  	if (type >= MAXQUOTAS)
  		return -EINVAL;
  	ret = security_quotactl(Q_SYNC, type, 0, NULL);
01a05b337   Al Viro   new helper: itera...
57
58
59
  	if (!ret)
  		iterate_supers(quota_sync_one, &type);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
  }
c411e5f66   Christoph Hellwig   quota: split do_q...
61
  static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id,
f00c9e44a   Jan Kara   quota: Fix deadlo...
62
  		         struct path *path)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
  {
f00c9e44a   Jan Kara   quota: Fix deadlo...
64
65
66
67
68
69
70
  	if (!sb->s_qcop->quota_on && !sb->s_qcop->quota_on_meta)
  		return -ENOSYS;
  	if (sb->s_qcop->quota_on_meta)
  		return sb->s_qcop->quota_on_meta(sb, type, id);
  	if (IS_ERR(path))
  		return PTR_ERR(path);
  	return sb->s_qcop->quota_on(sb, type, id, path);
c411e5f66   Christoph Hellwig   quota: split do_q...
71
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72

c411e5f66   Christoph Hellwig   quota: split do_q...
73
74
75
  static int quota_getfmt(struct super_block *sb, int type, void __user *addr)
  {
  	__u32 fmt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76

c411e5f66   Christoph Hellwig   quota: split do_q...
77
78
79
80
81
82
83
84
85
86
87
  	down_read(&sb_dqopt(sb)->dqptr_sem);
  	if (!sb_has_quota_active(sb, type)) {
  		up_read(&sb_dqopt(sb)->dqptr_sem);
  		return -ESRCH;
  	}
  	fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
  	up_read(&sb_dqopt(sb)->dqptr_sem);
  	if (copy_to_user(addr, &fmt, sizeof(fmt)))
  		return -EFAULT;
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88

c411e5f66   Christoph Hellwig   quota: split do_q...
89
90
91
92
  static int quota_getinfo(struct super_block *sb, int type, void __user *addr)
  {
  	struct if_dqinfo info;
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93

f450d4fee   Christoph Hellwig   quota: clean up c...
94
95
  	if (!sb->s_qcop->get_info)
  		return -ENOSYS;
c411e5f66   Christoph Hellwig   quota: split do_q...
96
97
98
99
100
  	ret = sb->s_qcop->get_info(sb, type, &info);
  	if (!ret && copy_to_user(addr, &info, sizeof(info)))
  		return -EFAULT;
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101

c411e5f66   Christoph Hellwig   quota: split do_q...
102
103
104
  static int quota_setinfo(struct super_block *sb, int type, void __user *addr)
  {
  	struct if_dqinfo info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105

c411e5f66   Christoph Hellwig   quota: split do_q...
106
107
  	if (copy_from_user(&info, addr, sizeof(info)))
  		return -EFAULT;
f450d4fee   Christoph Hellwig   quota: clean up c...
108
109
  	if (!sb->s_qcop->set_info)
  		return -ENOSYS;
c411e5f66   Christoph Hellwig   quota: split do_q...
110
111
  	return sb->s_qcop->set_info(sb, type, &info);
  }
b9b2dd36c   Christoph Hellwig   quota: unify ->ge...
112
113
114
115
116
117
118
119
120
121
122
123
  static void copy_to_if_dqblk(struct if_dqblk *dst, struct fs_disk_quota *src)
  {
  	dst->dqb_bhardlimit = src->d_blk_hardlimit;
  	dst->dqb_bsoftlimit = src->d_blk_softlimit;
  	dst->dqb_curspace = src->d_bcount;
  	dst->dqb_ihardlimit = src->d_ino_hardlimit;
  	dst->dqb_isoftlimit = src->d_ino_softlimit;
  	dst->dqb_curinodes = src->d_icount;
  	dst->dqb_btime = src->d_btimer;
  	dst->dqb_itime = src->d_itimer;
  	dst->dqb_valid = QIF_ALL;
  }
c411e5f66   Christoph Hellwig   quota: split do_q...
124
125
126
  static int quota_getquota(struct super_block *sb, int type, qid_t id,
  			  void __user *addr)
  {
b9b2dd36c   Christoph Hellwig   quota: unify ->ge...
127
  	struct fs_disk_quota fdq;
c411e5f66   Christoph Hellwig   quota: split do_q...
128
129
  	struct if_dqblk idq;
  	int ret;
f450d4fee   Christoph Hellwig   quota: clean up c...
130
131
  	if (!sb->s_qcop->get_dqblk)
  		return -ENOSYS;
b9b2dd36c   Christoph Hellwig   quota: unify ->ge...
132
  	ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq);
c411e5f66   Christoph Hellwig   quota: split do_q...
133
134
  	if (ret)
  		return ret;
b9b2dd36c   Christoph Hellwig   quota: unify ->ge...
135
  	copy_to_if_dqblk(&idq, &fdq);
c411e5f66   Christoph Hellwig   quota: split do_q...
136
137
138
139
  	if (copy_to_user(addr, &idq, sizeof(idq)))
  		return -EFAULT;
  	return 0;
  }
c472b4327   Christoph Hellwig   quota: unify ->se...
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
  static void copy_from_if_dqblk(struct fs_disk_quota *dst, struct if_dqblk *src)
  {
  	dst->d_blk_hardlimit = src->dqb_bhardlimit;
  	dst->d_blk_softlimit  = src->dqb_bsoftlimit;
  	dst->d_bcount = src->dqb_curspace;
  	dst->d_ino_hardlimit = src->dqb_ihardlimit;
  	dst->d_ino_softlimit = src->dqb_isoftlimit;
  	dst->d_icount = src->dqb_curinodes;
  	dst->d_btimer = src->dqb_btime;
  	dst->d_itimer = src->dqb_itime;
  
  	dst->d_fieldmask = 0;
  	if (src->dqb_valid & QIF_BLIMITS)
  		dst->d_fieldmask |= FS_DQ_BSOFT | FS_DQ_BHARD;
  	if (src->dqb_valid & QIF_SPACE)
  		dst->d_fieldmask |= FS_DQ_BCOUNT;
  	if (src->dqb_valid & QIF_ILIMITS)
  		dst->d_fieldmask |= FS_DQ_ISOFT | FS_DQ_IHARD;
  	if (src->dqb_valid & QIF_INODES)
  		dst->d_fieldmask |= FS_DQ_ICOUNT;
  	if (src->dqb_valid & QIF_BTIME)
  		dst->d_fieldmask |= FS_DQ_BTIMER;
  	if (src->dqb_valid & QIF_ITIME)
  		dst->d_fieldmask |= FS_DQ_ITIMER;
  }
c411e5f66   Christoph Hellwig   quota: split do_q...
165
166
167
  static int quota_setquota(struct super_block *sb, int type, qid_t id,
  			  void __user *addr)
  {
c472b4327   Christoph Hellwig   quota: unify ->se...
168
  	struct fs_disk_quota fdq;
c411e5f66   Christoph Hellwig   quota: split do_q...
169
170
171
172
  	struct if_dqblk idq;
  
  	if (copy_from_user(&idq, addr, sizeof(idq)))
  		return -EFAULT;
f450d4fee   Christoph Hellwig   quota: clean up c...
173
174
  	if (!sb->s_qcop->set_dqblk)
  		return -ENOSYS;
c472b4327   Christoph Hellwig   quota: unify ->se...
175
176
  	copy_from_if_dqblk(&fdq, &idq);
  	return sb->s_qcop->set_dqblk(sb, type, id, &fdq);
c411e5f66   Christoph Hellwig   quota: split do_q...
177
178
179
180
181
182
183
184
  }
  
  static int quota_setxstate(struct super_block *sb, int cmd, void __user *addr)
  {
  	__u32 flags;
  
  	if (copy_from_user(&flags, addr, sizeof(flags)))
  		return -EFAULT;
f450d4fee   Christoph Hellwig   quota: clean up c...
185
186
  	if (!sb->s_qcop->set_xstate)
  		return -ENOSYS;
c411e5f66   Christoph Hellwig   quota: split do_q...
187
188
189
190
191
192
193
  	return sb->s_qcop->set_xstate(sb, flags, cmd);
  }
  
  static int quota_getxstate(struct super_block *sb, void __user *addr)
  {
  	struct fs_quota_stat fqs;
  	int ret;
f450d4fee   Christoph Hellwig   quota: clean up c...
194
195
196
  
  	if (!sb->s_qcop->get_xstate)
  		return -ENOSYS;
c411e5f66   Christoph Hellwig   quota: split do_q...
197
198
199
200
201
  	ret = sb->s_qcop->get_xstate(sb, &fqs);
  	if (!ret && copy_to_user(addr, &fqs, sizeof(fqs)))
  		return -EFAULT;
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202

c411e5f66   Christoph Hellwig   quota: split do_q...
203
204
205
206
207
208
209
  static int quota_setxquota(struct super_block *sb, int type, qid_t id,
  			   void __user *addr)
  {
  	struct fs_disk_quota fdq;
  
  	if (copy_from_user(&fdq, addr, sizeof(fdq)))
  		return -EFAULT;
c472b4327   Christoph Hellwig   quota: unify ->se...
210
  	if (!sb->s_qcop->set_dqblk)
f450d4fee   Christoph Hellwig   quota: clean up c...
211
  		return -ENOSYS;
c472b4327   Christoph Hellwig   quota: unify ->se...
212
  	return sb->s_qcop->set_dqblk(sb, type, id, &fdq);
c411e5f66   Christoph Hellwig   quota: split do_q...
213
214
215
216
217
218
219
  }
  
  static int quota_getxquota(struct super_block *sb, int type, qid_t id,
  			   void __user *addr)
  {
  	struct fs_disk_quota fdq;
  	int ret;
b9b2dd36c   Christoph Hellwig   quota: unify ->ge...
220
  	if (!sb->s_qcop->get_dqblk)
f450d4fee   Christoph Hellwig   quota: clean up c...
221
  		return -ENOSYS;
b9b2dd36c   Christoph Hellwig   quota: unify ->ge...
222
  	ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq);
c411e5f66   Christoph Hellwig   quota: split do_q...
223
224
225
226
227
228
229
  	if (!ret && copy_to_user(addr, &fdq, sizeof(fdq)))
  		return -EFAULT;
  	return ret;
  }
  
  /* Copy parameters and call proper function */
  static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
f00c9e44a   Jan Kara   quota: Fix deadlo...
230
  		       void __user *addr, struct path *path)
c411e5f66   Christoph Hellwig   quota: split do_q...
231
  {
c988afb5f   Christoph Hellwig   quota: simplify p...
232
233
234
235
236
237
238
239
240
241
  	int ret;
  
  	if (type >= (XQM_COMMAND(cmd) ? XQM_MAXQUOTAS : MAXQUOTAS))
  		return -EINVAL;
  	if (!sb->s_qcop)
  		return -ENOSYS;
  
  	ret = check_quotactl_permission(sb, type, cmd, id);
  	if (ret < 0)
  		return ret;
c411e5f66   Christoph Hellwig   quota: split do_q...
242
243
  	switch (cmd) {
  	case Q_QUOTAON:
f00c9e44a   Jan Kara   quota: Fix deadlo...
244
  		return quota_quotaon(sb, type, cmd, id, path);
c411e5f66   Christoph Hellwig   quota: split do_q...
245
  	case Q_QUOTAOFF:
f450d4fee   Christoph Hellwig   quota: clean up c...
246
247
  		if (!sb->s_qcop->quota_off)
  			return -ENOSYS;
307ae18a5   Christoph Hellwig   quota: drop remou...
248
  		return sb->s_qcop->quota_off(sb, type);
c411e5f66   Christoph Hellwig   quota: split do_q...
249
250
251
252
253
254
255
256
257
258
259
  	case Q_GETFMT:
  		return quota_getfmt(sb, type, addr);
  	case Q_GETINFO:
  		return quota_getinfo(sb, type, addr);
  	case Q_SETINFO:
  		return quota_setinfo(sb, type, addr);
  	case Q_GETQUOTA:
  		return quota_getquota(sb, type, id, addr);
  	case Q_SETQUOTA:
  		return quota_setquota(sb, type, id, addr);
  	case Q_SYNC:
6ae09575b   Christoph Hellwig   quota: special ca...
260
261
  		if (!sb->s_qcop->quota_sync)
  			return -ENOSYS;
5fb324ad2   Christoph Hellwig   quota: move code ...
262
  		return sb->s_qcop->quota_sync(sb, type, 1);
c411e5f66   Christoph Hellwig   quota: split do_q...
263
264
265
266
267
268
269
270
271
272
273
  	case Q_XQUOTAON:
  	case Q_XQUOTAOFF:
  	case Q_XQUOTARM:
  		return quota_setxstate(sb, cmd, addr);
  	case Q_XGETQSTAT:
  		return quota_getxstate(sb, addr);
  	case Q_XSETQLIM:
  		return quota_setxquota(sb, type, id, addr);
  	case Q_XGETQUOTA:
  		return quota_getxquota(sb, type, id, addr);
  	case Q_XQUOTASYNC:
8c4e4acd6   Christoph Hellwig   quota: clean up Q...
274
275
276
  		/* caller already holds s_umount */
  		if (sb->s_flags & MS_RDONLY)
  			return -EROFS;
0e175a183   Curt Wohlgemuth   writeback: Add a ...
277
  		writeback_inodes_sb(sb, WB_REASON_SYNC);
8c4e4acd6   Christoph Hellwig   quota: clean up Q...
278
  		return 0;
c411e5f66   Christoph Hellwig   quota: split do_q...
279
  	default:
f450d4fee   Christoph Hellwig   quota: clean up c...
280
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
  }
  
  /*
9361401eb   David Howells   [PATCH] BLOCK: Ma...
285
286
287
   * look up a superblock on which quota ops will be performed
   * - use the name of a block device to find the superblock thereon
   */
7a2435d87   Jan Kara   quota: Remove sup...
288
  static struct super_block *quotactl_block(const char __user *special)
9361401eb   David Howells   [PATCH] BLOCK: Ma...
289
290
291
292
293
294
295
  {
  #ifdef CONFIG_BLOCK
  	struct block_device *bdev;
  	struct super_block *sb;
  	char *tmp = getname(special);
  
  	if (IS_ERR(tmp))
e231c2ee6   David Howells   Convert ERR_PTR(P...
296
  		return ERR_CAST(tmp);
9361401eb   David Howells   [PATCH] BLOCK: Ma...
297
298
299
  	bdev = lookup_bdev(tmp);
  	putname(tmp);
  	if (IS_ERR(bdev))
e231c2ee6   David Howells   Convert ERR_PTR(P...
300
  		return ERR_CAST(bdev);
9361401eb   David Howells   [PATCH] BLOCK: Ma...
301
302
303
304
305
306
307
308
309
310
311
312
  	sb = get_super(bdev);
  	bdput(bdev);
  	if (!sb)
  		return ERR_PTR(-ENODEV);
  
  	return sb;
  #else
  	return ERR_PTR(-ENODEV);
  #endif
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
315
316
317
   * This is the system call interface. This communicates with
   * the user-level programs. Currently this only supports diskquota
   * calls. Maybe we need to add the process quotas etc. in the future,
   * but we probably should use rlimits for that.
   */
3cdad4288   Heiko Carstens   [CVE-2009-0029] S...
318
319
  SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
  		qid_t, id, void __user *, addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
  {
  	uint cmds, type;
  	struct super_block *sb = NULL;
f00c9e44a   Jan Kara   quota: Fix deadlo...
323
  	struct path path, *pathp = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
325
326
327
  	int ret;
  
  	cmds = cmd >> SUBCMDSHIFT;
  	type = cmd & SUBCMDMASK;
6ae09575b   Christoph Hellwig   quota: special ca...
328
329
330
331
332
333
334
335
336
  	/*
  	 * As a special case Q_SYNC can be called without a specific device.
  	 * It will iterate all superblocks that have quota enabled and call
  	 * the sync action on each of them.
  	 */
  	if (!special) {
  		if (cmds == Q_SYNC)
  			return quota_sync_all(type);
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
  	}
f00c9e44a   Jan Kara   quota: Fix deadlo...
338
339
340
341
342
343
  	/*
  	 * Path for quotaon has to be resolved before grabbing superblock
  	 * because that gets s_umount sem which is also possibly needed by path
  	 * resolution (think about autofs) and thus deadlocks could arise.
  	 */
  	if (cmds == Q_QUOTAON) {
815d405ce   Trond Myklebust   VFS: Fix the rema...
344
  		ret = user_path_at(AT_FDCWD, addr, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path);
f00c9e44a   Jan Kara   quota: Fix deadlo...
345
346
347
348
349
  		if (ret)
  			pathp = ERR_PTR(ret);
  		else
  			pathp = &path;
  	}
6ae09575b   Christoph Hellwig   quota: special ca...
350
  	sb = quotactl_block(special);
0aaa61886   Jan Kara   quota: Drop path ...
351
352
353
354
  	if (IS_ERR(sb)) {
  		ret = PTR_ERR(sb);
  		goto out;
  	}
6ae09575b   Christoph Hellwig   quota: special ca...
355

f00c9e44a   Jan Kara   quota: Fix deadlo...
356
  	ret = do_quotactl(sb, type, cmds, id, addr, pathp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357

6ae09575b   Christoph Hellwig   quota: special ca...
358
  	drop_super(sb);
0aaa61886   Jan Kara   quota: Drop path ...
359
  out:
f00c9e44a   Jan Kara   quota: Fix deadlo...
360
361
  	if (pathp && !IS_ERR(pathp))
  		path_put(pathp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
363
  	return ret;
  }