Blame view
fs/quota/quota.c
9.27 KB
1da177e4c 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 Linux-2.6.12-rc2 |
14 15 |
#include <linux/security.h> #include <linux/syscalls.h> |
16f7e0fe2 [PATCH] capable/c... |
16 |
#include <linux/capability.h> |
be586bab8 [PATCH] quota: sm... |
17 |
#include <linux/quotaops.h> |
b716395e2 diskquota: 32bit ... |
18 |
#include <linux/types.h> |
8c4e4acd6 quota: clean up Q... |
19 |
#include <linux/writeback.h> |
1da177e4c Linux-2.6.12-rc2 |
20 |
|
c988afb5f quota: simplify p... |
21 22 |
static int check_quotactl_permission(struct super_block *sb, int type, int cmd, qid_t id) |
1da177e4c Linux-2.6.12-rc2 |
23 |
{ |
c988afb5f 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 Linux-2.6.12-rc2 |
40 41 42 |
if (!capable(CAP_SYS_ADMIN)) return -EPERM; } |
c988afb5f quota: simplify p... |
43 |
return security_quotactl(cmd, type, id, sb); |
1da177e4c Linux-2.6.12-rc2 |
44 |
} |
01a05b337 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 quota: special ca... |
50 |
static int quota_sync_all(int type) |
1da177e4c Linux-2.6.12-rc2 |
51 |
{ |
6ae09575b quota: special ca... |
52 53 54 55 56 |
int ret; if (type >= MAXQUOTAS) return -EINVAL; ret = security_quotactl(Q_SYNC, type, 0, NULL); |
01a05b337 new helper: itera... |
57 58 59 |
if (!ret) iterate_supers(quota_sync_one, &type); return ret; |
1da177e4c Linux-2.6.12-rc2 |
60 |
} |
c411e5f66 quota: split do_q... |
61 |
static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id, |
f00c9e44a quota: Fix deadlo... |
62 |
struct path *path) |
1da177e4c Linux-2.6.12-rc2 |
63 |
{ |
f00c9e44a 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 quota: split do_q... |
71 |
} |
1da177e4c Linux-2.6.12-rc2 |
72 |
|
c411e5f66 quota: split do_q... |
73 74 75 |
static int quota_getfmt(struct super_block *sb, int type, void __user *addr) { __u32 fmt; |
1da177e4c Linux-2.6.12-rc2 |
76 |
|
c411e5f66 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 Linux-2.6.12-rc2 |
88 |
|
c411e5f66 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 Linux-2.6.12-rc2 |
93 |
|
f450d4fee quota: clean up c... |
94 95 |
if (!sb->s_qcop->get_info) return -ENOSYS; |
c411e5f66 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 Linux-2.6.12-rc2 |
101 |
|
c411e5f66 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 Linux-2.6.12-rc2 |
105 |
|
c411e5f66 quota: split do_q... |
106 107 |
if (copy_from_user(&info, addr, sizeof(info))) return -EFAULT; |
f450d4fee quota: clean up c... |
108 109 |
if (!sb->s_qcop->set_info) return -ENOSYS; |
c411e5f66 quota: split do_q... |
110 111 |
return sb->s_qcop->set_info(sb, type, &info); } |
b9b2dd36c 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 quota: split do_q... |
124 125 126 |
static int quota_getquota(struct super_block *sb, int type, qid_t id, void __user *addr) { |
b9b2dd36c quota: unify ->ge... |
127 |
struct fs_disk_quota fdq; |
c411e5f66 quota: split do_q... |
128 129 |
struct if_dqblk idq; int ret; |
f450d4fee quota: clean up c... |
130 131 |
if (!sb->s_qcop->get_dqblk) return -ENOSYS; |
b9b2dd36c quota: unify ->ge... |
132 |
ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq); |
c411e5f66 quota: split do_q... |
133 134 |
if (ret) return ret; |
b9b2dd36c quota: unify ->ge... |
135 |
copy_to_if_dqblk(&idq, &fdq); |
c411e5f66 quota: split do_q... |
136 137 138 139 |
if (copy_to_user(addr, &idq, sizeof(idq))) return -EFAULT; return 0; } |
c472b4327 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 quota: split do_q... |
165 166 167 |
static int quota_setquota(struct super_block *sb, int type, qid_t id, void __user *addr) { |
c472b4327 quota: unify ->se... |
168 |
struct fs_disk_quota fdq; |
c411e5f66 quota: split do_q... |
169 170 171 172 |
struct if_dqblk idq; if (copy_from_user(&idq, addr, sizeof(idq))) return -EFAULT; |
f450d4fee quota: clean up c... |
173 174 |
if (!sb->s_qcop->set_dqblk) return -ENOSYS; |
c472b4327 quota: unify ->se... |
175 176 |
copy_from_if_dqblk(&fdq, &idq); return sb->s_qcop->set_dqblk(sb, type, id, &fdq); |
c411e5f66 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 quota: clean up c... |
185 186 |
if (!sb->s_qcop->set_xstate) return -ENOSYS; |
c411e5f66 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 quota: clean up c... |
194 195 196 |
if (!sb->s_qcop->get_xstate) return -ENOSYS; |
c411e5f66 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 Linux-2.6.12-rc2 |
202 |
|
c411e5f66 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 quota: unify ->se... |
210 |
if (!sb->s_qcop->set_dqblk) |
f450d4fee quota: clean up c... |
211 |
return -ENOSYS; |
c472b4327 quota: unify ->se... |
212 |
return sb->s_qcop->set_dqblk(sb, type, id, &fdq); |
c411e5f66 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 quota: unify ->ge... |
220 |
if (!sb->s_qcop->get_dqblk) |
f450d4fee quota: clean up c... |
221 |
return -ENOSYS; |
b9b2dd36c quota: unify ->ge... |
222 |
ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq); |
c411e5f66 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 quota: Fix deadlo... |
230 |
void __user *addr, struct path *path) |
c411e5f66 quota: split do_q... |
231 |
{ |
c988afb5f 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 quota: split do_q... |
242 243 |
switch (cmd) { case Q_QUOTAON: |
f00c9e44a quota: Fix deadlo... |
244 |
return quota_quotaon(sb, type, cmd, id, path); |
c411e5f66 quota: split do_q... |
245 |
case Q_QUOTAOFF: |
f450d4fee quota: clean up c... |
246 247 |
if (!sb->s_qcop->quota_off) return -ENOSYS; |
307ae18a5 quota: drop remou... |
248 |
return sb->s_qcop->quota_off(sb, type); |
c411e5f66 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 quota: special ca... |
260 261 |
if (!sb->s_qcop->quota_sync) return -ENOSYS; |
5fb324ad2 quota: move code ... |
262 |
return sb->s_qcop->quota_sync(sb, type, 1); |
c411e5f66 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 quota: clean up Q... |
274 275 276 |
/* caller already holds s_umount */ if (sb->s_flags & MS_RDONLY) return -EROFS; |
0e175a183 writeback: Add a ... |
277 |
writeback_inodes_sb(sb, WB_REASON_SYNC); |
8c4e4acd6 quota: clean up Q... |
278 |
return 0; |
c411e5f66 quota: split do_q... |
279 |
default: |
f450d4fee quota: clean up c... |
280 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
281 |
} |
1da177e4c Linux-2.6.12-rc2 |
282 283 284 |
} /* |
9361401eb [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 quota: Remove sup... |
288 |
static struct super_block *quotactl_block(const char __user *special) |
9361401eb [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 Convert ERR_PTR(P... |
296 |
return ERR_CAST(tmp); |
9361401eb [PATCH] BLOCK: Ma... |
297 298 299 |
bdev = lookup_bdev(tmp); putname(tmp); if (IS_ERR(bdev)) |
e231c2ee6 Convert ERR_PTR(P... |
300 |
return ERR_CAST(bdev); |
9361401eb [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 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 [CVE-2009-0029] S... |
318 319 |
SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special, qid_t, id, void __user *, addr) |
1da177e4c Linux-2.6.12-rc2 |
320 321 322 |
{ uint cmds, type; struct super_block *sb = NULL; |
f00c9e44a quota: Fix deadlo... |
323 |
struct path path, *pathp = NULL; |
1da177e4c Linux-2.6.12-rc2 |
324 325 326 327 |
int ret; cmds = cmd >> SUBCMDSHIFT; type = cmd & SUBCMDMASK; |
6ae09575b 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 Linux-2.6.12-rc2 |
337 |
} |
f00c9e44a 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 VFS: Fix the rema... |
344 |
ret = user_path_at(AT_FDCWD, addr, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path); |
f00c9e44a quota: Fix deadlo... |
345 346 347 348 349 |
if (ret) pathp = ERR_PTR(ret); else pathp = &path; } |
6ae09575b quota: special ca... |
350 |
sb = quotactl_block(special); |
0aaa61886 quota: Drop path ... |
351 352 353 354 |
if (IS_ERR(sb)) { ret = PTR_ERR(sb); goto out; } |
6ae09575b quota: special ca... |
355 |
|
f00c9e44a quota: Fix deadlo... |
356 |
ret = do_quotactl(sb, type, cmds, id, addr, pathp); |
1da177e4c Linux-2.6.12-rc2 |
357 |
|
6ae09575b quota: special ca... |
358 |
drop_super(sb); |
0aaa61886 quota: Drop path ... |
359 |
out: |
f00c9e44a quota: Fix deadlo... |
360 361 |
if (pathp && !IS_ERR(pathp)) path_put(pathp); |
1da177e4c Linux-2.6.12-rc2 |
362 363 |
return ret; } |