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