1/*
2 * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * Copyright (c) 1982, 1986, 1990, 1993, 1995
30 *	The Regents of the University of California.  All rights reserved.
31 *
32 * This code is derived from software contributed to Berkeley by
33 * Robert Elz at The University of Melbourne.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 *    notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 *    notice, this list of conditions and the following disclaimer in the
42 *    documentation and/or other materials provided with the distribution.
43 * 3. All advertising materials mentioning features or use of this software
44 *    must display the following acknowledgement:
45 *	This product includes software developed by the University of
46 *	California, Berkeley and its contributors.
47 * 4. Neither the name of the University nor the names of its contributors
48 *    may be used to endorse or promote products derived from this software
49 *    without specific prior written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 *
63 *	@(#)vfs_quota.c
64 *	derived from @(#)ufs_quota.c	8.5 (Berkeley) 5/20/95
65 */
66
67#include <sys/param.h>
68#include <sys/kernel.h>
69#include <sys/systm.h>
70#include <sys/malloc.h>
71#include <sys/file_internal.h>
72#include <sys/proc_internal.h>
73#include <sys/vnode_internal.h>
74#include <sys/mount_internal.h>
75#include <sys/quota.h>
76#include <sys/uio_internal.h>
77
78#include <libkern/OSByteOrder.h>
79
80
81/* vars for quota file lock */
82lck_grp_t	* qf_lck_grp;
83lck_grp_attr_t	* qf_lck_grp_attr;
84lck_attr_t	* qf_lck_attr;
85
86/* vars for quota list lock */
87lck_grp_t	* quota_list_lck_grp;
88lck_grp_attr_t	* quota_list_lck_grp_attr;
89lck_attr_t	* quota_list_lck_attr;
90lck_mtx_t	* quota_list_mtx_lock;
91
92/* Routines to lock and unlock the quota global data */
93static int dq_list_lock(void);
94static void dq_list_unlock(void);
95
96static void dq_lock_internal(struct dquot *dq);
97static void dq_unlock_internal(struct dquot *dq);
98
99static u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS;
100
101
102/*
103 * Code pertaining to management of the in-core dquot data structures.
104 */
105#define DQHASH(dqvp, id) \
106	(&dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & dqhash])
107LIST_HEAD(dqhash, dquot) *dqhashtbl;
108u_long dqhash;
109
110#define	DQUOTINC	5	/* minimum free dquots desired */
111long numdquot, desireddquot = DQUOTINC;
112
113/*
114 * Dquot free list.
115 */
116TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
117/*
118 * Dquot dirty orphans list
119 */
120TAILQ_HEAD(dqdirtylist, dquot) dqdirtylist;
121
122
123static int  dqlookup(struct quotafile *, u_int32_t, struct	dqblk *, u_int32_t *);
124static int  dqsync_locked(struct dquot *dq);
125
126static void qf_lock(struct quotafile *);
127static void qf_unlock(struct quotafile *);
128static int  qf_ref(struct quotafile *);
129static void qf_rele(struct quotafile *);
130
131
132/*
133 * Initialize locks for the quota system.
134 */
135void
136dqinit(void)
137{
138	/*
139	 * Allocate quota list lock group attribute and group
140	 */
141	quota_list_lck_grp_attr= lck_grp_attr_alloc_init();
142	quota_list_lck_grp = lck_grp_alloc_init("quota list",  quota_list_lck_grp_attr);
143
144	/*
145	 * Allocate qouta list lock attribute
146	 */
147	quota_list_lck_attr = lck_attr_alloc_init();
148
149	/*
150	 * Allocate quota list lock
151	 */
152	quota_list_mtx_lock = lck_mtx_alloc_init(quota_list_lck_grp, quota_list_lck_attr);
153
154
155	/*
156	 * allocate quota file lock group attribute and group
157	 */
158	qf_lck_grp_attr= lck_grp_attr_alloc_init();
159	qf_lck_grp = lck_grp_alloc_init("quota file", qf_lck_grp_attr);
160
161	/*
162	 * Allocate quota file lock attribute
163	 */
164	qf_lck_attr = lck_attr_alloc_init();
165}
166
167/*
168 * Report whether dqhashinit has been run.
169 */
170int
171dqisinitialized(void)
172{
173	return (dqhashtbl != NULL);
174}
175
176/*
177 * Initialize hash table for dquot structures.
178 */
179void
180dqhashinit(void)
181{
182	dq_list_lock();
183	if (dqisinitialized())
184		goto out;
185
186	TAILQ_INIT(&dqfreelist);
187	TAILQ_INIT(&dqdirtylist);
188	dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
189out:
190	dq_list_unlock();
191}
192
193
194static volatile int dq_list_lock_cnt = 0;
195
196static int
197dq_list_lock(void)
198{
199	lck_mtx_lock(quota_list_mtx_lock);
200	return ++dq_list_lock_cnt;
201}
202
203static int
204dq_list_lock_changed(int oldval) {
205	return (dq_list_lock_cnt != oldval);
206}
207
208static int
209dq_list_lock_val(void) {
210	return dq_list_lock_cnt;
211}
212
213void
214dq_list_unlock(void)
215{
216	lck_mtx_unlock(quota_list_mtx_lock);
217}
218
219
220/*
221 * must be called with the quota_list_lock held
222 */
223void
224dq_lock_internal(struct dquot *dq)
225{
226        while (dq->dq_lflags & DQ_LLOCK) {
227	        dq->dq_lflags |= DQ_LWANT;
228	        msleep(&dq->dq_lflags, quota_list_mtx_lock, PVFS, "dq_lock_internal", NULL);
229	}
230	dq->dq_lflags |= DQ_LLOCK;
231}
232
233/*
234 * must be called with the quota_list_lock held
235 */
236void
237dq_unlock_internal(struct dquot *dq)
238{
239        int wanted = dq->dq_lflags & DQ_LWANT;
240
241	dq->dq_lflags &= ~(DQ_LLOCK | DQ_LWANT);
242
243	if (wanted)
244	        wakeup(&dq->dq_lflags);
245}
246
247void
248dqlock(struct dquot *dq) {
249
250	lck_mtx_lock(quota_list_mtx_lock);
251
252	dq_lock_internal(dq);
253
254	lck_mtx_unlock(quota_list_mtx_lock);
255}
256
257void
258dqunlock(struct dquot *dq) {
259
260	lck_mtx_lock(quota_list_mtx_lock);
261
262	dq_unlock_internal(dq);
263
264	lck_mtx_unlock(quota_list_mtx_lock);
265}
266
267
268
269int
270qf_get(struct quotafile *qfp, int type)
271{
272        int error = 0;
273
274        dq_list_lock();
275
276        switch (type) {
277
278	case QTF_OPENING:
279	        while ( (qfp->qf_qflags & (QTF_OPENING | QTF_CLOSING)) ) {
280	                if ( (qfp->qf_qflags & QTF_OPENING) ) {
281		                error = EBUSY;
282				break;
283			}
284			if ( (qfp->qf_qflags & QTF_CLOSING) ) {
285		                qfp->qf_qflags |= QTF_WANTED;
286				msleep(&qfp->qf_qflags, quota_list_mtx_lock, PVFS, "qf_get", NULL);
287			}
288		}
289		if (qfp->qf_vp != NULLVP)
290		        error = EBUSY;
291		if (error == 0)
292		        qfp->qf_qflags |= QTF_OPENING;
293		break;
294
295	case QTF_CLOSING:
296	        if ( (qfp->qf_qflags & QTF_CLOSING) ) {
297		        error = EBUSY;
298			break;
299		}
300		qfp->qf_qflags |= QTF_CLOSING;
301
302		while ( (qfp->qf_qflags & QTF_OPENING) || qfp->qf_refcnt ) {
303		        qfp->qf_qflags |= QTF_WANTED;
304			msleep(&qfp->qf_qflags, quota_list_mtx_lock, PVFS, "qf_get", NULL);
305		}
306		if (qfp->qf_vp == NULLVP) {
307		        qfp->qf_qflags &= ~QTF_CLOSING;
308			error = EBUSY;
309		}
310		break;
311	}
312	dq_list_unlock();
313
314	return (error);
315}
316
317void
318qf_put(struct quotafile *qfp, int type)
319{
320
321        dq_list_lock();
322
323        switch (type) {
324
325	case QTF_OPENING:
326	case QTF_CLOSING:
327	        qfp->qf_qflags &= ~type;
328		break;
329	}
330	if ( (qfp->qf_qflags & QTF_WANTED) ) {
331	        qfp->qf_qflags &= ~QTF_WANTED;
332		wakeup(&qfp->qf_qflags);
333	}
334	dq_list_unlock();
335}
336
337
338static void
339qf_lock(struct quotafile *qfp)
340{
341	lck_mtx_lock(&qfp->qf_lock);
342}
343
344static void
345qf_unlock(struct quotafile *qfp)
346{
347	lck_mtx_unlock(&qfp->qf_lock);
348}
349
350
351/*
352 * take a reference on the quota file while we're
353 * in dqget... this will prevent a quota_off from
354 * occurring while we're potentially playing with
355 * the quota file... the quota_off will stall until
356 * all the current references 'die'... once we start
357 * into quoto_off, all new references will be rejected
358 * we also don't want any dqgets being processed while
359 * we're in the middle of the quota_on... once we've
360 * actually got the quota file open and the associated
361 * struct quotafile inited, we can let them come through
362 *
363 * quota list lock must be held on entry
364 */
365static int
366qf_ref(struct quotafile *qfp)
367{
368        int error = 0;
369
370	if ( (qfp->qf_qflags & (QTF_OPENING | QTF_CLOSING)) || (qfp->qf_vp == NULLVP) )
371	        error = EINVAL;
372	else
373	        qfp->qf_refcnt++;
374
375	return (error);
376}
377
378/*
379 * drop our reference and wakeup any waiters if
380 * we were the last one holding a ref
381 *
382 * quota list lock must be held on entry
383 */
384static void
385qf_rele(struct quotafile *qfp)
386{
387	qfp->qf_refcnt--;
388
389	if ( (qfp->qf_qflags & QTF_WANTED) && qfp->qf_refcnt == 0) {
390	        qfp->qf_qflags &= ~QTF_WANTED;
391	        wakeup(&qfp->qf_qflags);
392	}
393}
394
395
396void
397dqfileinit(struct quotafile *qfp)
398{
399        qfp->qf_vp = NULLVP;
400	qfp->qf_qflags = 0;
401
402	lck_mtx_init(&qfp->qf_lock, qf_lck_grp, qf_lck_attr);
403}
404
405
406/*
407 * Initialize a quota file
408 *
409 * must be called with the quota file lock held
410 */
411int
412dqfileopen(struct quotafile *qfp, int type)
413{
414	struct dqfilehdr header;
415	struct vfs_context context;
416	off_t file_size;
417	uio_t auio;
418	int error = 0;
419	char uio_buf[ UIO_SIZEOF(1) ];
420
421	context.vc_thread = current_thread();
422	context.vc_ucred = qfp->qf_cred;
423
424	/* Obtain the file size */
425	if ((error = vnode_size(qfp->qf_vp, &file_size, &context)) != 0)
426	        goto out;
427
428	/* Read the file header */
429	auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
430				    &uio_buf[0], sizeof(uio_buf));
431	uio_addiov(auio, CAST_USER_ADDR_T(&header), sizeof (header));
432	error = VNOP_READ(qfp->qf_vp, auio, 0, &context);
433	if (error)
434	        goto out;
435	else if (uio_resid(auio)) {
436		error = EINVAL;
437		goto out;
438	}
439	/* Sanity check the quota file header. */
440	if ((OSSwapBigToHostInt32(header.dqh_magic) != quotamagic[type]) ||
441	    (OSSwapBigToHostInt32(header.dqh_version) > QF_VERSION) ||
442	    (!powerof2(OSSwapBigToHostInt32(header.dqh_maxentries))) ||
443	    (OSSwapBigToHostInt32(header.dqh_maxentries) > (file_size / sizeof(struct dqblk)))) {
444		error = EINVAL;
445		goto out;
446	}
447	/* Set up the time limits for this quota. */
448	if (header.dqh_btime != 0)
449		qfp->qf_btime = OSSwapBigToHostInt32(header.dqh_btime);
450	else
451		qfp->qf_btime = MAX_DQ_TIME;
452	if (header.dqh_itime != 0)
453		qfp->qf_itime = OSSwapBigToHostInt32(header.dqh_itime);
454	else
455		qfp->qf_itime = MAX_IQ_TIME;
456
457	/* Calculate the hash table constants. */
458	qfp->qf_maxentries = OSSwapBigToHostInt32(header.dqh_maxentries);
459	qfp->qf_entrycnt = OSSwapBigToHostInt32(header.dqh_entrycnt);
460	qfp->qf_shift = dqhashshift(qfp->qf_maxentries);
461out:
462	return (error);
463}
464
465/*
466 * Close down a quota file
467 */
468void
469dqfileclose(struct quotafile *qfp, __unused int type)
470{
471	struct dqfilehdr header;
472	struct vfs_context context;
473	uio_t auio;
474	char uio_buf[ UIO_SIZEOF(1) ];
475
476	auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
477								  &uio_buf[0], sizeof(uio_buf));
478	uio_addiov(auio, CAST_USER_ADDR_T(&header), sizeof (header));
479
480	context.vc_thread = current_thread();
481	context.vc_ucred = qfp->qf_cred;
482
483	if (VNOP_READ(qfp->qf_vp, auio, 0, &context) == 0) {
484		header.dqh_entrycnt = OSSwapHostToBigInt32(qfp->qf_entrycnt);
485		uio_reset(auio, 0, UIO_SYSSPACE, UIO_WRITE);
486		uio_addiov(auio, CAST_USER_ADDR_T(&header), sizeof (header));
487		(void) VNOP_WRITE(qfp->qf_vp, auio, 0, &context);
488	}
489}
490
491
492/*
493 * Obtain a dquot structure for the specified identifier and quota file
494 * reading the information from the file if necessary.
495 */
496int
497dqget(u_int32_t id, struct quotafile *qfp, int type, struct dquot **dqp)
498{
499	struct dquot *dq;
500	struct dquot *ndq = NULL;
501	struct dquot *fdq = NULL;
502	struct dqhash *dqh;
503	struct vnode *dqvp;
504	int error = 0;
505	int listlockval = 0;
506
507	if (!dqisinitialized()) {
508		*dqp = NODQUOT;
509		return (EINVAL);
510	}
511
512	if ( id == 0 || qfp->qf_vp == NULLVP ) {
513		*dqp = NODQUOT;
514		return (EINVAL);
515	}
516	dq_list_lock();
517
518	if ( (qf_ref(qfp)) ) {
519	        dq_list_unlock();
520
521		*dqp = NODQUOT;
522		return (EINVAL);
523	}
524	if ( (dqvp = qfp->qf_vp) == NULLVP ) {
525	        qf_rele(qfp);
526		dq_list_unlock();
527
528		*dqp = NODQUOT;
529		return (EINVAL);
530	}
531	dqh = DQHASH(dqvp, id);
532
533relookup:
534	listlockval = dq_list_lock_val();
535
536	/*
537	 * Check the cache first.
538	 */
539	for (dq = dqh->lh_first; dq; dq = dq->dq_hash.le_next) {
540		if (dq->dq_id != id ||
541		    dq->dq_qfile->qf_vp != dqvp)
542			continue;
543
544		dq_lock_internal(dq);
545		if (dq_list_lock_changed(listlockval)) {
546			dq_unlock_internal(dq);
547			goto relookup;
548		}
549
550		/*
551		 * dq_lock_internal may drop the quota_list_lock to msleep, so
552		 * we need to re-evaluate the identity of this dq
553		 */
554		if (dq->dq_id != id || dq->dq_qfile == NULL ||
555		    dq->dq_qfile->qf_vp != dqvp) {
556		        dq_unlock_internal(dq);
557			goto relookup;
558		}
559		/*
560		 * Cache hit with no references.  Take
561		 * the structure off the free list.
562		 */
563		if (dq->dq_cnt++ == 0) {
564			if (dq->dq_flags & DQ_MOD)
565				TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist);
566			else
567				TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
568		}
569		dq_unlock_internal(dq);
570
571		if (fdq != NULL) {
572		        /*
573			 * we grabbed this from the free list in the first pass
574			 * but we found the dq we were looking for in
575			 * the cache the 2nd time through
576			 * so stick it back on the free list and return the cached entry
577			 */
578		        TAILQ_INSERT_HEAD(&dqfreelist, fdq, dq_freelist);
579		}
580		qf_rele(qfp);
581	        dq_list_unlock();
582
583		if (ndq != NULL) {
584		        /*
585			 * we allocated this in the first pass
586			 * but we found the dq we were looking for in
587			 * the cache the 2nd time through so free it
588			 */
589		        _FREE(ndq, M_DQUOT);
590		}
591		*dqp = dq;
592
593		return (0);
594	}
595	/*
596	 * Not in cache, allocate a new one.
597	 */
598	if (TAILQ_EMPTY(&dqfreelist) &&
599	    numdquot < MAXQUOTAS * desiredvnodes)
600		desireddquot += DQUOTINC;
601
602	if (fdq != NULL) {
603	        /*
604		 * we captured this from the free list
605		 * in the first pass through, so go
606		 * ahead and use it
607		 */
608	        dq = fdq;
609		fdq = NULL;
610	} else if (numdquot < desireddquot) {
611	        if (ndq == NULL) {
612		        /*
613			 * drop the quota list lock since MALLOC may block
614			 */
615		        dq_list_unlock();
616
617			ndq = (struct dquot *)_MALLOC(sizeof *dq, M_DQUOT, M_WAITOK);
618			bzero((char *)ndq, sizeof *dq);
619
620		        listlockval = dq_list_lock();
621			/*
622			 * need to look for the entry again in the cache
623			 * since we dropped the quota list lock and
624			 * someone else may have beaten us to creating it
625			 */
626			goto relookup;
627		} else {
628		        /*
629			 * we allocated this in the first pass through
630			 * and we're still under out target, so go
631			 * ahead and use it
632			 */
633		        dq = ndq;
634			ndq = NULL;
635			numdquot++;
636		}
637	} else {
638	        if (TAILQ_EMPTY(&dqfreelist)) {
639		        qf_rele(qfp);
640		        dq_list_unlock();
641
642			if (ndq) {
643			        /*
644				 * we allocated this in the first pass through
645				 * but we're now at the limit of our cache size
646				 * so free it
647				 */
648			        _FREE(ndq, M_DQUOT);
649			}
650			tablefull("dquot");
651			*dqp = NODQUOT;
652			return (EUSERS);
653		}
654		dq = TAILQ_FIRST(&dqfreelist);
655
656		dq_lock_internal(dq);
657
658		if (dq_list_lock_changed(listlockval) || dq->dq_cnt || (dq->dq_flags & DQ_MOD)) {
659		        /*
660			 * we lost the race while we weren't holding
661			 * the quota list lock... dq_lock_internal
662			 * will drop it to msleep... this dq has been
663			 * reclaimed... go find another
664			 */
665		        dq_unlock_internal(dq);
666
667			/*
668			 * need to look for the entry again in the cache
669			 * since we dropped the quota list lock and
670			 * someone else may have beaten us to creating it
671			 */
672			goto relookup;
673		}
674		TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
675
676		if (dq->dq_qfile != NULL) {
677		        LIST_REMOVE(dq, dq_hash);
678			dq->dq_qfile = NULL;
679			dq->dq_id = 0;
680		}
681		dq_unlock_internal(dq);
682
683		/*
684		 * because we may have dropped the quota list lock
685		 * in the call to dq_lock_internal, we need to
686		 * relookup in the hash in case someone else
687		 * caused a dq with this identity to be created...
688		 * if we don't find it, we'll use this one
689		 */
690		fdq = dq;
691		goto relookup;
692	}
693	/*
694	 * we've either freshly allocated a dq
695	 * or we've atomically pulled it out of
696	 * the hash and freelists... no one else
697	 * can have a reference, which means no
698	 * one else can be trying to use this dq
699	 */
700	dq_lock_internal(dq);
701	if (dq_list_lock_changed(listlockval)) {
702		dq_unlock_internal(dq);
703		goto relookup;
704	}
705
706	/*
707	 * Initialize the contents of the dquot structure.
708	 */
709	dq->dq_cnt = 1;
710	dq->dq_flags = 0;
711	dq->dq_id = id;
712	dq->dq_qfile = qfp;
713	dq->dq_type = type;
714	/*
715	 * once we insert it in the hash and
716	 * drop the quota_list_lock, it can be
717	 * 'found'... however, we're still holding
718	 * the dq_lock which will keep us from doing
719	 * anything with it until we've finished
720	 * initializing it...
721	 */
722	LIST_INSERT_HEAD(dqh, dq, dq_hash);
723	dq_list_unlock();
724
725	if (ndq) {
726	        /*
727		 * we allocated this in the first pass through
728		 * but we didn't need it, so free it after
729		 * we've droped the quota list lock
730		 */
731	        _FREE(ndq, M_DQUOT);
732	}
733
734	error = dqlookup(qfp, id, &dq->dq_dqb, &dq->dq_index);
735
736	/*
737	 * I/O error in reading quota file, release
738	 * quota structure and reflect problem to caller.
739	 */
740	if (error) {
741	        dq_list_lock();
742
743		dq->dq_id = 0;
744		dq->dq_qfile = NULL;
745		LIST_REMOVE(dq, dq_hash);
746
747		dq_unlock_internal(dq);
748		qf_rele(qfp);
749	        dq_list_unlock();
750
751		dqrele(dq);
752
753		*dqp = NODQUOT;
754		return (error);
755	}
756	/*
757	 * Check for no limit to enforce.
758	 * Initialize time values if necessary.
759	 */
760	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
761	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
762		dq->dq_flags |= DQ_FAKE;
763	if (dq->dq_id != 0) {
764		struct timeval tv;
765
766		microtime(&tv);
767		if (dq->dq_btime == 0)
768			dq->dq_btime = tv.tv_sec + qfp->qf_btime;
769		if (dq->dq_itime == 0)
770			dq->dq_itime = tv.tv_sec + qfp->qf_itime;
771	}
772	dq_list_lock();
773	dq_unlock_internal(dq);
774	qf_rele(qfp);
775	dq_list_unlock();
776
777	*dqp = dq;
778	return (0);
779}
780
781/*
782 * Lookup a dqblk structure for the specified identifier and
783 * quota file.  If there is no entry for this identifier then
784 * one is inserted.  The actual hash table index is returned.
785 */
786static int
787dqlookup(struct quotafile *qfp, u_int32_t id, struct dqblk *dqb, uint32_t *index)
788{
789	struct vnode *dqvp;
790	struct vfs_context context;
791	uio_t auio;
792	int i, skip, last;
793	u_int32_t mask;
794	int error = 0;
795	char uio_buf[ UIO_SIZEOF(1) ];
796
797
798	qf_lock(qfp);
799
800	dqvp = qfp->qf_vp;
801
802	context.vc_thread = current_thread();
803	context.vc_ucred = qfp->qf_cred;
804
805	mask = qfp->qf_maxentries - 1;
806	i = dqhash1(id, qfp->qf_shift, mask);
807	skip = dqhash2(id, mask);
808
809	for (last = (i + (qfp->qf_maxentries-1) * skip) & mask;
810	     i != last;
811	     i = (i + skip) & mask) {
812		auio = uio_createwithbuffer(1, dqoffset(i), UIO_SYSSPACE, UIO_READ,
813									  &uio_buf[0], sizeof(uio_buf));
814		uio_addiov(auio, CAST_USER_ADDR_T(dqb), sizeof (struct dqblk));
815		error = VNOP_READ(dqvp, auio, 0, &context);
816		if (error) {
817			printf("dqlookup: error %d looking up id %u at index %d\n", error, id, i);
818			break;
819		} else if (uio_resid(auio)) {
820			error = EIO;
821			printf("dqlookup: error looking up id %u at index %d\n", id, i);
822			break;
823		}
824		/*
825		 * An empty entry means there is no entry
826		 * with that id.  In this case a new dqb
827		 * record will be inserted.
828		 */
829		if (dqb->dqb_id == 0) {
830			bzero(dqb, sizeof(struct dqblk));
831			dqb->dqb_id = OSSwapHostToBigInt32(id);
832			/*
833			 * Write back to reserve entry for this id
834			 */
835			uio_reset(auio, dqoffset(i), UIO_SYSSPACE, UIO_WRITE);
836			uio_addiov(auio, CAST_USER_ADDR_T(dqb), sizeof (struct dqblk));
837			error = VNOP_WRITE(dqvp, auio, 0, &context);
838			if (uio_resid(auio) && error == 0)
839				error = EIO;
840			if (error == 0)
841				++qfp->qf_entrycnt;
842			dqb->dqb_id = id;
843			break;
844		}
845		/* An id match means an entry was found. */
846		if (OSSwapBigToHostInt32(dqb->dqb_id) == id) {
847			dqb->dqb_bhardlimit = OSSwapBigToHostInt64(dqb->dqb_bhardlimit);
848			dqb->dqb_bsoftlimit = OSSwapBigToHostInt64(dqb->dqb_bsoftlimit);
849			dqb->dqb_curbytes   = OSSwapBigToHostInt64(dqb->dqb_curbytes);
850			dqb->dqb_ihardlimit = OSSwapBigToHostInt32(dqb->dqb_ihardlimit);
851			dqb->dqb_isoftlimit = OSSwapBigToHostInt32(dqb->dqb_isoftlimit);
852			dqb->dqb_curinodes  = OSSwapBigToHostInt32(dqb->dqb_curinodes);
853			dqb->dqb_btime      = OSSwapBigToHostInt32(dqb->dqb_btime);
854			dqb->dqb_itime      = OSSwapBigToHostInt32(dqb->dqb_itime);
855			dqb->dqb_id         = OSSwapBigToHostInt32(dqb->dqb_id);
856			break;
857		}
858	}
859	qf_unlock(qfp);
860
861	*index = i;  /* remember index so we don't have to recompute it later */
862
863	return (error);
864}
865
866
867/*
868 * Release a reference to a dquot.
869 */
870void
871dqrele(struct dquot *dq)
872{
873
874	if (dq == NODQUOT)
875		return;
876	dqlock(dq);
877
878	if (dq->dq_cnt > 1) {
879		dq->dq_cnt--;
880
881		dqunlock(dq);
882		return;
883	}
884	if (dq->dq_flags & DQ_MOD)
885		(void) dqsync_locked(dq);
886	dq->dq_cnt--;
887
888	dq_list_lock();
889	TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
890        dq_unlock_internal(dq);
891	dq_list_unlock();
892}
893
894/*
895 * Release a reference to a dquot but don't do any I/O.
896 */
897void
898dqreclaim(struct dquot *dq)
899{
900
901	if (dq == NODQUOT)
902		return;
903
904	dq_list_lock();
905	dq_lock_internal(dq);
906
907	if (--dq->dq_cnt > 0) {
908	        dq_unlock_internal(dq);
909		dq_list_unlock();
910		return;
911	}
912	if (dq->dq_flags & DQ_MOD)
913		TAILQ_INSERT_TAIL(&dqdirtylist, dq, dq_freelist);
914	else
915		TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
916
917	dq_unlock_internal(dq);
918	dq_list_unlock();
919}
920
921/*
922 * Update a quota file's orphaned disk quotas.
923 */
924void
925dqsync_orphans(struct quotafile *qfp)
926{
927	struct dquot *dq;
928
929	dq_list_lock();
930  loop:
931	TAILQ_FOREACH(dq, &dqdirtylist, dq_freelist) {
932		if (dq->dq_qfile != qfp)
933		        continue;
934
935		dq_lock_internal(dq);
936
937		if (dq->dq_qfile != qfp) {
938		        /*
939			 * the identity of this dq changed while
940			 * the quota_list_lock was dropped
941			 * dq_lock_internal can drop it to msleep
942			 */
943		        dq_unlock_internal(dq);
944			goto loop;
945		}
946		if ((dq->dq_flags & DQ_MOD) == 0) {
947		        /*
948			 * someone cleaned and removed this from
949			 * the dq from the dirty list while the
950			 * quota_list_lock was dropped
951			 */
952		        dq_unlock_internal(dq);
953			goto loop;
954		}
955		if (dq->dq_cnt != 0)
956			panic("dqsync_orphans: dquot in use");
957
958		TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist);
959
960		dq_list_unlock();
961		/*
962		 * we're still holding the dqlock at this point
963		 * with the reference count == 0
964		 * we shouldn't be able
965		 * to pick up another one since we hold dqlock
966		 */
967		(void) dqsync_locked(dq);
968
969		dq_list_lock();
970
971		TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
972
973		dq_unlock_internal(dq);
974		goto loop;
975	}
976	dq_list_unlock();
977}
978
979int
980dqsync(struct dquot *dq)
981{
982        int error = 0;
983
984	if (dq != NODQUOT) {
985	        dqlock(dq);
986
987		if ( (dq->dq_flags & DQ_MOD) )
988	        error = dqsync_locked(dq);
989
990		dqunlock(dq);
991	}
992	return (error);
993}
994
995
996/*
997 * Update the disk quota in the quota file.
998 */
999int
1000dqsync_locked(struct dquot *dq)
1001{
1002	struct vfs_context context;
1003	struct vnode *dqvp;
1004	struct dqblk dqb, *dqblkp;
1005	uio_t auio;
1006	int error;
1007	char uio_buf[ UIO_SIZEOF(1) ];
1008
1009	if (dq->dq_id == 0) {
1010	        dq->dq_flags &= ~DQ_MOD;
1011		return (0);
1012	}
1013	if (dq->dq_qfile == NULL)
1014		panic("dqsync: NULL dq_qfile");
1015	if ((dqvp = dq->dq_qfile->qf_vp) == NULLVP)
1016		panic("dqsync: NULL qf_vp");
1017
1018	auio = uio_createwithbuffer(1, dqoffset(dq->dq_index), UIO_SYSSPACE,
1019								  UIO_WRITE, &uio_buf[0], sizeof(uio_buf));
1020	uio_addiov(auio, CAST_USER_ADDR_T(&dqb), sizeof (struct dqblk));
1021
1022	context.vc_thread = current_thread();	/* XXX */
1023	context.vc_ucred = dq->dq_qfile->qf_cred;
1024
1025	dqblkp = &dq->dq_dqb;
1026	dqb.dqb_bhardlimit = OSSwapHostToBigInt64(dqblkp->dqb_bhardlimit);
1027	dqb.dqb_bsoftlimit = OSSwapHostToBigInt64(dqblkp->dqb_bsoftlimit);
1028	dqb.dqb_curbytes   = OSSwapHostToBigInt64(dqblkp->dqb_curbytes);
1029	dqb.dqb_ihardlimit = OSSwapHostToBigInt32(dqblkp->dqb_ihardlimit);
1030	dqb.dqb_isoftlimit = OSSwapHostToBigInt32(dqblkp->dqb_isoftlimit);
1031	dqb.dqb_curinodes  = OSSwapHostToBigInt32(dqblkp->dqb_curinodes);
1032	dqb.dqb_btime      = OSSwapHostToBigInt32(dqblkp->dqb_btime);
1033	dqb.dqb_itime      = OSSwapHostToBigInt32(dqblkp->dqb_itime);
1034	dqb.dqb_id         = OSSwapHostToBigInt32(dqblkp->dqb_id);
1035	dqb.dqb_spare[0]   = 0;
1036	dqb.dqb_spare[1]   = 0;
1037	dqb.dqb_spare[2]   = 0;
1038	dqb.dqb_spare[3]   = 0;
1039
1040	error = VNOP_WRITE(dqvp, auio, 0, &context);
1041	if (uio_resid(auio) && error == 0)
1042		error = EIO;
1043	dq->dq_flags &= ~DQ_MOD;
1044
1045	return (error);
1046}
1047
1048/*
1049 * Flush all entries from the cache for a particular vnode.
1050 */
1051void
1052dqflush(struct vnode *vp)
1053{
1054	struct dquot *dq, *nextdq;
1055	struct dqhash *dqh;
1056
1057	if (!dqisinitialized())
1058		return;
1059
1060	/*
1061	 * Move all dquot's that used to refer to this quota
1062	 * file off their hash chains (they will eventually
1063	 * fall off the head of the free list and be re-used).
1064	 */
1065	dq_list_lock();
1066
1067	for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
1068		for (dq = dqh->lh_first; dq; dq = nextdq) {
1069			nextdq = dq->dq_hash.le_next;
1070			if (dq->dq_qfile->qf_vp != vp)
1071				continue;
1072			if (dq->dq_cnt)
1073				panic("dqflush: stray dquot");
1074			LIST_REMOVE(dq, dq_hash);
1075			dq->dq_qfile = NULL;
1076		}
1077	}
1078	dq_list_unlock();
1079}
1080
1081/*
1082 * LP64 support for munging dqblk structure.
1083 * XXX conversion of user_time_t to time_t loses precision; not an issue for
1084 * XXX us now, since we are only ever setting 32 bits worth of time into it.
1085 */
1086__private_extern__ void
1087munge_dqblk(struct dqblk *dqblkp, struct user_dqblk *user_dqblkp, boolean_t to64)
1088{
1089	if (to64) {
1090		/* munge kernel (32 bit) dqblk into user (64 bit) dqblk */
1091		bcopy((caddr_t)dqblkp, (caddr_t)user_dqblkp, offsetof(struct dqblk, dqb_btime));
1092		user_dqblkp->dqb_id = dqblkp->dqb_id;
1093		user_dqblkp->dqb_itime = dqblkp->dqb_itime;
1094		user_dqblkp->dqb_btime = dqblkp->dqb_btime;
1095	}
1096	else {
1097
1098		/* munge user (64 bit) dqblk into kernel (32 bit) dqblk */
1099		bcopy((caddr_t)user_dqblkp, (caddr_t)dqblkp, offsetof(struct dqblk, dqb_btime));
1100		dqblkp->dqb_id = user_dqblkp->dqb_id;
1101		dqblkp->dqb_itime = user_dqblkp->dqb_itime;	/* XXX - lose precision */
1102		dqblkp->dqb_btime = user_dqblkp->dqb_btime;	/* XXX - lose precision */
1103	}
1104}
1105