2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 1992-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 * This is one of the most system dependent parts of Samba, and its
23 * done a litle differently. Each system has its own way of doing
30 #define DBGC_CLASS DBGC_QUOTA
32 #ifndef HAVE_SYS_QUOTAS
34 /* just a quick hack because sysquotas.h is included before linux/quota.h */
35 #ifdef QUOTABLOCK_SIZE
36 #undef QUOTABLOCK_SIZE
41 #if defined(VXFS_QUOTA)
44 * In addition to their native filesystems, some systems have Veritas VxFS.
45 * Declare here, define at end: reduces likely "include" interaction problems.
46 * David Lee <T.D.Lee@durham.ac.uk>
48 bool disk_quotas_vxfs(const char *name
, char *path
, SMB_BIG_UINT
*bsize
, SMB_BIG_UINT
*dfree
, SMB_BIG_UINT
*dsize
);
50 #endif /* VXFS_QUOTA */
54 #include <sys/types.h>
58 * This shouldn't be neccessary - it should be /usr/include/sys/quota.h
59 * So we include all the files has *should* be in the system into a large,
60 * grungy samba_linux_quoatas.h Sometimes I *hate* Linux :-). JRA.
63 #include "samba_linux_quota.h"
65 typedef struct _LINUX_SMB_DISK_QUOTA
{
67 SMB_BIG_UINT hardlimit
; /* In bsize units. */
68 SMB_BIG_UINT softlimit
; /* In bsize units. */
69 SMB_BIG_UINT curblocks
; /* In bsize units. */
70 SMB_BIG_UINT ihardlimit
; /* inode hard limit. */
71 SMB_BIG_UINT isoftlimit
; /* inode soft limit. */
72 SMB_BIG_UINT curinodes
; /* Current used inodes. */
73 } LINUX_SMB_DISK_QUOTA
;
76 #ifdef HAVE_LINUX_DQBLK_XFS_H
77 #include <linux/dqblk_xfs.h>
79 /****************************************************************************
80 Abstract out the XFS Quota Manager quota get call.
81 ****************************************************************************/
83 static int get_smb_linux_xfs_quota(char *path
, uid_t euser_id
, gid_t egrp_id
, LINUX_SMB_DISK_QUOTA
*dp
)
85 struct fs_disk_quota D
;
90 ret
= quotactl(QCMD(Q_XGETQUOTA
,USRQUOTA
), path
, euser_id
, (caddr_t
)&D
);
93 ret
= quotactl(QCMD(Q_XGETQUOTA
,GRPQUOTA
), path
, egrp_id
, (caddr_t
)&D
);
98 dp
->bsize
= (SMB_BIG_UINT
)512;
99 dp
->softlimit
= (SMB_BIG_UINT
)D
.d_blk_softlimit
;
100 dp
->hardlimit
= (SMB_BIG_UINT
)D
.d_blk_hardlimit
;
101 dp
->ihardlimit
= (SMB_BIG_UINT
)D
.d_ino_hardlimit
;
102 dp
->isoftlimit
= (SMB_BIG_UINT
)D
.d_ino_softlimit
;
103 dp
->curinodes
= (SMB_BIG_UINT
)D
.d_icount
;
104 dp
->curblocks
= (SMB_BIG_UINT
)D
.d_bcount
;
109 static int get_smb_linux_xfs_quota(char *path
, uid_t euser_id
, gid_t egrp_id
, LINUX_SMB_DISK_QUOTA
*dp
)
111 DEBUG(0,("XFS quota support not available\n"));
118 /****************************************************************************
119 Abstract out the old and new Linux quota get calls.
120 ****************************************************************************/
122 static int get_smb_linux_v1_quota(char *path
, uid_t euser_id
, gid_t egrp_id
, LINUX_SMB_DISK_QUOTA
*dp
)
124 struct v1_kern_dqblk D
;
129 ret
= quotactl(QCMD(Q_V1_GETQUOTA
,USRQUOTA
), path
, euser_id
, (caddr_t
)&D
);
131 if (ret
&& errno
!= EDQUOT
)
132 ret
= quotactl(QCMD(Q_V1_GETQUOTA
,GRPQUOTA
), path
, egrp_id
, (caddr_t
)&D
);
134 if (ret
&& errno
!= EDQUOT
)
137 dp
->bsize
= (SMB_BIG_UINT
)QUOTABLOCK_SIZE
;
138 dp
->softlimit
= (SMB_BIG_UINT
)D
.dqb_bsoftlimit
;
139 dp
->hardlimit
= (SMB_BIG_UINT
)D
.dqb_bhardlimit
;
140 dp
->ihardlimit
= (SMB_BIG_UINT
)D
.dqb_ihardlimit
;
141 dp
->isoftlimit
= (SMB_BIG_UINT
)D
.dqb_isoftlimit
;
142 dp
->curinodes
= (SMB_BIG_UINT
)D
.dqb_curinodes
;
143 dp
->curblocks
= (SMB_BIG_UINT
)D
.dqb_curblocks
;
148 static int get_smb_linux_v2_quota(char *path
, uid_t euser_id
, gid_t egrp_id
, LINUX_SMB_DISK_QUOTA
*dp
)
150 struct v2_kern_dqblk D
;
155 ret
= quotactl(QCMD(Q_V2_GETQUOTA
,USRQUOTA
), path
, euser_id
, (caddr_t
)&D
);
157 if (ret
&& errno
!= EDQUOT
)
158 ret
= quotactl(QCMD(Q_V2_GETQUOTA
,GRPQUOTA
), path
, egrp_id
, (caddr_t
)&D
);
160 if (ret
&& errno
!= EDQUOT
)
163 dp
->bsize
= (SMB_BIG_UINT
)QUOTABLOCK_SIZE
;
164 dp
->softlimit
= (SMB_BIG_UINT
)D
.dqb_bsoftlimit
;
165 dp
->hardlimit
= (SMB_BIG_UINT
)D
.dqb_bhardlimit
;
166 dp
->ihardlimit
= (SMB_BIG_UINT
)D
.dqb_ihardlimit
;
167 dp
->isoftlimit
= (SMB_BIG_UINT
)D
.dqb_isoftlimit
;
168 dp
->curinodes
= (SMB_BIG_UINT
)D
.dqb_curinodes
;
169 dp
->curblocks
= ((SMB_BIG_UINT
)D
.dqb_curspace
) / dp
->bsize
;
174 /****************************************************************************
175 Brand-new generic quota interface.
176 ****************************************************************************/
178 static int get_smb_linux_gen_quota(char *path
, uid_t euser_id
, gid_t egrp_id
, LINUX_SMB_DISK_QUOTA
*dp
)
185 ret
= quotactl(QCMD(Q_GETQUOTA
,USRQUOTA
), path
, euser_id
, (caddr_t
)&D
);
187 if (ret
&& errno
!= EDQUOT
)
188 ret
= quotactl(QCMD(Q_GETQUOTA
,GRPQUOTA
), path
, egrp_id
, (caddr_t
)&D
);
190 if (ret
&& errno
!= EDQUOT
)
193 dp
->bsize
= (SMB_BIG_UINT
)QUOTABLOCK_SIZE
;
194 dp
->softlimit
= (SMB_BIG_UINT
)D
.dqb_bsoftlimit
;
195 dp
->hardlimit
= (SMB_BIG_UINT
)D
.dqb_bhardlimit
;
196 dp
->ihardlimit
= (SMB_BIG_UINT
)D
.dqb_ihardlimit
;
197 dp
->isoftlimit
= (SMB_BIG_UINT
)D
.dqb_isoftlimit
;
198 dp
->curinodes
= (SMB_BIG_UINT
)D
.dqb_curinodes
;
199 dp
->curblocks
= ((SMB_BIG_UINT
)D
.dqb_curspace
) / dp
->bsize
;
204 /****************************************************************************
205 Try to get the disk space from disk quotas (LINUX version).
206 ****************************************************************************/
208 bool disk_quotas(const char *path
, SMB_BIG_UINT
*bsize
, SMB_BIG_UINT
*dfree
, SMB_BIG_UINT
*dsize
)
213 LINUX_SMB_DISK_QUOTA D
;
222 euser_id
= geteuid();
225 /* find the block device file */
227 if ( sys_stat(path
, &S
) == -1 )
232 if ((fp
= setmntent(MOUNTED
,"r")) == NULL
)
237 while ((mnt
= getmntent(fp
))) {
238 if ( sys_stat(mnt
->mnt_dir
,&S
) == -1 )
241 if (S
.st_dev
== devno
) {
254 if (strcmp(mnt
->mnt_type
, "xfs")==0) {
255 r
=get_smb_linux_xfs_quota(mnt
->mnt_fsname
, euser_id
, egrp_id
, &D
);
257 r
=get_smb_linux_gen_quota(mnt
->mnt_fsname
, euser_id
, egrp_id
, &D
);
258 if (r
== -1 && errno
!= EDQUOT
) {
259 r
=get_smb_linux_v2_quota(mnt
->mnt_fsname
, euser_id
, egrp_id
, &D
);
260 if (r
== -1 && errno
!= EDQUOT
)
261 r
=get_smb_linux_v1_quota(mnt
->mnt_fsname
, euser_id
, egrp_id
, &D
);
267 /* Use softlimit to determine disk space, except when it has been exceeded */
270 if (errno
== EDQUOT
) {
279 /* Use softlimit to determine disk space, except when it has been exceeded */
281 (D
.softlimit
&& D
.curblocks
>= D
.softlimit
) ||
282 (D
.hardlimit
&& D
.curblocks
>= D
.hardlimit
) ||
283 (D
.isoftlimit
&& D
.curinodes
>= D
.isoftlimit
) ||
284 (D
.ihardlimit
&& D
.curinodes
>=D
.ihardlimit
)
287 *dsize
= D
.curblocks
;
288 } else if (D
.softlimit
==0 && D
.hardlimit
==0) {
291 if (D
.softlimit
== 0)
292 D
.softlimit
= D
.hardlimit
;
293 *dfree
= D
.softlimit
- D
.curblocks
;
294 *dsize
= D
.softlimit
;
302 #include <sys/quota.h>
305 /****************************************************************************
306 try to get the disk space from disk quotas (CRAY VERSION)
307 ****************************************************************************/
309 bool disk_quotas(const char *path
, SMB_BIG_UINT
*bsize
, SMB_BIG_UINT
*dfree
, SMB_BIG_UINT
*dsize
)
313 SMB_STRUCT_STAT sbuf
;
315 struct q_request request
;
316 struct qf_header header
;
317 int quota_default
= 0 ;
320 if (sys_stat(path
,&sbuf
) == -1) {
324 devno
= sbuf
.st_dev
;
326 if ((fd
= setmntent(KMTAB
)) == NULL
) {
330 while ((mnt
= getmntent(fd
)) != NULL
) {
331 if (sys_stat(mnt
->mnt_dir
,&sbuf
) == -1) {
334 if (sbuf
.st_dev
== devno
) {
340 name
= talloc_strdup(talloc_tos(), mnt
->mnt_dir
);
350 request
.qf_magic
= QF_MAGIC
;
351 request
.qf_entry
.id
= geteuid() ;
353 if (quotactl(name
, Q_GETQUOTA
, &request
) == -1) {
361 if (request
.qf_entry
.user_q
.f_quota
== QFV_DEFAULT
) {
362 if (!quota_default
) {
363 if (quotactl(name
, Q_GETHEADER
, &header
) == -1) {
366 quota_default
= header
.user_h
.def_fq
;
369 *dfree
= quota_default
;
370 } else if (request
.qf_entry
.user_q
.f_quota
== QFV_PREVENT
) {
373 *dfree
= request
.qf_entry
.user_q
.f_quota
;
376 *dsize
= request
.qf_entry
.user_q
.f_use
;
378 if (*dfree
< *dsize
) {
384 *bsize
= 4096 ; /* Cray blocksize */
389 #elif defined(SUNOS5) || defined(SUNOS4)
392 #include <sys/param.h>
394 #include <sys/fs/ufs_quota.h>
395 #include <sys/mnttab.h>
396 #include <sys/mntent.h>
397 #else /* defined(SUNOS4) */
398 #include <ufs/quota.h>
404 /****************************************************************************
405 Allows querying of remote hosts for quotas on NFS mounted shares.
406 Supports normal NFS and AMD mounts.
407 Alan Romeril <a.romeril@ic.ac.uk> July 2K.
408 ****************************************************************************/
411 #include <rpc/types.h>
412 #include <rpcsvc/rquota.h>
413 #include <rpc/nettype.h>
416 static int quotastat
;
418 static int my_xdr_getquota_args(XDR
*xdrsp
, struct getquota_args
*args
)
420 if (!xdr_string(xdrsp
, &args
->gqa_pathp
, RQ_PATHLEN
))
422 if (!xdr_int(xdrsp
, &args
->gqa_uid
))
427 static int my_xdr_getquota_rslt(XDR
*xdrsp
, struct getquota_rslt
*gqr
)
429 if (!xdr_int(xdrsp
, "astat
)) {
430 DEBUG(6,("nfs_quotas: Status bad or zero\n"));
433 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bsize
)) {
434 DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
437 if (!xdr_bool(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_active
)) {
438 DEBUG(6,("nfs_quotas: Active bad or zero\n"));
441 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
)) {
442 DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
445 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
)) {
446 DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
449 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_curblocks
)) {
450 DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
456 /* Restricted to SUNOS5 for the moment, I haven`t access to others to test. */
457 static bool nfs_quotas(char *nfspath
, uid_t euser_id
, SMB_BIG_UINT
*bsize
, SMB_BIG_UINT
*dfree
, SMB_BIG_UINT
*dsize
)
459 uid_t uid
= euser_id
;
461 char *mnttype
= nfspath
;
463 struct getquota_rslt gqr
;
464 struct getquota_args args
;
465 char *cutstr
, *pathname
, *host
, *testpath
;
467 static struct timeval timeout
= {2,0};
468 enum clnt_stat clnt_stat
;
471 *bsize
= *dfree
= *dsize
= (SMB_BIG_UINT
)0;
473 len
=strcspn(mnttype
, ":");
474 pathname
=strstr(mnttype
, ":");
475 cutstr
= (char *) SMB_MALLOC(len
+1);
479 memset(cutstr
, '\0', len
+1);
480 host
= strncat(cutstr
,mnttype
, sizeof(char) * len
);
481 DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr
));
482 DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype
));
483 testpath
=strchr_m(mnttype
, ':');
484 args
.gqa_pathp
= testpath
+1;
487 DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host
, RQUOTAPROG
, RQUOTAVERS
, "udp"));
489 if ((clnt
= clnt_create(host
, RQUOTAPROG
, RQUOTAVERS
, "udp")) == NULL
) {
494 clnt
->cl_auth
= authunix_create_default();
495 DEBUG(9,("nfs_quotas: auth_success\n"));
497 clnt_stat
=clnt_call(clnt
, RQUOTAPROC_GETQUOTA
, my_xdr_getquota_args
, (caddr_t
)&args
, my_xdr_getquota_rslt
, (caddr_t
)&gqr
, timeout
);
499 if (clnt_stat
!= RPC_SUCCESS
) {
500 DEBUG(9,("nfs_quotas: clnt_call fail\n"));
506 * quotastat returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is
507 * no quota set, and 3 if no permission to get the quota. If 0 or 3 return
508 * something sensible.
511 switch ( quotastat
) {
513 DEBUG(9,("nfs_quotas: Remote Quotas Failed! Error \"%i\" \n", quotastat
));
518 DEBUG(9,("nfs_quotas: Good quota data\n"));
519 D
.dqb_bsoftlimit
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
;
520 D
.dqb_bhardlimit
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
;
521 D
.dqb_curblocks
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
;
526 D
.dqb_bsoftlimit
= 1;
528 DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", quotastat
));
532 DEBUG(9,("nfs_quotas: Remote Quotas Questionable! Error \"%i\" \n", quotastat
));
536 DEBUG(10,("nfs_quotas: Let`s look at D a bit closer... status \"%i\" bsize \"%i\" active? \"%i\" bhard \"%i\" bsoft \"%i\" curb \"%i\" \n",
538 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsize
,
539 gqr
.getquota_rslt_u
.gqr_rquota
.rq_active
,
540 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
,
541 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
,
542 gqr
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
));
544 *bsize
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsize
;
545 *dsize
= D
.dqb_bsoftlimit
;
547 if (D
.dqb_curblocks
== D
.dqb_curblocks
== 1)
550 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
552 *dsize
= D
.dqb_curblocks
;
554 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
560 auth_destroy(clnt
->cl_auth
);
564 DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",args
.gqa_pathp
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
567 DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
572 /****************************************************************************
573 try to get the disk space from disk quotas (SunOS & Solaris2 version)
574 Quota code by Peter Urbanec (amiga@cse.unsw.edu.au).
575 ****************************************************************************/
577 bool disk_quotas(const char *path
,
586 struct quotctl command
;
594 SMB_STRUCT_STAT sbuf
;
598 euser_id
= geteuid();
600 if (sys_stat(path
,&sbuf
) == -1) {
604 devno
= sbuf
.st_dev
;
605 DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n",
606 path
, (unsigned int)devno
));
608 if ((fd
= sys_fopen(MNTTAB
, "r")) == NULL
) {
612 while (getmntent(fd
, &mnt
) == 0) {
613 if (sys_stat(mnt
.mnt_mountp
, &sbuf
) == -1) {
617 DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n",
618 mnt
.mnt_mountp
, (unsigned int)devno
));
620 /* quotas are only on vxfs, UFS or NFS */
621 if ((sbuf
.st_dev
== devno
) && (
622 strcmp( mnt
.mnt_fstype
, MNTTYPE_UFS
) == 0 ||
623 strcmp( mnt
.mnt_fstype
, "nfs" ) == 0 ||
624 strcmp( mnt
.mnt_fstype
, "vxfs" ) == 0 )) {
626 name
= talloc_asprintf(talloc_tos(),
635 if ((fd
= setmntent(MOUNTED
, "r")) == NULL
) {
639 while ((mnt
= getmntent(fd
)) != NULL
) {
640 if (sys_stat(mnt
->mnt_dir
,&sbuf
) == -1) {
643 DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n",
645 (unsigned int)sbuf
.st_dev
));
646 if (sbuf
.st_dev
== devno
) {
648 name
= talloc_strdup(talloc_tos(),
666 if (strcmp(mnt
.mnt_fstype
, "nfs") == 0) {
668 DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n",
670 retval
= nfs_quotas(mnt
.mnt_special
,
671 euser_id
, bsize
, dfree
, dsize
);
676 DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name
));
677 if((file
=sys_open(name
, O_RDONLY
,0))<0) {
681 command
.op
= Q_GETQUOTA
;
682 command
.uid
= euser_id
;
683 command
.addr
= (caddr_t
) &D
;
684 ret
= ioctl(file
, Q_QUOTACTL
, &command
);
687 DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name
));
688 ret
= quotactl(Q_GETQUOTA
, name
, euser_id
, &D
);
694 DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n",
697 #if defined(SUNOS5) && defined(VXFS_QUOTA)
698 /* If normal quotactl() fails, try vxfs private calls */
699 set_effective_uid(euser_id
);
700 DEBUG(5,("disk_quotas: mount type \"%s\"\n", mnt
.mnt_fstype
));
701 if ( 0 == strcmp ( mnt
.mnt_fstype
, "vxfs" )) {
703 retval
= disk_quotas_vxfs(name
, path
,
704 bsize
, dfree
, dsize
);
712 /* If softlimit is zero, set it equal to hardlimit.
715 if (D
.dqb_bsoftlimit
==0) {
716 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
719 /* Use softlimit to determine disk space. A user exceeding the quota
720 * is told that there's no space left. Writes might actually work for
721 * a bit if the hardlimit is set higher than softlimit. Effectively
722 * the disk becomes made of rubber latex and begins to expand to
723 * accommodate the user :-)
726 if (D
.dqb_bsoftlimit
==0)
729 *dsize
= D
.dqb_bsoftlimit
;
731 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
733 *dsize
= D
.dqb_curblocks
;
735 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
738 DEBUG(5,("disk_quotas for path \"%s\" returning "
739 "bsize %.0f, dfree %.0f, dsize %.0f\n",
740 path
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
747 #include <ufs/quota.h>
749 /****************************************************************************
750 try to get the disk space from disk quotas - OSF1 version
751 ****************************************************************************/
753 bool disk_quotas(const char *path
, SMB_BIG_UINT
*bsize
, SMB_BIG_UINT
*dfree
, SMB_BIG_UINT
*dsize
)
761 * This code presumes that OSF1 will only
762 * give out quota info when the real uid
763 * matches the effective uid. JRA.
765 euser_id
= geteuid();
767 if (set_re_uid() != 0) return False
;
769 r
= quotactl(path
,QCMD(Q_GETQUOTA
, USRQUOTA
),euser_id
,(char *) &D
);
780 if (save_errno
== EDQUOT
) /* disk quota exceeded */
783 *dsize
= D
.dqb_curblocks
;
790 /* If softlimit is zero, set it equal to hardlimit.
793 if (D
.dqb_bsoftlimit
==0)
794 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
796 /* Use softlimit to determine disk space, except when it has been exceeded */
798 if (D
.dqb_bsoftlimit
==0)
801 if ((D
.dqb_curblocks
>D
.dqb_bsoftlimit
)) {
803 *dsize
= D
.dqb_curblocks
;
805 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
806 *dsize
= D
.dqb_bsoftlimit
;
811 #elif defined (IRIX6)
812 /****************************************************************************
813 try to get the disk space from disk quotas (IRIX 6.2 version)
814 ****************************************************************************/
816 #include <sys/quota.h>
819 bool disk_quotas(const char *path
, SMB_BIG_UINT
*bsize
, SMB_BIG_UINT
*dfree
, SMB_BIG_UINT
*dsize
)
824 struct fs_disk_quota F
;
831 /* find the block device file */
833 if ( sys_stat(path
, &S
) == -1 ) {
839 fp
= setmntent(MOUNTED
,"r");
842 while ((mnt
= getmntent(fp
))) {
843 if ( sys_stat(mnt
->mnt_dir
,&S
) == -1 )
845 if (S
.st_dev
== devno
) {
859 /* Use softlimit to determine disk space, except when it has been exceeded */
863 if ( 0 == strcmp ( mnt
->mnt_type
, "efs" ))
865 r
=quotactl (Q_GETQUOTA
, mnt
->mnt_fsname
, euser_id
, (caddr_t
) &D
);
872 /* Use softlimit to determine disk space, except when it has been exceeded */
874 (D
.dqb_bsoftlimit
&& D
.dqb_curblocks
>=D
.dqb_bsoftlimit
) ||
875 (D
.dqb_bhardlimit
&& D
.dqb_curblocks
>=D
.dqb_bhardlimit
) ||
876 (D
.dqb_fsoftlimit
&& D
.dqb_curfiles
>=D
.dqb_fsoftlimit
) ||
877 (D
.dqb_fhardlimit
&& D
.dqb_curfiles
>=D
.dqb_fhardlimit
)
881 *dsize
= D
.dqb_curblocks
;
883 else if (D
.dqb_bsoftlimit
==0 && D
.dqb_bhardlimit
==0)
889 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
890 *dsize
= D
.dqb_bsoftlimit
;
894 else if ( 0 == strcmp ( mnt
->mnt_type
, "xfs" ))
896 r
=quotactl (Q_XGETQUOTA
, mnt
->mnt_fsname
, euser_id
, (caddr_t
) &F
);
902 DEBUG(5, ("quotactl for uid=%u: %s", euser_id
, strerror(errno
)));
906 /* No quota for this user. */
907 if (F
.d_blk_softlimit
==0 && F
.d_blk_hardlimit
==0)
912 /* Use softlimit to determine disk space, except when it has been exceeded */
914 (F
.d_blk_softlimit
&& F
.d_bcount
>=F
.d_blk_softlimit
) ||
915 (F
.d_blk_hardlimit
&& F
.d_bcount
>=F
.d_blk_hardlimit
) ||
916 (F
.d_ino_softlimit
&& F
.d_icount
>=F
.d_ino_softlimit
) ||
917 (F
.d_ino_hardlimit
&& F
.d_icount
>=F
.d_ino_hardlimit
)
925 *dfree
= (F
.d_blk_softlimit
- F
.d_bcount
);
926 *dsize
= F
.d_blk_softlimit
? F
.d_blk_softlimit
: F
.d_blk_hardlimit
;
942 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
943 #include <ufs/ufs/quota.h>
944 #include <machine/param.h>
946 /* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
947 #include <jfs/quota.h>
948 /* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
949 #define dqb_curfiles dqb_curinodes
950 #define dqb_fhardlimit dqb_ihardlimit
951 #define dqb_fsoftlimit dqb_isoftlimit
952 #ifdef _AIXVERSION_530
953 #include <sys/statfs.h>
954 #include <sys/vmount.h>
956 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ && !__DragonFly__ */
957 #include <sys/quota.h>
961 #if defined(__FreeBSD__) || defined(__DragonFly__)
964 #include <rpc/types.h>
965 #include <rpcsvc/rquota.h>
966 #ifdef HAVE_RPC_NETTYPE_H
967 #include <rpc/nettype.h>
971 static int quotastat
;
973 static int my_xdr_getquota_args(XDR
*xdrsp
, struct getquota_args
*args
)
975 if (!xdr_string(xdrsp
, &args
->gqa_pathp
, RQ_PATHLEN
))
977 if (!xdr_int(xdrsp
, &args
->gqa_uid
))
982 static int my_xdr_getquota_rslt(XDR
*xdrsp
, struct getquota_rslt
*gqr
)
984 if (!xdr_int(xdrsp
, "astat
)) {
985 DEBUG(6,("nfs_quotas: Status bad or zero\n"));
988 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bsize
)) {
989 DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
992 if (!xdr_bool(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_active
)) {
993 DEBUG(6,("nfs_quotas: Active bad or zero\n"));
996 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
)) {
997 DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
1000 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
)) {
1001 DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
1004 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_curblocks
)) {
1005 DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
1011 /* Works on FreeBSD, too. :-) */
1012 static bool nfs_quotas(char *nfspath
, uid_t euser_id
, SMB_BIG_UINT
*bsize
, SMB_BIG_UINT
*dfree
, SMB_BIG_UINT
*dsize
)
1014 uid_t uid
= euser_id
;
1016 char *mnttype
= nfspath
;
1018 struct getquota_rslt gqr
;
1019 struct getquota_args args
;
1020 char *cutstr
, *pathname
, *host
, *testpath
;
1022 static struct timeval timeout
= {2,0};
1023 enum clnt_stat clnt_stat
;
1026 *bsize
= *dfree
= *dsize
= (SMB_BIG_UINT
)0;
1028 len
=strcspn(mnttype
, ":");
1029 pathname
=strstr(mnttype
, ":");
1030 cutstr
= (char *) SMB_MALLOC(len
+1);
1034 memset(cutstr
, '\0', len
+1);
1035 host
= strncat(cutstr
,mnttype
, sizeof(char) * len
);
1036 DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr
));
1037 DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype
));
1038 testpath
=strchr_m(mnttype
, ':');
1039 args
.gqa_pathp
= testpath
+1;
1042 DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host
, RQUOTAPROG
, RQUOTAVERS
, "udp"));
1044 if ((clnt
= clnt_create(host
, RQUOTAPROG
, RQUOTAVERS
, "udp")) == NULL
) {
1049 clnt
->cl_auth
= authunix_create_default();
1050 DEBUG(9,("nfs_quotas: auth_success\n"));
1052 clnt_stat
=clnt_call(clnt
, RQUOTAPROC_GETQUOTA
, (const xdrproc_t
) my_xdr_getquota_args
, (caddr_t
)&args
, (const xdrproc_t
) my_xdr_getquota_rslt
, (caddr_t
)&gqr
, timeout
);
1054 if (clnt_stat
!= RPC_SUCCESS
) {
1055 DEBUG(9,("nfs_quotas: clnt_call fail\n"));
1061 * quotastat returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is
1062 * no quota set, and 3 if no permission to get the quota. If 0 or 3 return
1063 * something sensible.
1066 switch ( quotastat
) {
1068 DEBUG(9,("nfs_quotas: Remote Quotas Failed! Error \"%i\" \n", quotastat
));
1073 DEBUG(9,("nfs_quotas: Good quota data\n"));
1074 D
.dqb_bsoftlimit
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
;
1075 D
.dqb_bhardlimit
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
;
1076 D
.dqb_curblocks
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
;
1081 D
.dqb_bsoftlimit
= 1;
1082 D
.dqb_curblocks
= 1;
1083 DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", quotastat
));
1087 DEBUG(9,("nfs_quotas: Remote Quotas Questionable! Error \"%i\" \n", quotastat
));
1091 DEBUG(10,("nfs_quotas: Let`s look at D a bit closer... status \"%i\" bsize \"%i\" active? \"%i\" bhard \"%i\" bsoft \"%i\" curb \"%i\" \n",
1093 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsize
,
1094 gqr
.getquota_rslt_u
.gqr_rquota
.rq_active
,
1095 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
,
1096 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
,
1097 gqr
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
));
1099 if (D
.dqb_bsoftlimit
== 0)
1100 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
1101 if (D
.dqb_bsoftlimit
== 0)
1104 *bsize
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsize
;
1105 *dsize
= D
.dqb_bsoftlimit
;
1107 if (D
.dqb_curblocks
== D
.dqb_curblocks
== 1)
1110 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
1112 *dsize
= D
.dqb_curblocks
;
1114 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
1120 auth_destroy(clnt
->cl_auth
);
1124 DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",args
.gqa_pathp
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
1127 DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
1133 /****************************************************************************
1134 try to get the disk space from disk quotas - default version
1135 ****************************************************************************/
1137 bool disk_quotas(const char *path
, SMB_BIG_UINT
*bsize
, SMB_BIG_UINT
*dfree
, SMB_BIG_UINT
*dsize
)
1142 #if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__) && !defined(__DragonFly__)
1146 /* find the block device file */
1149 /* Need to set the cache flag to 1 for HPUX. Seems
1150 * to have a significant performance boost when
1151 * lstat calls on /dev access this function.
1153 if ((sys_stat(path
, &S
)<0) || (devnm(S_IFBLK
, S
.st_dev
, dev_disk
, 256, 1)<0))
1155 if ((sys_stat(path
, &S
)<0) || (devnm(S_IFBLK
, S
.st_dev
, dev_disk
, 256, 0)<0))
1157 #endif /* ifdef HPUX */
1159 #endif /* !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__) && !defined(__DragonFly__) */
1161 euser_id
= geteuid();
1164 /* for HPUX, real uid must be same as euid to execute quotactl for euid */
1166 if (set_re_uid() != 0) return False
;
1168 r
=quotactl(Q_GETQUOTA
, dev_disk
, euser_id
, &D
);
1172 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
1174 /* FreeBSD patches from Marty Moll <martym@arbor.edu> */
1176 #if defined(__FreeBSD__) || defined(__DragonFly__)
1178 struct statfs
*mnts
;
1182 if (sys_stat(path
,&st
) < 0)
1186 mntsize
= getmntinfo(&mnts
,MNT_NOWAIT
);
1190 for (i
= 0; i
< mntsize
; i
++) {
1191 if (sys_stat(mnts
[i
].f_mntonname
,&st
) < 0)
1193 if (st
.st_dev
== devno
)
1202 #if defined(__FreeBSD__) || defined(__DragonFly__)
1203 if (strcmp(mnts
[i
].f_fstypename
,"nfs") == 0) {
1205 retval
= nfs_quotas(mnts
[i
].f_mntfromname
,euser_id
,bsize
,dfree
,dsize
);
1211 egrp_id
= getegid();
1212 r
= quotactl(path
,QCMD(Q_GETQUOTA
,USRQUOTA
),euser_id
,(char *) &D
);
1214 /* As FreeBSD has group quotas, if getting the user
1215 quota fails, try getting the group instead. */
1217 r
= quotactl(path
,QCMD(Q_GETQUOTA
,GRPQUOTA
),egrp_id
,(char *) &D
);
1223 /* AIX has both USER and GROUP quotas:
1224 Get the USER quota (ohnielse@fysik.dtu.dk) */
1225 #ifdef _AIXVERSION_530
1227 struct statfs statbuf
;
1228 quota64_t user_quota
;
1229 if (statfs(path
,&statbuf
) != 0)
1231 if(statbuf
.f_vfstype
== MNT_J2
)
1233 /* For some reason we need to be root for jfs2 */
1235 r
= quotactl(path
,QCMD(Q_J2GETQUOTA
,USRQUOTA
),euser_id
,(char *) &user_quota
);
1237 /* Copy results to old struct to let the following code work as before */
1238 D
.dqb_curblocks
= user_quota
.bused
;
1239 D
.dqb_bsoftlimit
= user_quota
.bsoft
;
1240 D
.dqb_bhardlimit
= user_quota
.bhard
;
1241 D
.dqb_curfiles
= user_quota
.iused
;
1242 D
.dqb_fsoftlimit
= user_quota
.isoft
;
1243 D
.dqb_fhardlimit
= user_quota
.ihard
;
1245 else if(statbuf
.f_vfstype
== MNT_JFS
)
1247 #endif /* AIX 5.3 */
1249 if (set_re_uid() != 0)
1251 r
= quotactl(path
,QCMD(Q_GETQUOTA
,USRQUOTA
),euser_id
,(char *) &D
);
1253 #ifdef _AIXVERSION_530
1256 r
= 1; /* Fail for other FS-types */
1258 #endif /* AIX 5.3 */
1259 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ && !__DragonFly__ */
1260 r
=quotactl(Q_GETQUOTA
, dev_disk
, euser_id
, &D
);
1261 #endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ && !__DragonFly__ */
1264 /* Use softlimit to determine disk space, except when it has been exceeded */
1265 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
1267 #else /* !__FreeBSD__ && !__OpenBSD__ && !__DragonFly__ */
1269 #endif /*!__FreeBSD__ && !__OpenBSD__ && !__DragonFly__ */
1273 if (errno
== EDQUOT
)
1276 *dsize
=D
.dqb_curblocks
;
1282 /* If softlimit is zero, set it equal to hardlimit.
1285 if (D
.dqb_bsoftlimit
==0)
1286 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
1288 if (D
.dqb_bsoftlimit
==0)
1290 /* Use softlimit to determine disk space, except when it has been exceeded */
1291 if ((D
.dqb_curblocks
>D
.dqb_bsoftlimit
)
1292 #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__)
1293 ||((D
.dqb_curfiles
>D
.dqb_fsoftlimit
) && (D
.dqb_fsoftlimit
!= 0))
1297 *dsize
= D
.dqb_curblocks
;
1300 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
1301 *dsize
= D
.dqb_bsoftlimit
;
1308 #if defined(VXFS_QUOTA)
1310 /****************************************************************************
1311 Try to get the disk space from Veritas disk quotas.
1312 David Lee <T.D.Lee@durham.ac.uk> August 1999.
1314 Background assumptions:
1315 Potentially under many Operating Systems. Initially Solaris 2.
1317 My guess is that Veritas is largely, though not entirely,
1318 independent of OS. So I have separated it out.
1320 There may be some details. For example, OS-specific "include" files.
1322 It is understood that HPUX 10 somehow gets Veritas quotas without
1323 any special effort; if so, this routine need not be compiled in.
1324 Dirk De Wachter <Dirk.DeWachter@rug.ac.be>
1327 It is understood that Veritas do not publicly support this ioctl interface.
1328 Rather their preference would be for the user (us) to call the native
1329 OS and then for the OS itself to call through to the VxFS filesystem.
1330 Presumably HPUX 10, see above, does this.
1333 Add your OS to "IFLIST" below.
1334 Get it to compile successfully:
1335 Almost certainly "include"s require attention: see SUNOS5.
1336 In the main code above, arrange for it to be called: see SUNOS5.
1339 ****************************************************************************/
1342 * This "if" is a list of ports:
1343 * if defined(OS1) || defined(OS2) || ...
1348 #include <sys/fs/vx_solaris.h>
1350 #include <sys/fs/vx_machdep.h>
1351 #include <sys/fs/vx_layout.h>
1352 #include <sys/fs/vx_quota.h>
1353 #include <sys/fs/vx_aioctl.h>
1354 #include <sys/fs/vx_ioctl.h>
1356 bool disk_quotas_vxfs(const char *name
, char *path
, SMB_BIG_UINT
*bsize
, SMB_BIG_UINT
*dfree
, SMB_BIG_UINT
*dsize
)
1358 uid_t user_id
, euser_id
;
1361 struct vx_quotctl quotabuf
;
1362 struct vx_genioctl genbuf
;
1367 * "name" may or may not include a trailing "/quotas".
1368 * Arranging consistency of calling here in "quotas.c" may not be easy and
1369 * it might be easier to examine and adjust it here.
1370 * Fortunately, VxFS seems not to mind at present.
1372 qfname
= talloc_strdup(talloc_tos(), name
);
1376 /* pstrcat(qfname, "/quotas") ; */ /* possibly examine and adjust "name" */
1378 euser_id
= geteuid();
1379 set_effective_uid(0);
1381 DEBUG(5,("disk_quotas: looking for VxFS quotas file \"%s\"\n", qfname
));
1382 if((file
=sys_open(qfname
, O_RDONLY
,0))<0) {
1383 set_effective_uid(euser_id
);
1386 genbuf
.ioc_cmd
= VX_QUOTACTL
;
1387 genbuf
.ioc_up
= (void *) "abuf
;
1389 quotabuf
.cmd
= VX_GETQUOTA
;
1390 quotabuf
.uid
= euser_id
;
1391 quotabuf
.addr
= (caddr_t
) &D
;
1392 ret
= ioctl(file
, VX_ADMIN_IOCTL
, &genbuf
);
1395 set_effective_uid(euser_id
);
1398 DEBUG(5,("disk_quotas ioctl (VxFS) failed. Error = %s\n", strerror(errno
) ));
1402 /* If softlimit is zero, set it equal to hardlimit.
1405 if (D
.dqb_bsoftlimit
==0)
1406 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
1408 /* Use softlimit to determine disk space. A user exceeding the quota is told
1409 * that there's no space left. Writes might actually work for a bit if the
1410 * hardlimit is set higher than softlimit. Effectively the disk becomes
1411 * made of rubber latex and begins to expand to accommodate the user :-)
1413 DEBUG(5,("disk_quotas for path \"%s\" block c/s/h %ld/%ld/%ld; file c/s/h %ld/%ld/%ld\n",
1414 path
, D
.dqb_curblocks
, D
.dqb_bsoftlimit
, D
.dqb_bhardlimit
,
1415 D
.dqb_curfiles
, D
.dqb_fsoftlimit
, D
.dqb_fhardlimit
));
1417 if (D
.dqb_bsoftlimit
==0)
1420 *dsize
= D
.dqb_bsoftlimit
;
1422 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
1424 *dsize
= D
.dqb_curblocks
;
1426 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
1428 DEBUG(5,("disk_quotas for path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",
1429 path
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
1434 #endif /* SUNOS5 || ... */
1436 #endif /* VXFS_QUOTA */
1438 #else /* WITH_QUOTAS */
1440 bool disk_quotas(const char *path
,SMB_BIG_UINT
*bsize
,SMB_BIG_UINT
*dfree
,SMB_BIG_UINT
*dsize
)
1442 (*bsize
) = 512; /* This value should be ignored */
1444 /* And just to be sure we set some values that hopefully */
1445 /* will be larger that any possible real-world value */
1446 (*dfree
) = (SMB_BIG_UINT
)-1;
1447 (*dsize
) = (SMB_BIG_UINT
)-1;
1449 /* As we have select not to use quotas, allways fail */
1452 #endif /* WITH_QUOTAS */
1454 #else /* HAVE_SYS_QUOTAS */
1455 /* wrapper to the new sys_quota interface
1456 this file should be removed later
1458 bool disk_quotas(const char *path
,SMB_BIG_UINT
*bsize
,SMB_BIG_UINT
*dfree
,SMB_BIG_UINT
*dsize
)
1467 r
=sys_get_quota(path
, SMB_USER_QUOTA_TYPE
, id
, &D
);
1469 /* Use softlimit to determine disk space, except when it has been exceeded */
1472 if (errno
== EDQUOT
) {
1474 *dsize
=D
.curblocks
;
1477 goto try_group_quota
;
1481 /* Use softlimit to determine disk space, except when it has been exceeded */
1483 (D
.softlimit
&& D
.curblocks
>= D
.softlimit
) ||
1484 (D
.hardlimit
&& D
.curblocks
>= D
.hardlimit
) ||
1485 (D
.isoftlimit
&& D
.curinodes
>= D
.isoftlimit
) ||
1486 (D
.ihardlimit
&& D
.curinodes
>=D
.ihardlimit
)
1489 *dsize
= D
.curblocks
;
1490 } else if (D
.softlimit
==0 && D
.hardlimit
==0) {
1491 goto try_group_quota
;
1493 if (D
.softlimit
== 0)
1494 D
.softlimit
= D
.hardlimit
;
1495 *dfree
= D
.softlimit
- D
.curblocks
;
1496 *dsize
= D
.softlimit
;
1505 r
=sys_get_quota(path
, SMB_GROUP_QUOTA_TYPE
, id
, &D
);
1507 /* Use softlimit to determine disk space, except when it has been exceeded */
1510 if (errno
== EDQUOT
) {
1512 *dsize
=D
.curblocks
;
1519 /* Use softlimit to determine disk space, except when it has been exceeded */
1521 (D
.softlimit
&& D
.curblocks
>= D
.softlimit
) ||
1522 (D
.hardlimit
&& D
.curblocks
>= D
.hardlimit
) ||
1523 (D
.isoftlimit
&& D
.curinodes
>= D
.isoftlimit
) ||
1524 (D
.ihardlimit
&& D
.curinodes
>=D
.ihardlimit
)
1527 *dsize
= D
.curblocks
;
1528 } else if (D
.softlimit
==0 && D
.hardlimit
==0) {
1531 if (D
.softlimit
== 0)
1532 D
.softlimit
= D
.hardlimit
;
1533 *dfree
= D
.softlimit
- D
.curblocks
;
1534 *dsize
= D
.softlimit
;
1539 #endif /* HAVE_SYS_QUOTAS */