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));
340 #else /* WITH_QUOTAS */
342 bool disk_quotas(connection_struct *conn, struct smb_filename *fname,
343 uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
345 (*bsize) = 512; /* This value should be ignored */
347 /* And just to be sure we set some values that hopefully */
348 /* will be larger that any possible real-world value */
349 (*dfree) = (uint64_t)-1;
350 (*dsize) = (uint64_t)-1;
352 /* As we have select not to use quotas, allways fail */
355 #endif /* WITH_QUOTAS */
357 #else /* HAVE_SYS_QUOTAS */
358 /* wrapper to the new sys_quota interface
359 this file should be removed later
361 bool disk_quotas(connection_struct *conn, struct smb_filename *fname,
362 uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
369 * First of all, check whether user quota is
370 * enforced. If the call fails, assume it is
375 r = SMB_VFS_GET_QUOTA(conn, fname, SMB_USER_FS_QUOTA_TYPE,
377 if (r == -1 && errno != ENOSYS) {
378 goto try_group_quota;
380 if (r == 0 && (D.qflags & QUOTAS_DENY_DISK) == 0) {
381 goto try_group_quota;
387 /* if new files created under this folder get this
388 * folder's UID, then available space is governed by
389 * the quota of the folder's UID, not the creating user.
391 if (lp_inherit_owner(SNUM(conn)) != INHERIT_OWNER_NO &&
392 id.uid != fname->st.st_ex_uid && id.uid != sec_initial_uid()) {
395 id.uid = fname->st.st_ex_uid;
397 r = SMB_VFS_GET_QUOTA(conn, fname,
398 SMB_USER_QUOTA_TYPE, id, &D);
403 r = SMB_VFS_GET_QUOTA(conn, fname,
404 SMB_USER_QUOTA_TYPE, id, &D);
408 goto try_group_quota;
412 /* Use softlimit to determine disk space, except when it has been exceeded */
414 (D.softlimit && D.curblocks >= D.softlimit) ||
415 (D.hardlimit && D.curblocks >= D.hardlimit) ||
416 (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
417 (D.ihardlimit && D.curinodes>=D.ihardlimit)
420 *dsize = D.curblocks;
421 } else if (D.softlimit==0 && D.hardlimit==0) {
422 goto try_group_quota;
424 if (D.softlimit == 0) {
425 D.softlimit = D.hardlimit;
427 *dfree = D.softlimit - D.curblocks;
428 *dsize = D.softlimit;
435 * First of all, check whether group quota is
436 * enforced. If the call fails, assume it is
441 r = SMB_VFS_GET_QUOTA(conn, fname, SMB_GROUP_FS_QUOTA_TYPE,
443 if (r == -1 && errno != ENOSYS) {
446 if (r == 0 && (D.qflags & QUOTAS_DENY_DISK) == 0) {
453 * If new files created under this folder get this folder's
454 * GID, then available space is governed by the quota of the
455 * folder's GID, not the primary group of the creating user.
457 if (VALID_STAT(fname->st) &&
458 S_ISDIR(fname->st.st_ex_mode) &&
459 fname->st.st_ex_mode & S_ISGID) {
460 id.gid = fname->st.st_ex_gid;
462 r = SMB_VFS_GET_QUOTA(conn, fname, SMB_GROUP_QUOTA_TYPE, id,
467 r = SMB_VFS_GET_QUOTA(conn, fname, SMB_GROUP_QUOTA_TYPE, id,
476 /* Use softlimit to determine disk space, except when it has been exceeded */
478 (D.softlimit && D.curblocks >= D.softlimit) ||
479 (D.hardlimit && D.curblocks >= D.hardlimit) ||
480 (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
481 (D.ihardlimit && D.curinodes>=D.ihardlimit)
484 *dsize = D.curblocks;
485 } else if (D.softlimit==0 && D.hardlimit==0) {
488 if (D.softlimit == 0) {
489 D.softlimit = D.hardlimit;
491 *dfree = D.softlimit - D.curblocks;
492 *dsize = D.softlimit;
497 #endif /* HAVE_SYS_QUOTAS */