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(SUNOS5) /* Solaris */
46 #include <sys/param.h>
47 #include <sys/fs/ufs_quota.h>
48 #include <sys/mnttab.h>
49 #include <sys/mntent.h>
51 /****************************************************************************
52 Allows querying of remote hosts for quotas on NFS mounted shares.
53 Supports normal NFS and AMD mounts.
54 Alan Romeril <a.romeril@ic.ac.uk> July 2K.
55 ****************************************************************************/
58 #include <rpc/types.h>
59 #include <rpcsvc/rquota.h>
60 #include <rpc/nettype.h>
63 static int my_xdr_getquota_rslt(XDR
*xdrsp
, struct getquota_rslt
*gqr
)
67 if (!xdr_int(xdrsp
, "astat
)) {
68 DEBUG(6,("nfs_quotas: Status bad or zero\n"));
71 gqr
->status
= quotastat
;
73 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bsize
)) {
74 DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
77 if (!xdr_bool(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_active
)) {
78 DEBUG(6,("nfs_quotas: Active bad or zero\n"));
81 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
)) {
82 DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
85 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
)) {
86 DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
89 if (!xdr_int(xdrsp
, &gqr
->getquota_rslt_u
.gqr_rquota
.rq_curblocks
)) {
90 DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
96 static int my_xdr_getquota_args(XDR
*xdrsp
, struct getquota_args
*args
)
98 if (!xdr_string(xdrsp
, &args
->gqa_pathp
, RQ_PATHLEN
))
100 if (!xdr_int(xdrsp
, &args
->gqa_uid
))
105 /* Restricted to SUNOS5 for the moment, I haven`t access to others to test. */
106 static bool nfs_quotas(char *nfspath
, uid_t euser_id
, uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
108 uid_t uid
= euser_id
;
110 char *mnttype
= nfspath
;
112 struct getquota_rslt gqr
;
113 struct getquota_args args
;
114 char *cutstr
, *pathname
, *host
, *testpath
;
116 static struct timeval timeout
= {2,0};
117 enum clnt_stat clnt_stat
;
120 *bsize
= *dfree
= *dsize
= (uint64_t)0;
122 len
=strcspn(mnttype
, ":");
123 pathname
=strstr(mnttype
, ":");
124 cutstr
= (char *) SMB_MALLOC(len
+1);
128 memset(cutstr
, '\0', len
+1);
129 host
= strncat(cutstr
,mnttype
, sizeof(char) * len
);
130 DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr
));
131 DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype
));
132 testpath
=strchr_m(mnttype
, ':');
133 args
.gqa_pathp
= testpath
+1;
136 DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host
, RQUOTAPROG
, RQUOTAVERS
, "udp"));
138 if ((clnt
= clnt_create(host
, RQUOTAPROG
, RQUOTAVERS
, "udp")) == NULL
) {
143 clnt
->cl_auth
= authunix_create_default();
144 DEBUG(9,("nfs_quotas: auth_success\n"));
146 clnt_stat
=clnt_call(clnt
, RQUOTAPROC_GETQUOTA
, my_xdr_getquota_args
, (caddr_t
)&args
, my_xdr_getquota_rslt
, (caddr_t
)&gqr
, timeout
);
148 if (clnt_stat
!= RPC_SUCCESS
) {
149 DEBUG(9,("nfs_quotas: clnt_call fail\n"));
155 * gqr.status returns 1 if quotas exist, 2 if there is
156 * no quota set, and 3 if no permission to get the quota.
157 * If 3, return something sensible.
160 switch (gqr
.status
) {
162 DEBUG(9,("nfs_quotas: Good quota data\n"));
163 D
.dqb_bsoftlimit
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
;
164 D
.dqb_bhardlimit
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
;
165 D
.dqb_curblocks
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
;
170 D
.dqb_bsoftlimit
= 1;
172 DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", gqr
.status
));
176 DEBUG(9, ("nfs_quotas: Unknown Remote Quota Status \"%i\"\n",
182 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",
184 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsize
,
185 gqr
.getquota_rslt_u
.gqr_rquota
.rq_active
,
186 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bhardlimit
,
187 gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsoftlimit
,
188 gqr
.getquota_rslt_u
.gqr_rquota
.rq_curblocks
));
190 *bsize
= gqr
.getquota_rslt_u
.gqr_rquota
.rq_bsize
;
191 *dsize
= D
.dqb_bsoftlimit
;
193 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
195 *dsize
= D
.dqb_curblocks
;
197 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
203 auth_destroy(clnt
->cl_auth
);
207 DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",args
.gqa_pathp
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
210 DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
214 /****************************************************************************
215 try to get the disk space from disk quotas (SunOS & Solaris2 version)
216 Quota code by Peter Urbanec (amiga@cse.unsw.edu.au).
217 ****************************************************************************/
219 bool disk_quotas(connection_struct
*conn
, struct smb_filename
*fname
,
220 uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
225 struct quotctl command
;
230 SMB_STRUCT_STAT sbuf
;
233 const char *path
= fname
->base_name
;
235 euser_id
= geteuid();
237 devno
= fname
->st
.st_ex_dev
;
238 DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n",
239 path
, (unsigned int)devno
));
240 if ((fd
= fopen(MNTTAB
, "r")) == NULL
) {
244 while (getmntent(fd
, &mnt
) == 0) {
245 if (sys_stat(mnt
.mnt_mountp
, &sbuf
, false) == -1) {
249 DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n",
250 mnt
.mnt_mountp
, (unsigned int)devno
));
252 /* quotas are only on vxfs, UFS or NFS */
253 if ((sbuf
.st_ex_dev
== devno
) && (
254 strcmp( mnt
.mnt_fstype
, MNTTYPE_UFS
) == 0 ||
255 strcmp( mnt
.mnt_fstype
, "nfs" ) == 0 ||
256 strcmp( mnt
.mnt_fstype
, "vxfs" ) == 0 )) {
258 name
= talloc_asprintf(talloc_tos(),
275 if (strcmp(mnt
.mnt_fstype
, "nfs") == 0) {
277 DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n",
279 retval
= nfs_quotas(mnt
.mnt_special
,
280 euser_id
, bsize
, dfree
, dsize
);
285 DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name
));
286 if((file
=open(name
, O_RDONLY
,0))<0) {
290 command
.op
= Q_GETQUOTA
;
291 command
.uid
= euser_id
;
292 command
.addr
= (caddr_t
) &D
;
293 ret
= ioctl(file
, Q_QUOTACTL
, &command
);
299 DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n",
305 /* If softlimit is zero, set it equal to hardlimit.
308 if (D
.dqb_bsoftlimit
==0) {
309 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
312 /* Use softlimit to determine disk space. A user exceeding the quota
313 * is told that there's no space left. Writes might actually work for
314 * a bit if the hardlimit is set higher than softlimit. Effectively
315 * the disk becomes made of rubber latex and begins to expand to
316 * accommodate the user :-)
319 if (D
.dqb_bsoftlimit
==0)
322 *dsize
= D
.dqb_bsoftlimit
;
324 if (D
.dqb_curblocks
> D
.dqb_bsoftlimit
) {
326 *dsize
= D
.dqb_curblocks
;
328 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
331 DEBUG(5,("disk_quotas for path \"%s\" returning "
332 "bsize %.0f, dfree %.0f, dsize %.0f\n",
333 path
,(double)*bsize
,(double)*dfree
,(double)*dsize
));
339 #else /* not Solaris */
342 /* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
343 #include <jfs/quota.h>
344 /* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
345 #define dqb_curfiles dqb_curinodes
346 #define dqb_fhardlimit dqb_ihardlimit
347 #define dqb_fsoftlimit dqb_isoftlimit
348 #ifdef _AIXVERSION_530
349 #include <sys/statfs.h>
350 #include <sys/vmount.h>
353 #include <sys/quota.h>
358 /****************************************************************************
359 try to get the disk space from disk quotas - default version
360 ****************************************************************************/
362 bool disk_quotas(connection_struct
*conn
, struct smb_filename
*fname
,
363 uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
368 const char *path
= fname
->base_name
;
371 SMB_STRUCT_STAT S
= fname
->st
;
373 /* find the block device file */
375 if ((sys_stat(path
, &S
, false)<0)
376 || (devnm(S_IFBLK
, S
.st_ex_dev
, dev_disk
, 256, 0)<0))
379 #endif /* !defined(AIX) */
381 euser_id
= geteuid();
384 /* AIX has both USER and GROUP quotas:
385 Get the USER quota (ohnielse@fysik.dtu.dk) */
386 #ifdef _AIXVERSION_530
388 struct statfs statbuf
;
389 quota64_t user_quota
;
390 if (statfs(path
,&statbuf
) != 0)
392 if(statbuf
.f_vfstype
== MNT_J2
)
394 /* For some reason we need to be root for jfs2 */
396 r
= quotactl(path
,QCMD(Q_J2GETQUOTA
,USRQUOTA
),euser_id
,(char *) &user_quota
);
398 /* Copy results to old struct to let the following code work as before */
399 D
.dqb_curblocks
= user_quota
.bused
;
400 D
.dqb_bsoftlimit
= user_quota
.bsoft
;
401 D
.dqb_bhardlimit
= user_quota
.bhard
;
402 D
.dqb_curfiles
= user_quota
.iused
;
403 D
.dqb_fsoftlimit
= user_quota
.isoft
;
404 D
.dqb_fhardlimit
= user_quota
.ihard
;
406 else if(statbuf
.f_vfstype
== MNT_JFS
)
410 if (set_re_uid() != 0)
412 r
= quotactl(path
,QCMD(Q_GETQUOTA
,USRQUOTA
),euser_id
,(char *) &D
);
414 #ifdef _AIXVERSION_530
417 r
= 1; /* Fail for other FS-types */
421 r
=quotactl(Q_GETQUOTA
, dev_disk
, euser_id
, &D
);
424 /* Use softlimit to determine disk space, except when it has been exceeded */
432 *dsize
=D
.dqb_curblocks
;
438 /* If softlimit is zero, set it equal to hardlimit.
441 if (D
.dqb_bsoftlimit
==0)
442 D
.dqb_bsoftlimit
= D
.dqb_bhardlimit
;
444 if (D
.dqb_bsoftlimit
==0)
446 /* Use softlimit to determine disk space, except when it has been exceeded */
447 if ((D
.dqb_curblocks
>D
.dqb_bsoftlimit
)
448 ||((D
.dqb_curfiles
>D
.dqb_fsoftlimit
) && (D
.dqb_fsoftlimit
!= 0))
451 *dsize
= D
.dqb_curblocks
;
454 *dfree
= D
.dqb_bsoftlimit
- D
.dqb_curblocks
;
455 *dsize
= D
.dqb_bsoftlimit
;
462 #else /* WITH_QUOTAS */
464 bool disk_quotas(connection_struct
*conn
, struct smb_filename
*fname
,
465 uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
467 (*bsize
) = 512; /* This value should be ignored */
469 /* And just to be sure we set some values that hopefully */
470 /* will be larger that any possible real-world value */
471 (*dfree
) = (uint64_t)-1;
472 (*dsize
) = (uint64_t)-1;
474 /* As we have select not to use quotas, allways fail */
477 #endif /* WITH_QUOTAS */
479 #else /* HAVE_SYS_QUOTAS */
480 /* wrapper to the new sys_quota interface
481 this file should be removed later
483 bool disk_quotas(connection_struct
*conn
, struct smb_filename
*fname
,
484 uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
491 * First of all, check whether user quota is
492 * enforced. If the call fails, assume it is
497 r
= SMB_VFS_GET_QUOTA(conn
, fname
->base_name
, SMB_USER_FS_QUOTA_TYPE
,
499 if (r
== -1 && errno
!= ENOSYS
) {
500 goto try_group_quota
;
502 if (r
== 0 && (D
.qflags
& QUOTAS_DENY_DISK
) == 0) {
503 goto try_group_quota
;
509 /* if new files created under this folder get this
510 * folder's UID, then available space is governed by
511 * the quota of the folder's UID, not the creating user.
513 if (lp_inherit_owner(SNUM(conn
)) &&
514 id
.uid
!= fname
->st
.st_ex_uid
&& id
.uid
!= sec_initial_uid()) {
517 id
.uid
= fname
->st
.st_ex_uid
;
519 r
= SMB_VFS_GET_QUOTA(conn
, fname
->base_name
,
520 SMB_USER_QUOTA_TYPE
, id
, &D
);
525 r
= SMB_VFS_GET_QUOTA(conn
, fname
->base_name
,
526 SMB_USER_QUOTA_TYPE
, id
, &D
);
530 goto try_group_quota
;
534 /* Use softlimit to determine disk space, except when it has been exceeded */
536 (D
.softlimit
&& D
.curblocks
>= D
.softlimit
) ||
537 (D
.hardlimit
&& D
.curblocks
>= D
.hardlimit
) ||
538 (D
.isoftlimit
&& D
.curinodes
>= D
.isoftlimit
) ||
539 (D
.ihardlimit
&& D
.curinodes
>=D
.ihardlimit
)
542 *dsize
= D
.curblocks
;
543 } else if (D
.softlimit
==0 && D
.hardlimit
==0) {
544 goto try_group_quota
;
546 if (D
.softlimit
== 0) {
547 D
.softlimit
= D
.hardlimit
;
549 *dfree
= D
.softlimit
- D
.curblocks
;
550 *dsize
= D
.softlimit
;
557 * First of all, check whether group quota is
558 * enforced. If the call fails, assume it is
563 r
= SMB_VFS_GET_QUOTA(conn
, fname
->base_name
, SMB_GROUP_FS_QUOTA_TYPE
,
565 if (r
== -1 && errno
!= ENOSYS
) {
568 if (r
== 0 && (D
.qflags
& QUOTAS_DENY_DISK
) == 0) {
575 r
= SMB_VFS_GET_QUOTA(conn
, fname
->base_name
, SMB_GROUP_QUOTA_TYPE
, id
,
583 /* Use softlimit to determine disk space, except when it has been exceeded */
585 (D
.softlimit
&& D
.curblocks
>= D
.softlimit
) ||
586 (D
.hardlimit
&& D
.curblocks
>= D
.hardlimit
) ||
587 (D
.isoftlimit
&& D
.curinodes
>= D
.isoftlimit
) ||
588 (D
.ihardlimit
&& D
.curinodes
>=D
.ihardlimit
)
591 *dsize
= D
.curblocks
;
592 } else if (D
.softlimit
==0 && D
.hardlimit
==0) {
595 if (D
.softlimit
== 0) {
596 D
.softlimit
= D
.hardlimit
;
598 *dfree
= D
.softlimit
- D
.curblocks
;
599 *dsize
= D
.softlimit
;
604 #endif /* HAVE_SYS_QUOTAS */