Blame view
fs/quota/quota.c
8.76 KB
1da177e4c
|
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
|
14 15 16 |
#include <linux/security.h> #include <linux/syscalls.h> #include <linux/buffer_head.h> |
16f7e0fe2
|
17 |
#include <linux/capability.h> |
be586bab8
|
18 |
#include <linux/quotaops.h> |
b716395e2
|
19 |
#include <linux/types.h> |
8c4e4acd6
|
20 |
#include <linux/writeback.h> |
1da177e4c
|
21 |
|
c988afb5f
|
22 23 |
static int check_quotactl_permission(struct super_block *sb, int type, int cmd, qid_t id) |
1da177e4c
|
24 |
{ |
c988afb5f
|
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
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
|
41 42 43 |
if (!capable(CAP_SYS_ADMIN)) return -EPERM; } |
c988afb5f
|
44 |
return security_quotactl(cmd, type, id, sb); |
1da177e4c
|
45 |
} |
01a05b337
|
46 47 48 49 50 |
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
|
51 |
static int quota_sync_all(int type) |
1da177e4c
|
52 |
{ |
6ae09575b
|
53 54 55 56 57 |
int ret; if (type >= MAXQUOTAS) return -EINVAL; ret = security_quotactl(Q_SYNC, type, 0, NULL); |
01a05b337
|
58 59 60 |
if (!ret) iterate_supers(quota_sync_one, &type); return ret; |
1da177e4c
|
61 |
} |
c411e5f66
|
62 63 |
static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id, void __user *addr) |
1da177e4c
|
64 |
{ |
c411e5f66
|
65 |
char *pathname; |
f450d4fee
|
66 |
int ret = -ENOSYS; |
1da177e4c
|
67 |
|
c411e5f66
|
68 69 70 |
pathname = getname(addr); if (IS_ERR(pathname)) return PTR_ERR(pathname); |
f450d4fee
|
71 |
if (sb->s_qcop->quota_on) |
307ae18a5
|
72 |
ret = sb->s_qcop->quota_on(sb, type, id, pathname); |
c411e5f66
|
73 74 75 |
putname(pathname); return ret; } |
1da177e4c
|
76 |
|
c411e5f66
|
77 78 79 |
static int quota_getfmt(struct super_block *sb, int type, void __user *addr) { __u32 fmt; |
1da177e4c
|
80 |
|
c411e5f66
|
81 82 83 84 85 86 87 88 89 90 91 |
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
|
92 |
|
c411e5f66
|
93 94 95 96 |
static int quota_getinfo(struct super_block *sb, int type, void __user *addr) { struct if_dqinfo info; int ret; |
1da177e4c
|
97 |
|
f450d4fee
|
98 99 |
if (!sb->s_qcop->get_info) return -ENOSYS; |
c411e5f66
|
100 101 102 103 104 |
ret = sb->s_qcop->get_info(sb, type, &info); if (!ret && copy_to_user(addr, &info, sizeof(info))) return -EFAULT; return ret; } |
1da177e4c
|
105 |
|
c411e5f66
|
106 107 108 |
static int quota_setinfo(struct super_block *sb, int type, void __user *addr) { struct if_dqinfo info; |
1da177e4c
|
109 |
|
c411e5f66
|
110 111 |
if (copy_from_user(&info, addr, sizeof(info))) return -EFAULT; |
f450d4fee
|
112 113 |
if (!sb->s_qcop->set_info) return -ENOSYS; |
c411e5f66
|
114 115 |
return sb->s_qcop->set_info(sb, type, &info); } |
b9b2dd36c
|
116 117 118 119 120 121 122 123 124 125 126 127 |
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
|
128 129 130 |
static int quota_getquota(struct super_block *sb, int type, qid_t id, void __user *addr) { |
b9b2dd36c
|
131 |
struct fs_disk_quota fdq; |
c411e5f66
|
132 133 |
struct if_dqblk idq; int ret; |
f450d4fee
|
134 135 |
if (!sb->s_qcop->get_dqblk) return -ENOSYS; |
b9b2dd36c
|
136 |
ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq); |
c411e5f66
|
137 138 |
if (ret) return ret; |
b9b2dd36c
|
139 |
copy_to_if_dqblk(&idq, &fdq); |
c411e5f66
|
140 141 142 143 |
if (copy_to_user(addr, &idq, sizeof(idq))) return -EFAULT; return 0; } |
c472b4327
|
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
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
|
169 170 171 |
static int quota_setquota(struct super_block *sb, int type, qid_t id, void __user *addr) { |
c472b4327
|
172 |
struct fs_disk_quota fdq; |
c411e5f66
|
173 174 175 176 |
struct if_dqblk idq; if (copy_from_user(&idq, addr, sizeof(idq))) return -EFAULT; |
f450d4fee
|
177 178 |
if (!sb->s_qcop->set_dqblk) return -ENOSYS; |
c472b4327
|
179 180 |
copy_from_if_dqblk(&fdq, &idq); return sb->s_qcop->set_dqblk(sb, type, id, &fdq); |
c411e5f66
|
181 182 183 184 185 186 187 188 |
} 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
|
189 190 |
if (!sb->s_qcop->set_xstate) return -ENOSYS; |
c411e5f66
|
191 192 193 194 195 196 197 |
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
|
198 199 200 |
if (!sb->s_qcop->get_xstate) return -ENOSYS; |
c411e5f66
|
201 202 203 204 205 |
ret = sb->s_qcop->get_xstate(sb, &fqs); if (!ret && copy_to_user(addr, &fqs, sizeof(fqs))) return -EFAULT; return ret; } |
1da177e4c
|
206 |
|
c411e5f66
|
207 208 209 210 211 212 213 |
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
|
214 |
if (!sb->s_qcop->set_dqblk) |
f450d4fee
|
215 |
return -ENOSYS; |
c472b4327
|
216 |
return sb->s_qcop->set_dqblk(sb, type, id, &fdq); |
c411e5f66
|
217 218 219 220 221 222 223 |
} static int quota_getxquota(struct super_block *sb, int type, qid_t id, void __user *addr) { struct fs_disk_quota fdq; int ret; |
b9b2dd36c
|
224 |
if (!sb->s_qcop->get_dqblk) |
f450d4fee
|
225 |
return -ENOSYS; |
b9b2dd36c
|
226 |
ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq); |
c411e5f66
|
227 228 229 230 231 232 233 234 235 |
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, void __user *addr) { |
c988afb5f
|
236 237 238 239 240 241 242 243 244 245 |
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
|
246 247 248 249 |
switch (cmd) { case Q_QUOTAON: return quota_quotaon(sb, type, cmd, id, addr); case Q_QUOTAOFF: |
f450d4fee
|
250 251 |
if (!sb->s_qcop->quota_off) return -ENOSYS; |
307ae18a5
|
252 |
return sb->s_qcop->quota_off(sb, type); |
c411e5f66
|
253 254 255 256 257 258 259 260 261 262 263 |
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
|
264 265 |
if (!sb->s_qcop->quota_sync) return -ENOSYS; |
5fb324ad2
|
266 |
return sb->s_qcop->quota_sync(sb, type, 1); |
c411e5f66
|
267 268 269 270 271 272 273 274 275 276 277 |
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
|
278 279 280 281 282 |
/* caller already holds s_umount */ if (sb->s_flags & MS_RDONLY) return -EROFS; writeback_inodes_sb(sb); return 0; |
c411e5f66
|
283 |
default: |
f450d4fee
|
284 |
return -EINVAL; |
1da177e4c
|
285 |
} |
1da177e4c
|
286 287 288 |
} /* |
9361401eb
|
289 290 291 |
* look up a superblock on which quota ops will be performed * - use the name of a block device to find the superblock thereon */ |
7a2435d87
|
292 |
static struct super_block *quotactl_block(const char __user *special) |
9361401eb
|
293 294 295 296 297 298 299 |
{ #ifdef CONFIG_BLOCK struct block_device *bdev; struct super_block *sb; char *tmp = getname(special); if (IS_ERR(tmp)) |
e231c2ee6
|
300 |
return ERR_CAST(tmp); |
9361401eb
|
301 302 303 |
bdev = lookup_bdev(tmp); putname(tmp); if (IS_ERR(bdev)) |
e231c2ee6
|
304 |
return ERR_CAST(bdev); |
9361401eb
|
305 306 307 308 309 310 311 312 313 314 315 316 |
sb = get_super(bdev); bdput(bdev); if (!sb) return ERR_PTR(-ENODEV); return sb; #else return ERR_PTR(-ENODEV); #endif } /* |
1da177e4c
|
317 318 319 320 321 |
* 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
|
322 323 |
SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special, qid_t, id, void __user *, addr) |
1da177e4c
|
324 325 326 |
{ uint cmds, type; struct super_block *sb = NULL; |
1da177e4c
|
327 328 329 330 |
int ret; cmds = cmd >> SUBCMDSHIFT; type = cmd & SUBCMDMASK; |
6ae09575b
|
331 332 333 334 335 336 337 338 339 |
/* * 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
|
340 |
} |
6ae09575b
|
341 342 343 |
sb = quotactl_block(special); if (IS_ERR(sb)) return PTR_ERR(sb); |
c988afb5f
|
344 |
ret = do_quotactl(sb, type, cmds, id, addr); |
1da177e4c
|
345 |
|
6ae09575b
|
346 |
drop_super(sb); |
1da177e4c
|
347 348 |
return ret; } |