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