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
28 #include "smbd/smbd.h"
29 #include "system/filesys.h"
32 #define DBGC_CLASS DBGC_QUOTA
34 #ifndef HAVE_SYS_QUOTAS
36 /* just a quick hack because sysquotas.h is included before linux/quota.h */
37 #ifdef QUOTABLOCK_SIZE
38 #undef QUOTABLOCK_SIZE
43 #if defined(VXFS_QUOTA)
46 * In addition to their native filesystems, some systems have Veritas VxFS.
47 * Declare here, define at end: reduces likely "include" interaction problems.
48 * David Lee <T.D.Lee@durham.ac.uk>
50 bool disk_quotas_vxfs(const char *name
, char *path
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
);
52 #endif /* VXFS_QUOTA */
55 #if defined(SUNOS5) || defined(SUNOS4)
58 #include <sys/param.h>
60 #include <sys/fs/ufs_quota.h>
61 #include <sys/mnttab.h>
62 #include <sys/mntent.h>
63 #else /* defined(SUNOS4) */
64 #include <ufs/quota.h>
70 /****************************************************************************
71 Allows querying of remote hosts for quotas on NFS mounted shares.
72 Supports normal NFS and AMD mounts.
73 Alan Romeril <a.romeril@ic.ac.uk> July 2K.
74 ****************************************************************************/
77 #include <rpc/types.h>
78 #include <rpcsvc/rquota.h>
79 #include <rpc/nettype.h>
82 static int my_xdr_getquota_rslt(XDR
*xdrsp
, struct getquota_rslt
*gqr
)
86 if (!xdr_int(xdrsp
, "astat
)) {
87 DEBUG(6,("nfs_quotas: Status bad or zero\n"));
90 gqr
->status
= quotastat
;
92 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bsize
)) {
93 DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
96 if (!xdr_bool(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_active
)) {
97 DEBUG(6,("nfs_quotas: Active bad or zero\n"));
100 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
)) {
101 DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
104 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
)) {
105 DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
108 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_curblocks
)) {
109 DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
115 static int my_xdr_getquota_args(XDR
*xdrsp
, struct getquota_args
*args
)
117 if (!xdr_string(xdrsp
, &args
->gqa_pathp
, RQ_PATHLEN
))
119 if (!xdr_int(xdrsp
, &args
->gqa_uid
))
124 /* Restricted to SUNOS5 for the moment, I haven`t access to others to test. */
125 static bool nfs_quotas(char *nfspath
, uid_t euser_id
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
127 uid_t uid
= euser_id
;
129 char *mnttype
= nfspath
;
131 struct getquota_rslt gqr
;
132 struct getquota_args args
;
133 char *cutstr
, *pathname
, *host
, *testpath
;
135 static struct timeval timeout
= {2,0};
136 enum clnt_stat clnt_stat
;
139 *bsize
= *dfree
= *dsize
= (uint64_t)0;
141 len
=strcspn(mnttype
, ":");
142 pathname
=strstr(mnttype
, ":");
143 cutstr
= (char *) SMB_MALLOC(len
+1);
147 memset(cutstr
, '\0', len
+1);
148 host
= strncat(cutstr
,mnttype
, sizeof(char) * len
);
149 DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr
));
150 DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype
));
151 testpath
=strchr_m(mnttype
, ':');
152 args
.gqa_pathp
= testpath
+1;
155 DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host
, RQUOTAPROG
, RQUOTAVERS
, "udp"));
157 if ((clnt
= clnt_create(host
, RQUOTAPROG
, RQUOTAVERS
, "udp")) == NULL
) {
162 clnt
->cl_auth
= authunix_create_default();
163 DEBUG(9,("nfs_quotas: auth_success\n"));
165 clnt_stat
=clnt_call(clnt
, RQUOTAPROC_GETQUOTA
, my_xdr_getquota_args
, (caddr_t
)&args
, my_xdr_getquota_rslt
, (caddr_t
)&gqr
, timeout
);
167 if (clnt_stat
!= RPC_SUCCESS
) {
168 DEBUG(9,("nfs_quotas: clnt_call fail\n"));
174 * gqr.status returns 1 if quotas exist, 2 if there is
175 * no quota set, and 3 if no permission to get the quota.
176 * If 3, return something sensible.
179 switch (gqr
.status
) {
181 DEBUG(9,("nfs_quotas: Good quota data\n"));
182 D
.dqb_bsoftlimit
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
;
183 D
.dqb_bhardlimit
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
;
184 D
.dqb_curblocks
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
;
189 D
.dqb_bsoftlimit
= 1;
191 DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", gqr
.status
));
195 DEBUG(9, ("nfs_quotas: Unknown Remote Quota Status \"%i\"\n",
201 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",
203 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsize
,
204 gqr
.getquota_rslt_u
.gqr_rquota
.rq_active
,
205 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
,
206 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
,
207 gqr
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
));
209 *bsize
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsize
;
210 *dsize
= D
.dqb_bsoftlimit
;
212 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
214 *dsize
= D
.dqb_curblocks
;
216 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
222 auth_destroy(clnt
->cl_auth
);
226 DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",args
.gqa_pathp
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
229 DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
234 /****************************************************************************
235 try to get the disk space from disk quotas (SunOS & Solaris2 version)
236 Quota code by Peter Urbanec (amiga@cse.unsw.edu.au).
237 ****************************************************************************/
239 bool disk_quotas(connection_struct
*conn
, struct smb_filename
*fname
,
240 uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
246 struct quotctl command
;
254 SMB_STRUCT_STAT sbuf
;
257 const char *path
= fname
->base_name
;
259 euser_id
= geteuid();
261 devno
= fname
->st
.st_ex_dev
;
262 DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n",
263 path
, (unsigned int)devno
));
265 if ((fd
= fopen(MNTTAB
, "r")) == NULL
) {
269 while (getmntent(fd
, &mnt
) == 0) {
270 if (sys_stat(mnt
.mnt_mountp
, &sbuf
, false) == -1) {
274 DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n",
275 mnt
.mnt_mountp
, (unsigned int)devno
));
277 /* quotas are only on vxfs, UFS or NFS */
278 if ((sbuf
.st_ex_dev
== devno
) && (
279 strcmp( mnt
.mnt_fstype
, MNTTYPE_UFS
) == 0 ||
280 strcmp( mnt
.mnt_fstype
, "nfs" ) == 0 ||
281 strcmp( mnt
.mnt_fstype
, "vxfs" ) == 0 )) {
283 name
= talloc_asprintf(talloc_tos(),
292 if ((fd
= setmntent(MOUNTED
, "r")) == NULL
) {
296 while ((mnt
= getmntent(fd
)) != NULL
) {
297 if (sys_stat(mnt
->mnt_dir
, &sbuf
, false) == -1) {
300 DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n",
302 (unsigned int)sbuf
.st_ex_dev
));
303 if (sbuf
.st_ex_dev
== devno
) {
305 name
= talloc_strdup(talloc_tos(),
323 if (strcmp(mnt
.mnt_fstype
, "nfs") == 0) {
325 DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n",
327 retval
= nfs_quotas(mnt
.mnt_special
,
328 euser_id
, bsize
, dfree
, dsize
);
333 DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name
));
334 if((file
=open(name
, O_RDONLY
,0))<0) {
338 command
.op
= Q_GETQUOTA
;
339 command
.uid
= euser_id
;
340 command
.addr
= (caddr_t
) &D
;
341 ret
= ioctl(file
, Q_QUOTACTL
, &command
);
344 DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name
));
345 ret
= quotactl(Q_GETQUOTA
, name
, euser_id
, &D
);
351 DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n",
354 #if defined(SUNOS5) && defined(VXFS_QUOTA)
355 /* If normal quotactl() fails, try vxfs private calls */
356 set_effective_uid(euser_id
);
357 DEBUG(5,("disk_quotas: mount type \"%s\"\n", mnt
.mnt_fstype
));
358 if ( 0 == strcmp ( mnt
.mnt_fstype
, "vxfs" )) {
360 retval
= disk_quotas_vxfs(name
, path
,
361 bsize
, dfree
, dsize
);
369 /* If softlimit is zero, set it equal to hardlimit.
372 if (D
.dqb_bsoftlimit
==0) {
373 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
376 /* Use softlimit to determine disk space. A user exceeding the quota
377 * is told that there's no space left. Writes might actually work for
378 * a bit if the hardlimit is set higher than softlimit. Effectively
379 * the disk becomes made of rubber latex and begins to expand to
380 * accommodate the user :-)
383 if (D
.dqb_bsoftlimit
==0)
386 *dsize
= D
.dqb_bsoftlimit
;
388 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
390 *dsize
= D
.dqb_curblocks
;
392 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
395 DEBUG(5,("disk_quotas for path \"%s\" returning "
396 "bsize %.0f, dfree %.0f, dsize %.0f\n",
397 path
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
406 /* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
407 #include <jfs/quota.h>
408 /* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
409 #define dqb_curfiles dqb_curinodes
410 #define dqb_fhardlimit dqb_ihardlimit
411 #define dqb_fsoftlimit dqb_isoftlimit
412 #ifdef _AIXVERSION_530
413 #include <sys/statfs.h>
414 #include <sys/vmount.h>
417 #include <sys/quota.h>
422 /****************************************************************************
423 try to get the disk space from disk quotas - default version
424 ****************************************************************************/
426 bool disk_quotas(connection_struct
*conn
, struct smb_filename
*fname
,
427 uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
432 const char *path
= fname
->base_name
;
435 SMB_STRUCT_STAT S
= fname
->st
;
437 /* find the block device file */
439 if ((sys_stat(path
, &S
, false)<0)
440 || (devnm(S_IFBLK
, S
.st_ex_dev
, dev_disk
, 256, 0)<0))
443 #endif /* !defined(AIX) */
445 euser_id
= geteuid();
448 /* AIX has both USER and GROUP quotas:
449 Get the USER quota (ohnielse@fysik.dtu.dk) */
450 #ifdef _AIXVERSION_530
452 struct statfs statbuf
;
453 quota64_t user_quota
;
454 if (statfs(path
,&statbuf
) != 0)
456 if(statbuf
.f_vfstype
== MNT_J2
)
458 /* For some reason we need to be root for jfs2 */
460 r
= quotactl(path
,QCMD(Q_J2GETQUOTA
,USRQUOTA
),euser_id
,(char *) &user_quota
);
462 /* Copy results to old struct to let the following code work as before */
463 D
.dqb_curblocks
= user_quota
.bused
;
464 D
.dqb_bsoftlimit
= user_quota
.bsoft
;
465 D
.dqb_bhardlimit
= user_quota
.bhard
;
466 D
.dqb_curfiles
= user_quota
.iused
;
467 D
.dqb_fsoftlimit
= user_quota
.isoft
;
468 D
.dqb_fhardlimit
= user_quota
.ihard
;
470 else if(statbuf
.f_vfstype
== MNT_JFS
)
474 if (set_re_uid() != 0)
476 r
= quotactl(path
,QCMD(Q_GETQUOTA
,USRQUOTA
),euser_id
,(char *) &D
);
478 #ifdef _AIXVERSION_530
481 r
= 1; /* Fail for other FS-types */
485 r
=quotactl(Q_GETQUOTA
, dev_disk
, euser_id
, &D
);
488 /* Use softlimit to determine disk space, except when it has been exceeded */
496 *dsize
=D
.dqb_curblocks
;
502 /* If softlimit is zero, set it equal to hardlimit.
505 if (D
.dqb_bsoftlimit
==0)
506 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
508 if (D
.dqb_bsoftlimit
==0)
510 /* Use softlimit to determine disk space, except when it has been exceeded */
511 if ((D
.dqb_curblocks
>D
.dqb_bsoftlimit
)
512 ||((D
.dqb_curfiles
>D
.dqb_fsoftlimit
) && (D
.dqb_fsoftlimit
!= 0))
515 *dsize
= D
.dqb_curblocks
;
518 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
519 *dsize
= D
.dqb_bsoftlimit
;
526 #if defined(VXFS_QUOTA)
528 /****************************************************************************
529 Try to get the disk space from Veritas disk quotas.
530 David Lee <T.D.Lee@durham.ac.uk> August 1999.
532 Background assumptions:
533 Potentially under many Operating Systems. Initially Solaris 2.
535 My guess is that Veritas is largely, though not entirely,
536 independent of OS. So I have separated it out.
538 There may be some details. For example, OS-specific "include" files.
540 It is understood that HPUX 10 somehow gets Veritas quotas without
541 any special effort; if so, this routine need not be compiled in.
542 Dirk De Wachter <Dirk.DeWachter@rug.ac.be>
545 It is understood that Veritas do not publicly support this ioctl interface.
546 Rather their preference would be for the user (us) to call the native
547 OS and then for the OS itself to call through to the VxFS filesystem.
548 Presumably HPUX 10, see above, does this.
551 Add your OS to "IFLIST" below.
552 Get it to compile successfully:
553 Almost certainly "include"s require attention: see SUNOS5.
554 In the main code above, arrange for it to be called: see SUNOS5.
557 ****************************************************************************/
560 * This "if" is a list of ports:
561 * if defined(OS1) || defined(OS2) || ...
566 #include <sys/fs/vx_solaris.h>
568 #include <sys/fs/vx_machdep.h>
569 #include <sys/fs/vx_layout.h>
570 #include <sys/fs/vx_quota.h>
571 #include <sys/fs/vx_aioctl.h>
572 #include <sys/fs/vx_ioctl.h>
574 bool disk_quotas_vxfs(const char *name
, char *path
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
576 uid_t user_id
, euser_id
;
579 struct vx_quotctl quotabuf
;
580 struct vx_genioctl genbuf
;
585 * "name" may or may not include a trailing "/quotas".
586 * Arranging consistency of calling here in "quotas.c" may not be easy and
587 * it might be easier to examine and adjust it here.
588 * Fortunately, VxFS seems not to mind at present.
590 qfname
= talloc_strdup(talloc_tos(), name
);
594 /* pstrcat(qfname, "/quotas") ; */ /* possibly examine and adjust "name" */
596 euser_id
= geteuid();
597 set_effective_uid(0);
599 DEBUG(5,("disk_quotas: looking for VxFS quotas file \"%s\"\n", qfname
));
600 if((file
=open(qfname
, O_RDONLY
,0))<0) {
601 set_effective_uid(euser_id
);
604 genbuf
.ioc_cmd
= VX_QUOTACTL
;
605 genbuf
.ioc_up
= (void *) "abuf
;
607 quotabuf
.cmd
= VX_GETQUOTA
;
608 quotabuf
.uid
= euser_id
;
609 quotabuf
.addr
= (caddr_t
) &D
;
610 ret
= ioctl(file
, VX_ADMIN_IOCTL
, &genbuf
);
613 set_effective_uid(euser_id
);
616 DEBUG(5,("disk_quotas ioctl (VxFS) failed. Error = %s\n", strerror(errno
) ));
620 /* If softlimit is zero, set it equal to hardlimit.
623 if (D
.dqb_bsoftlimit
==0)
624 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
626 /* Use softlimit to determine disk space. A user exceeding the quota is told
627 * that there's no space left. Writes might actually work for a bit if the
628 * hardlimit is set higher than softlimit. Effectively the disk becomes
629 * made of rubber latex and begins to expand to accommodate the user :-)
631 DEBUG(5,("disk_quotas for path \"%s\" block c/s/h %ld/%ld/%ld; file c/s/h %ld/%ld/%ld\n",
632 path
, D
.dqb_curblocks
, D
.dqb_bsoftlimit
, D
.dqb_bhardlimit
,
633 D
.dqb_curfiles
, D
.dqb_fsoftlimit
, D
.dqb_fhardlimit
));
635 if (D
.dqb_bsoftlimit
==0)
638 *dsize
= D
.dqb_bsoftlimit
;
640 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
642 *dsize
= D
.dqb_curblocks
;
644 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
646 DEBUG(5,("disk_quotas for path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",
647 path
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
652 #endif /* SUNOS5 || ... */
654 #endif /* VXFS_QUOTA */
656 #else /* WITH_QUOTAS */
658 bool disk_quotas(connection_struct
*conn
, struct smb_filename
*fname
,
659 uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
661 (*bsize
) = 512; /* This value should be ignored */
663 /* And just to be sure we set some values that hopefully */
664 /* will be larger that any possible real-world value */
665 (*dfree
) = (uint64_t)-1;
666 (*dsize
) = (uint64_t)-1;
668 /* As we have select not to use quotas, allways fail */
671 #endif /* WITH_QUOTAS */
673 #else /* HAVE_SYS_QUOTAS */
674 /* wrapper to the new sys_quota interface
675 this file should be removed later
677 bool disk_quotas(connection_struct
*conn
, struct smb_filename
*fname
,
678 uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
685 * First of all, check whether user quota is
686 * enforced. If the call fails, assume it is
691 r
= SMB_VFS_GET_QUOTA(conn
, fname
->base_name
, SMB_USER_FS_QUOTA_TYPE
,
693 if (r
== -1 && errno
!= ENOSYS
) {
694 goto try_group_quota
;
696 if (r
== 0 && (D
.qflags
& QUOTAS_DENY_DISK
) == 0) {
697 goto try_group_quota
;
703 r
= SMB_VFS_GET_QUOTA(conn
, fname
->base_name
, SMB_USER_QUOTA_TYPE
, id
,
706 /* Use softlimit to determine disk space, except when it has been exceeded */
709 if (errno
== EDQUOT
) {
714 goto try_group_quota
;
718 /* Use softlimit to determine disk space, except when it has been exceeded */
720 (D
.softlimit
&& D
.curblocks
>= D
.softlimit
) ||
721 (D
.hardlimit
&& D
.curblocks
>= D
.hardlimit
) ||
722 (D
.isoftlimit
&& D
.curinodes
>= D
.isoftlimit
) ||
723 (D
.ihardlimit
&& D
.curinodes
>=D
.ihardlimit
)
726 *dsize
= D
.curblocks
;
727 } else if (D
.softlimit
==0 && D
.hardlimit
==0) {
728 goto try_group_quota
;
730 if (D
.softlimit
== 0) {
731 D
.softlimit
= D
.hardlimit
;
733 *dfree
= D
.softlimit
- D
.curblocks
;
734 *dsize
= D
.softlimit
;
741 * First of all, check whether group quota is
742 * enforced. If the call fails, assume it is
747 r
= SMB_VFS_GET_QUOTA(conn
, fname
->base_name
, SMB_GROUP_FS_QUOTA_TYPE
,
749 if (r
== -1 && errno
!= ENOSYS
) {
752 if (r
== 0 && (D
.qflags
& QUOTAS_DENY_DISK
) == 0) {
759 r
= SMB_VFS_GET_QUOTA(conn
, fname
->base_name
, SMB_GROUP_QUOTA_TYPE
, id
,
762 /* Use softlimit to determine disk space, except when it has been exceeded */
765 if (errno
== EDQUOT
) {
774 /* Use softlimit to determine disk space, except when it has been exceeded */
776 (D
.softlimit
&& D
.curblocks
>= D
.softlimit
) ||
777 (D
.hardlimit
&& D
.curblocks
>= D
.hardlimit
) ||
778 (D
.isoftlimit
&& D
.curinodes
>= D
.isoftlimit
) ||
779 (D
.ihardlimit
&& D
.curinodes
>=D
.ihardlimit
)
782 *dsize
= D
.curblocks
;
783 } else if (D
.softlimit
==0 && D
.hardlimit
==0) {
786 if (D
.softlimit
== 0) {
787 D
.softlimit
= D
.hardlimit
;
789 *dfree
= D
.softlimit
- D
.curblocks
;
790 *dsize
= D
.softlimit
;
795 #endif /* HAVE_SYS_QUOTAS */