1/*
2   Unix SMB/CIFS implementation.
3   support for quotas
4   Copyright (C) Andrew Tridgell 1992-1998
5
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 2 of the License, or
9   (at your option) any later version.
10
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.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21
22/*
23 * This is one of the most system dependent parts of Samba, and its
24 * done a litle differently. Each system has its own way of doing
25 * things :-(
26 */
27
28#include "includes.h"
29
30#undef DBGC_CLASS
31#define DBGC_CLASS DBGC_QUOTA
32
33#ifndef HAVE_SYS_QUOTAS
34
35/* just a quick hack because sysquotas.h is included before linux/quota.h */
36#ifdef QUOTABLOCK_SIZE
37#undef QUOTABLOCK_SIZE
38#endif
39
40#ifdef WITH_QUOTAS
41
42#if defined(VXFS_QUOTA)
43
44/*
45 * In addition to their native filesystems, some systems have Veritas VxFS.
46 * Declare here, define at end: reduces likely "include" interaction problems.
47 *	David Lee <T.D.Lee@durham.ac.uk>
48 */
49BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize);
50
51#endif /* VXFS_QUOTA */
52
53#ifdef LINUX
54
55#include <sys/types.h>
56#include <mntent.h>
57
58/*
59 * This shouldn't be neccessary - it should be /usr/include/sys/quota.h
60 * So we include all the files has *should* be in the system into a large,
61 * grungy samba_linux_quoatas.h Sometimes I *hate* Linux :-). JRA.
62 */
63
64#include "samba_linux_quota.h"
65#include "samba_xfs_quota.h"
66
67typedef struct _LINUX_SMB_DISK_QUOTA {
68	SMB_BIG_UINT bsize;
69	SMB_BIG_UINT hardlimit; /* In bsize units. */
70	SMB_BIG_UINT softlimit; /* In bsize units. */
71	SMB_BIG_UINT curblocks; /* In bsize units. */
72	SMB_BIG_UINT ihardlimit; /* inode hard limit. */
73	SMB_BIG_UINT isoftlimit; /* inode soft limit. */
74	SMB_BIG_UINT curinodes; /* Current used inodes. */
75} LINUX_SMB_DISK_QUOTA;
76
77/****************************************************************************
78 Abstract out the XFS Quota Manager quota get call.
79****************************************************************************/
80
81static int get_smb_linux_xfs_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
82{
83	struct fs_disk_quota D;
84	int ret;
85
86	ZERO_STRUCT(D);
87
88	ret = quotactl(QCMD(Q_XGETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
89
90	if (ret)
91		ret = quotactl(QCMD(Q_XGETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
92
93	if (ret)
94		return ret;
95
96	dp->bsize = (SMB_BIG_UINT)512;
97	dp->softlimit = (SMB_BIG_UINT)D.d_blk_softlimit;
98	dp->hardlimit = (SMB_BIG_UINT)D.d_blk_hardlimit;
99	dp->ihardlimit = (SMB_BIG_UINT)D.d_ino_hardlimit;
100	dp->isoftlimit = (SMB_BIG_UINT)D.d_ino_softlimit;
101	dp->curinodes = (SMB_BIG_UINT)D.d_icount;
102	dp->curblocks = (SMB_BIG_UINT)D.d_bcount;
103
104	return ret;
105}
106
107/****************************************************************************
108 Abstract out the old and new Linux quota get calls.
109****************************************************************************/
110
111static int get_smb_linux_v1_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
112{
113	struct v1_kern_dqblk D;
114	int ret;
115
116	ZERO_STRUCT(D);
117
118	ret = quotactl(QCMD(Q_V1_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
119
120	if (ret && errno != EDQUOT)
121		ret = quotactl(QCMD(Q_V1_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
122
123	if (ret && errno != EDQUOT)
124		return ret;
125
126	dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
127	dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
128	dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
129	dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
130	dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
131	dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
132	dp->curblocks = (SMB_BIG_UINT)D.dqb_curblocks;
133
134	return ret;
135}
136
137static int get_smb_linux_v2_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
138{
139	struct v2_kern_dqblk D;
140	int ret;
141
142	ZERO_STRUCT(D);
143
144	ret = quotactl(QCMD(Q_V2_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
145
146	if (ret && errno != EDQUOT)
147		ret = quotactl(QCMD(Q_V2_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
148
149	if (ret && errno != EDQUOT)
150		return ret;
151
152	dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
153	dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
154	dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
155	dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
156	dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
157	dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
158	dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace) / dp->bsize;
159
160	return ret;
161}
162
163/****************************************************************************
164 Brand-new generic quota interface.
165****************************************************************************/
166
167static int get_smb_linux_gen_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
168{
169	struct if_dqblk D;
170	int ret;
171
172	ZERO_STRUCT(D);
173
174	ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
175
176	if (ret && errno != EDQUOT)
177		ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
178
179	if (ret && errno != EDQUOT)
180		return ret;
181
182	dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
183	dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
184	dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
185	dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
186	dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
187	dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
188	dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace) / dp->bsize;
189
190	return ret;
191}
192
193/****************************************************************************
194 Try to get the disk space from disk quotas (LINUX version).
195****************************************************************************/
196
197BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
198{
199	int r;
200	SMB_STRUCT_STAT S;
201	FILE *fp;
202	LINUX_SMB_DISK_QUOTA D;
203	struct mntent *mnt;
204	SMB_DEV_T devno;
205	int found;
206	uid_t euser_id;
207	gid_t egrp_id;
208
209	euser_id = geteuid();
210	egrp_id = getegid();
211
212	/* find the block device file */
213
214	if ( sys_stat(path, &S) == -1 )
215		return(False) ;
216
217	devno = S.st_dev ;
218
219	fp = setmntent(MOUNTED,"r");
220	found = False ;
221
222	while ((mnt = getmntent(fp))) {
223		if ( sys_stat(mnt->mnt_dir,&S) == -1 )
224			continue ;
225
226		if (S.st_dev == devno) {
227			found = True ;
228			break;
229		}
230	}
231
232	endmntent(fp) ;
233
234	if (!found)
235		return(False);
236
237	save_re_uid();
238	set_effective_uid(0);
239
240	if (strcmp(mnt->mnt_type, "xfs")==0) {
241		r=get_smb_linux_xfs_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
242	} else {
243		r=get_smb_linux_gen_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
244		if (r == -1 && errno != EDQUOT) {
245			r=get_smb_linux_v2_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
246			if (r == -1 && errno != EDQUOT)
247				r=get_smb_linux_v1_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
248		}
249	}
250
251	restore_re_uid();
252
253	/* Use softlimit to determine disk space, except when it has been exceeded */
254	*bsize = D.bsize;
255	if (r == -1) {
256		if (errno == EDQUOT) {
257			*dfree =0;
258			*dsize =D.curblocks;
259			return (True);
260		} else {
261			return(False);
262		}
263	}
264
265	/* Use softlimit to determine disk space, except when it has been exceeded */
266	if (
267		(D.softlimit && D.curblocks >= D.softlimit) ||
268		(D.hardlimit && D.curblocks >= D.hardlimit) ||
269		(D.isoftlimit && D.curinodes >= D.isoftlimit) ||
270		(D.ihardlimit && D.curinodes>=D.ihardlimit)
271	) {
272		*dfree = 0;
273		*dsize = D.curblocks;
274	} else if (D.softlimit==0 && D.hardlimit==0) {
275		return(False);
276	} else {
277		if (D.softlimit == 0)
278			D.softlimit = D.hardlimit;
279		*dfree = D.softlimit - D.curblocks;
280		*dsize = D.softlimit;
281	}
282
283	return (True);
284}
285
286#elif defined(CRAY)
287
288#include <sys/quota.h>
289#include <mntent.h>
290
291/****************************************************************************
292try to get the disk space from disk quotas (CRAY VERSION)
293****************************************************************************/
294
295BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
296{
297  struct mntent *mnt;
298  FILE *fd;
299  SMB_STRUCT_STAT sbuf;
300  SMB_DEV_T devno ;
301  static SMB_DEV_T devno_cached = 0 ;
302  static pstring name;
303  struct q_request request ;
304  struct qf_header header ;
305  static int quota_default = 0 ;
306  int found ;
307
308  if ( sys_stat(path,&sbuf) == -1 )
309    return(False) ;
310
311  devno = sbuf.st_dev ;
312
313  if ( devno != devno_cached ) {
314
315    devno_cached = devno ;
316
317    if ((fd = setmntent(KMTAB)) == NULL)
318      return(False) ;
319
320    found = False ;
321
322    while ((mnt = getmntent(fd)) != NULL) {
323
324      if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
325	continue ;
326
327      if (sbuf.st_dev == devno) {
328
329	found = True ;
330	break ;
331
332      }
333
334    }
335
336    pstrcpy(name,mnt->mnt_dir) ;
337    endmntent(fd) ;
338
339    if ( ! found )
340      return(False) ;
341  }
342
343  request.qf_magic = QF_MAGIC ;
344  request.qf_entry.id = geteuid() ;
345
346  if (quotactl(name, Q_GETQUOTA, &request) == -1)
347    return(False) ;
348
349  if ( ! request.user )
350    return(False) ;
351
352  if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) {
353
354    if ( ! quota_default ) {
355
356      if ( quotactl(name, Q_GETHEADER, &header) == -1 )
357	return(False) ;
358      else
359	quota_default = header.user_h.def_fq ;
360    }
361
362    *dfree = quota_default ;
363
364  }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) {
365
366    *dfree = 0 ;
367
368  }else{
369
370    *dfree = request.qf_entry.user_q.f_quota ;
371
372  }
373
374  *dsize = request.qf_entry.user_q.f_use ;
375
376  if ( *dfree < *dsize )
377    *dfree = 0 ;
378  else
379    *dfree -= *dsize ;
380
381  *bsize = 4096 ;  /* Cray blocksize */
382
383  return(True) ;
384
385}
386
387
388#elif defined(SUNOS5) || defined(SUNOS4)
389
390#include <fcntl.h>
391#include <sys/param.h>
392#if defined(SUNOS5)
393#include <sys/fs/ufs_quota.h>
394#include <sys/mnttab.h>
395#include <sys/mntent.h>
396#else /* defined(SUNOS4) */
397#include <ufs/quota.h>
398#include <mntent.h>
399#endif
400
401#if defined(SUNOS5)
402
403/****************************************************************************
404 Allows querying of remote hosts for quotas on NFS mounted shares.
405 Supports normal NFS and AMD mounts.
406 Alan Romeril <a.romeril@ic.ac.uk> July 2K.
407****************************************************************************/
408
409#include <rpc/rpc.h>
410#include <rpc/types.h>
411#include <rpcsvc/rquota.h>
412#include <rpc/nettype.h>
413#include <rpc/xdr.h>
414
415static int quotastat;
416
417static int xdr_getquota_args(XDR *xdrsp, struct getquota_args *args)
418{
419	if (!xdr_string(xdrsp, &args->gqa_pathp, RQ_PATHLEN ))
420		return(0);
421	if (!xdr_int(xdrsp, &args->gqa_uid))
422		return(0);
423	return (1);
424}
425
426static int xdr_getquota_rslt(XDR *xdrsp, struct getquota_rslt *gqr)
427{
428	if (!xdr_int(xdrsp, &quotastat)) {
429		DEBUG(6,("nfs_quotas: Status bad or zero\n"));
430		return 0;
431	}
432	if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsize)) {
433		DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
434		return 0;
435	}
436	if (!xdr_bool(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_active)) {
437		DEBUG(6,("nfs_quotas: Active bad or zero\n"));
438		return 0;
439	}
440	if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bhardlimit)) {
441		DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
442		return 0;
443	}
444	if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsoftlimit)) {
445		DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
446		return 0;
447	}
448	if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_curblocks)) {
449		DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
450		return 0;
451	}
452	return (1);
453}
454
455/* Restricted to SUNOS5 for the moment, I haven`t access to others to test. */
456static BOOL nfs_quotas(char *nfspath, uid_t euser_id, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
457{
458	uid_t uid = euser_id;
459	struct dqblk D;
460	char *mnttype = nfspath;
461	CLIENT *clnt;
462	struct getquota_rslt gqr;
463	struct getquota_args args;
464	char *cutstr, *pathname, *host, *testpath;
465	int len;
466	static struct timeval timeout = {2,0};
467	enum clnt_stat clnt_stat;
468	BOOL ret = True;
469
470	*bsize = *dfree = *dsize = (SMB_BIG_UINT)0;
471
472	len=strcspn(mnttype, ":");
473	pathname=strstr(mnttype, ":");
474	cutstr = (char *) malloc(len+1);
475	if (!cutstr)
476		return False;
477
478	memset(cutstr, '\0', len+1);
479	host = strncat(cutstr,mnttype, sizeof(char) * len );
480	DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr));
481	DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype));
482	testpath=strchr_m(mnttype, ':');
483	args.gqa_pathp = testpath+1;
484	args.gqa_uid = uid;
485
486	DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host, RQUOTAPROG, RQUOTAVERS, "udp"));
487
488	if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) == NULL) {
489		ret = False;
490		goto out;
491	}
492
493	clnt->cl_auth = authunix_create_default();
494	DEBUG(9,("nfs_quotas: auth_success\n"));
495
496	clnt_stat=clnt_call(clnt, RQUOTAPROC_GETQUOTA, xdr_getquota_args, (caddr_t)&args, xdr_getquota_rslt, (caddr_t)&gqr, timeout);
497
498	if (clnt_stat != RPC_SUCCESS) {
499		DEBUG(9,("nfs_quotas: clnt_call fail\n"));
500		ret = False;
501		goto out;
502	}
503
504	/*
505	 * quotastat returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is
506	 * no quota set, and 3 if no permission to get the quota.  If 0 or 3 return
507	 * something sensible.
508	 */
509
510	switch ( quotastat ) {
511	case 0:
512		DEBUG(9,("nfs_quotas: Remote Quotas Failed!  Error \"%i\" \n", quotastat ));
513		ret = False;
514		goto out;
515
516	case 1:
517		DEBUG(9,("nfs_quotas: Good quota data\n"));
518		D.dqb_bsoftlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit;
519		D.dqb_bhardlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit;
520		D.dqb_curblocks = gqr.getquota_rslt_u.gqr_rquota.rq_curblocks;
521		break;
522
523	case 2:
524	case 3:
525		D.dqb_bsoftlimit = 1;
526		D.dqb_curblocks = 1;
527		DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", quotastat ));
528		break;
529
530	default:
531		DEBUG(9,("nfs_quotas: Remote Quotas Questionable!  Error \"%i\" \n", quotastat ));
532		break;
533	}
534
535	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",
536			quotastat,
537			gqr.getquota_rslt_u.gqr_rquota.rq_bsize,
538			gqr.getquota_rslt_u.gqr_rquota.rq_active,
539			gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit,
540			gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit,
541			gqr.getquota_rslt_u.gqr_rquota.rq_curblocks));
542
543	*bsize = gqr.getquota_rslt_u.gqr_rquota.rq_bsize;
544	*dsize = D.dqb_bsoftlimit;
545
546	if (D.dqb_curblocks == D.dqb_curblocks == 1)
547		*bsize = 512;
548
549	if (D.dqb_curblocks > D.dqb_bsoftlimit) {
550		*dfree = 0;
551		*dsize = D.dqb_curblocks;
552	} else
553		*dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
554
555  out:
556
557	if (clnt) {
558		if (clnt->cl_auth)
559			auth_destroy(clnt->cl_auth);
560		clnt_destroy(clnt);
561	}
562
563	DEBUG(5,("nfs_quotas: For path \"%s\" returning  bsize %llu, dfree %llu, dsize %llu\n",args.gqa_pathp,*bsize,*dfree,*dsize));
564
565	SAFE_FREE(cutstr);
566	DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
567	return ret;
568}
569#endif
570
571/****************************************************************************
572try to get the disk space from disk quotas (SunOS & Solaris2 version)
573Quota code by Peter Urbanec (amiga@cse.unsw.edu.au).
574****************************************************************************/
575
576BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
577{
578	uid_t euser_id;
579	int ret;
580	struct dqblk D;
581#if defined(SUNOS5)
582	struct quotctl command;
583	int file;
584	static struct mnttab mnt;
585	static pstring name;
586	pstring devopt;
587#else /* SunOS4 */
588	struct mntent *mnt;
589	static pstring name;
590#endif
591	FILE *fd;
592	SMB_STRUCT_STAT sbuf;
593	SMB_DEV_T devno ;
594	static SMB_DEV_T devno_cached = 0 ;
595	static int found ;
596
597	euser_id = geteuid();
598
599	if ( sys_stat(path,&sbuf) == -1 )
600		return(False) ;
601
602	devno = sbuf.st_dev ;
603	DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n", path,(unsigned int)devno));
604	if ( devno != devno_cached ) {
605		devno_cached = devno ;
606#if defined(SUNOS5)
607		if ((fd = sys_fopen(MNTTAB, "r")) == NULL)
608			return(False) ;
609
610		found = False ;
611		slprintf(devopt, sizeof(devopt) - 1, "dev=%x", (unsigned int)devno);
612		while (getmntent(fd, &mnt) == 0) {
613			if( !hasmntopt(&mnt, devopt) )
614				continue;
615
616			DEBUG(5,("disk_quotas: testing \"%s\" %s\n", mnt.mnt_mountp,devopt));
617
618			/* quotas are only on vxfs, UFS or NFS */
619			if ( strcmp( mnt.mnt_fstype, MNTTYPE_UFS ) == 0 ||
620				strcmp( mnt.mnt_fstype, "nfs" ) == 0    ||
621				strcmp( mnt.mnt_fstype, "vxfs" ) == 0  ) {
622					found = True ;
623					break;
624			}
625		}
626
627		pstrcpy(name,mnt.mnt_mountp) ;
628		pstrcat(name,"/quotas") ;
629		fclose(fd) ;
630#else /* SunOS4 */
631		if ((fd = setmntent(MOUNTED, "r")) == NULL)
632			return(False) ;
633
634		found = False ;
635		while ((mnt = getmntent(fd)) != NULL) {
636			if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
637				continue ;
638			DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n", mnt->mnt_dir,(unsigned int)sbuf.st_dev));
639			if (sbuf.st_dev == devno) {
640				found = True ;
641				break;
642			}
643		}
644
645		pstrcpy(name,mnt->mnt_fsname) ;
646		endmntent(fd) ;
647#endif
648	}
649
650	if ( ! found )
651		return(False) ;
652
653	save_re_uid();
654	set_effective_uid(0);
655
656#if defined(SUNOS5)
657	if ( strcmp( mnt.mnt_fstype, "nfs" ) == 0) {
658		BOOL retval;
659		DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n", mnt.mnt_special));
660		retval = nfs_quotas(mnt.mnt_special, euser_id, bsize, dfree, dsize);
661		restore_re_uid();
662		return retval;
663	}
664
665	DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name));
666	if((file=sys_open(name, O_RDONLY,0))<0) {
667		restore_re_uid();
668		return(False);
669	}
670	command.op = Q_GETQUOTA;
671	command.uid = euser_id;
672	command.addr = (caddr_t) &D;
673	ret = ioctl(file, Q_QUOTACTL, &command);
674	close(file);
675#else
676	DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name));
677	ret = quotactl(Q_GETQUOTA, name, euser_id, &D);
678#endif
679
680	restore_re_uid();
681
682	if (ret < 0) {
683		DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n", strerror(errno) ));
684
685#if defined(SUNOS5) && defined(VXFS_QUOTA)
686		/* If normal quotactl() fails, try vxfs private calls */
687		set_effective_uid(euser_id);
688		DEBUG(5,("disk_quotas: mount type \"%s\"\n", mnt.mnt_fstype));
689		if ( 0 == strcmp ( mnt.mnt_fstype, "vxfs" )) {
690			BOOL retval;
691			retval = disk_quotas_vxfs(name, path, bsize, dfree, dsize);
692			return(retval);
693		}
694#else
695		return(False);
696#endif
697	}
698
699	/* If softlimit is zero, set it equal to hardlimit.
700	 */
701
702	if (D.dqb_bsoftlimit==0)
703		D.dqb_bsoftlimit = D.dqb_bhardlimit;
704
705	/* Use softlimit to determine disk space. A user exceeding the quota is told
706	 * that there's no space left. Writes might actually work for a bit if the
707	 * hardlimit is set higher than softlimit. Effectively the disk becomes
708	 * made of rubber latex and begins to expand to accommodate the user :-)
709	 */
710
711	if (D.dqb_bsoftlimit==0)
712		return(False);
713	*bsize = DEV_BSIZE;
714	*dsize = D.dqb_bsoftlimit;
715
716	if (D.dqb_curblocks > D.dqb_bsoftlimit) {
717		*dfree = 0;
718		*dsize = D.dqb_curblocks;
719	} else
720		*dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
721
722	DEBUG(5,("disk_quotas for path \"%s\" returning  bsize %llu, dfree %llu, dsize %llu\n",
723		path,*bsize,*dfree,*dsize));
724
725	return(True);
726}
727
728
729#elif defined(OSF1)
730#include <ufs/quota.h>
731
732/****************************************************************************
733try to get the disk space from disk quotas - OSF1 version
734****************************************************************************/
735
736BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
737{
738  int r, save_errno;
739  struct dqblk D;
740  SMB_STRUCT_STAT S;
741  uid_t euser_id;
742
743  /*
744   * This code presumes that OSF1 will only
745   * give out quota info when the real uid
746   * matches the effective uid. JRA.
747   */
748  euser_id = geteuid();
749  save_re_uid();
750  if (set_re_uid() != 0) return False;
751
752  r= quotactl(path,QCMD(Q_GETQUOTA, USRQUOTA),euser_id,(char *) &D);
753  if (r) {
754     save_errno = errno;
755  }
756
757  restore_re_uid();
758
759  *bsize = DEV_BSIZE;
760
761  if (r)
762  {
763      if (save_errno == EDQUOT)   /* disk quota exceeded */
764      {
765         *dfree = 0;
766         *dsize = D.dqb_curblocks;
767         return (True);
768      }
769      else
770         return (False);
771  }
772
773  /* If softlimit is zero, set it equal to hardlimit.
774   */
775
776  if (D.dqb_bsoftlimit==0)
777    D.dqb_bsoftlimit = D.dqb_bhardlimit;
778
779  /* Use softlimit to determine disk space, except when it has been exceeded */
780
781  if (D.dqb_bsoftlimit==0)
782    return(False);
783
784  if ((D.dqb_curblocks>D.dqb_bsoftlimit)) {
785    *dfree = 0;
786    *dsize = D.dqb_curblocks;
787  } else {
788    *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
789    *dsize = D.dqb_bsoftlimit;
790  }
791  return (True);
792}
793
794#elif defined (IRIX6)
795/****************************************************************************
796try to get the disk space from disk quotas (IRIX 6.2 version)
797****************************************************************************/
798
799#include <sys/quota.h>
800#include <mntent.h>
801
802BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
803{
804  uid_t euser_id;
805  int r;
806  struct dqblk D;
807  struct fs_disk_quota        F;
808  SMB_STRUCT_STAT S;
809  FILE *fp;
810  struct mntent *mnt;
811  SMB_DEV_T devno;
812  int found;
813
814  /* find the block device file */
815
816  if ( sys_stat(path, &S) == -1 ) {
817    return(False) ;
818  }
819
820  devno = S.st_dev ;
821
822  fp = setmntent(MOUNTED,"r");
823  found = False ;
824
825  while ((mnt = getmntent(fp))) {
826    if ( sys_stat(mnt->mnt_dir,&S) == -1 )
827      continue ;
828    if (S.st_dev == devno) {
829      found = True ;
830      break ;
831    }
832  }
833  endmntent(fp) ;
834
835  if (!found) {
836    return(False);
837  }
838
839  euser_id=geteuid();
840  save_re_uid();
841  set_effective_uid(0);
842
843  /* Use softlimit to determine disk space, except when it has been exceeded */
844
845  *bsize = 512;
846
847  if ( 0 == strcmp ( mnt->mnt_type, "efs" ))
848  {
849    r=quotactl (Q_GETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &D);
850
851    restore_re_uid();
852
853    if (r==-1)
854      return(False);
855
856    /* Use softlimit to determine disk space, except when it has been exceeded */
857    if (
858        (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) ||
859        (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) ||
860        (D.dqb_fsoftlimit && D.dqb_curfiles>=D.dqb_fsoftlimit) ||
861        (D.dqb_fhardlimit && D.dqb_curfiles>=D.dqb_fhardlimit)
862       )
863    {
864      *dfree = 0;
865      *dsize = D.dqb_curblocks;
866    }
867    else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0)
868    {
869      return(False);
870    }
871    else
872    {
873      *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
874      *dsize = D.dqb_bsoftlimit;
875    }
876
877  }
878  else if ( 0 == strcmp ( mnt->mnt_type, "xfs" ))
879  {
880    r=quotactl (Q_XGETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &F);
881
882    restore_re_uid();
883
884    if (r==-1)
885      return(False);
886
887    /* Use softlimit to determine disk space, except when it has been exceeded */
888    if (
889        (F.d_blk_softlimit && F.d_bcount>=F.d_blk_softlimit) ||
890        (F.d_blk_hardlimit && F.d_bcount>=F.d_blk_hardlimit) ||
891        (F.d_ino_softlimit && F.d_icount>=F.d_ino_softlimit) ||
892        (F.d_ino_hardlimit && F.d_icount>=F.d_ino_hardlimit)
893       )
894    {
895      *dfree = 0;
896      *dsize = F.d_bcount;
897    }
898    else if (F.d_blk_softlimit==0 && F.d_blk_hardlimit==0)
899    {
900      return(False);
901    }
902    else
903    {
904      *dfree = (F.d_blk_softlimit - F.d_bcount);
905      *dsize = F.d_blk_softlimit;
906    }
907
908  }
909  else
910  {
911	  restore_re_uid();
912	  return(False);
913  }
914
915  return (True);
916
917}
918
919#else
920
921#if    defined(__FreeBSD__) || defined(__OpenBSD__)
922#include <ufs/ufs/quota.h>
923#include <machine/param.h>
924#elif         AIX
925/* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
926#include <jfs/quota.h>
927/* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
928#define dqb_curfiles dqb_curinodes
929#define dqb_fhardlimit dqb_ihardlimit
930#define dqb_fsoftlimit dqb_isoftlimit
931#else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
932#include <sys/quota.h>
933#include <devnm.h>
934#endif
935
936/****************************************************************************
937try to get the disk space from disk quotas - default version
938****************************************************************************/
939
940BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
941{
942  int r;
943  struct dqblk D;
944  uid_t euser_id;
945#if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__)
946  char dev_disk[256];
947  SMB_STRUCT_STAT S;
948
949  /* find the block device file */
950
951#ifdef HPUX
952  /* Need to set the cache flag to 1 for HPUX. Seems
953   * to have a significant performance boost when
954   * lstat calls on /dev access this function.
955   */
956  if ((sys_stat(path, &S)<0) || (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 1)<0))
957#else
958  if ((sys_stat(path, &S)<0) || (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0))
959	return (False);
960#endif /* ifdef HPUX */
961
962#endif /* !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__) */
963
964  euser_id = geteuid();
965
966#ifdef HPUX
967  /* for HPUX, real uid must be same as euid to execute quotactl for euid */
968  save_re_uid();
969  if (set_re_uid() != 0) return False;
970
971  r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
972
973  restore_re_uid();
974#else
975#if defined(__FreeBSD__) || defined(__OpenBSD__)
976  {
977    /* FreeBSD patches from Marty Moll <martym@arbor.edu> */
978    gid_t egrp_id;
979
980    save_re_uid();
981    set_effective_uid(0);
982
983    egrp_id = getegid();
984    r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
985
986    /* As FreeBSD has group quotas, if getting the user
987       quota fails, try getting the group instead. */
988    if (r) {
989	    r= quotactl(path,QCMD(Q_GETQUOTA,GRPQUOTA),egrp_id,(char *) &D);
990    }
991
992    restore_re_uid();
993  }
994#elif defined(AIX)
995  /* AIX has both USER and GROUP quotas:
996     Get the USER quota (ohnielse@fysik.dtu.dk) */
997  r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
998#else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
999  r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
1000#endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
1001#endif /* HPUX */
1002
1003  /* Use softlimit to determine disk space, except when it has been exceeded */
1004#if defined(__FreeBSD__) || defined(__OpenBSD__)
1005  *bsize = DEV_BSIZE;
1006#else /* !__FreeBSD__ && !__OpenBSD__ */
1007  *bsize = 1024;
1008#endif /*!__FreeBSD__ && !__OpenBSD__ */
1009
1010  if (r)
1011    {
1012      if (errno == EDQUOT)
1013	{
1014 	  *dfree =0;
1015 	  *dsize =D.dqb_curblocks;
1016 	  return (True);
1017	}
1018      else return(False);
1019    }
1020
1021  /* If softlimit is zero, set it equal to hardlimit.
1022   */
1023
1024  if (D.dqb_bsoftlimit==0)
1025    D.dqb_bsoftlimit = D.dqb_bhardlimit;
1026
1027  if (D.dqb_bsoftlimit==0)
1028    return(False);
1029  /* Use softlimit to determine disk space, except when it has been exceeded */
1030  if ((D.dqb_curblocks>D.dqb_bsoftlimit)
1031#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
1032||((D.dqb_curfiles>D.dqb_fsoftlimit) && (D.dqb_fsoftlimit != 0))
1033#endif
1034    ) {
1035      *dfree = 0;
1036      *dsize = D.dqb_curblocks;
1037    }
1038  else {
1039    *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
1040    *dsize = D.dqb_bsoftlimit;
1041  }
1042  return (True);
1043}
1044
1045#endif
1046
1047#if defined(VXFS_QUOTA)
1048
1049/****************************************************************************
1050Try to get the disk space from Veritas disk quotas.
1051    David Lee <T.D.Lee@durham.ac.uk> August 1999.
1052
1053Background assumptions:
1054    Potentially under many Operating Systems.  Initially Solaris 2.
1055
1056    My guess is that Veritas is largely, though not entirely,
1057    independent of OS.  So I have separated it out.
1058
1059    There may be some details.  For example, OS-specific "include" files.
1060
1061    It is understood that HPUX 10 somehow gets Veritas quotas without
1062    any special effort; if so, this routine need not be compiled in.
1063        Dirk De Wachter <Dirk.DeWachter@rug.ac.be>
1064
1065Warning:
1066    It is understood that Veritas do not publicly support this ioctl interface.
1067    Rather their preference would be for the user (us) to call the native
1068    OS and then for the OS itself to call through to the VxFS filesystem.
1069    Presumably HPUX 10, see above, does this.
1070
1071Hints for porting:
1072    Add your OS to "IFLIST" below.
1073    Get it to compile successfully:
1074        Almost certainly "include"s require attention: see SUNOS5.
1075    In the main code above, arrange for it to be called: see SUNOS5.
1076    Test!
1077
1078****************************************************************************/
1079
1080/* "IFLIST"
1081 * This "if" is a list of ports:
1082 *	if defined(OS1) || defined(OS2) || ...
1083 */
1084#if defined(SUNOS5)
1085
1086#if defined(SUNOS5)
1087#include <sys/fs/vx_solaris.h>
1088#endif
1089#include <sys/fs/vx_machdep.h>
1090#include <sys/fs/vx_layout.h>
1091#include <sys/fs/vx_quota.h>
1092#include <sys/fs/vx_aioctl.h>
1093#include <sys/fs/vx_ioctl.h>
1094
1095BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
1096{
1097  uid_t user_id, euser_id;
1098  int ret;
1099  struct vx_dqblk D;
1100  struct vx_quotctl quotabuf;
1101  struct vx_genioctl genbuf;
1102  pstring qfname;
1103  int file;
1104
1105  /*
1106   * "name" may or may not include a trailing "/quotas".
1107   * Arranging consistency of calling here in "quotas.c" may not be easy and
1108   * it might be easier to examine and adjust it here.
1109   * Fortunately, VxFS seems not to mind at present.
1110   */
1111  pstrcpy(qfname, name) ;
1112  /* pstrcat(qfname, "/quotas") ; */	/* possibly examine and adjust "name" */
1113
1114  euser_id = geteuid();
1115  set_effective_uid(0);
1116
1117  DEBUG(5,("disk_quotas: looking for VxFS quotas file \"%s\"\n", qfname));
1118  if((file=sys_open(qfname, O_RDONLY,0))<0) {
1119    set_effective_uid(euser_id);
1120    return(False);
1121  }
1122  genbuf.ioc_cmd = VX_QUOTACTL;
1123  genbuf.ioc_up = (void *) &quotabuf;
1124
1125  quotabuf.cmd = VX_GETQUOTA;
1126  quotabuf.uid = euser_id;
1127  quotabuf.addr = (caddr_t) &D;
1128  ret = ioctl(file, VX_ADMIN_IOCTL, &genbuf);
1129  close(file);
1130
1131  set_effective_uid(euser_id);
1132
1133  if (ret < 0) {
1134    DEBUG(5,("disk_quotas ioctl (VxFS) failed. Error = %s\n", strerror(errno) ));
1135    return(False);
1136  }
1137
1138  /* If softlimit is zero, set it equal to hardlimit.
1139   */
1140
1141  if (D.dqb_bsoftlimit==0)
1142    D.dqb_bsoftlimit = D.dqb_bhardlimit;
1143
1144  /* Use softlimit to determine disk space. A user exceeding the quota is told
1145   * that there's no space left. Writes might actually work for a bit if the
1146   * hardlimit is set higher than softlimit. Effectively the disk becomes
1147   * made of rubber latex and begins to expand to accommodate the user :-)
1148   */
1149  DEBUG(5,("disk_quotas for path \"%s\" block c/s/h %ld/%ld/%ld; file c/s/h %ld/%ld/%ld\n",
1150         path, D.dqb_curblocks, D.dqb_bsoftlimit, D.dqb_bhardlimit,
1151         D.dqb_curfiles, D.dqb_fsoftlimit, D.dqb_fhardlimit));
1152
1153  if (D.dqb_bsoftlimit==0)
1154    return(False);
1155  *bsize = DEV_BSIZE;
1156  *dsize = D.dqb_bsoftlimit;
1157
1158  if (D.dqb_curblocks > D.dqb_bsoftlimit) {
1159     *dfree = 0;
1160     *dsize = D.dqb_curblocks;
1161  } else
1162    *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
1163
1164  DEBUG(5,("disk_quotas for path \"%s\" returning  bsize %llu, dfree %llu, dsize %llu\n",
1165         path,*bsize,*dfree,*dsize));
1166
1167  return(True);
1168}
1169
1170#endif /* SUNOS5 || ... */
1171
1172#endif /* VXFS_QUOTA */
1173
1174#else /* WITH_QUOTAS */
1175
1176BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
1177{
1178  (*bsize) = 512; /* This value should be ignored */
1179
1180  /* And just to be sure we set some values that hopefully */
1181  /* will be larger that any possible real-world value     */
1182  (*dfree) = (SMB_BIG_UINT)-1;
1183  (*dsize) = (SMB_BIG_UINT)-1;
1184
1185  /* As we have select not to use quotas, allways fail */
1186  return False;
1187}
1188#endif /* WITH_QUOTAS */
1189
1190#else /* HAVE_SYS_QUOTAS */
1191/* wrapper to the new sys_quota interface
1192   this file should be removed later
1193   */
1194BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
1195{
1196	int r;
1197	SMB_DISK_QUOTA D;
1198	unid_t id;
1199
1200	id.uid = geteuid();
1201
1202	ZERO_STRUCT(D);
1203  	r=sys_get_quota(path, SMB_USER_QUOTA_TYPE, id, &D);
1204
1205	/* Use softlimit to determine disk space, except when it has been exceeded */
1206	*bsize = D.bsize;
1207	if (r == -1) {
1208		if (errno == EDQUOT) {
1209			*dfree =0;
1210			*dsize =D.curblocks;
1211			return (True);
1212		} else {
1213			goto try_group_quota;
1214		}
1215	}
1216
1217	/* Use softlimit to determine disk space, except when it has been exceeded */
1218	if (
1219		(D.softlimit && D.curblocks >= D.softlimit) ||
1220		(D.hardlimit && D.curblocks >= D.hardlimit) ||
1221		(D.isoftlimit && D.curinodes >= D.isoftlimit) ||
1222		(D.ihardlimit && D.curinodes>=D.ihardlimit)
1223	) {
1224		*dfree = 0;
1225		*dsize = D.curblocks;
1226	} else if (D.softlimit==0 && D.hardlimit==0) {
1227		goto try_group_quota;
1228	} else {
1229		if (D.softlimit == 0)
1230			D.softlimit = D.hardlimit;
1231		*dfree = D.softlimit - D.curblocks;
1232		*dsize = D.softlimit;
1233	}
1234
1235	return True;
1236
1237try_group_quota:
1238	id.gid = getegid();
1239
1240	ZERO_STRUCT(D);
1241  	r=sys_get_quota(path, SMB_GROUP_QUOTA_TYPE, id, &D);
1242
1243	/* Use softlimit to determine disk space, except when it has been exceeded */
1244	*bsize = D.bsize;
1245	if (r == -1) {
1246		if (errno == EDQUOT) {
1247			*dfree =0;
1248			*dsize =D.curblocks;
1249			return (True);
1250		} else {
1251			return False;
1252		}
1253	}
1254
1255	/* Use softlimit to determine disk space, except when it has been exceeded */
1256	if (
1257		(D.softlimit && D.curblocks >= D.softlimit) ||
1258		(D.hardlimit && D.curblocks >= D.hardlimit) ||
1259		(D.isoftlimit && D.curinodes >= D.isoftlimit) ||
1260		(D.ihardlimit && D.curinodes>=D.ihardlimit)
1261	) {
1262		*dfree = 0;
1263		*dsize = D.curblocks;
1264	} else if (D.softlimit==0 && D.hardlimit==0) {
1265		return False;
1266	} else {
1267		if (D.softlimit == 0)
1268			D.softlimit = D.hardlimit;
1269		*dfree = D.softlimit - D.curblocks;
1270		*dsize = D.softlimit;
1271	}
1272
1273	return (True);
1274}
1275#endif /* HAVE_SYS_QUOTAS */
1276