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