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
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *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 uint64_t hardlimit
; /* In bsize units. */
68 uint64_t softlimit
; /* In bsize units. */
69 uint64_t curblocks
; /* In bsize units. */
70 uint64_t ihardlimit
; /* inode hard limit. */
71 uint64_t isoftlimit
; /* inode soft limit. */
72 uint64_t 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
= (uint64_t)512;
99 dp
->softlimit
= (uint64_t)D
.d_blk_softlimit
;
100 dp
->hardlimit
= (uint64_t)D
.d_blk_hardlimit
;
101 dp
->ihardlimit
= (uint64_t)D
.d_ino_hardlimit
;
102 dp
->isoftlimit
= (uint64_t)D
.d_ino_softlimit
;
103 dp
->curinodes
= (uint64_t)D
.d_icount
;
104 dp
->curblocks
= (uint64_t)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
= (uint64_t)QUOTABLOCK_SIZE
;
138 dp
->softlimit
= (uint64_t)D
.dqb_bsoftlimit
;
139 dp
->hardlimit
= (uint64_t)D
.dqb_bhardlimit
;
140 dp
->ihardlimit
= (uint64_t)D
.dqb_ihardlimit
;
141 dp
->isoftlimit
= (uint64_t)D
.dqb_isoftlimit
;
142 dp
->curinodes
= (uint64_t)D
.dqb_curinodes
;
143 dp
->curblocks
= (uint64_t)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
= (uint64_t)QUOTABLOCK_SIZE
;
164 dp
->softlimit
= (uint64_t)D
.dqb_bsoftlimit
;
165 dp
->hardlimit
= (uint64_t)D
.dqb_bhardlimit
;
166 dp
->ihardlimit
= (uint64_t)D
.dqb_ihardlimit
;
167 dp
->isoftlimit
= (uint64_t)D
.dqb_isoftlimit
;
168 dp
->curinodes
= (uint64_t)D
.dqb_curinodes
;
169 dp
->curblocks
= ((uint64_t)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
= (uint64_t)QUOTABLOCK_SIZE
;
194 dp
->softlimit
= (uint64_t)D
.dqb_bsoftlimit
;
195 dp
->hardlimit
= (uint64_t)D
.dqb_bhardlimit
;
196 dp
->ihardlimit
= (uint64_t)D
.dqb_ihardlimit
;
197 dp
->isoftlimit
= (uint64_t)D
.dqb_isoftlimit
;
198 dp
->curinodes
= (uint64_t)D
.dqb_curinodes
;
199 dp
->curblocks
= ((uint64_t)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
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
213 LINUX_SMB_DISK_QUOTA D
;
222 euser_id
= geteuid();
225 /* find the block device file */
227 if ( sys_stat(path
, &S
, lp_fake_dir_create_times()) == -1 )
230 devno
= S
.st_ex_dev
;
232 if ((fp
= setmntent(MOUNTED
,"r")) == NULL
)
237 while ((mnt
= getmntent(fp
))) {
238 if ( sys_stat(mnt
->mnt_dir
, &S
, lp_fake_dir_create_times())
242 if (S
.st_ex_dev
== devno
) {
255 if (strcmp(mnt
->mnt_type
, "xfs")==0) {
256 r
=get_smb_linux_xfs_quota(mnt
->mnt_fsname
, euser_id
, egrp_id
, &D
);
258 r
=get_smb_linux_gen_quota(mnt
->mnt_fsname
, euser_id
, egrp_id
, &D
);
259 if (r
== -1 && errno
!= EDQUOT
) {
260 r
=get_smb_linux_v2_quota(mnt
->mnt_fsname
, euser_id
, egrp_id
, &D
);
261 if (r
== -1 && errno
!= EDQUOT
)
262 r
=get_smb_linux_v1_quota(mnt
->mnt_fsname
, euser_id
, egrp_id
, &D
);
268 /* Use softlimit to determine disk space, except when it has been exceeded */
271 if (errno
== EDQUOT
) {
280 /* Use softlimit to determine disk space, except when it has been exceeded */
282 (D
.softlimit
&& D
.curblocks
>= D
.softlimit
) ||
283 (D
.hardlimit
&& D
.curblocks
>= D
.hardlimit
) ||
284 (D
.isoftlimit
&& D
.curinodes
>= D
.isoftlimit
) ||
285 (D
.ihardlimit
&& D
.curinodes
>=D
.ihardlimit
)
288 *dsize
= D
.curblocks
;
289 } else if (D
.softlimit
==0 && D
.hardlimit
==0) {
292 if (D
.softlimit
== 0)
293 D
.softlimit
= D
.hardlimit
;
294 *dfree
= D
.softlimit
- D
.curblocks
;
295 *dsize
= D
.softlimit
;
303 #include <sys/quota.h>
306 /****************************************************************************
307 try to get the disk space from disk quotas (CRAY VERSION)
308 ****************************************************************************/
310 bool disk_quotas(const char *path
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
314 SMB_STRUCT_STAT sbuf
;
316 struct q_request request
;
317 struct qf_header header
;
318 int quota_default
= 0 ;
321 if (sys_stat(path
, &sbuf
, lp_fake_dir_create_times()) == -1) {
325 devno
= sbuf
.st_ex_dev
;
327 if ((fd
= setmntent(KMTAB
)) == NULL
) {
331 while ((mnt
= getmntent(fd
)) != NULL
) {
332 if (sys_stat(mnt
->mnt_dir
, &sbuf
, lp_fake_dir_create_times())
336 if (sbuf
.st_ex_dev
== devno
) {
342 name
= talloc_strdup(talloc_tos(), mnt
->mnt_dir
);
352 request
.qf_magic
= QF_MAGIC
;
353 request
.qf_entry
.id
= geteuid() ;
355 if (quotactl(name
, Q_GETQUOTA
, &request
) == -1) {
363 if (request
.qf_entry
.user_q
.f_quota
== QFV_DEFAULT
) {
364 if (!quota_default
) {
365 if (quotactl(name
, Q_GETHEADER
, &header
) == -1) {
368 quota_default
= header
.user_h
.def_fq
;
371 *dfree
= quota_default
;
372 } else if (request
.qf_entry
.user_q
.f_quota
== QFV_PREVENT
) {
375 *dfree
= request
.qf_entry
.user_q
.f_quota
;
378 *dsize
= request
.qf_entry
.user_q
.f_use
;
380 if (*dfree
< *dsize
) {
386 *bsize
= 4096 ; /* Cray blocksize */
391 #elif defined(SUNOS5) || defined(SUNOS4)
394 #include <sys/param.h>
396 #include <sys/fs/ufs_quota.h>
397 #include <sys/mnttab.h>
398 #include <sys/mntent.h>
399 #else /* defined(SUNOS4) */
400 #include <ufs/quota.h>
406 /****************************************************************************
407 Allows querying of remote hosts for quotas on NFS mounted shares.
408 Supports normal NFS and AMD mounts.
409 Alan Romeril <a.romeril@ic.ac.uk> July 2K.
410 ****************************************************************************/
413 #include <rpc/types.h>
414 #include <rpcsvc/rquota.h>
415 #include <rpc/nettype.h>
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
)
431 if (!xdr_int(xdrsp
, "astat
)) {
432 DEBUG(6,("nfs_quotas: Status bad or zero\n"));
435 gqr
->status
= quotastat
;
437 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bsize
)) {
438 DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
441 if (!xdr_bool(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_active
)) {
442 DEBUG(6,("nfs_quotas: Active bad or zero\n"));
445 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
)) {
446 DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
449 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
)) {
450 DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
453 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_curblocks
)) {
454 DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
460 /* Restricted to SUNOS5 for the moment, I haven`t access to others to test. */
461 static bool nfs_quotas(char *nfspath
, uid_t euser_id
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
463 uid_t uid
= euser_id
;
465 char *mnttype
= nfspath
;
467 struct getquota_rslt gqr
;
468 struct getquota_args args
;
469 char *cutstr
, *pathname
, *host
, *testpath
;
471 static struct timeval timeout
= {2,0};
472 enum clnt_stat clnt_stat
;
475 *bsize
= *dfree
= *dsize
= (uint64_t)0;
477 len
=strcspn(mnttype
, ":");
478 pathname
=strstr(mnttype
, ":");
479 cutstr
= (char *) SMB_MALLOC(len
+1);
483 memset(cutstr
, '\0', len
+1);
484 host
= strncat(cutstr
,mnttype
, sizeof(char) * len
);
485 DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr
));
486 DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype
));
487 testpath
=strchr_m(mnttype
, ':');
488 args
.gqa_pathp
= testpath
+1;
491 DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host
, RQUOTAPROG
, RQUOTAVERS
, "udp"));
493 if ((clnt
= clnt_create(host
, RQUOTAPROG
, RQUOTAVERS
, "udp")) == NULL
) {
498 clnt
->cl_auth
= authunix_create_default();
499 DEBUG(9,("nfs_quotas: auth_success\n"));
501 clnt_stat
=clnt_call(clnt
, RQUOTAPROC_GETQUOTA
, my_xdr_getquota_args
, (caddr_t
)&args
, my_xdr_getquota_rslt
, (caddr_t
)&gqr
, timeout
);
503 if (clnt_stat
!= RPC_SUCCESS
) {
504 DEBUG(9,("nfs_quotas: clnt_call fail\n"));
510 * gqr.status returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is
511 * no quota set, and 3 if no permission to get the quota. If 0 or 3 return
512 * something sensible.
515 switch (gqr
.status
) {
517 DEBUG(9,("nfs_quotas: Remote Quotas Failed! Error \"%i\" \n", gqr
.status
));
522 DEBUG(9,("nfs_quotas: Good quota data\n"));
523 D
.dqb_bsoftlimit
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
;
524 D
.dqb_bhardlimit
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
;
525 D
.dqb_curblocks
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
;
530 D
.dqb_bsoftlimit
= 1;
532 DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", gqr
.status
));
536 DEBUG(9,("nfs_quotas: Remote Quotas Questionable! Error \"%i\" \n", gqr
.status
));
540 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",
542 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsize
,
543 gqr
.getquota_rslt_u
.gqr_rquota
.rq_active
,
544 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
,
545 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
,
546 gqr
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
));
548 *bsize
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsize
;
549 *dsize
= D
.dqb_bsoftlimit
;
551 if (D
.dqb_curblocks
== 1)
554 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
556 *dsize
= D
.dqb_curblocks
;
558 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
564 auth_destroy(clnt
->cl_auth
);
568 DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",args
.gqa_pathp
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
571 DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
576 /****************************************************************************
577 try to get the disk space from disk quotas (SunOS & Solaris2 version)
578 Quota code by Peter Urbanec (amiga@cse.unsw.edu.au).
579 ****************************************************************************/
581 bool disk_quotas(const char *path
,
590 struct quotctl command
;
598 SMB_STRUCT_STAT sbuf
;
602 euser_id
= geteuid();
604 if (sys_stat(path
, &sbuf
, lp_fake_dir_create_times()) == -1) {
608 devno
= sbuf
.st_ex_dev
;
609 DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n",
610 path
, (unsigned int)devno
));
612 if ((fd
= sys_fopen(MNTTAB
, "r")) == NULL
) {
616 while (getmntent(fd
, &mnt
) == 0) {
617 if (sys_stat(mnt
.mnt_mountp
, &sbuf
,
618 lp_fake_dir_create_times()) == -1) {
622 DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n",
623 mnt
.mnt_mountp
, (unsigned int)devno
));
625 /* quotas are only on vxfs, UFS or NFS */
626 if ((sbuf
.st_ex_dev
== devno
) && (
627 strcmp( mnt
.mnt_fstype
, MNTTYPE_UFS
) == 0 ||
628 strcmp( mnt
.mnt_fstype
, "nfs" ) == 0 ||
629 strcmp( mnt
.mnt_fstype
, "vxfs" ) == 0 )) {
631 name
= talloc_asprintf(talloc_tos(),
640 if ((fd
= setmntent(MOUNTED
, "r")) == NULL
) {
644 while ((mnt
= getmntent(fd
)) != NULL
) {
645 if (sys_stat(mnt
->mnt_dir
, &sbuf
,
646 lp_fake_dir_create_times()) == -1) {
649 DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n",
651 (unsigned int)sbuf
.st_ex_dev
));
652 if (sbuf
.st_ex_dev
== devno
) {
654 name
= talloc_strdup(talloc_tos(),
672 if (strcmp(mnt
.mnt_fstype
, "nfs") == 0) {
674 DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n",
676 retval
= nfs_quotas(mnt
.mnt_special
,
677 euser_id
, bsize
, dfree
, dsize
);
682 DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name
));
683 if((file
=sys_open(name
, O_RDONLY
,0))<0) {
687 command
.op
= Q_GETQUOTA
;
688 command
.uid
= euser_id
;
689 command
.addr
= (caddr_t
) &D
;
690 ret
= ioctl(file
, Q_QUOTACTL
, &command
);
693 DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name
));
694 ret
= quotactl(Q_GETQUOTA
, name
, euser_id
, &D
);
700 DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n",
703 #if defined(SUNOS5) && defined(VXFS_QUOTA)
704 /* If normal quotactl() fails, try vxfs private calls */
705 set_effective_uid(euser_id
);
706 DEBUG(5,("disk_quotas: mount type \"%s\"\n", mnt
.mnt_fstype
));
707 if ( 0 == strcmp ( mnt
.mnt_fstype
, "vxfs" )) {
709 retval
= disk_quotas_vxfs(name
, path
,
710 bsize
, dfree
, dsize
);
718 /* If softlimit is zero, set it equal to hardlimit.
721 if (D
.dqb_bsoftlimit
==0) {
722 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
725 /* Use softlimit to determine disk space. A user exceeding the quota
726 * is told that there's no space left. Writes might actually work for
727 * a bit if the hardlimit is set higher than softlimit. Effectively
728 * the disk becomes made of rubber latex and begins to expand to
729 * accommodate the user :-)
732 if (D
.dqb_bsoftlimit
==0)
735 *dsize
= D
.dqb_bsoftlimit
;
737 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
739 *dsize
= D
.dqb_curblocks
;
741 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
744 DEBUG(5,("disk_quotas for path \"%s\" returning "
745 "bsize %.0f, dfree %.0f, dsize %.0f\n",
746 path
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
753 #include <ufs/quota.h>
755 /****************************************************************************
756 try to get the disk space from disk quotas - OSF1 version
757 ****************************************************************************/
759 bool disk_quotas(const char *path
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
767 * This code presumes that OSF1 will only
768 * give out quota info when the real uid
769 * matches the effective uid. JRA.
771 euser_id
= geteuid();
773 if (set_re_uid() != 0) return False
;
775 r
= quotactl(path
,QCMD(Q_GETQUOTA
, USRQUOTA
),euser_id
,(char *) &D
);
786 if (save_errno
== EDQUOT
) /* disk quota exceeded */
789 *dsize
= D
.dqb_curblocks
;
796 /* If softlimit is zero, set it equal to hardlimit.
799 if (D
.dqb_bsoftlimit
==0)
800 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
802 /* Use softlimit to determine disk space, except when it has been exceeded */
804 if (D
.dqb_bsoftlimit
==0)
807 if ((D
.dqb_curblocks
>D
.dqb_bsoftlimit
)) {
809 *dsize
= D
.dqb_curblocks
;
811 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
812 *dsize
= D
.dqb_bsoftlimit
;
817 #elif defined (IRIX6)
818 /****************************************************************************
819 try to get the disk space from disk quotas (IRIX 6.2 version)
820 ****************************************************************************/
822 #include <sys/quota.h>
825 bool disk_quotas(const char *path
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
830 struct fs_disk_quota F
;
837 /* find the block device file */
839 if ( sys_stat(path
, &S
, lp_fake_dir_create_times()) == -1 ) {
843 devno
= S
.st_ex_dev
;
845 fp
= setmntent(MOUNTED
,"r");
848 while ((mnt
= getmntent(fp
))) {
849 if ( sys_stat(mnt
->mnt_dir
, &S
, lp_fake_dir_create_times()) == -1 )
851 if (S
.st_ex_dev
== devno
) {
865 /* Use softlimit to determine disk space, except when it has been exceeded */
869 if ( 0 == strcmp ( mnt
->mnt_type
, "efs" ))
871 r
=quotactl (Q_GETQUOTA
, mnt
->mnt_fsname
, euser_id
, (caddr_t
) &D
);
878 /* Use softlimit to determine disk space, except when it has been exceeded */
880 (D
.dqb_bsoftlimit
&& D
.dqb_curblocks
>=D
.dqb_bsoftlimit
) ||
881 (D
.dqb_bhardlimit
&& D
.dqb_curblocks
>=D
.dqb_bhardlimit
) ||
882 (D
.dqb_fsoftlimit
&& D
.dqb_curfiles
>=D
.dqb_fsoftlimit
) ||
883 (D
.dqb_fhardlimit
&& D
.dqb_curfiles
>=D
.dqb_fhardlimit
)
887 *dsize
= D
.dqb_curblocks
;
889 else if (D
.dqb_bsoftlimit
==0 && D
.dqb_bhardlimit
==0)
895 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
896 *dsize
= D
.dqb_bsoftlimit
;
900 else if ( 0 == strcmp ( mnt
->mnt_type
, "xfs" ))
902 r
=quotactl (Q_XGETQUOTA
, mnt
->mnt_fsname
, euser_id
, (caddr_t
) &F
);
908 DEBUG(5, ("quotactl for uid=%u: %s", euser_id
, strerror(errno
)));
912 /* No quota for this user. */
913 if (F
.d_blk_softlimit
==0 && F
.d_blk_hardlimit
==0)
918 /* Use softlimit to determine disk space, except when it has been exceeded */
920 (F
.d_blk_softlimit
&& F
.d_bcount
>=F
.d_blk_softlimit
) ||
921 (F
.d_blk_hardlimit
&& F
.d_bcount
>=F
.d_blk_hardlimit
) ||
922 (F
.d_ino_softlimit
&& F
.d_icount
>=F
.d_ino_softlimit
) ||
923 (F
.d_ino_hardlimit
&& F
.d_icount
>=F
.d_ino_hardlimit
)
931 *dfree
= (F
.d_blk_softlimit
- F
.d_bcount
);
932 *dsize
= F
.d_blk_softlimit
? F
.d_blk_softlimit
: F
.d_blk_hardlimit
;
948 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
949 #include <ufs/ufs/quota.h>
950 #include <machine/param.h>
952 /* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
953 #include <jfs/quota.h>
954 /* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
955 #define dqb_curfiles dqb_curinodes
956 #define dqb_fhardlimit dqb_ihardlimit
957 #define dqb_fsoftlimit dqb_isoftlimit
958 #ifdef _AIXVERSION_530
959 #include <sys/statfs.h>
960 #include <sys/vmount.h>
962 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ && !__DragonFly__ */
963 #include <sys/quota.h>
967 #if defined(__FreeBSD__) || defined(__DragonFly__)
970 #include <rpc/types.h>
971 #include <rpcsvc/rquota.h>
972 #ifdef HAVE_RPC_NETTYPE_H
973 #include <rpc/nettype.h>
977 static int my_xdr_getquota_args(XDR
*xdrsp
, struct getquota_args
*args
)
979 if (!xdr_string(xdrsp
, &args
->gqa_pathp
, RQ_PATHLEN
))
981 if (!xdr_int(xdrsp
, &args
->gqa_uid
))
986 static int my_xdr_getquota_rslt(XDR
*xdrsp
, struct getquota_rslt
*gqr
)
990 if (!xdr_int(xdrsp
, "astat
)) {
991 DEBUG(6,("nfs_quotas: Status bad or zero\n"));
994 gqr
->status
= quotastat
;
996 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bsize
)) {
997 DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
1000 if (!xdr_bool(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_active
)) {
1001 DEBUG(6,("nfs_quotas: Active bad or zero\n"));
1004 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
)) {
1005 DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
1008 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
)) {
1009 DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
1012 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_curblocks
)) {
1013 DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
1019 /* Works on FreeBSD, too. :-) */
1020 static bool nfs_quotas(char *nfspath
, uid_t euser_id
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
1022 uid_t uid
= euser_id
;
1024 char *mnttype
= nfspath
;
1026 struct getquota_rslt gqr
;
1027 struct getquota_args args
;
1028 char *cutstr
, *pathname
, *host
, *testpath
;
1030 static struct timeval timeout
= {2,0};
1031 enum clnt_stat clnt_stat
;
1034 *bsize
= *dfree
= *dsize
= (uint64_t)0;
1036 len
=strcspn(mnttype
, ":");
1037 pathname
=strstr(mnttype
, ":");
1038 cutstr
= (char *) SMB_MALLOC(len
+1);
1042 memset(cutstr
, '\0', len
+1);
1043 host
= strncat(cutstr
,mnttype
, sizeof(char) * len
);
1044 DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr
));
1045 DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype
));
1046 testpath
=strchr_m(mnttype
, ':');
1047 args
.gqa_pathp
= testpath
+1;
1050 DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host
, RQUOTAPROG
, RQUOTAVERS
, "udp"));
1052 if ((clnt
= clnt_create(host
, RQUOTAPROG
, RQUOTAVERS
, "udp")) == NULL
) {
1057 clnt
->cl_auth
= authunix_create_default();
1058 DEBUG(9,("nfs_quotas: auth_success\n"));
1060 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
);
1062 if (clnt_stat
!= RPC_SUCCESS
) {
1063 DEBUG(9,("nfs_quotas: clnt_call fail\n"));
1069 * gqr->status returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is
1070 * no quota set, and 3 if no permission to get the quota. If 0 or 3 return
1071 * something sensible.
1074 switch (gqr
.status
) {
1076 DEBUG(9,("nfs_quotas: Remote Quotas Failed! Error \"%i\" \n", gqr
.status
));
1081 DEBUG(9,("nfs_quotas: Good quota data\n"));
1082 D
.dqb_bsoftlimit
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
;
1083 D
.dqb_bhardlimit
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
;
1084 D
.dqb_curblocks
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
;
1089 D
.dqb_bsoftlimit
= 1;
1090 D
.dqb_curblocks
= 1;
1091 DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", gqr
.status
));
1095 DEBUG(9,("nfs_quotas: Remote Quotas Questionable! Error \"%i\" \n", gqr
.status
));
1099 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",
1101 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsize
,
1102 gqr
.getquota_rslt_u
.gqr_rquota
.rq_active
,
1103 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
,
1104 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
,
1105 gqr
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
));
1107 if (D
.dqb_bsoftlimit
== 0)
1108 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
1109 if (D
.dqb_bsoftlimit
== 0)
1112 *bsize
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsize
;
1113 *dsize
= D
.dqb_bsoftlimit
;
1115 if (D
.dqb_curblocks
== 1)
1118 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
1120 *dsize
= D
.dqb_curblocks
;
1122 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
1128 auth_destroy(clnt
->cl_auth
);
1132 DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",args
.gqa_pathp
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
1135 DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
1141 /****************************************************************************
1142 try to get the disk space from disk quotas - default version
1143 ****************************************************************************/
1145 bool disk_quotas(const char *path
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
1150 #if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__) && !defined(__DragonFly__)
1154 /* find the block device file */
1157 /* Need to set the cache flag to 1 for HPUX. Seems
1158 * to have a significant performance boost when
1159 * lstat calls on /dev access this function.
1161 if ((sys_stat(path
, &S
, lp_fake_dir_create_times())<0)
1162 || (devnm(S_IFBLK
, S
.st_ex_dev
, dev_disk
, 256, 1)<0))
1164 if ((sys_stat(path
, &S
, lp_fake_dir_create_times())<0)
1165 || (devnm(S_IFBLK
, S
.st_ex_dev
, dev_disk
, 256, 0)<0))
1167 #endif /* ifdef HPUX */
1169 #endif /* !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__) && !defined(__DragonFly__) */
1171 euser_id
= geteuid();
1174 /* for HPUX, real uid must be same as euid to execute quotactl for euid */
1176 if (set_re_uid() != 0) return False
;
1178 r
=quotactl(Q_GETQUOTA
, dev_disk
, euser_id
, &D
);
1182 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
1184 /* FreeBSD patches from Marty Moll <martym@arbor.edu> */
1186 #if defined(__FreeBSD__) || defined(__DragonFly__)
1188 struct statfs
*mnts
;
1192 if (sys_stat(path
, &st
, lp_fake_dir_create_times()) < 0)
1194 devno
= st
.st_ex_dev
;
1196 mntsize
= getmntinfo(&mnts
,MNT_NOWAIT
);
1200 for (i
= 0; i
< mntsize
; i
++) {
1201 if (sys_stat(mnts
[i
].f_mntonname
, &st
, lp_fake_dir_create_times()) < 0)
1203 if (st
.st_ex_dev
== devno
)
1212 #if defined(__FreeBSD__) || defined(__DragonFly__)
1213 if (strcmp(mnts
[i
].f_fstypename
,"nfs") == 0) {
1215 retval
= nfs_quotas(mnts
[i
].f_mntfromname
,euser_id
,bsize
,dfree
,dsize
);
1221 egrp_id
= getegid();
1222 r
= quotactl(path
,QCMD(Q_GETQUOTA
,USRQUOTA
),euser_id
,(char *) &D
);
1224 /* As FreeBSD has group quotas, if getting the user
1225 quota fails, try getting the group instead. */
1227 r
= quotactl(path
,QCMD(Q_GETQUOTA
,GRPQUOTA
),egrp_id
,(char *) &D
);
1233 /* AIX has both USER and GROUP quotas:
1234 Get the USER quota (ohnielse@fysik.dtu.dk) */
1235 #ifdef _AIXVERSION_530
1237 struct statfs statbuf
;
1238 quota64_t user_quota
;
1239 if (statfs(path
,&statbuf
) != 0)
1241 if(statbuf
.f_vfstype
== MNT_J2
)
1243 /* For some reason we need to be root for jfs2 */
1245 r
= quotactl(path
,QCMD(Q_J2GETQUOTA
,USRQUOTA
),euser_id
,(char *) &user_quota
);
1247 /* Copy results to old struct to let the following code work as before */
1248 D
.dqb_curblocks
= user_quota
.bused
;
1249 D
.dqb_bsoftlimit
= user_quota
.bsoft
;
1250 D
.dqb_bhardlimit
= user_quota
.bhard
;
1251 D
.dqb_curfiles
= user_quota
.iused
;
1252 D
.dqb_fsoftlimit
= user_quota
.isoft
;
1253 D
.dqb_fhardlimit
= user_quota
.ihard
;
1255 else if(statbuf
.f_vfstype
== MNT_JFS
)
1257 #endif /* AIX 5.3 */
1259 if (set_re_uid() != 0)
1261 r
= quotactl(path
,QCMD(Q_GETQUOTA
,USRQUOTA
),euser_id
,(char *) &D
);
1263 #ifdef _AIXVERSION_530
1266 r
= 1; /* Fail for other FS-types */
1268 #endif /* AIX 5.3 */
1269 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ && !__DragonFly__ */
1270 r
=quotactl(Q_GETQUOTA
, dev_disk
, euser_id
, &D
);
1271 #endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ && !__DragonFly__ */
1274 /* Use softlimit to determine disk space, except when it has been exceeded */
1275 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
1277 #else /* !__FreeBSD__ && !__OpenBSD__ && !__DragonFly__ */
1279 #endif /*!__FreeBSD__ && !__OpenBSD__ && !__DragonFly__ */
1283 if (errno
== EDQUOT
)
1286 *dsize
=D
.dqb_curblocks
;
1292 /* If softlimit is zero, set it equal to hardlimit.
1295 if (D
.dqb_bsoftlimit
==0)
1296 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
1298 if (D
.dqb_bsoftlimit
==0)
1300 /* Use softlimit to determine disk space, except when it has been exceeded */
1301 if ((D
.dqb_curblocks
>D
.dqb_bsoftlimit
)
1302 #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__)
1303 ||((D
.dqb_curfiles
>D
.dqb_fsoftlimit
) && (D
.dqb_fsoftlimit
!= 0))
1307 *dsize
= D
.dqb_curblocks
;
1310 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
1311 *dsize
= D
.dqb_bsoftlimit
;
1318 #if defined(VXFS_QUOTA)
1320 /****************************************************************************
1321 Try to get the disk space from Veritas disk quotas.
1322 David Lee <T.D.Lee@durham.ac.uk> August 1999.
1324 Background assumptions:
1325 Potentially under many Operating Systems. Initially Solaris 2.
1327 My guess is that Veritas is largely, though not entirely,
1328 independent of OS. So I have separated it out.
1330 There may be some details. For example, OS-specific "include" files.
1332 It is understood that HPUX 10 somehow gets Veritas quotas without
1333 any special effort; if so, this routine need not be compiled in.
1334 Dirk De Wachter <Dirk.DeWachter@rug.ac.be>
1337 It is understood that Veritas do not publicly support this ioctl interface.
1338 Rather their preference would be for the user (us) to call the native
1339 OS and then for the OS itself to call through to the VxFS filesystem.
1340 Presumably HPUX 10, see above, does this.
1343 Add your OS to "IFLIST" below.
1344 Get it to compile successfully:
1345 Almost certainly "include"s require attention: see SUNOS5.
1346 In the main code above, arrange for it to be called: see SUNOS5.
1349 ****************************************************************************/
1352 * This "if" is a list of ports:
1353 * if defined(OS1) || defined(OS2) || ...
1358 #include <sys/fs/vx_solaris.h>
1360 #include <sys/fs/vx_machdep.h>
1361 #include <sys/fs/vx_layout.h>
1362 #include <sys/fs/vx_quota.h>
1363 #include <sys/fs/vx_aioctl.h>
1364 #include <sys/fs/vx_ioctl.h>
1366 bool disk_quotas_vxfs(const char *name
, char *path
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
1368 uid_t user_id
, euser_id
;
1371 struct vx_quotctl quotabuf
;
1372 struct vx_genioctl genbuf
;
1377 * "name" may or may not include a trailing "/quotas".
1378 * Arranging consistency of calling here in "quotas.c" may not be easy and
1379 * it might be easier to examine and adjust it here.
1380 * Fortunately, VxFS seems not to mind at present.
1382 qfname
= talloc_strdup(talloc_tos(), name
);
1386 /* pstrcat(qfname, "/quotas") ; */ /* possibly examine and adjust "name" */
1388 euser_id
= geteuid();
1389 set_effective_uid(0);
1391 DEBUG(5,("disk_quotas: looking for VxFS quotas file \"%s\"\n", qfname
));
1392 if((file
=sys_open(qfname
, O_RDONLY
,0))<0) {
1393 set_effective_uid(euser_id
);
1396 genbuf
.ioc_cmd
= VX_QUOTACTL
;
1397 genbuf
.ioc_up
= (void *) "abuf
;
1399 quotabuf
.cmd
= VX_GETQUOTA
;
1400 quotabuf
.uid
= euser_id
;
1401 quotabuf
.addr
= (caddr_t
) &D
;
1402 ret
= ioctl(file
, VX_ADMIN_IOCTL
, &genbuf
);
1405 set_effective_uid(euser_id
);
1408 DEBUG(5,("disk_quotas ioctl (VxFS) failed. Error = %s\n", strerror(errno
) ));
1412 /* If softlimit is zero, set it equal to hardlimit.
1415 if (D
.dqb_bsoftlimit
==0)
1416 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
1418 /* Use softlimit to determine disk space. A user exceeding the quota is told
1419 * that there's no space left. Writes might actually work for a bit if the
1420 * hardlimit is set higher than softlimit. Effectively the disk becomes
1421 * made of rubber latex and begins to expand to accommodate the user :-)
1423 DEBUG(5,("disk_quotas for path \"%s\" block c/s/h %ld/%ld/%ld; file c/s/h %ld/%ld/%ld\n",
1424 path
, D
.dqb_curblocks
, D
.dqb_bsoftlimit
, D
.dqb_bhardlimit
,
1425 D
.dqb_curfiles
, D
.dqb_fsoftlimit
, D
.dqb_fhardlimit
));
1427 if (D
.dqb_bsoftlimit
==0)
1430 *dsize
= D
.dqb_bsoftlimit
;
1432 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
1434 *dsize
= D
.dqb_curblocks
;
1436 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
1438 DEBUG(5,("disk_quotas for path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",
1439 path
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
1444 #endif /* SUNOS5 || ... */
1446 #endif /* VXFS_QUOTA */
1448 #else /* WITH_QUOTAS */
1450 bool disk_quotas(const char *path
,uint64_t *bsize
,uint64_t *dfree
,uint64_t *dsize
)
1452 (*bsize
) = 512; /* This value should be ignored */
1454 /* And just to be sure we set some values that hopefully */
1455 /* will be larger that any possible real-world value */
1456 (*dfree
) = (uint64_t)-1;
1457 (*dsize
) = (uint64_t)-1;
1459 /* As we have select not to use quotas, allways fail */
1462 #endif /* WITH_QUOTAS */
1464 #else /* HAVE_SYS_QUOTAS */
1465 /* wrapper to the new sys_quota interface
1466 this file should be removed later
1468 bool disk_quotas(const char *path
,uint64_t *bsize
,uint64_t *dfree
,uint64_t *dsize
)
1477 r
=sys_get_quota(path
, SMB_USER_QUOTA_TYPE
, id
, &D
);
1479 /* Use softlimit to determine disk space, except when it has been exceeded */
1482 if (errno
== EDQUOT
) {
1484 *dsize
=D
.curblocks
;
1487 goto try_group_quota
;
1491 /* Use softlimit to determine disk space, except when it has been exceeded */
1493 (D
.softlimit
&& D
.curblocks
>= D
.softlimit
) ||
1494 (D
.hardlimit
&& D
.curblocks
>= D
.hardlimit
) ||
1495 (D
.isoftlimit
&& D
.curinodes
>= D
.isoftlimit
) ||
1496 (D
.ihardlimit
&& D
.curinodes
>=D
.ihardlimit
)
1499 *dsize
= D
.curblocks
;
1500 } else if (D
.softlimit
==0 && D
.hardlimit
==0) {
1501 goto try_group_quota
;
1503 if (D
.softlimit
== 0)
1504 D
.softlimit
= D
.hardlimit
;
1505 *dfree
= D
.softlimit
- D
.curblocks
;
1506 *dsize
= D
.softlimit
;
1515 r
=sys_get_quota(path
, SMB_GROUP_QUOTA_TYPE
, id
, &D
);
1517 /* Use softlimit to determine disk space, except when it has been exceeded */
1520 if (errno
== EDQUOT
) {
1522 *dsize
=D
.curblocks
;
1529 /* Use softlimit to determine disk space, except when it has been exceeded */
1531 (D
.softlimit
&& D
.curblocks
>= D
.softlimit
) ||
1532 (D
.hardlimit
&& D
.curblocks
>= D
.hardlimit
) ||
1533 (D
.isoftlimit
&& D
.curinodes
>= D
.isoftlimit
) ||
1534 (D
.ihardlimit
&& D
.curinodes
>=D
.ihardlimit
)
1537 *dsize
= D
.curblocks
;
1538 } else if (D
.softlimit
==0 && D
.hardlimit
==0) {
1541 if (D
.softlimit
== 0)
1542 D
.softlimit
= D
.hardlimit
;
1543 *dfree
= D
.softlimit
- D
.curblocks
;
1544 *dsize
= D
.softlimit
;
1549 #endif /* HAVE_SYS_QUOTAS */