]>
Commit | Line | Data |
---|---|---|
9bccf70c A |
1 | /* |
2 | * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
e5568f75 A |
6 | * The contents of this file constitute Original Code as defined in and |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
9bccf70c | 11 | * |
e5568f75 A |
12 | * This Original Code and all software distributed under the License are |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
9bccf70c A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
e5568f75 A |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
9bccf70c A |
19 | * |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * Copyright (c) 1982, 1986, 1990, 1993, 1995 | |
24 | * The Regents of the University of California. All rights reserved. | |
25 | * | |
26 | * This code is derived from software contributed to Berkeley by | |
27 | * Robert Elz at The University of Melbourne. | |
28 | * | |
29 | * Redistribution and use in source and binary forms, with or without | |
30 | * modification, are permitted provided that the following conditions | |
31 | * are met: | |
32 | * 1. Redistributions of source code must retain the above copyright | |
33 | * notice, this list of conditions and the following disclaimer. | |
34 | * 2. Redistributions in binary form must reproduce the above copyright | |
35 | * notice, this list of conditions and the following disclaimer in the | |
36 | * documentation and/or other materials provided with the distribution. | |
37 | * 3. All advertising materials mentioning features or use of this software | |
38 | * must display the following acknowledgement: | |
39 | * This product includes software developed by the University of | |
40 | * California, Berkeley and its contributors. | |
41 | * 4. Neither the name of the University nor the names of its contributors | |
42 | * may be used to endorse or promote products derived from this software | |
43 | * without specific prior written permission. | |
44 | * | |
45 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
46 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
47 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
48 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
49 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
50 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
51 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
52 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
53 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
54 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
55 | * SUCH DAMAGE. | |
56 | * | |
57 | * @(#)vfs_quota.c | |
58 | * derived from @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95 | |
59 | */ | |
60 | ||
61 | #include <sys/param.h> | |
62 | #include <sys/kernel.h> | |
63 | #include <sys/systm.h> | |
64 | #include <sys/malloc.h> | |
65 | #include <sys/file.h> | |
66 | #include <sys/proc.h> | |
67 | #include <sys/vnode.h> | |
68 | #include <sys/mount.h> | |
69 | #include <sys/quota.h> | |
70 | ||
71 | ||
72 | static u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS; | |
73 | ||
74 | ||
75 | /* | |
76 | * Code pertaining to management of the in-core dquot data structures. | |
77 | */ | |
78 | #define DQHASH(dqvp, id) \ | |
79 | (&dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash]) | |
80 | LIST_HEAD(dqhash, dquot) *dqhashtbl; | |
81 | u_long dqhash; | |
82 | ||
83 | /* | |
84 | * Dquot free list. | |
85 | */ | |
86 | #define DQUOTINC 5 /* minimum free dquots desired */ | |
87 | TAILQ_HEAD(dqfreelist, dquot) dqfreelist; | |
88 | long numdquot, desireddquot = DQUOTINC; | |
89 | ||
d7e50217 A |
90 | /* |
91 | * Dquot dirty orphans list. | |
92 | */ | |
93 | TAILQ_HEAD(dqdirtylist, dquot) dqdirtylist; | |
94 | ||
9bccf70c A |
95 | |
96 | static int dqlookup(struct quotafile *, u_long, struct dqblk *, u_int32_t *); | |
97 | ||
98 | ||
99 | /* | |
100 | * Initialize the quota system. | |
101 | */ | |
102 | void | |
103 | dqinit() | |
104 | { | |
105 | ||
106 | dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash); | |
107 | TAILQ_INIT(&dqfreelist); | |
d7e50217 | 108 | TAILQ_INIT(&dqdirtylist); |
9bccf70c A |
109 | } |
110 | ||
111 | ||
112 | /* | |
113 | * Initialize a quota file | |
114 | */ | |
115 | int | |
116 | dqfileopen(qfp, type) | |
117 | struct quotafile *qfp; | |
118 | int type; | |
119 | { | |
120 | struct dqfilehdr header; | |
121 | struct vattr vattr; | |
122 | struct iovec aiov; | |
123 | struct uio auio; | |
124 | int error; | |
125 | ||
126 | /* Obtain the file size */ | |
127 | error = VOP_GETATTR(qfp->qf_vp, &vattr, qfp->qf_cred, current_proc()); | |
128 | if (error) | |
129 | return (error); | |
130 | ||
131 | /* Read the file header */ | |
132 | auio.uio_iov = &aiov; | |
133 | auio.uio_iovcnt = 1; | |
134 | aiov.iov_base = (caddr_t)&header; | |
135 | aiov.iov_len = sizeof (header); | |
136 | auio.uio_resid = sizeof (header); | |
137 | auio.uio_offset = (off_t)(0); | |
138 | auio.uio_segflg = UIO_SYSSPACE; | |
139 | auio.uio_rw = UIO_READ; | |
140 | auio.uio_procp = (struct proc *)0; | |
141 | error = VOP_READ(qfp->qf_vp, &auio, 0, qfp->qf_cred); | |
142 | if (error) | |
143 | return (error); | |
144 | else if (auio.uio_resid) | |
145 | return (EINVAL); | |
146 | ||
147 | /* Sanity check the quota file header. */ | |
148 | if ((header.dqh_magic != quotamagic[type]) || | |
149 | (header.dqh_version > QF_VERSION) || | |
150 | (!powerof2(header.dqh_maxentries)) || | |
151 | (header.dqh_maxentries > (vattr.va_size / sizeof(struct dqblk)))) | |
152 | return (EINVAL); | |
153 | ||
154 | /* Set up the time limits for this quota. */ | |
155 | if (header.dqh_btime > 0) | |
156 | qfp->qf_btime = header.dqh_btime; | |
157 | else | |
158 | qfp->qf_btime = MAX_DQ_TIME; | |
159 | if (header.dqh_itime > 0) | |
160 | qfp->qf_itime = header.dqh_itime; | |
161 | else | |
162 | qfp->qf_itime = MAX_IQ_TIME; | |
163 | ||
164 | /* Calculate the hash table constants. */ | |
165 | qfp->qf_maxentries = header.dqh_maxentries; | |
166 | qfp->qf_entrycnt = header.dqh_entrycnt; | |
167 | qfp->qf_shift = dqhashshift(header.dqh_maxentries); | |
168 | ||
169 | return (0); | |
170 | } | |
171 | ||
172 | /* | |
173 | * Close down a quota file | |
174 | */ | |
175 | void | |
176 | dqfileclose(qfp, type) | |
177 | struct quotafile *qfp; | |
178 | int type; | |
179 | { | |
180 | struct dqfilehdr header; | |
181 | struct iovec aiov; | |
182 | struct uio auio; | |
183 | ||
184 | auio.uio_iov = &aiov; | |
185 | auio.uio_iovcnt = 1; | |
186 | aiov.iov_base = (caddr_t)&header; | |
187 | aiov.iov_len = sizeof (header); | |
188 | auio.uio_resid = sizeof (header); | |
189 | auio.uio_offset = (off_t)(0); | |
190 | auio.uio_segflg = UIO_SYSSPACE; | |
191 | auio.uio_rw = UIO_READ; | |
192 | auio.uio_procp = (struct proc *)0; | |
193 | if (VOP_READ(qfp->qf_vp, &auio, 0, qfp->qf_cred) == 0) { | |
194 | header.dqh_entrycnt = qfp->qf_entrycnt; | |
195 | ||
196 | auio.uio_iov = &aiov; | |
197 | auio.uio_iovcnt = 1; | |
198 | aiov.iov_base = (caddr_t)&header; | |
199 | aiov.iov_len = sizeof (header); | |
200 | auio.uio_resid = sizeof (header); | |
201 | auio.uio_offset = (off_t)(0); | |
202 | auio.uio_segflg = UIO_SYSSPACE; | |
203 | auio.uio_rw = UIO_WRITE; | |
204 | auio.uio_procp = (struct proc *)0; | |
205 | (void) VOP_WRITE(qfp->qf_vp, &auio, 0, qfp->qf_cred); | |
206 | } | |
207 | } | |
208 | ||
209 | ||
210 | /* | |
211 | * Obtain a dquot structure for the specified identifier and quota file | |
212 | * reading the information from the file if necessary. | |
213 | */ | |
214 | int | |
215 | dqget(vp, id, qfp, type, dqp) | |
216 | struct vnode *vp; | |
217 | u_long id; | |
218 | struct quotafile *qfp; | |
219 | register int type; | |
220 | struct dquot **dqp; | |
221 | { | |
222 | struct proc *p = current_proc(); /* XXX */ | |
223 | struct dquot *dq; | |
224 | struct dqhash *dqh; | |
225 | struct vnode *dqvp; | |
226 | int error = 0; | |
227 | ||
228 | dqvp = qfp->qf_vp; | |
229 | if (id == 0 || dqvp == NULLVP || (qfp->qf_qflags & QTF_CLOSING)) { | |
230 | *dqp = NODQUOT; | |
231 | return (EINVAL); | |
232 | } | |
233 | /* | |
234 | * Check the cache first. | |
235 | */ | |
236 | dqh = DQHASH(dqvp, id); | |
237 | for (dq = dqh->lh_first; dq; dq = dq->dq_hash.le_next) { | |
238 | if (dq->dq_id != id || | |
239 | dq->dq_qfile->qf_vp != dqvp) | |
240 | continue; | |
241 | /* | |
242 | * Cache hit with no references. Take | |
243 | * the structure off the free list. | |
244 | */ | |
d7e50217 A |
245 | if (dq->dq_cnt == 0) { |
246 | if (dq->dq_flags & DQ_MOD) | |
247 | TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist); | |
248 | else | |
249 | TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); | |
250 | } | |
9bccf70c A |
251 | DQREF(dq); |
252 | *dqp = dq; | |
253 | return (0); | |
254 | } | |
255 | /* | |
256 | * Not in cache, allocate a new one. | |
257 | */ | |
258 | if (dqfreelist.tqh_first == NODQUOT && | |
259 | numdquot < MAXQUOTAS * desiredvnodes) | |
260 | desireddquot += DQUOTINC; | |
261 | if (numdquot < desireddquot) { | |
262 | dq = (struct dquot *)_MALLOC(sizeof *dq, M_DQUOT, M_WAITOK); | |
263 | bzero((char *)dq, sizeof *dq); | |
264 | numdquot++; | |
265 | } else { | |
266 | if ((dq = dqfreelist.tqh_first) == NULL) { | |
267 | tablefull("dquot"); | |
268 | *dqp = NODQUOT; | |
269 | return (EUSERS); | |
270 | } | |
271 | if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) | |
272 | panic("free dquot isn't"); | |
273 | TAILQ_REMOVE(&dqfreelist, dq, dq_freelist); | |
274 | LIST_REMOVE(dq, dq_hash); | |
275 | } | |
276 | /* | |
277 | * Initialize the contents of the dquot structure. | |
278 | */ | |
279 | if (vp != dqvp) | |
280 | vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p); | |
281 | LIST_INSERT_HEAD(dqh, dq, dq_hash); | |
282 | DQREF(dq); | |
283 | dq->dq_flags = DQ_LOCK; | |
284 | dq->dq_id = id; | |
285 | dq->dq_qfile = qfp; | |
286 | dq->dq_type = type; | |
287 | error = dqlookup(qfp, id, &dq->dq_dqb, &dq->dq_index); | |
288 | ||
289 | if (vp != dqvp) | |
290 | VOP_UNLOCK(dqvp, 0, p); | |
291 | if (dq->dq_flags & DQ_WANT) | |
292 | wakeup((caddr_t)dq); | |
293 | dq->dq_flags = 0; | |
294 | /* | |
295 | * I/O error in reading quota file, release | |
296 | * quota structure and reflect problem to caller. | |
297 | */ | |
298 | if (error) { | |
299 | LIST_REMOVE(dq, dq_hash); | |
300 | dqrele(vp, dq); | |
301 | *dqp = NODQUOT; | |
302 | return (error); | |
303 | } | |
304 | /* | |
305 | * Check for no limit to enforce. | |
306 | * Initialize time values if necessary. | |
307 | */ | |
308 | if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 && | |
309 | dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0) | |
310 | dq->dq_flags |= DQ_FAKE; | |
311 | if (dq->dq_id != 0) { | |
312 | if (dq->dq_btime == 0) | |
313 | dq->dq_btime = time.tv_sec + qfp->qf_btime; | |
314 | if (dq->dq_itime == 0) | |
315 | dq->dq_itime = time.tv_sec + qfp->qf_itime; | |
316 | } | |
317 | *dqp = dq; | |
318 | return (0); | |
319 | } | |
320 | ||
321 | /* | |
322 | * Lookup a dqblk structure for the specified identifier and | |
323 | * quota file. If there is no enetry for this identifier then | |
324 | * one is inserted. The actual hash table index is returned. | |
325 | */ | |
326 | static int | |
327 | dqlookup(qfp, id, dqb, index) | |
328 | struct quotafile *qfp; | |
329 | u_long id; | |
330 | struct dqblk *dqb; | |
331 | u_int32_t *index; | |
332 | { | |
333 | struct vnode *dqvp; | |
334 | struct ucred *cred; | |
335 | struct iovec aiov; | |
336 | struct uio auio; | |
337 | int i, skip, last; | |
338 | u_long mask; | |
339 | int error = 0; | |
340 | ||
341 | if (id == 0) | |
342 | return (EINVAL); | |
343 | dqvp = qfp->qf_vp; | |
344 | cred = qfp->qf_cred; | |
345 | ||
346 | auio.uio_iov = &aiov; | |
347 | auio.uio_iovcnt = 1; | |
348 | auio.uio_segflg = UIO_SYSSPACE; | |
349 | auio.uio_procp = (struct proc *)0; | |
350 | ||
351 | mask = qfp->qf_maxentries - 1; | |
352 | i = dqhash1(id, qfp->qf_shift, mask); | |
353 | skip = dqhash2(id, mask); | |
354 | ||
355 | for (last = (i + (qfp->qf_maxentries-1) * skip) & mask; | |
356 | i != last; | |
357 | i = (i + skip) & mask) { | |
358 | ||
359 | aiov.iov_base = (caddr_t)dqb; | |
360 | aiov.iov_len = sizeof (struct dqblk); | |
361 | auio.uio_resid = sizeof (struct dqblk); | |
362 | auio.uio_offset = (off_t)dqoffset(i); | |
363 | auio.uio_rw = UIO_READ; | |
364 | error = VOP_READ(dqvp, &auio, 0, cred); | |
365 | if (error) { | |
366 | printf("dqlookup: error %d looking up id %d at index %d\n", error, id, i); | |
367 | break; | |
368 | } else if (auio.uio_resid) { | |
369 | error = EIO; | |
370 | printf("dqlookup: error looking up id %d at index %d\n", id, i); | |
371 | break; | |
372 | } | |
373 | /* | |
374 | * An empty entry means there is no entry | |
375 | * with that id. In this case a new dqb | |
376 | * record will be inserted. | |
377 | */ | |
378 | if (dqb->dqb_id == 0) { | |
379 | bzero(dqb, sizeof(struct dqblk)); | |
380 | dqb->dqb_id = id; | |
381 | /* | |
382 | * Write back to reserve entry for this id | |
383 | */ | |
384 | aiov.iov_base = (caddr_t)dqb; | |
385 | aiov.iov_len = sizeof (struct dqblk); | |
386 | auio.uio_resid = sizeof (struct dqblk); | |
387 | auio.uio_offset = (off_t)dqoffset(i); | |
388 | auio.uio_rw = UIO_WRITE; | |
389 | error = VOP_WRITE(dqvp, &auio, 0, cred); | |
390 | if (auio.uio_resid && error == 0) | |
391 | error = EIO; | |
392 | if (error == 0) | |
393 | ++qfp->qf_entrycnt; | |
394 | break; | |
395 | } | |
396 | /* An id match means an entry was found. */ | |
397 | if (dqb->dqb_id == id) | |
398 | break; | |
399 | } | |
400 | ||
401 | *index = i; /* remember index so we don't have to recompute it later */ | |
402 | return (error); | |
403 | } | |
404 | ||
405 | /* | |
406 | * Obtain a reference to a dquot. | |
407 | */ | |
408 | void | |
409 | dqref(dq) | |
410 | struct dquot *dq; | |
411 | { | |
412 | ||
413 | dq->dq_cnt++; | |
414 | } | |
415 | ||
416 | /* | |
417 | * Release a reference to a dquot. | |
418 | */ | |
419 | void | |
420 | dqrele(vp, dq) | |
421 | struct vnode *vp; | |
422 | register struct dquot *dq; | |
423 | { | |
424 | ||
425 | if (dq == NODQUOT) | |
426 | return; | |
427 | if (dq->dq_cnt > 1) { | |
428 | dq->dq_cnt--; | |
429 | return; | |
430 | } | |
431 | if (dq->dq_flags & DQ_MOD) | |
432 | (void) dqsync(vp, dq); | |
433 | if (--dq->dq_cnt > 0) | |
434 | return; | |
435 | TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist); | |
436 | } | |
437 | ||
d7e50217 A |
438 | /* |
439 | * Release a reference to a dquot but don't do any I/O. | |
440 | */ | |
441 | void | |
442 | dqreclaim(vp, dq) | |
443 | struct vnode *vp; | |
444 | register struct dquot *dq; | |
445 | { | |
446 | if (dq == NODQUOT) | |
447 | return; | |
448 | ||
449 | if (--dq->dq_cnt > 0) | |
450 | return; | |
451 | ||
452 | if (dq->dq_flags & DQ_MOD) | |
453 | TAILQ_INSERT_TAIL(&dqdirtylist, dq, dq_freelist); | |
454 | else | |
455 | TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist); | |
456 | } | |
457 | ||
458 | /* | |
459 | * Update a quota file's orphaned disk quotas. | |
460 | */ | |
461 | void | |
462 | dqsync_orphans(qfp) | |
463 | struct quotafile *qfp; | |
464 | { | |
465 | struct dquot *dq; | |
466 | ||
467 | loop: | |
468 | TAILQ_FOREACH(dq, &dqdirtylist, dq_freelist) { | |
469 | if ((dq->dq_flags & DQ_MOD) == 0) | |
470 | panic("dqsync_orphans: dirty dquot isn't"); | |
471 | if (dq->dq_cnt != 0) | |
472 | panic("dqsync_orphans: dquot in use"); | |
473 | ||
474 | if (dq->dq_qfile == qfp) { | |
475 | TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist); | |
476 | ||
477 | dq->dq_cnt++; | |
478 | (void) dqsync(NULLVP, dq); | |
479 | dq->dq_cnt--; | |
480 | ||
481 | if ((dq->dq_cnt == 0) && (dq->dq_flags & DQ_MOD) == 0) | |
482 | TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist); | |
483 | ||
484 | goto loop; | |
485 | } | |
486 | } | |
487 | } | |
488 | ||
9bccf70c A |
489 | /* |
490 | * Update the disk quota in the quota file. | |
491 | */ | |
492 | int | |
493 | dqsync(vp, dq) | |
494 | struct vnode *vp; | |
495 | struct dquot *dq; | |
496 | { | |
497 | struct proc *p = current_proc(); /* XXX */ | |
498 | struct vnode *dqvp; | |
499 | struct iovec aiov; | |
500 | struct uio auio; | |
501 | int error; | |
502 | ||
503 | if (dq == NODQUOT) | |
504 | panic("dqsync: dquot"); | |
505 | if ((dq->dq_flags & DQ_MOD) == 0) | |
506 | return (0); | |
507 | if (dq->dq_id == 0) | |
508 | return(0); | |
509 | if ((dqvp = dq->dq_qfile->qf_vp) == NULLVP) | |
510 | panic("dqsync: file"); | |
511 | if (vp != dqvp) | |
512 | vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p); | |
513 | while (dq->dq_flags & DQ_LOCK) { | |
514 | dq->dq_flags |= DQ_WANT; | |
515 | sleep((caddr_t)dq, PINOD+2); | |
516 | if ((dq->dq_flags & DQ_MOD) == 0) { | |
517 | if (vp != dqvp) | |
518 | VOP_UNLOCK(dqvp, 0, p); | |
519 | return (0); | |
520 | } | |
521 | } | |
522 | dq->dq_flags |= DQ_LOCK; | |
523 | auio.uio_iov = &aiov; | |
524 | auio.uio_iovcnt = 1; | |
525 | aiov.iov_base = (caddr_t)&dq->dq_dqb; | |
526 | aiov.iov_len = sizeof (struct dqblk); | |
527 | auio.uio_resid = sizeof (struct dqblk); | |
528 | auio.uio_offset = (off_t)dqoffset(dq->dq_index); | |
529 | auio.uio_segflg = UIO_SYSSPACE; | |
530 | auio.uio_rw = UIO_WRITE; | |
531 | auio.uio_procp = (struct proc *)0; | |
532 | error = VOP_WRITE(dqvp, &auio, 0, dq->dq_qfile->qf_cred); | |
533 | if (auio.uio_resid && error == 0) | |
534 | error = EIO; | |
535 | if (dq->dq_flags & DQ_WANT) | |
536 | wakeup((caddr_t)dq); | |
537 | dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT); | |
538 | if (vp != dqvp) | |
539 | VOP_UNLOCK(dqvp, 0, p); | |
540 | return (error); | |
541 | } | |
542 | ||
543 | /* | |
544 | * Flush all entries from the cache for a particular vnode. | |
545 | */ | |
546 | void | |
547 | dqflush(vp) | |
548 | register struct vnode *vp; | |
549 | { | |
550 | register struct dquot *dq, *nextdq; | |
551 | struct dqhash *dqh; | |
552 | ||
553 | /* | |
554 | * Move all dquot's that used to refer to this quota | |
555 | * file off their hash chains (they will eventually | |
556 | * fall off the head of the free list and be re-used). | |
557 | */ | |
558 | for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) { | |
559 | for (dq = dqh->lh_first; dq; dq = nextdq) { | |
560 | nextdq = dq->dq_hash.le_next; | |
561 | if (dq->dq_qfile->qf_vp != vp) | |
562 | continue; | |
563 | if (dq->dq_cnt) | |
564 | panic("dqflush: stray dquot"); | |
565 | LIST_REMOVE(dq, dq_hash); | |
566 | dq->dq_qfile = (struct quotafile *)0; | |
567 | } | |
568 | } | |
569 | } |