]>
git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_quota.c
61b0b83cd01ea3f454397e9fd68dcf7abea74ac5
2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright (c) 1982, 1986, 1990, 1993, 1995
24 * The Regents of the University of California. All rights reserved.
26 * This code is derived from software contributed to Berkeley by
27 * Robert Elz at The University of Melbourne.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
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.
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
58 * derived from @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
61 #include <sys/param.h>
62 #include <sys/kernel.h>
63 #include <sys/systm.h>
64 #include <sys/mount.h>
65 #include <sys/namei.h>
66 #include <sys/malloc.h>
69 #include <sys/vnode.h>
70 #include <sys/quota.h>
73 #include <hfs/hfs_cnode.h>
74 #include <hfs/hfs_quota.h>
75 #include <hfs/hfs_mount.h>
78 * Quota name to error message mapping.
80 static char *quotatypes
[] = INITQFNAMES
;
83 * Set up the quotas for a cnode.
85 * This routine completely defines the semantics of quotas.
86 * If other criterion want to be used to establish quotas, the
87 * MAXQUOTAS value in quotas.h should be increased, and the
88 * additional dquots set up here.
92 register struct cnode
*cp
;
94 struct hfsmount
*hfsmp
;
98 vp
= cp
->c_vp
? cp
->c_vp
: cp
->c_rsrc_vp
;
99 hfsmp
= VFSTOHFS(vp
->v_mount
);
101 * Set up the user quota based on file uid.
102 * EINVAL means that quotas are not enabled.
104 if (cp
->c_dquot
[USRQUOTA
] == NODQUOT
&&
106 dqget(vp
, cp
->c_uid
, &hfsmp
->hfs_qfiles
[USRQUOTA
], USRQUOTA
, &cp
->c_dquot
[USRQUOTA
])) &&
110 * Set up the group quota based on file gid.
111 * EINVAL means that quotas are not enabled.
113 if (cp
->c_dquot
[GRPQUOTA
] == NODQUOT
&&
115 dqget(vp
, cp
->c_gid
, &hfsmp
->hfs_qfiles
[GRPQUOTA
], GRPQUOTA
, &cp
->c_dquot
[GRPQUOTA
])) &&
122 * Update disk usage, and take corrective action.
125 hfs_chkdq(cp
, change
, cred
, flags
)
126 register struct cnode
*cp
;
131 register struct dquot
*dq
;
138 if ((flags
& CHOWN
) == 0)
144 for (i
= 0; i
< MAXQUOTAS
; i
++) {
145 if ((dq
= cp
->c_dquot
[i
]) == NODQUOT
)
147 while (dq
->dq_flags
& DQ_LOCK
) {
148 dq
->dq_flags
|= DQ_WANT
;
149 sleep((caddr_t
)dq
, PINOD
+1);
151 ncurbytes
= dq
->dq_curbytes
+ change
;
153 dq
->dq_curbytes
= ncurbytes
;
156 dq
->dq_flags
&= ~DQ_BLKS
;
157 dq
->dq_flags
|= DQ_MOD
;
163 cred
= kernproc
->p_ucred
;
164 if ((flags
& FORCE
) == 0 && ((cred
->cr_uid
!= 0) || (p
->p_flag
& P_FORCEQUOTA
))) {
165 for (i
= 0; i
< MAXQUOTAS
; i
++) {
166 if ((dq
= cp
->c_dquot
[i
]) == NODQUOT
)
168 if (error
= hfs_chkdqchg(cp
, change
, cred
, i
))
172 for (i
= 0; i
< MAXQUOTAS
; i
++) {
173 if ((dq
= cp
->c_dquot
[i
]) == NODQUOT
)
175 while (dq
->dq_flags
& DQ_LOCK
) {
176 dq
->dq_flags
|= DQ_WANT
;
177 sleep((caddr_t
)dq
, PINOD
+1);
179 dq
->dq_curbytes
+= change
;
180 dq
->dq_flags
|= DQ_MOD
;
186 * Check for a valid change to a users allocation.
187 * Issue an error message if appropriate.
190 hfs_chkdqchg(cp
, change
, cred
, type
)
196 register struct dquot
*dq
= cp
->c_dquot
[type
];
197 u_int64_t ncurbytes
= dq
->dq_curbytes
+ change
;
198 struct vnode
*vp
= cp
->c_vp
? cp
->c_vp
: cp
->c_rsrc_vp
;
201 * If user would exceed their hard limit, disallow space allocation.
203 if (ncurbytes
>= dq
->dq_bhardlimit
&& dq
->dq_bhardlimit
) {
204 if ((dq
->dq_flags
& DQ_BLKS
) == 0 &&
205 cp
->c_uid
== cred
->cr_uid
) {
207 printf("\n%s: write failed, %s disk limit reached\n",
208 vp
->v_mount
->mnt_stat
.f_mntonname
,
211 dq
->dq_flags
|= DQ_BLKS
;
216 * If user is over their soft limit for too long, disallow space
217 * allocation. Reset time limit as they cross their soft limit.
219 if (ncurbytes
>= dq
->dq_bsoftlimit
&& dq
->dq_bsoftlimit
) {
220 if (dq
->dq_curbytes
< dq
->dq_bsoftlimit
) {
221 dq
->dq_btime
= time
.tv_sec
+
222 VFSTOHFS(vp
->v_mount
)->hfs_qfiles
[type
].qf_btime
;
224 if (cp
->c_uid
== cred
->cr_uid
)
225 printf("\n%s: warning, %s %s\n",
226 vp
->v_mount
->mnt_stat
.f_mntonname
,
227 quotatypes
[type
], "disk quota exceeded");
231 if (time
.tv_sec
> dq
->dq_btime
) {
232 if ((dq
->dq_flags
& DQ_BLKS
) == 0 &&
233 cp
->c_uid
== cred
->cr_uid
) {
235 printf("\n%s: write failed, %s %s\n",
236 vp
->v_mount
->mnt_stat
.f_mntonname
,
238 "disk quota exceeded for too long");
240 dq
->dq_flags
|= DQ_BLKS
;
249 * Check the inode limit, applying corrective action.
252 hfs_chkiq(cp
, change
, cred
, flags
)
253 register struct cnode
*cp
;
258 register struct dquot
*dq
;
260 int ncurinodes
, error
;
264 if ((flags
& CHOWN
) == 0)
270 for (i
= 0; i
< MAXQUOTAS
; i
++) {
271 if ((dq
= cp
->c_dquot
[i
]) == NODQUOT
)
273 while (dq
->dq_flags
& DQ_LOCK
) {
274 dq
->dq_flags
|= DQ_WANT
;
275 sleep((caddr_t
)dq
, PINOD
+1);
277 ncurinodes
= dq
->dq_curinodes
+ change
;
279 dq
->dq_curinodes
= ncurinodes
;
281 dq
->dq_curinodes
= 0;
282 dq
->dq_flags
&= ~DQ_INODS
;
283 dq
->dq_flags
|= DQ_MOD
;
289 cred
= kernproc
->p_ucred
;
290 if ((flags
& FORCE
) == 0 && ((cred
->cr_uid
!= 0) || (p
->p_flag
& P_FORCEQUOTA
))) {
291 for (i
= 0; i
< MAXQUOTAS
; i
++) {
292 if ((dq
= cp
->c_dquot
[i
]) == NODQUOT
)
294 if (error
= hfs_chkiqchg(cp
, change
, cred
, i
))
298 for (i
= 0; i
< MAXQUOTAS
; i
++) {
299 if ((dq
= cp
->c_dquot
[i
]) == NODQUOT
)
301 while (dq
->dq_flags
& DQ_LOCK
) {
302 dq
->dq_flags
|= DQ_WANT
;
303 sleep((caddr_t
)dq
, PINOD
+1);
305 dq
->dq_curinodes
+= change
;
306 dq
->dq_flags
|= DQ_MOD
;
312 * Check for a valid change to a users allocation.
313 * Issue an error message if appropriate.
316 hfs_chkiqchg(cp
, change
, cred
, type
)
322 register struct dquot
*dq
= cp
->c_dquot
[type
];
323 long ncurinodes
= dq
->dq_curinodes
+ change
;
324 struct vnode
*vp
= cp
->c_vp
? cp
->c_vp
: cp
->c_rsrc_vp
;
327 * If user would exceed their hard limit, disallow cnode allocation.
329 if (ncurinodes
>= dq
->dq_ihardlimit
&& dq
->dq_ihardlimit
) {
330 if ((dq
->dq_flags
& DQ_INODS
) == 0 &&
331 cp
->c_uid
== cred
->cr_uid
) {
333 printf("\n%s: write failed, %s cnode limit reached\n",
334 vp
->v_mount
->mnt_stat
.f_mntonname
,
337 dq
->dq_flags
|= DQ_INODS
;
342 * If user is over their soft limit for too long, disallow cnode
343 * allocation. Reset time limit as they cross their soft limit.
345 if (ncurinodes
>= dq
->dq_isoftlimit
&& dq
->dq_isoftlimit
) {
346 if (dq
->dq_curinodes
< dq
->dq_isoftlimit
) {
347 dq
->dq_itime
= time
.tv_sec
+
348 VFSTOHFS(vp
->v_mount
)->hfs_qfiles
[type
].qf_itime
;
350 if (cp
->c_uid
== cred
->cr_uid
)
351 printf("\n%s: warning, %s %s\n",
352 vp
->v_mount
->mnt_stat
.f_mntonname
,
353 quotatypes
[type
], "cnode quota exceeded");
357 if (time
.tv_sec
> dq
->dq_itime
) {
358 if ((dq
->dq_flags
& DQ_INODS
) == 0 &&
359 cp
->c_uid
== cred
->cr_uid
) {
361 printf("\n%s: write failed, %s %s\n",
362 vp
->v_mount
->mnt_stat
.f_mntonname
,
364 "cnode quota exceeded for too long");
366 dq
->dq_flags
|= DQ_INODS
;
376 * On filesystems with quotas enabled, it is an error for a file to change
377 * size and not to have a dquot structure associated with it.
381 register struct cnode
*cp
;
383 struct vnode
*vp
= cp
->c_vp
? cp
->c_vp
: cp
->c_rsrc_vp
;
384 struct hfsmount
*hfsmp
= VFSTOHFS(vp
->v_mount
);
387 for (i
= 0; i
< MAXQUOTAS
; i
++) {
388 if (hfsmp
->hfs_qfiles
[i
].qf_vp
== NULLVP
||
389 (hfsmp
->hfs_qfiles
[i
].qf_qflags
& (QTF_OPENING
|QTF_CLOSING
)))
391 if (cp
->c_dquot
[i
] == NODQUOT
) {
392 vprint("chkdquot: missing dquot", vp
);
393 panic("missing dquot");
400 * Code to process quotactl commands.
404 * Q_QUOTAON - set up a quota file for a particular file system.
407 hfs_quotaon(p
, mp
, type
, fname
, segflg
)
414 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
415 struct vnode
*vp
, **vpp
;
416 struct vnode
*nextvp
;
421 vpp
= &hfsmp
->hfs_qfiles
[type
].qf_vp
;
422 NDINIT(&nd
, LOOKUP
, FOLLOW
, segflg
, fname
, p
);
423 if (error
= vn_open(&nd
, FREAD
|FWRITE
, 0))
426 VOP_UNLOCK(vp
, 0, p
);
427 if (vp
->v_type
!= VREG
) {
428 (void) vn_close(vp
, FREAD
|FWRITE
, p
->p_ucred
, p
);
432 hfs_quotaoff(p
, mp
, type
);
433 hfsmp
->hfs_qfiles
[type
].qf_qflags
|= QTF_OPENING
;
434 mp
->mnt_flag
|= MNT_QUOTA
;
435 vp
->v_flag
|= VNOFLUSH
;
438 * Save the credential of the process that turned on quotas.
441 hfsmp
->hfs_qfiles
[type
].qf_cred
= p
->p_ucred
;
442 /* Finish initializing the quota file */
443 if (error
= dqfileopen(&hfsmp
->hfs_qfiles
[type
], type
))
446 * Search vnodes associated with this mount point,
447 * adding references to quota file being opened.
448 * NB: only need to add dquot's for cnodes being modified.
451 for (vp
= mp
->mnt_vnodelist
.lh_first
; vp
!= NULL
; vp
= nextvp
) {
452 nextvp
= vp
->v_mntvnodes
.le_next
;
453 if (vp
->v_writecount
== 0)
455 if (vget(vp
, LK_EXCLUSIVE
, p
))
457 if (error
= hfs_getinoquota(VTOC(vp
))) {
462 if (vp
->v_mntvnodes
.le_next
!= nextvp
|| vp
->v_mount
!= mp
)
466 hfsmp
->hfs_qfiles
[type
].qf_qflags
&= ~QTF_OPENING
;
468 hfs_quotaoff(p
, mp
, type
);
473 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
476 hfs_quotaoff(p
, mp
, type
)
482 struct vnode
*qvp
, *nextvp
;
483 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
489 if ((qvp
= hfsmp
->hfs_qfiles
[type
].qf_vp
) == NULLVP
)
491 hfsmp
->hfs_qfiles
[type
].qf_qflags
|= QTF_CLOSING
;
493 * Search vnodes associated with this mount point,
494 * deleting any references to quota file being closed.
497 for (vp
= mp
->mnt_vnodelist
.lh_first
; vp
!= NULL
; vp
= nextvp
) {
498 nextvp
= vp
->v_mntvnodes
.le_next
;
499 if (vget(vp
, LK_EXCLUSIVE
, p
))
502 dq
= cp
->c_dquot
[type
];
503 cp
->c_dquot
[type
] = NODQUOT
;
506 if (vp
->v_mntvnodes
.le_next
!= nextvp
|| vp
->v_mount
!= mp
)
510 /* Finish tearing down the quota file */
511 dqfileclose(&hfsmp
->hfs_qfiles
[type
], type
);
512 qvp
->v_flag
&= ~VNOFLUSH
;
513 error
= vn_close(qvp
, FREAD
|FWRITE
, p
->p_ucred
, p
);
514 hfsmp
->hfs_qfiles
[type
].qf_vp
= NULLVP
;
515 cred
= hfsmp
->hfs_qfiles
[type
].qf_cred
;
516 if (cred
!= NOCRED
) {
517 hfsmp
->hfs_qfiles
[type
].qf_cred
= NOCRED
;
520 hfsmp
->hfs_qfiles
[type
].qf_qflags
&= ~QTF_CLOSING
;
521 for (type
= 0; type
< MAXQUOTAS
; type
++)
522 if (hfsmp
->hfs_qfiles
[type
].qf_vp
!= NULLVP
)
524 if (type
== MAXQUOTAS
)
525 mp
->mnt_flag
&= ~MNT_QUOTA
;
530 * Q_GETQUOTA - return current values in a dqblk structure.
533 hfs_getquota(mp
, id
, type
, addr
)
542 if (error
= dqget(NULLVP
, id
, &VFSTOHFS(mp
)->hfs_qfiles
[type
], type
, &dq
))
544 error
= copyout((caddr_t
)&dq
->dq_dqb
, addr
, sizeof (struct dqblk
));
550 * Q_SETQUOTA - assign an entire dqblk structure.
553 hfs_setquota(mp
, id
, type
, addr
)
559 register struct dquot
*dq
;
561 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
565 if (error
= copyin(addr
, (caddr_t
)&newlim
, sizeof (struct dqblk
)))
567 if (error
= dqget(NULLVP
, id
, &hfsmp
->hfs_qfiles
[type
], type
, &ndq
))
570 while (dq
->dq_flags
& DQ_LOCK
) {
571 dq
->dq_flags
|= DQ_WANT
;
572 sleep((caddr_t
)dq
, PINOD
+1);
575 * Copy all but the current values.
576 * Reset time limit if previously had no soft limit or were
577 * under it, but now have a soft limit and are over it.
579 newlim
.dqb_curbytes
= dq
->dq_curbytes
;
580 newlim
.dqb_curinodes
= dq
->dq_curinodes
;
581 if (dq
->dq_id
!= 0) {
582 newlim
.dqb_btime
= dq
->dq_btime
;
583 newlim
.dqb_itime
= dq
->dq_itime
;
585 if (newlim
.dqb_bsoftlimit
&&
586 dq
->dq_curbytes
>= newlim
.dqb_bsoftlimit
&&
587 (dq
->dq_bsoftlimit
== 0 || dq
->dq_curbytes
< dq
->dq_bsoftlimit
))
588 newlim
.dqb_btime
= time
.tv_sec
+ hfsmp
->hfs_qfiles
[type
].qf_btime
;
589 if (newlim
.dqb_isoftlimit
&&
590 dq
->dq_curinodes
>= newlim
.dqb_isoftlimit
&&
591 (dq
->dq_isoftlimit
== 0 || dq
->dq_curinodes
< dq
->dq_isoftlimit
))
592 newlim
.dqb_itime
= time
.tv_sec
+ hfsmp
->hfs_qfiles
[type
].qf_itime
;
594 if (dq
->dq_curbytes
< dq
->dq_bsoftlimit
)
595 dq
->dq_flags
&= ~DQ_BLKS
;
596 if (dq
->dq_curinodes
< dq
->dq_isoftlimit
)
597 dq
->dq_flags
&= ~DQ_INODS
;
598 if (dq
->dq_isoftlimit
== 0 && dq
->dq_bsoftlimit
== 0 &&
599 dq
->dq_ihardlimit
== 0 && dq
->dq_bhardlimit
== 0)
600 dq
->dq_flags
|= DQ_FAKE
;
602 dq
->dq_flags
&= ~DQ_FAKE
;
603 dq
->dq_flags
|= DQ_MOD
;
609 * Q_SETUSE - set current cnode and byte usage.
612 hfs_setuse(mp
, id
, type
, addr
)
618 register struct dquot
*dq
;
619 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
624 if (error
= copyin(addr
, (caddr_t
)&usage
, sizeof (struct dqblk
)))
626 if (error
= dqget(NULLVP
, id
, &hfsmp
->hfs_qfiles
[type
], type
, &ndq
))
629 while (dq
->dq_flags
& DQ_LOCK
) {
630 dq
->dq_flags
|= DQ_WANT
;
631 sleep((caddr_t
)dq
, PINOD
+1);
634 * Reset time limit if have a soft limit and were
635 * previously under it, but are now over it.
637 if (dq
->dq_bsoftlimit
&& dq
->dq_curbytes
< dq
->dq_bsoftlimit
&&
638 usage
.dqb_curbytes
>= dq
->dq_bsoftlimit
)
639 dq
->dq_btime
= time
.tv_sec
+ hfsmp
->hfs_qfiles
[type
].qf_btime
;
640 if (dq
->dq_isoftlimit
&& dq
->dq_curinodes
< dq
->dq_isoftlimit
&&
641 usage
.dqb_curinodes
>= dq
->dq_isoftlimit
)
642 dq
->dq_itime
= time
.tv_sec
+ hfsmp
->hfs_qfiles
[type
].qf_itime
;
643 dq
->dq_curbytes
= usage
.dqb_curbytes
;
644 dq
->dq_curinodes
= usage
.dqb_curinodes
;
645 if (dq
->dq_curbytes
< dq
->dq_bsoftlimit
)
646 dq
->dq_flags
&= ~DQ_BLKS
;
647 if (dq
->dq_curinodes
< dq
->dq_isoftlimit
)
648 dq
->dq_flags
&= ~DQ_INODS
;
649 dq
->dq_flags
|= DQ_MOD
;
655 * Q_SYNC - sync quota files to disk.
661 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
662 struct proc
*p
= current_proc(); /* XXX */
663 struct vnode
*vp
, *nextvp
;
668 * Check if the mount point has any quotas.
669 * If not, simply return.
671 for (i
= 0; i
< MAXQUOTAS
; i
++)
672 if (hfsmp
->hfs_qfiles
[i
].qf_vp
!= NULLVP
)
677 * Search vnodes associated with this mount point,
678 * synchronizing any modified dquot structures.
680 simple_lock(&mntvnode_slock
);
682 for (vp
= mp
->mnt_vnodelist
.lh_first
; vp
!= NULL
; vp
= nextvp
) {
683 if (vp
->v_mount
!= mp
)
685 nextvp
= vp
->v_mntvnodes
.le_next
;
686 simple_lock(&vp
->v_interlock
);
687 simple_unlock(&mntvnode_slock
);
688 error
= vget(vp
, LK_EXCLUSIVE
| LK_NOWAIT
| LK_INTERLOCK
, p
);
690 simple_lock(&mntvnode_slock
);
695 for (i
= 0; i
< MAXQUOTAS
; i
++) {
696 dq
= VTOC(vp
)->c_dquot
[i
];
697 if (dq
!= NODQUOT
&& (dq
->dq_flags
& DQ_MOD
))
701 simple_lock(&mntvnode_slock
);
702 if (vp
->v_mntvnodes
.le_next
!= nextvp
)
705 simple_unlock(&mntvnode_slock
);
710 * Q_QUOTASTAT - get quota on/off status
713 hfs_quotastat(mp
, type
, addr
)
718 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
722 if ((mp
->mnt_flag
& MNT_QUOTA
) && (hfsmp
->hfs_qfiles
[type
].qf_vp
!= NULLVP
))
723 qstat
= 1; /* quotas are on for this type */
725 qstat
= 0; /* quotas are off for this type */
727 error
= copyout ((caddr_t
)&qstat
, addr
, sizeof(qstat
));