Commit | Line | Data |
---|---|---|
c9ba0248 KM |
1 | /* |
2 | * Copyright (c) 1982, 1986, 1990 Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Robert Elz at The University of Melbourne. | |
7 | * | |
b702c21d | 8 | * %sccs.include.redist.c% |
c9ba0248 | 9 | * |
c0486eb9 | 10 | * @(#)ufs_quota.c 7.6 (Berkeley) %G% |
c9ba0248 KM |
11 | */ |
12 | #include "param.h" | |
13 | #include "time.h" | |
14 | #include "kernel.h" | |
15 | #include "systm.h" | |
16 | #include "ucred.h" | |
17 | #include "namei.h" | |
18 | #include "errno.h" | |
19 | #include "malloc.h" | |
20 | #include "file.h" | |
21 | #include "vnode.h" | |
22 | #include "mount.h" | |
23 | #include "../ufs/fs.h" | |
24 | #include "../ufs/quota.h" | |
25 | #include "../ufs/inode.h" | |
26 | #include "../ufs/ufsmount.h" | |
27 | ||
28 | /* | |
29 | * Quota name to error message mapping. | |
30 | */ | |
31 | static char *quotatypes[] = INITQFNAMES; | |
32 | ||
33 | /* | |
34 | * Set up the quotas for an inode. | |
35 | * | |
36 | * This routine completely defines the semantics of quotas. | |
37 | * If other criterion want to be used to establish quotas, the | |
38 | * MAXQUOTAS value in quotas.h should be increased, and the | |
39 | * additional dquots set up here. | |
40 | */ | |
41 | getinoquota(ip) | |
42 | register struct inode *ip; | |
43 | { | |
44 | struct ufsmount *ump; | |
45 | struct vnode *vp = ITOV(ip); | |
46 | int error; | |
47 | ||
48 | ump = VFSTOUFS(vp->v_mount); | |
49 | /* | |
50 | * Set up the user quota based on file uid. | |
51 | * EINVAL means that quotas are not enabled. | |
52 | */ | |
53 | if (ip->i_dquot[USRQUOTA] == NODQUOT && | |
54 | (error = | |
55 | dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) && | |
56 | error != EINVAL) | |
57 | return (error); | |
58 | /* | |
59 | * Set up the group quota based on file gid. | |
60 | * EINVAL means that quotas are not enabled. | |
61 | */ | |
62 | if (ip->i_dquot[GRPQUOTA] == NODQUOT && | |
63 | (error = | |
64 | dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) && | |
65 | error != EINVAL) | |
66 | return (error); | |
67 | return (0); | |
68 | } | |
69 | ||
70 | /* | |
71 | * Update disk usage, and take corrective action. | |
72 | */ | |
73 | chkdq(ip, change, cred, flags) | |
74 | register struct inode *ip; | |
75 | long change; | |
76 | struct ucred *cred; | |
77 | int flags; | |
78 | { | |
79 | register struct dquot *dq; | |
80 | register int i; | |
81 | int ncurblocks, error; | |
82 | ||
83 | #ifdef DIAGNOSTIC | |
84 | if ((flags & CHOWN) == 0) | |
85 | chkdquot(ip); | |
86 | #endif | |
87 | if (change == 0) | |
88 | return (0); | |
89 | if (change < 0) { | |
90 | for (i = 0; i < MAXQUOTAS; i++) { | |
91 | if ((dq = ip->i_dquot[i]) == NODQUOT) | |
92 | continue; | |
93 | while (dq->dq_flags & DQ_LOCK) { | |
94 | dq->dq_flags |= DQ_WANT; | |
95 | sleep((caddr_t)dq, PINOD+1); | |
96 | } | |
97 | ncurblocks = dq->dq_curblocks + change; | |
98 | if (ncurblocks >= 0) | |
99 | dq->dq_curblocks = ncurblocks; | |
100 | else | |
101 | dq->dq_curblocks = 0; | |
102 | dq->dq_flags &= ~DQ_BLKS; | |
103 | dq->dq_flags |= DQ_MOD; | |
104 | } | |
105 | return (0); | |
106 | } | |
107 | if ((flags & FORCE) == 0 && cred->cr_uid != 0) { | |
108 | for (i = 0; i < MAXQUOTAS; i++) { | |
109 | if ((dq = ip->i_dquot[i]) == NODQUOT) | |
110 | continue; | |
111 | if (error = chkdqchg(ip, change, cred, i)) | |
112 | return (error); | |
113 | } | |
114 | } | |
115 | for (i = 0; i < MAXQUOTAS; i++) { | |
116 | if ((dq = ip->i_dquot[i]) == NODQUOT) | |
117 | continue; | |
118 | while (dq->dq_flags & DQ_LOCK) { | |
119 | dq->dq_flags |= DQ_WANT; | |
120 | sleep((caddr_t)dq, PINOD+1); | |
121 | } | |
122 | dq->dq_curblocks += change; | |
123 | dq->dq_flags |= DQ_MOD; | |
124 | } | |
125 | return (0); | |
126 | } | |
127 | ||
128 | /* | |
129 | * Check for a valid change to a users allocation. | |
130 | * Issue an error message if appropriate. | |
131 | */ | |
132 | chkdqchg(ip, change, cred, type) | |
133 | struct inode *ip; | |
134 | long change; | |
135 | struct ucred *cred; | |
136 | int type; | |
137 | { | |
138 | register struct dquot *dq = ip->i_dquot[type]; | |
139 | long ncurblocks = dq->dq_curblocks + change; | |
140 | ||
141 | /* | |
142 | * If user would exceed their hard limit, disallow space allocation. | |
143 | */ | |
144 | if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) { | |
145 | if ((dq->dq_flags & DQ_BLKS) == 0 && | |
146 | ip->i_uid == cred->cr_uid) { | |
147 | uprintf("\n%s: write failed, %s disk limit reached\n", | |
148 | ip->i_fs->fs_fsmnt, quotatypes[type]); | |
149 | dq->dq_flags |= DQ_BLKS; | |
150 | } | |
151 | return (EDQUOT); | |
152 | } | |
153 | /* | |
154 | * If user is over their soft limit for too long, disallow space | |
155 | * allocation. Reset time limit as they cross their soft limit. | |
156 | */ | |
157 | if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) { | |
158 | if (dq->dq_curblocks < dq->dq_bsoftlimit) { | |
159 | dq->dq_btime = time.tv_sec + | |
160 | VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type]; | |
161 | if (ip->i_uid == cred->cr_uid) | |
162 | uprintf("\n%s: warning, %s %s\n", | |
163 | ip->i_fs->fs_fsmnt, quotatypes[type], | |
164 | "disk quota exceeded"); | |
165 | return (0); | |
166 | } | |
167 | if (time.tv_sec > dq->dq_btime) { | |
168 | if ((dq->dq_flags & DQ_BLKS) == 0 && | |
169 | ip->i_uid == cred->cr_uid) { | |
170 | uprintf("\n%s: write failed, %s %s\n", | |
171 | ip->i_fs->fs_fsmnt, quotatypes[type], | |
172 | "disk quota exceeded too long"); | |
173 | dq->dq_flags |= DQ_BLKS; | |
174 | } | |
175 | return (EDQUOT); | |
176 | } | |
177 | } | |
178 | return (0); | |
179 | } | |
180 | ||
181 | /* | |
182 | * Check the inode limit, applying corrective action. | |
183 | */ | |
184 | chkiq(ip, change, cred, flags) | |
185 | register struct inode *ip; | |
186 | long change; | |
187 | struct ucred *cred; | |
188 | int flags; | |
189 | { | |
190 | register struct dquot *dq; | |
191 | register int i; | |
192 | int ncurinodes, error; | |
193 | ||
194 | #ifdef DIAGNOSTIC | |
195 | if ((flags & CHOWN) == 0) | |
196 | chkdquot(ip); | |
197 | #endif | |
198 | if (change == 0) | |
199 | return (0); | |
200 | if (change < 0) { | |
201 | for (i = 0; i < MAXQUOTAS; i++) { | |
202 | if ((dq = ip->i_dquot[i]) == NODQUOT) | |
203 | continue; | |
204 | while (dq->dq_flags & DQ_LOCK) { | |
205 | dq->dq_flags |= DQ_WANT; | |
206 | sleep((caddr_t)dq, PINOD+1); | |
207 | } | |
208 | ncurinodes = dq->dq_curinodes + change; | |
209 | if (ncurinodes >= 0) | |
210 | dq->dq_curinodes = ncurinodes; | |
211 | else | |
212 | dq->dq_curinodes = 0; | |
213 | dq->dq_flags &= ~DQ_INODS; | |
214 | dq->dq_flags |= DQ_MOD; | |
215 | } | |
216 | return (0); | |
217 | } | |
218 | if ((flags & FORCE) == 0 && cred->cr_uid != 0) { | |
219 | for (i = 0; i < MAXQUOTAS; i++) { | |
220 | if ((dq = ip->i_dquot[i]) == NODQUOT) | |
221 | continue; | |
222 | if (error = chkiqchg(ip, change, cred, i)) | |
223 | return (error); | |
224 | } | |
225 | } | |
226 | for (i = 0; i < MAXQUOTAS; i++) { | |
227 | if ((dq = ip->i_dquot[i]) == NODQUOT) | |
228 | continue; | |
229 | while (dq->dq_flags & DQ_LOCK) { | |
230 | dq->dq_flags |= DQ_WANT; | |
231 | sleep((caddr_t)dq, PINOD+1); | |
232 | } | |
233 | dq->dq_curinodes += change; | |
234 | dq->dq_flags |= DQ_MOD; | |
235 | } | |
236 | return (0); | |
237 | } | |
238 | ||
239 | /* | |
240 | * Check for a valid change to a users allocation. | |
241 | * Issue an error message if appropriate. | |
242 | */ | |
243 | chkiqchg(ip, change, cred, type) | |
244 | struct inode *ip; | |
245 | long change; | |
246 | struct ucred *cred; | |
247 | int type; | |
248 | { | |
249 | register struct dquot *dq = ip->i_dquot[type]; | |
250 | long ncurinodes = dq->dq_curinodes + change; | |
251 | ||
252 | /* | |
253 | * If user would exceed their hard limit, disallow inode allocation. | |
254 | */ | |
255 | if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) { | |
256 | if ((dq->dq_flags & DQ_INODS) == 0 && | |
257 | ip->i_uid == cred->cr_uid) { | |
258 | uprintf("\n%s: write failed, %s inode limit reached\n", | |
259 | ip->i_fs->fs_fsmnt, quotatypes[type]); | |
260 | dq->dq_flags |= DQ_INODS; | |
261 | } | |
262 | return (EDQUOT); | |
263 | } | |
264 | /* | |
265 | * If user is over their soft limit for too long, disallow inode | |
266 | * allocation. Reset time limit as they cross their soft limit. | |
267 | */ | |
268 | if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) { | |
269 | if (dq->dq_curinodes < dq->dq_isoftlimit) { | |
270 | dq->dq_itime = time.tv_sec + | |
271 | VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type]; | |
272 | if (ip->i_uid == cred->cr_uid) | |
273 | uprintf("\n%s: warning, %s %s\n", | |
274 | ip->i_fs->fs_fsmnt, quotatypes[type], | |
275 | "inode quota exceeded"); | |
276 | return (0); | |
277 | } | |
278 | if (time.tv_sec > dq->dq_itime) { | |
279 | if ((dq->dq_flags & DQ_INODS) == 0 && | |
280 | ip->i_uid == cred->cr_uid) { | |
281 | uprintf("\n%s: write failed, %s %s\n", | |
282 | ip->i_fs->fs_fsmnt, quotatypes[type], | |
283 | "inode quota exceeded too long"); | |
284 | dq->dq_flags |= DQ_INODS; | |
285 | } | |
286 | return (EDQUOT); | |
287 | } | |
288 | } | |
289 | return (0); | |
290 | } | |
291 | ||
292 | #ifdef DIAGNOSTIC | |
293 | /* | |
294 | * On filesystems with quotas enabled, | |
295 | * it is an error for a file to change size and not | |
296 | * to have a dquot structure associated with it. | |
297 | */ | |
298 | chkdquot(ip) | |
299 | register struct inode *ip; | |
300 | { | |
301 | struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount); | |
302 | register int i; | |
303 | ||
304 | for (i = 0; i < MAXQUOTAS; i++) { | |
305 | if (ump->um_quotas[i] == NULLVP || | |
306 | (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING))) | |
307 | continue; | |
308 | if (ip->i_dquot[i] == NODQUOT) { | |
309 | vprint("chkdquot: missing dquot", ITOV(ip)); | |
310 | panic("missing dquot"); | |
311 | } | |
312 | } | |
313 | } | |
314 | #endif /* DIAGNOSTIC */ | |
315 | ||
316 | /* | |
317 | * Code to process quotactl commands. | |
318 | */ | |
319 | ||
320 | /* | |
321 | * Q_QUOTAON - set up a quota file for a particular file system. | |
322 | */ | |
323 | quotaon(ndp, mp, type, fname) | |
324 | register struct nameidata *ndp; | |
325 | struct mount *mp; | |
326 | register int type; | |
327 | caddr_t fname; | |
328 | { | |
329 | register struct ufsmount *ump = VFSTOUFS(mp); | |
330 | register struct vnode *vp, **vpp; | |
331 | struct vnode *nextvp; | |
332 | struct dquot *dq; | |
333 | int error; | |
334 | ||
335 | vpp = &ump->um_quotas[type]; | |
336 | ndp->ni_segflg = UIO_USERSPACE; | |
337 | ndp->ni_dirp = fname; | |
338 | if (error = vn_open(ndp, FREAD|FWRITE, 0)) | |
339 | return (error); | |
340 | vp = ndp->ni_vp; | |
341 | if (vp->v_type != VREG) { | |
342 | vrele(vp); | |
343 | return (EACCES); | |
344 | } | |
345 | if (vfs_busy(mp)) { | |
346 | vrele(vp); | |
347 | return (EBUSY); | |
348 | } | |
349 | if (*vpp != vp) | |
350 | quotaoff(mp, type); | |
351 | ump->um_qflags[type] |= QTF_OPENING; | |
82161bc8 | 352 | mp->mnt_flag |= MNT_QUOTA; |
c9ba0248 KM |
353 | vp->v_flag |= VSYSTEM; |
354 | *vpp = vp; | |
355 | /* | |
356 | * Save the credential of the process that turned on quotas. | |
357 | * Set up the time limits for this quota. | |
358 | */ | |
359 | crhold(ndp->ni_cred); | |
360 | ump->um_cred[type] = ndp->ni_cred; | |
361 | ump->um_btime[type] = MAX_DQ_TIME; | |
362 | ump->um_itime[type] = MAX_IQ_TIME; | |
363 | if (dqget(NULLVP, 0, ump, type, &dq) == 0) { | |
364 | if (dq->dq_btime > 0) | |
365 | ump->um_btime[type] = dq->dq_btime; | |
366 | if (dq->dq_itime > 0) | |
367 | ump->um_itime[type] = dq->dq_itime; | |
368 | dqrele(NULLVP, dq); | |
369 | } | |
370 | /* | |
371 | * Search vnodes associated with this mount point, | |
372 | * adding references to quota file being opened. | |
373 | * NB: only need to add dquot's for inodes being modified; | |
374 | * vp->v_usecount == 0 below should use vp->v_writecnt == 0. | |
375 | */ | |
376 | again: | |
82161bc8 | 377 | for (vp = mp->mnt_mounth; vp; vp = nextvp) { |
c9ba0248 KM |
378 | nextvp = vp->v_mountf; |
379 | if (vp->v_usecount == 0) | |
380 | continue; | |
381 | if (vget(vp)) | |
382 | goto again; | |
7460dc90 KM |
383 | if (error = getinoquota(VTOI(vp))) { |
384 | vput(vp); | |
c9ba0248 | 385 | break; |
7460dc90 | 386 | } |
c9ba0248 KM |
387 | vput(vp); |
388 | if (vp->v_mountf != nextvp || vp->v_mount != mp) | |
389 | goto again; | |
390 | } | |
391 | ump->um_qflags[type] &= ~QTF_OPENING; | |
392 | if (error) | |
393 | quotaoff(mp, type); | |
394 | vfs_unbusy(mp); | |
395 | return (error); | |
396 | } | |
397 | ||
398 | /* | |
399 | * Q_QUOTAOFF - turn off disk quotas for a filesystem. | |
400 | */ | |
401 | quotaoff(mp, type) | |
402 | struct mount *mp; | |
403 | register int type; | |
404 | { | |
405 | register struct vnode *vp; | |
406 | struct vnode *qvp, *nextvp; | |
407 | struct ufsmount *ump = VFSTOUFS(mp); | |
408 | register struct dquot *dq; | |
409 | register struct inode *ip; | |
410 | ||
82161bc8 | 411 | if ((mp->mnt_flag & MNT_MPBUSY) == 0) |
c9ba0248 KM |
412 | panic("quotaoff: not busy"); |
413 | if ((qvp = ump->um_quotas[type]) == NULLVP) | |
414 | return (0); | |
415 | ump->um_qflags[type] |= QTF_CLOSING; | |
416 | /* | |
417 | * Search vnodes associated with this mount point, | |
418 | * deleting any references to quota file being closed. | |
419 | */ | |
420 | again: | |
82161bc8 | 421 | for (vp = mp->mnt_mounth; vp; vp = nextvp) { |
c9ba0248 KM |
422 | nextvp = vp->v_mountf; |
423 | if (vget(vp)) | |
424 | goto again; | |
425 | ip = VTOI(vp); | |
426 | dq = ip->i_dquot[type]; | |
427 | ip->i_dquot[type] = NODQUOT; | |
428 | dqrele(vp, dq); | |
429 | vput(vp); | |
430 | if (vp->v_mountf != nextvp || vp->v_mount != mp) | |
431 | goto again; | |
432 | } | |
433 | dqflush(qvp); | |
434 | qvp->v_flag &= ~VSYSTEM; | |
435 | vrele(qvp); | |
436 | ump->um_quotas[type] = NULLVP; | |
437 | crfree(ump->um_cred[type]); | |
438 | ump->um_cred[type] = NOCRED; | |
439 | ump->um_qflags[type] &= ~QTF_CLOSING; | |
440 | for (type = 0; type < MAXQUOTAS; type++) | |
441 | if (ump->um_quotas[type] != NULLVP) | |
442 | break; | |
443 | if (type == MAXQUOTAS) | |
82161bc8 | 444 | mp->mnt_flag &= ~MNT_QUOTA; |
c9ba0248 KM |
445 | return (0); |
446 | } | |
447 | ||
448 | /* | |
449 | * Q_GETQUOTA - return current values in a dqblk structure. | |
450 | */ | |
451 | getquota(mp, id, type, addr) | |
452 | struct mount *mp; | |
453 | u_long id; | |
454 | int type; | |
455 | caddr_t addr; | |
456 | { | |
457 | struct dquot *dq; | |
458 | int error; | |
459 | ||
460 | if (error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq)) | |
461 | return (error); | |
462 | error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk)); | |
463 | dqrele(NULLVP, dq); | |
464 | return (error); | |
465 | } | |
466 | ||
467 | /* | |
468 | * Q_SETQUOTA - assign an entire dqblk structure. | |
469 | */ | |
470 | setquota(mp, id, type, addr) | |
471 | struct mount *mp; | |
472 | u_long id; | |
473 | int type; | |
474 | caddr_t addr; | |
475 | { | |
476 | register struct dquot *dq; | |
477 | struct dquot *ndq; | |
478 | struct ufsmount *ump = VFSTOUFS(mp); | |
479 | struct dqblk newlim; | |
480 | int error; | |
481 | ||
482 | if (error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk))) | |
483 | return (error); | |
484 | if (error = dqget(NULLVP, id, ump, type, &ndq)) | |
485 | return (error); | |
486 | dq = ndq; | |
487 | while (dq->dq_flags & DQ_LOCK) { | |
488 | dq->dq_flags |= DQ_WANT; | |
489 | sleep((caddr_t)dq, PINOD+1); | |
490 | } | |
491 | /* | |
492 | * Copy all but the current values. | |
493 | * Reset time limit if previously had no soft limit or were | |
494 | * under it, but now have a soft limit and are over it. | |
495 | */ | |
496 | newlim.dqb_curblocks = dq->dq_curblocks; | |
497 | newlim.dqb_curinodes = dq->dq_curinodes; | |
498 | if (dq->dq_id != 0) { | |
499 | newlim.dqb_btime = dq->dq_btime; | |
500 | newlim.dqb_itime = dq->dq_itime; | |
501 | } | |
502 | if (newlim.dqb_bsoftlimit && | |
503 | dq->dq_curblocks >= newlim.dqb_bsoftlimit && | |
504 | (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit)) | |
505 | newlim.dqb_btime = time.tv_sec + ump->um_btime[type]; | |
506 | if (newlim.dqb_isoftlimit && | |
507 | dq->dq_curinodes >= newlim.dqb_isoftlimit && | |
508 | (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit)) | |
509 | newlim.dqb_itime = time.tv_sec + ump->um_itime[type]; | |
510 | dq->dq_dqb = newlim; | |
511 | if (dq->dq_curblocks < dq->dq_bsoftlimit) | |
512 | dq->dq_flags &= ~DQ_BLKS; | |
513 | if (dq->dq_curinodes < dq->dq_isoftlimit) | |
514 | dq->dq_flags &= ~DQ_INODS; | |
515 | if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && | |
516 | dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) | |
517 | dq->dq_flags |= DQ_FAKE; | |
518 | else | |
519 | dq->dq_flags &= ~DQ_FAKE; | |
520 | dq->dq_flags |= DQ_MOD; | |
521 | dqrele(NULLVP, dq); | |
522 | return (0); | |
523 | } | |
524 | ||
525 | /* | |
526 | * Q_SETUSE - set current inode and block usage. | |
527 | */ | |
528 | setuse(mp, id, type, addr) | |
529 | struct mount *mp; | |
530 | u_long id; | |
531 | int type; | |
532 | caddr_t addr; | |
533 | { | |
534 | register struct dquot *dq; | |
535 | struct ufsmount *ump = VFSTOUFS(mp); | |
536 | struct dquot *ndq; | |
537 | struct dqblk usage; | |
538 | int error; | |
539 | ||
540 | if (error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk))) | |
541 | return (error); | |
542 | if (error = dqget(NULLVP, id, ump, type, &ndq)) | |
543 | return (error); | |
544 | dq = ndq; | |
545 | while (dq->dq_flags & DQ_LOCK) { | |
546 | dq->dq_flags |= DQ_WANT; | |
547 | sleep((caddr_t)dq, PINOD+1); | |
548 | } | |
549 | /* | |
550 | * Reset time limit if have a soft limit and were | |
551 | * previously under it, but are now over it. | |
552 | */ | |
553 | if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit && | |
554 | usage.dqb_curblocks >= dq->dq_bsoftlimit) | |
555 | dq->dq_btime = time.tv_sec + ump->um_btime[type]; | |
556 | if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit && | |
557 | usage.dqb_curinodes >= dq->dq_isoftlimit) | |
558 | dq->dq_itime = time.tv_sec + ump->um_itime[type]; | |
559 | dq->dq_curblocks = usage.dqb_curblocks; | |
560 | dq->dq_curinodes = usage.dqb_curinodes; | |
561 | if (dq->dq_curblocks < dq->dq_bsoftlimit) | |
562 | dq->dq_flags &= ~DQ_BLKS; | |
563 | if (dq->dq_curinodes < dq->dq_isoftlimit) | |
564 | dq->dq_flags &= ~DQ_INODS; | |
565 | dq->dq_flags |= DQ_MOD; | |
566 | dqrele(NULLVP, dq); | |
567 | return (0); | |
568 | } | |
569 | ||
570 | /* | |
571 | * Q_SYNC - sync quota files to disk. | |
572 | */ | |
573 | qsync(mp) | |
574 | struct mount *mp; | |
575 | { | |
576 | struct ufsmount *ump = VFSTOUFS(mp); | |
577 | register struct vnode *vp, *nextvp; | |
578 | register struct dquot *dq; | |
579 | register int i; | |
580 | ||
581 | /* | |
582 | * Search vnodes associated with this mount point, | |
583 | * synchronizing any modified dquot structures. | |
584 | */ | |
82161bc8 | 585 | if ((mp->mnt_flag & MNT_MPBUSY) == 0) |
c9ba0248 KM |
586 | panic("qsync: not busy"); |
587 | again: | |
82161bc8 | 588 | for (vp = mp->mnt_mounth; vp; vp = nextvp) { |
c9ba0248 KM |
589 | nextvp = vp->v_mountf; |
590 | if (vget(vp)) | |
591 | goto again; | |
592 | for (i = 0; i < MAXQUOTAS; i++) { | |
593 | dq = VTOI(vp)->i_dquot[i]; | |
594 | if (dq != NODQUOT && (dq->dq_flags & DQ_MOD)) | |
595 | dqsync(vp, dq); | |
596 | } | |
597 | vput(vp); | |
598 | if (vp->v_mountf != nextvp || vp->v_mount != mp) | |
599 | goto again; | |
600 | } | |
601 | return (0); | |
602 | } | |
603 | ||
604 | /* | |
605 | * Code pertaining to management of the in-core dquot data structures. | |
606 | */ | |
607 | ||
608 | /* | |
609 | * Dquot cache - hash chain headers. | |
610 | */ | |
611 | union dqhead { | |
612 | union dqhead *dqh_head[2]; | |
613 | struct dquot *dqh_chain[2]; | |
614 | }; | |
615 | #define dqh_forw dqh_chain[0] | |
616 | #define dqh_back dqh_chain[1] | |
617 | ||
618 | union dqhead *dqhashtbl; | |
619 | long dqhash; | |
620 | ||
621 | /* | |
622 | * Dquot free list. | |
623 | */ | |
624 | #define DQUOTINC 5 /* minimum free dquots desired */ | |
625 | struct dquot *dqfreel, **dqback = &dqfreel; | |
626 | long numdquot, desireddquot = DQUOTINC; | |
627 | ||
628 | /* | |
629 | * Initialize the quota system. | |
630 | */ | |
631 | dqinit() | |
632 | { | |
633 | register union dqhead *dhp; | |
634 | register long dqhashsize; | |
635 | ||
636 | dqhashsize = roundup((desiredvnodes + 1) * sizeof *dhp / 2, | |
637 | NBPG * CLSIZE); | |
638 | dqhashtbl = (union dqhead *)malloc(dqhashsize, M_DQUOT, M_WAITOK); | |
639 | for (dqhash = 1; dqhash <= dqhashsize / sizeof *dhp; dqhash <<= 1) | |
640 | /* void */; | |
641 | dqhash = (dqhash >> 1) - 1; | |
642 | for (dhp = &dqhashtbl[dqhash]; dhp >= dqhashtbl; dhp--) { | |
643 | dhp->dqh_head[0] = dhp; | |
644 | dhp->dqh_head[1] = dhp; | |
645 | } | |
646 | } | |
647 | ||
648 | /* | |
649 | * Obtain a dquot structure for the specified identifier and quota file | |
650 | * reading the information from the file if necessary. | |
651 | */ | |
652 | dqget(vp, id, ump, type, dqp) | |
653 | struct vnode *vp; | |
654 | u_long id; | |
655 | register struct ufsmount *ump; | |
656 | register int type; | |
657 | struct dquot **dqp; | |
658 | { | |
659 | register struct dquot *dq; | |
660 | register union dqhead *dh; | |
661 | register struct dquot *dp; | |
662 | register struct vnode *dqvp; | |
663 | struct iovec aiov; | |
664 | struct uio auio; | |
665 | int error; | |
666 | ||
667 | dqvp = ump->um_quotas[type]; | |
668 | if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) { | |
669 | *dqp = NODQUOT; | |
670 | return (EINVAL); | |
671 | } | |
672 | /* | |
673 | * Check the cache first. | |
674 | */ | |
675 | dh = &dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash]; | |
676 | for (dq = dh->dqh_forw; dq != (struct dquot *)dh; dq = dq->dq_forw) { | |
677 | if (dq->dq_id != id || | |
678 | dq->dq_ump->um_quotas[dq->dq_type] != dqvp) | |
679 | continue; | |
680 | /* | |
681 | * Cache hit with no references. Take | |
682 | * the structure off the free list. | |
683 | */ | |
684 | if (dq->dq_cnt == 0) { | |
685 | dp = dq->dq_freef; | |
686 | if (dp != NODQUOT) | |
687 | dp->dq_freeb = dq->dq_freeb; | |
688 | else | |
689 | dqback = dq->dq_freeb; | |
690 | *dq->dq_freeb = dp; | |
691 | } | |
692 | DQREF(dq); | |
693 | *dqp = dq; | |
694 | return (0); | |
695 | } | |
696 | /* | |
697 | * Not in cache, allocate a new one. | |
698 | */ | |
699 | if (dqfreel == NODQUOT && numdquot < MAXQUOTAS * desiredvnodes) | |
700 | desireddquot += DQUOTINC; | |
701 | if (numdquot < desireddquot) { | |
702 | dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK); | |
703 | bzero((char *)dq, sizeof *dq); | |
704 | numdquot++; | |
705 | } else { | |
706 | if ((dq = dqfreel) == NULL) { | |
707 | tablefull("dquot"); | |
708 | *dqp = NODQUOT; | |
709 | return (EUSERS); | |
710 | } | |
711 | if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) | |
712 | panic("free dquot isn't"); | |
713 | if ((dp = dq->dq_freef) != NODQUOT) | |
714 | dp->dq_freeb = &dqfreel; | |
715 | else | |
716 | dqback = &dqfreel; | |
717 | dqfreel = dp; | |
718 | dq->dq_freef = NULL; | |
719 | dq->dq_freeb = NULL; | |
720 | remque(dq); | |
721 | } | |
722 | /* | |
723 | * Initialize the contents of the dquot structure. | |
724 | */ | |
725 | if (vp != dqvp) | |
726 | VOP_LOCK(dqvp); | |
727 | insque(dq, dh); | |
728 | DQREF(dq); | |
729 | dq->dq_flags = DQ_LOCK; | |
730 | dq->dq_id = id; | |
731 | dq->dq_ump = ump; | |
732 | dq->dq_type = type; | |
733 | auio.uio_iov = &aiov; | |
734 | auio.uio_iovcnt = 1; | |
735 | aiov.iov_base = (caddr_t)&dq->dq_dqb; | |
736 | aiov.iov_len = sizeof (struct dqblk); | |
737 | auio.uio_resid = sizeof (struct dqblk); | |
738 | auio.uio_offset = (off_t)(id * sizeof (struct dqblk)); | |
739 | auio.uio_segflg = UIO_SYSSPACE; | |
740 | auio.uio_rw = UIO_READ; | |
741 | error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]); | |
2842f018 KM |
742 | if (auio.uio_resid == sizeof(struct dqblk) && error == 0) |
743 | bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk)); | |
c9ba0248 KM |
744 | if (vp != dqvp) |
745 | VOP_UNLOCK(dqvp); | |
746 | if (dq->dq_flags & DQ_WANT) | |
747 | wakeup((caddr_t)dq); | |
748 | dq->dq_flags = 0; | |
749 | /* | |
750 | * I/O error in reading quota file, release | |
751 | * quota structure and reflect problem to caller. | |
752 | */ | |
753 | if (error) { | |
754 | remque(dq); | |
755 | dq->dq_forw = dq; /* on a private, unfindable hash list */ | |
756 | dq->dq_back = dq; | |
757 | dqrele(vp, dq); | |
758 | *dqp = NODQUOT; | |
759 | return (error); | |
760 | } | |
761 | /* | |
762 | * Check for no limit to enforce. | |
763 | * Initialize time values if necessary. | |
764 | */ | |
765 | if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && | |
766 | dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) | |
767 | dq->dq_flags |= DQ_FAKE; | |
768 | if (dq->dq_id != 0) { | |
769 | if (dq->dq_btime == 0) | |
770 | dq->dq_btime = time.tv_sec + ump->um_btime[type]; | |
771 | if (dq->dq_itime == 0) | |
772 | dq->dq_itime = time.tv_sec + ump->um_itime[type]; | |
773 | } | |
774 | *dqp = dq; | |
775 | return (0); | |
776 | } | |
777 | ||
778 | /* | |
779 | * Obtain a reference to a dquot. | |
780 | */ | |
781 | dqref(dq) | |
782 | struct dquot *dq; | |
783 | { | |
784 | ||
785 | dq->dq_cnt++; | |
786 | } | |
787 | ||
788 | /* | |
789 | * Release a reference to a dquot. | |
790 | */ | |
791 | dqrele(vp, dq) | |
792 | struct vnode *vp; | |
793 | register struct dquot *dq; | |
794 | { | |
795 | ||
796 | if (dq == NODQUOT) | |
797 | return; | |
798 | if (dq->dq_cnt > 1) { | |
799 | dq->dq_cnt--; | |
800 | return; | |
801 | } | |
802 | if (dq->dq_flags & DQ_MOD) | |
803 | (void) dqsync(vp, dq); | |
804 | if (--dq->dq_cnt > 0) | |
805 | return; | |
806 | if (dqfreel != NODQUOT) { | |
807 | *dqback = dq; | |
808 | dq->dq_freeb = dqback; | |
809 | } else { | |
810 | dqfreel = dq; | |
811 | dq->dq_freeb = &dqfreel; | |
812 | } | |
813 | dq->dq_freef = NODQUOT; | |
814 | dqback = &dq->dq_freef; | |
815 | } | |
816 | ||
817 | /* | |
818 | * Update the disk quota in the quota file. | |
819 | */ | |
820 | dqsync(vp, dq) | |
821 | struct vnode *vp; | |
822 | register struct dquot *dq; | |
823 | { | |
824 | struct vnode *dqvp; | |
825 | struct iovec aiov; | |
826 | struct uio auio; | |
827 | int error; | |
828 | ||
829 | if (dq == NODQUOT) | |
830 | panic("dqsync: dquot"); | |
831 | if ((dq->dq_flags & DQ_MOD) == 0) | |
832 | return (0); | |
833 | if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP) | |
834 | panic("dqsync: file"); | |
835 | if (vp != dqvp) | |
836 | VOP_LOCK(dqvp); | |
837 | while (dq->dq_flags & DQ_LOCK) { | |
838 | dq->dq_flags |= DQ_WANT; | |
839 | sleep((caddr_t)dq, PINOD+2); | |
c0486eb9 KM |
840 | if ((dq->dq_flags & DQ_MOD) == 0) { |
841 | if (vp != dqvp) | |
842 | VOP_UNLOCK(dqvp); | |
c9ba0248 | 843 | return (0); |
c0486eb9 | 844 | } |
c9ba0248 KM |
845 | } |
846 | dq->dq_flags |= DQ_LOCK; | |
847 | auio.uio_iov = &aiov; | |
848 | auio.uio_iovcnt = 1; | |
849 | aiov.iov_base = (caddr_t)&dq->dq_dqb; | |
850 | aiov.iov_len = sizeof (struct dqblk); | |
851 | auio.uio_resid = sizeof (struct dqblk); | |
852 | auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk)); | |
853 | auio.uio_segflg = UIO_SYSSPACE; | |
854 | auio.uio_rw = UIO_WRITE; | |
855 | error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]); | |
856 | if (auio.uio_resid && error == 0) | |
857 | error = EIO; | |
858 | if (dq->dq_flags & DQ_WANT) | |
859 | wakeup((caddr_t)dq); | |
860 | dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT); | |
861 | if (vp != dqvp) | |
862 | VOP_UNLOCK(dqvp); | |
863 | return (error); | |
864 | } | |
865 | ||
866 | /* | |
867 | * Flush all entries from the cache for a particular vnode. | |
868 | */ | |
869 | dqflush(vp) | |
870 | register struct vnode *vp; | |
871 | { | |
872 | register union dqhead *dh; | |
873 | register struct dquot *dq, *nextdq; | |
874 | ||
875 | /* | |
876 | * Move all dquot's that used to refer to this quota | |
877 | * file off their hash chains (they will eventually | |
878 | * fall off the head of the free list and be re-used). | |
879 | */ | |
880 | for (dh = &dqhashtbl[dqhash]; dh >= dqhashtbl; dh--) { | |
881 | for (dq = dh->dqh_forw; dq != (struct dquot *)dh; dq = nextdq) { | |
882 | nextdq = dq->dq_forw; | |
883 | if (dq->dq_ump->um_quotas[dq->dq_type] != vp) | |
884 | continue; | |
885 | if (dq->dq_cnt) | |
886 | panic("dqflush: stray dquot"); | |
887 | remque(dq); | |
888 | dq->dq_forw = dq; | |
889 | dq->dq_back = dq; | |
890 | dq->dq_ump = (struct ufsmount *)0; | |
891 | } | |
892 | } | |
893 | } |