2 * Copyright (c) 1982, 1986, 1990, 1993, 1995
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Robert Elz at The University of Melbourne.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
37 * $FreeBSD: src/sys/ufs/ufs/ufs_quota.c,v 1.27.2.3 2002/01/15 10:33:32 phk Exp $
38 * $DragonFly: src/sys/vfs/ufs/ufs_quota.c,v 1.27 2008/01/05 14:02:41 swildner Exp $
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/malloc.h>
45 #include <sys/fcntl.h>
47 #include <sys/nlookup.h>
48 #include <sys/vnode.h>
49 #include <sys/mount.h>
50 #include <vm/vm_zone.h>
56 static MALLOC_DEFINE(M_DQUOT
, "UFS quota", "UFS quota entries");
59 * Quota name to error message mapping.
61 static char *quotatypes
[] = INITQFNAMES
;
63 static int ufs_chkdqchg (struct inode
*, long, struct ucred
*, int);
64 static int ufs_chkiqchg (struct inode
*, long, struct ucred
*, int);
65 static int ufs_dqget (struct vnode
*,
66 u_long
, struct ufsmount
*, int, struct ufs_dquot
**);
67 static int ufs_dqsync (struct vnode
*, struct ufs_dquot
*);
68 static void ufs_dqflush (struct vnode
*);
69 static void ufs_quotawarn(struct ufs_dquot
*dq
);
72 static void ufs_dqref (struct ufs_dquot
*);
73 static void ufs_chkdquot (struct inode
*);
77 * Set up the quotas for an inode.
79 * This routine completely defines the semantics of quotas.
80 * If other criterion want to be used to establish quotas, the
81 * MAXQUOTAS value in quotas.h should be increased, and the
82 * additional dquots set up here.
85 ufs_getinoquota(struct inode
*ip
)
88 struct vnode
*vp
= ITOV(ip
);
91 ump
= VFSTOUFS(vp
->v_mount
);
93 * Set up the user quota based on file uid.
94 * EINVAL means that quotas are not enabled.
96 if (ip
->i_dquot
[USRQUOTA
] == NODQUOT
&&
97 (error
= ufs_dqget(vp
, ip
->i_uid
, ump
, USRQUOTA
, &ip
->i_dquot
[USRQUOTA
])) &&
101 * Set up the group quota based on file gid.
102 * EINVAL means that quotas are not enabled.
104 if (ip
->i_dquot
[GRPQUOTA
] == NODQUOT
&&
105 (error
= ufs_dqget(vp
, ip
->i_gid
, ump
, GRPQUOTA
, &ip
->i_dquot
[GRPQUOTA
])) &&
112 * Update disk usage, and take corrective action.
115 ufs_chkdq(struct inode
*ip
, long change
, struct ucred
*cred
, int flags
)
117 struct ufs_dquot
*dq
;
119 int ncurblocks
, error
;
122 if ((flags
& CHOWN
) == 0)
128 for (i
= 0; i
< MAXQUOTAS
; i
++) {
129 if ((dq
= ip
->i_dquot
[i
]) == NODQUOT
)
131 if (dq
->dq_ump
->um_quotas
[dq
->dq_type
] == ip
->i_vnode
) {
135 while (dq
->dq_flags
& DQ_LOCK
) {
136 dq
->dq_flags
|= DQ_WANT
;
137 (void) tsleep((caddr_t
)dq
, 0, "chkdq1", 0);
139 ncurblocks
= dq
->dq_curblocks
+ change
;
141 dq
->dq_curblocks
= ncurblocks
;
143 dq
->dq_curblocks
= 0;
144 dq
->dq_flags
&= ~DQ_BLKS
;
145 dq
->dq_flags
|= DQ_MOD
;
149 if ((flags
& FORCE
) == 0 && cred
->cr_uid
!= 0) {
150 for (i
= 0; i
< MAXQUOTAS
; i
++) {
151 if ((dq
= ip
->i_dquot
[i
]) == NODQUOT
)
153 if (dq
->dq_ump
->um_quotas
[dq
->dq_type
] == ip
->i_vnode
) {
157 error
= ufs_chkdqchg(ip
, change
, cred
, i
);
162 for (i
= 0; i
< MAXQUOTAS
; i
++) {
163 if ((dq
= ip
->i_dquot
[i
]) == NODQUOT
)
165 if (dq
->dq_ump
->um_quotas
[dq
->dq_type
] == ip
->i_vnode
) {
169 while (dq
->dq_flags
& DQ_LOCK
) {
170 dq
->dq_flags
|= DQ_WANT
;
171 (void) tsleep((caddr_t
)dq
, 0, "chkdq2", 0);
173 /* Reset timer when crossing soft limit */
174 if (dq
->dq_curblocks
+ change
>= dq
->dq_bsoftlimit
&&
175 dq
->dq_curblocks
< dq
->dq_bsoftlimit
)
176 dq
->dq_btime
= time_second
+
177 VFSTOUFS(ITOV(ip
)->v_mount
)->um_btime
[i
];
178 dq
->dq_curblocks
+= change
;
179 dq
->dq_flags
|= DQ_MOD
;
185 * Check for a valid change to a users allocation.
186 * Issue an error message if appropriate.
189 ufs_chkdqchg(struct inode
*ip
, long change
, struct ucred
*cred
, int type
)
191 struct ufs_dquot
*dq
= ip
->i_dquot
[type
];
192 long ncurblocks
= dq
->dq_curblocks
+ change
;
195 * If user would exceed their hard limit, disallow space allocation.
197 if (ncurblocks
>= dq
->dq_bhardlimit
&& dq
->dq_bhardlimit
) {
198 if ((dq
->dq_flags
& DQ_BLKS
) == 0 &&
199 ip
->i_uid
== cred
->cr_uid
) {
200 uprintf("\n%s: write failed, %s disk limit reached\n",
201 ITOV(ip
)->v_mount
->mnt_stat
.f_mntfromname
,
203 dq
->dq_flags
|= DQ_BLKS
;
208 * If user is over their soft limit for too long, disallow space
209 * allocation. Reset time limit as they cross their soft limit.
211 if (ncurblocks
>= dq
->dq_bsoftlimit
&& dq
->dq_bsoftlimit
) {
212 if (dq
->dq_curblocks
< dq
->dq_bsoftlimit
) {
213 dq
->dq_btime
= time_second
+
214 VFSTOUFS(ITOV(ip
)->v_mount
)->um_btime
[type
];
215 if (ip
->i_uid
== cred
->cr_uid
)
216 uprintf("\n%s: warning, %s %s\n",
217 ITOV(ip
)->v_mount
->mnt_stat
.f_mntfromname
,
218 quotatypes
[type
], "disk quota exceeded");
221 if (time_second
> dq
->dq_btime
) {
222 if ((dq
->dq_flags
& DQ_BLKS
) == 0 &&
223 ip
->i_uid
== cred
->cr_uid
) {
224 uprintf("\n%s: write failed, %s %s\n",
225 ITOV(ip
)->v_mount
->mnt_stat
.f_mntfromname
,
227 "disk quota exceeded for too long");
228 dq
->dq_flags
|= DQ_BLKS
;
237 * Check the inode limit, applying corrective action.
240 ufs_chkiq(struct inode
*ip
, long change
, struct ucred
*cred
, int flags
)
242 struct ufs_dquot
*dq
;
244 int ncurinodes
, error
;
247 if ((flags
& CHOWN
) == 0)
253 for (i
= 0; i
< MAXQUOTAS
; i
++) {
254 if ((dq
= ip
->i_dquot
[i
]) == NODQUOT
)
256 if (dq
->dq_ump
->um_quotas
[dq
->dq_type
] == ip
->i_vnode
) {
260 while (dq
->dq_flags
& DQ_LOCK
) {
261 dq
->dq_flags
|= DQ_WANT
;
262 (void) tsleep((caddr_t
)dq
, 0, "chkiq1", 0);
264 ncurinodes
= dq
->dq_curinodes
+ change
;
266 dq
->dq_curinodes
= ncurinodes
;
268 dq
->dq_curinodes
= 0;
269 dq
->dq_flags
&= ~DQ_INODS
;
270 dq
->dq_flags
|= DQ_MOD
;
274 if ((flags
& FORCE
) == 0 && cred
->cr_uid
!= 0) {
275 for (i
= 0; i
< MAXQUOTAS
; i
++) {
276 if ((dq
= ip
->i_dquot
[i
]) == NODQUOT
)
278 if (dq
->dq_ump
->um_quotas
[dq
->dq_type
] == ip
->i_vnode
) {
282 error
= ufs_chkiqchg(ip
, change
, cred
, i
);
287 for (i
= 0; i
< MAXQUOTAS
; i
++) {
288 if ((dq
= ip
->i_dquot
[i
]) == NODQUOT
)
290 if (dq
->dq_ump
->um_quotas
[dq
->dq_type
] == ip
->i_vnode
) {
294 while (dq
->dq_flags
& DQ_LOCK
) {
295 dq
->dq_flags
|= DQ_WANT
;
296 (void) tsleep((caddr_t
)dq
, 0, "chkiq2", 0);
298 /* Reset timer when crossing soft limit */
299 if (dq
->dq_curinodes
+ change
>= dq
->dq_isoftlimit
&&
300 dq
->dq_curinodes
< dq
->dq_isoftlimit
)
301 dq
->dq_itime
= time_second
+
302 VFSTOUFS(ITOV(ip
)->v_mount
)->um_itime
[i
];
303 dq
->dq_curinodes
+= change
;
304 dq
->dq_flags
|= DQ_MOD
;
310 * Check for a valid change to a users allocation.
311 * Issue an error message if appropriate.
314 ufs_chkiqchg(struct inode
*ip
, long change
, struct ucred
*cred
, int type
)
316 struct ufs_dquot
*dq
= ip
->i_dquot
[type
];
317 long ncurinodes
= dq
->dq_curinodes
+ change
;
320 * If user would exceed their hard limit, disallow inode allocation.
322 if (ncurinodes
>= dq
->dq_ihardlimit
&& dq
->dq_ihardlimit
) {
323 if ((dq
->dq_flags
& DQ_INODS
) == 0 &&
324 ip
->i_uid
== cred
->cr_uid
) {
325 uprintf("\n%s: write failed, %s inode limit reached\n",
326 ITOV(ip
)->v_mount
->mnt_stat
.f_mntfromname
,
328 dq
->dq_flags
|= DQ_INODS
;
333 * If user is over their soft limit for too long, disallow inode
334 * allocation. Reset time limit as they cross their soft limit.
336 if (ncurinodes
>= dq
->dq_isoftlimit
&& dq
->dq_isoftlimit
) {
337 if (dq
->dq_curinodes
< dq
->dq_isoftlimit
) {
338 dq
->dq_itime
= time_second
+
339 VFSTOUFS(ITOV(ip
)->v_mount
)->um_itime
[type
];
340 if (ip
->i_uid
== cred
->cr_uid
)
341 uprintf("\n%s: warning, %s %s\n",
342 ITOV(ip
)->v_mount
->mnt_stat
.f_mntfromname
,
343 quotatypes
[type
], "inode quota exceeded");
346 if (time_second
> dq
->dq_itime
) {
347 if ((dq
->dq_flags
& DQ_INODS
) == 0 &&
348 ip
->i_uid
== cred
->cr_uid
) {
349 uprintf("\n%s: write failed, %s %s\n",
350 ITOV(ip
)->v_mount
->mnt_stat
.f_mntfromname
,
352 "inode quota exceeded for too long");
353 dq
->dq_flags
|= DQ_INODS
;
362 * To avoid a deadlock we disallow quota operations on the quota file itself.
363 * This generally means that quotacheck was not run on the filesystem.
367 ufs_quotawarn(struct ufs_dquot
*dq
)
371 if (dqticks
!= ticks
/ hz
) {
372 dqticks
= ticks
/ hz
;
373 uprintf("%s: warning, quota file expanded, quotacheck "
375 dq
->dq_ump
->um_mountp
->mnt_stat
.f_mntfromname
);
381 * On filesystems with quotas enabled, it is an error for a file to change
382 * size and not to have a dquot structure associated with it.
385 ufs_chkdquot(struct inode
*ip
)
387 struct ufsmount
*ump
= VFSTOUFS(ITOV(ip
)->v_mount
);
390 for (i
= 0; i
< MAXQUOTAS
; i
++) {
391 if (ump
->um_quotas
[i
] == NULLVP
||
392 (ump
->um_qflags
[i
] & (QTF_OPENING
|QTF_CLOSING
)))
394 if (ip
->i_dquot
[i
] == NODQUOT
) {
395 vprint("chkdquot: missing dquot", ITOV(ip
));
396 panic("chkdquot: missing dquot");
403 * Code to process quotactl commands.
412 * Q_QUOTAON - set up a quota file for a particular filesystem.
414 static int ufs_quotaon_scan(struct mount
*mp
, struct vnode
*vp
, void *data
);
417 ufs_quotaon(struct ucred
*cred
, struct mount
*mp
, int type
, caddr_t fname
)
419 struct ufsmount
*ump
= VFSTOUFS(mp
);
420 struct vnode
*vp
, **vpp
;
421 struct ufs_dquot
*dq
;
423 struct nlookupdata nd
;
424 struct scaninfo scaninfo
;
426 vpp
= &ump
->um_quotas
[type
];
427 error
= nlookup_init(&nd
, fname
, UIO_USERSPACE
, NLC_FOLLOW
|NLC_LOCKVP
);
429 error
= vn_open(&nd
, NULL
, FREAD
|FWRITE
, 0);
430 if (error
== 0 && nd
.nl_open_vp
->v_type
!= VREG
)
437 nd
.nl_open_vp
= NULL
;
442 ufs_quotaoff(mp
, type
);
443 ump
->um_qflags
[type
] |= QTF_OPENING
;
444 mp
->mnt_flag
|= MNT_QUOTA
;
445 vp
->v_flag
|= VSYSTEM
;
447 /* XXX release duplicate vp if *vpp == vp? */
449 * Save the credential of the process that turned on quotas.
450 * Set up the time limits for this quota.
452 ump
->um_cred
[type
] = crhold(cred
);
453 ump
->um_btime
[type
] = MAX_DQ_TIME
;
454 ump
->um_itime
[type
] = MAX_IQ_TIME
;
455 if (ufs_dqget(NULLVP
, 0, ump
, type
, &dq
) == 0) {
456 if (dq
->dq_btime
> 0)
457 ump
->um_btime
[type
] = dq
->dq_btime
;
458 if (dq
->dq_itime
> 0)
459 ump
->um_itime
[type
] = dq
->dq_itime
;
460 ufs_dqrele(NULLVP
, dq
);
463 * Search vnodes associated with this mount point,
464 * adding references to quota file being opened.
465 * NB: only need to add dquot's for inodes being modified.
468 while (scaninfo
.rescan
) {
470 error
= vmntvnodescan(mp
, VMSC_GETVP
,
471 NULL
, ufs_quotaon_scan
, &scaninfo
);
475 ump
->um_qflags
[type
] &= ~QTF_OPENING
;
477 ufs_quotaoff(mp
, type
);
482 ufs_quotaon_scan(struct mount
*mp
, struct vnode
*vp
, void *data
)
485 /*struct scaninfo *info = data;*/
487 if (vp
->v_writecount
== 0)
489 error
= ufs_getinoquota(VTOI(vp
));
494 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
497 static int ufs_quotaoff_scan(struct mount
*mp
, struct vnode
*vp
, void *data
);
500 ufs_quotaoff(struct mount
*mp
, int type
)
503 struct ufsmount
*ump
= VFSTOUFS(mp
);
505 struct scaninfo scaninfo
;
507 if ((qvp
= ump
->um_quotas
[type
]) == NULLVP
)
509 ump
->um_qflags
[type
] |= QTF_CLOSING
;
512 * Search vnodes associated with this mount point,
513 * deleting any references to quota file being closed.
516 scaninfo
.type
= type
;
517 while (scaninfo
.rescan
) {
519 vmntvnodescan(mp
, VMSC_GETVP
, NULL
, ufs_quotaoff_scan
, &scaninfo
);
522 qvp
->v_flag
&= ~VSYSTEM
;
523 error
= vn_close(qvp
, FREAD
|FWRITE
);
524 ump
->um_quotas
[type
] = NULLVP
;
525 crfree(ump
->um_cred
[type
]);
526 ump
->um_cred
[type
] = NOCRED
;
527 ump
->um_qflags
[type
] &= ~QTF_CLOSING
;
528 for (type
= 0; type
< MAXQUOTAS
; type
++) {
529 if (ump
->um_quotas
[type
] != NULLVP
)
532 if (type
== MAXQUOTAS
)
533 mp
->mnt_flag
&= ~MNT_QUOTA
;
538 ufs_quotaoff_scan(struct mount
*mp
, struct vnode
*vp
, void *data
)
540 struct scaninfo
*info
= data
;
541 struct ufs_dquot
*dq
;
544 if (vp
->v_type
== VNON
) {
548 dq
= ip
->i_dquot
[info
->type
];
549 ip
->i_dquot
[info
->type
] = NODQUOT
;
555 * Q_GETQUOTA - return current values in a dqblk structure.
558 ufs_getquota(struct mount
*mp
, u_long id
, int type
, caddr_t addr
)
560 struct ufs_dquot
*dq
;
563 error
= ufs_dqget(NULLVP
, id
, VFSTOUFS(mp
), type
, &dq
);
566 error
= copyout((caddr_t
)&dq
->dq_dqb
, addr
, sizeof (struct ufs_dqblk
));
567 ufs_dqrele(NULLVP
, dq
);
572 * Q_SETQUOTA - assign an entire dqblk structure.
575 ufs_setquota(struct mount
*mp
, u_long id
, int type
, caddr_t addr
)
577 struct ufs_dquot
*dq
;
578 struct ufs_dquot
*ndq
;
579 struct ufsmount
*ump
= VFSTOUFS(mp
);
580 struct ufs_dqblk newlim
;
583 error
= copyin(addr
, (caddr_t
)&newlim
, sizeof (struct ufs_dqblk
));
586 error
= ufs_dqget(NULLVP
, id
, ump
, type
, &ndq
);
590 while (dq
->dq_flags
& DQ_LOCK
) {
591 dq
->dq_flags
|= DQ_WANT
;
592 (void) tsleep((caddr_t
)dq
, 0, "setqta", 0);
595 * Copy all but the current values.
596 * Reset time limit if previously had no soft limit or were
597 * under it, but now have a soft limit and are over it.
599 newlim
.dqb_curblocks
= dq
->dq_curblocks
;
600 newlim
.dqb_curinodes
= dq
->dq_curinodes
;
601 if (dq
->dq_id
!= 0) {
602 newlim
.dqb_btime
= dq
->dq_btime
;
603 newlim
.dqb_itime
= dq
->dq_itime
;
605 if (newlim
.dqb_bsoftlimit
&&
606 dq
->dq_curblocks
>= newlim
.dqb_bsoftlimit
&&
607 (dq
->dq_bsoftlimit
== 0 || dq
->dq_curblocks
< dq
->dq_bsoftlimit
))
608 newlim
.dqb_btime
= time_second
+ ump
->um_btime
[type
];
609 if (newlim
.dqb_isoftlimit
&&
610 dq
->dq_curinodes
>= newlim
.dqb_isoftlimit
&&
611 (dq
->dq_isoftlimit
== 0 || dq
->dq_curinodes
< dq
->dq_isoftlimit
))
612 newlim
.dqb_itime
= time_second
+ ump
->um_itime
[type
];
614 if (dq
->dq_curblocks
< dq
->dq_bsoftlimit
)
615 dq
->dq_flags
&= ~DQ_BLKS
;
616 if (dq
->dq_curinodes
< dq
->dq_isoftlimit
)
617 dq
->dq_flags
&= ~DQ_INODS
;
618 if (dq
->dq_isoftlimit
== 0 && dq
->dq_bsoftlimit
== 0 &&
619 dq
->dq_ihardlimit
== 0 && dq
->dq_bhardlimit
== 0)
620 dq
->dq_flags
|= DQ_FAKE
;
622 dq
->dq_flags
&= ~DQ_FAKE
;
623 dq
->dq_flags
|= DQ_MOD
;
624 ufs_dqrele(NULLVP
, dq
);
629 * Q_SETUSE - set current inode and block usage.
632 ufs_setuse(struct mount
*mp
, u_long id
, int type
, caddr_t addr
)
634 struct ufs_dquot
*dq
;
635 struct ufsmount
*ump
= VFSTOUFS(mp
);
636 struct ufs_dquot
*ndq
;
637 struct ufs_dqblk usage
;
640 error
= copyin(addr
, (caddr_t
)&usage
, sizeof (struct ufs_dqblk
));
643 error
= ufs_dqget(NULLVP
, id
, ump
, type
, &ndq
);
647 while (dq
->dq_flags
& DQ_LOCK
) {
648 dq
->dq_flags
|= DQ_WANT
;
649 (void) tsleep((caddr_t
)dq
, 0, "setuse", 0);
652 * Reset time limit if have a soft limit and were
653 * previously under it, but are now over it.
655 if (dq
->dq_bsoftlimit
&& dq
->dq_curblocks
< dq
->dq_bsoftlimit
&&
656 usage
.dqb_curblocks
>= dq
->dq_bsoftlimit
)
657 dq
->dq_btime
= time_second
+ ump
->um_btime
[type
];
658 if (dq
->dq_isoftlimit
&& dq
->dq_curinodes
< dq
->dq_isoftlimit
&&
659 usage
.dqb_curinodes
>= dq
->dq_isoftlimit
)
660 dq
->dq_itime
= time_second
+ ump
->um_itime
[type
];
661 dq
->dq_curblocks
= usage
.dqb_curblocks
;
662 dq
->dq_curinodes
= usage
.dqb_curinodes
;
663 if (dq
->dq_curblocks
< dq
->dq_bsoftlimit
)
664 dq
->dq_flags
&= ~DQ_BLKS
;
665 if (dq
->dq_curinodes
< dq
->dq_isoftlimit
)
666 dq
->dq_flags
&= ~DQ_INODS
;
667 dq
->dq_flags
|= DQ_MOD
;
668 ufs_dqrele(NULLVP
, dq
);
673 * Q_SYNC - sync quota files to disk.
676 static int ufs_qsync_scan(struct mount
*mp
, struct vnode
*vp
, void *data
);
679 ufs_qsync(struct mount
*mp
)
681 struct ufsmount
*ump
= VFSTOUFS(mp
);
682 struct scaninfo scaninfo
;
686 * Check if the mount point has any quotas.
687 * If not, simply return.
689 for (i
= 0; i
< MAXQUOTAS
; i
++)
690 if (ump
->um_quotas
[i
] != NULLVP
)
695 * Search vnodes associated with this mount point,
696 * synchronizing any modified ufs_dquot structures.
699 while (scaninfo
.rescan
) {
701 vmntvnodescan(mp
, VMSC_GETVP
|VMSC_NOWAIT
,
702 NULL
, ufs_qsync_scan
, &scaninfo
);
708 ufs_qsync_scan(struct mount
*mp
, struct vnode
*vp
, void *data
)
710 /*struct scaninfo *info = data;*/
711 struct ufs_dquot
*dq
;
715 for (i
= 0; i
< MAXQUOTAS
; i
++) {
716 dq
= VTOI(vp
)->i_dquot
[i
];
717 if (dq
!= NODQUOT
&& (dq
->dq_flags
& DQ_MOD
))
724 * Code pertaining to management of the in-core dquot data structures.
726 #define DQHASH(dqvp, id) \
727 (&ufs_dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & ufs_dqhash])
728 static LIST_HEAD(ufs_dqhash
, ufs_dquot
) *ufs_dqhashtbl
;
729 static u_long ufs_dqhash
;
734 #define DQUOTINC 5 /* minimum free dquots desired */
735 static TAILQ_HEAD(ufs_dqfreelist
, ufs_dquot
) ufs_dqfreelist
;
736 static long ufs_numdquot
, ufs_desireddquot
= DQUOTINC
;
739 * Initialize the quota system.
744 ufs_dqhashtbl
= hashinit(desiredvnodes
, M_DQUOT
, &ufs_dqhash
);
745 TAILQ_INIT(&ufs_dqfreelist
);
749 * Obtain a dquot structure for the specified identifier and quota file
750 * reading the information from the file if necessary.
753 ufs_dqget(struct vnode
*vp
, u_long id
, struct ufsmount
*ump
, int type
,
754 struct ufs_dquot
**dqp
)
756 struct ufs_dquot
*dq
;
757 struct ufs_dqhash
*dqh
;
763 dqvp
= ump
->um_quotas
[type
];
764 if (dqvp
== NULLVP
|| (ump
->um_qflags
[type
] & QTF_CLOSING
)) {
769 * Check the cache first.
771 dqh
= DQHASH(dqvp
, id
);
772 for (dq
= dqh
->lh_first
; dq
; dq
= dq
->dq_hash
.le_next
) {
773 if (dq
->dq_id
!= id
||
774 dq
->dq_ump
->um_quotas
[dq
->dq_type
] != dqvp
)
777 * Cache hit with no references. Take
778 * the structure off the free list.
781 TAILQ_REMOVE(&ufs_dqfreelist
, dq
, dq_freelist
);
787 * Not in cache, allocate a new one.
789 if (TAILQ_EMPTY(&ufs_dqfreelist
) && ufs_numdquot
< MAXQUOTAS
* desiredvnodes
)
790 ufs_desireddquot
+= DQUOTINC
;
791 if (ufs_numdquot
< ufs_desireddquot
) {
792 dq
= (struct ufs_dquot
*)
793 kmalloc(sizeof *dq
, M_DQUOT
, M_WAITOK
| M_ZERO
);
796 if ((dq
= TAILQ_FIRST(&ufs_dqfreelist
)) == NULL
) {
801 if (dq
->dq_cnt
|| (dq
->dq_flags
& DQ_MOD
))
802 panic("dqget: free dquot isn't");
803 TAILQ_REMOVE(&ufs_dqfreelist
, dq
, dq_freelist
);
804 if (dq
->dq_ump
!= NULL
)
805 LIST_REMOVE(dq
, dq_hash
);
808 * Initialize the contents of the dquot structure.
811 vn_lock(dqvp
, LK_EXCLUSIVE
| LK_RETRY
);
812 LIST_INSERT_HEAD(dqh
, dq
, dq_hash
);
814 dq
->dq_flags
= DQ_LOCK
;
818 auio
.uio_iov
= &aiov
;
820 aiov
.iov_base
= (caddr_t
)&dq
->dq_dqb
;
821 aiov
.iov_len
= sizeof (struct ufs_dqblk
);
822 auio
.uio_resid
= sizeof (struct ufs_dqblk
);
823 auio
.uio_offset
= (off_t
)(id
* sizeof (struct ufs_dqblk
));
824 auio
.uio_segflg
= UIO_SYSSPACE
;
825 auio
.uio_rw
= UIO_READ
;
827 error
= VOP_READ(dqvp
, &auio
, 0, ump
->um_cred
[type
]);
828 if (auio
.uio_resid
== sizeof(struct ufs_dqblk
) && error
== 0)
829 bzero((caddr_t
)&dq
->dq_dqb
, sizeof(struct ufs_dqblk
));
832 if (dq
->dq_flags
& DQ_WANT
)
836 * I/O error in reading quota file, release
837 * quota structure and reflect problem to caller.
840 LIST_REMOVE(dq
, dq_hash
);
846 * Check for no limit to enforce.
847 * Initialize time values if necessary.
849 if (dq
->dq_isoftlimit
== 0 && dq
->dq_bsoftlimit
== 0 &&
850 dq
->dq_ihardlimit
== 0 && dq
->dq_bhardlimit
== 0)
851 dq
->dq_flags
|= DQ_FAKE
;
852 if (dq
->dq_id
!= 0) {
853 if (dq
->dq_btime
== 0)
854 dq
->dq_btime
= time_second
+ ump
->um_btime
[type
];
855 if (dq
->dq_itime
== 0)
856 dq
->dq_itime
= time_second
+ ump
->um_itime
[type
];
864 * Obtain a reference to a dquot.
867 ufs_dqref(struct ufs_dquot
*dq
)
874 * Release a reference to a dquot.
877 ufs_dqrele(struct vnode
*vp
, struct ufs_dquot
*dq
)
881 if (dq
->dq_cnt
> 1) {
885 if (dq
->dq_flags
& DQ_MOD
)
886 (void)ufs_dqsync(vp
, dq
);
887 if (--dq
->dq_cnt
> 0)
889 TAILQ_INSERT_TAIL(&ufs_dqfreelist
, dq
, dq_freelist
);
893 * Update the disk quota in the quota file.
896 ufs_dqsync(struct vnode
*vp
, struct ufs_dquot
*dq
)
904 panic("dqsync: dquot");
905 if ((dq
->dq_flags
& DQ_MOD
) == 0)
907 if ((dqvp
= dq
->dq_ump
->um_quotas
[dq
->dq_type
]) == NULLVP
)
908 panic("dqsync: file");
910 vn_lock(dqvp
, LK_EXCLUSIVE
| LK_RETRY
);
911 while (dq
->dq_flags
& DQ_LOCK
) {
912 dq
->dq_flags
|= DQ_WANT
;
913 (void) tsleep((caddr_t
)dq
, 0, "dqsync", 0);
914 if ((dq
->dq_flags
& DQ_MOD
) == 0) {
920 dq
->dq_flags
|= DQ_LOCK
;
921 auio
.uio_iov
= &aiov
;
923 aiov
.iov_base
= (caddr_t
)&dq
->dq_dqb
;
924 aiov
.iov_len
= sizeof (struct ufs_dqblk
);
925 auio
.uio_resid
= sizeof (struct ufs_dqblk
);
926 auio
.uio_offset
= (off_t
)(dq
->dq_id
* sizeof (struct ufs_dqblk
));
927 auio
.uio_segflg
= UIO_SYSSPACE
;
928 auio
.uio_rw
= UIO_WRITE
;
930 error
= VOP_WRITE(dqvp
, &auio
, 0, dq
->dq_ump
->um_cred
[dq
->dq_type
]);
931 if (auio
.uio_resid
&& error
== 0)
933 if (dq
->dq_flags
& DQ_WANT
)
935 dq
->dq_flags
&= ~(DQ_MOD
|DQ_LOCK
|DQ_WANT
);
942 * Flush all entries from the cache for a particular vnode.
945 ufs_dqflush(struct vnode
*vp
)
947 struct ufs_dquot
*dq
, *nextdq
;
948 struct ufs_dqhash
*dqh
;
951 * Move all dquot's that used to refer to this quota
952 * file off their hash chains (they will eventually
953 * fall off the head of the free list and be re-used).
955 for (dqh
= &ufs_dqhashtbl
[ufs_dqhash
]; dqh
>= ufs_dqhashtbl
; dqh
--) {
956 for (dq
= dqh
->lh_first
; dq
; dq
= nextdq
) {
957 nextdq
= dq
->dq_hash
.le_next
;
958 if (dq
->dq_ump
->um_quotas
[dq
->dq_type
] != vp
)
961 panic("dqflush: stray dquot");
962 LIST_REMOVE(dq
, dq_hash
);
963 dq
->dq_ump
= (struct ufsmount
*)0;