Fix from "Romeril, Alan" <a.romeril@ic.ac.uk> to get his NFS quota code
[gd/samba-autobuild/.git] / source3 / smbd / quotas.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    support for quotas
5    Copyright (C) Andrew Tridgell 1992-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22
23 /* 
24  * This is one of the most system dependent parts of Samba, and its
25  * done a litle differently. Each system has its own way of doing 
26  * things :-(
27  */
28
29 #include "includes.h"
30
31 extern int DEBUGLEVEL;
32
33 #if defined(VXFS_QUOTA)
34
35 /*
36  * In addition to their native filesystems, some systems have Veritas VxFS.
37  * Declare here, define at end: reduces likely "include" interaction problems.
38  *      David Lee <T.D.Lee@durham.ac.uk>
39  */
40 BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize);
41
42 #endif /* VXFS_QUOTA */
43
44 #ifdef LINUX
45
46 #include <sys/types.h>
47 #include <asm/types.h>
48 #include <sys/quota.h>
49
50 #include <mntent.h>
51 #include <linux/unistd.h>
52
53 _syscall4(int, quotactl, int, cmd, const char *, special, int, id, caddr_t, addr);
54
55 /****************************************************************************
56 try to get the disk space from disk quotas (LINUX version)
57 ****************************************************************************/
58
59 BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
60 {
61   int r;
62   struct dqblk D;
63   SMB_STRUCT_STAT S;
64   FILE *fp;
65   struct mntent *mnt;
66   SMB_DEV_T devno;
67   int found;
68   uid_t euser_id;
69
70   euser_id = geteuid();
71   
72   /* find the block device file */
73   
74   if ( sys_stat(path, &S) == -1 ) {
75     return(False) ;
76   }
77
78   devno = S.st_dev ;
79   
80   fp = setmntent(MOUNTED,"r");
81   found = False ;
82   
83   while ((mnt = getmntent(fp))) {
84     if ( sys_stat(mnt->mnt_dir,&S) == -1 )
85       continue ;
86     if (S.st_dev == devno) {
87       found = True ;
88       break ;
89     }
90   }
91   endmntent(fp) ;
92   
93   if (!found) {
94       return(False);
95     }
96
97   save_re_uid();
98   set_effective_uid(0);  
99   r=quotactl(QCMD(Q_GETQUOTA,USRQUOTA), mnt->mnt_fsname, euser_id, (caddr_t)&D);
100   restore_re_uid();
101
102   /* Use softlimit to determine disk space, except when it has been exceeded */
103   *bsize = 1024;
104   if (r)
105     {
106       if (errno == EDQUOT) 
107        {
108          *dfree =0;
109          *dsize =D.dqb_curblocks;
110          return (True);
111     }
112       else return(False);
113   }
114   /* Use softlimit to determine disk space, except when it has been exceeded */
115   if (
116       (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) ||
117       (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) ||
118       (D.dqb_isoftlimit && D.dqb_curinodes>=D.dqb_isoftlimit) ||
119       (D.dqb_ihardlimit && D.dqb_curinodes>=D.dqb_ihardlimit)
120      )
121     {
122       *dfree = 0;
123       *dsize = D.dqb_curblocks;
124     }
125   else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0)
126     {
127       return(False);
128     }
129   else {
130     if (D.dqb_bsoftlimit == 0)
131       D.dqb_bsoftlimit = D.dqb_bhardlimit;
132     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
133     *dsize = D.dqb_bsoftlimit;
134   }
135   return (True);
136 }
137
138 #elif defined(CRAY)
139
140 #include <sys/quota.h>
141 #include <mntent.h>
142
143 /****************************************************************************
144 try to get the disk space from disk quotas (CRAY VERSION)
145 ****************************************************************************/
146
147 BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
148 {
149   struct mntent *mnt;
150   FILE *fd;
151   SMB_STRUCT_STAT sbuf;
152   SMB_DEV_T devno ;
153   static SMB_DEV_T devno_cached = 0 ;
154   static pstring name;
155   struct q_request request ;
156   struct qf_header header ;
157   static int quota_default = 0 ;
158   int found ;
159   
160   if ( sys_stat(path,&sbuf) == -1 )
161     return(False) ;
162   
163   devno = sbuf.st_dev ;
164   
165   if ( devno != devno_cached ) {
166     
167     devno_cached = devno ;
168     
169     if ((fd = setmntent(KMTAB)) == NULL)
170       return(False) ;
171     
172     found = False ;
173     
174     while ((mnt = getmntent(fd)) != NULL) {
175       
176       if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
177         continue ;
178       
179       if (sbuf.st_dev == devno) {
180         
181         found = True ;
182         break ;
183         
184       }
185       
186     }
187     
188     pstrcpy(name,mnt->mnt_dir) ;
189     endmntent(fd) ;
190     
191     if ( ! found )
192       return(False) ;
193   }
194   
195   request.qf_magic = QF_MAGIC ;
196   request.qf_entry.id = geteuid() ;
197   
198   if (quotactl(name, Q_GETQUOTA, &request) == -1)
199     return(False) ;
200   
201   if ( ! request.user )
202     return(False) ;
203   
204   if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) {
205     
206     if ( ! quota_default ) {
207       
208       if ( quotactl(name, Q_GETHEADER, &header) == -1 )
209         return(False) ;
210       else
211         quota_default = header.user_h.def_fq ;
212     }
213     
214     *dfree = quota_default ;
215     
216   }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) {
217     
218     *dfree = 0 ;
219     
220   }else{
221     
222     *dfree = request.qf_entry.user_q.f_quota ;
223     
224   }
225   
226   *dsize = request.qf_entry.user_q.f_use ;
227   
228   if ( *dfree < *dsize )
229     *dfree = 0 ;
230   else
231     *dfree -= *dsize ;
232   
233   *bsize = 4096 ;  /* Cray blocksize */
234   
235   return(True) ;
236   
237 }
238
239
240 #elif defined(SUNOS5) || defined(SUNOS4)
241
242 #include <fcntl.h>
243 #include <sys/param.h>
244 #if defined(SUNOS5)
245 #include <sys/fs/ufs_quota.h>
246 #include <sys/mnttab.h>
247 #include <sys/mntent.h>
248 #else /* defined(SUNOS4) */
249 #include <ufs/quota.h>
250 #include <mntent.h>
251 #endif
252
253 #if defined(SUNOS5)
254
255 /****************************************************************************
256  Allows querying of remote hosts for quotas on NFS mounted shares.
257  Supports normal NFS and AMD mounts.
258  Alan Romeril <a.romeril@ic.ac.uk> July 2K.
259 ****************************************************************************/
260
261 #include <rpc/rpc.h>
262 #include <rpc/types.h>
263 #include <rpcsvc/rquota.h>
264 #include <rpc/nettype.h>
265 #include <rpc/xdr.h>
266
267 static int quotastat;
268
269 static int xdr_getquota_args(XDR *xdrsp, struct getquota_args *args)
270 {
271         if (!xdr_string(xdrsp, &args->gqa_pathp, RQ_PATHLEN ))
272                 return(0);
273         if (!xdr_int(xdrsp, &args->gqa_uid))
274                 return(0);
275         return (1);
276 }
277
278 static int xdr_getquota_rslt(XDR *xdrsp, struct getquota_rslt *gqr)
279 {
280         gqr_status status;
281         union {
282                 rquota gqr_rquota;
283         } getquota_rslt_u;
284   
285         if (!xdr_int(xdrsp, &quotastat)) {
286                 DEBUG(6,("nfs_quotas: Status bad or zero\n"));
287                 return 0;
288         }
289         if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsize)) {
290                 DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
291                 return 0;
292         }
293         if (!xdr_bool(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_active)) {
294                 DEBUG(6,("nfs_quotas: Active bad or zero\n"));
295                 return 0;
296         }
297         if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bhardlimit)) {
298                 DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
299                 return 0;
300         }
301         if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsoftlimit)) {
302                 DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
303                 return 0;
304         }
305         if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_curblocks)) {
306                 DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
307                 return 0;
308         }
309         return (1);
310 }
311
312 /* Restricted to SUNOS5 for the moment, I haven`t access to others to test. */ 
313 static BOOL nfs_quotas(char *nfspath, uid_t euser_id, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
314 {
315         uid_t uid = euser_id;
316         struct dqblk D;
317         char *mnttype = nfspath;
318         CLIENT *clnt;
319         struct getquota_rslt gqr;
320         struct getquota_args args;
321         char *cutstr, *pathname, *host, *testpath;
322         int len;
323         static struct timeval timeout = {2,0};
324         enum clnt_stat clnt_stat;
325
326         len=strcspn(mnttype, ":");
327         pathname=strstr(mnttype, ":");
328         cutstr = (char *) malloc(sizeof(char) * len );
329         if (!cutstr)
330                 return False;
331
332         host = strncat(cutstr,mnttype, sizeof(char) * len );
333         DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr));
334         DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype));
335         testpath=strchr(mnttype, ':');
336         args.gqa_pathp = testpath+1;
337         args.gqa_uid = uid;
338
339         DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host, RQUOTAPROG, RQUOTAVERS, "udp"));
340
341         if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) != NULL) {
342                 clnt->cl_auth = authunix_create_default();
343                 DEBUG(9,("nfs_quotas: auth_success\n"));
344
345                 clnt_stat=clnt_call(clnt, RQUOTAPROC_GETQUOTA, xdr_getquota_args, (caddr_t)&args, xdr_getquota_rslt, (caddr_t)&gqr, timeout);
346                 if (clnt_stat == RPC_SUCCESS)
347                         DEBUG(9,("nfs_quotas: rpccall_success\n"));
348         };
349
350
351         /* 
352          * quotastat returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is
353          * no quota set, and 3 if no permission to get the quota.  If 0 or 3 return
354          * something sensible.
355          */   
356
357         if (quotastat == 1) {
358                 DEBUG(9,("nfs_quotas: Good quota data\n"));
359                 D.dqb_bsoftlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit;
360                 D.dqb_bhardlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit;
361                 D.dqb_curblocks = gqr.getquota_rslt_u.gqr_rquota.rq_curblocks;
362         }
363
364         if (quotastat == 0 || quotastat == 3) {
365                 D.dqb_bsoftlimit = 1;
366                 D.dqb_curblocks = 1;
367                 DEBUG(9,("nfs_quotas: Remote Quotas Failed!  Error \"%i\" \n", quotastat ));
368         }
369
370         if (quotastat == 2) {
371                 DEBUG(9,("nfs_quotas: Remote Quotas Failed!  Error \"%i\" \n", quotastat ));
372                 auth_destroy(clnt->cl_auth);
373                 clnt_destroy(clnt);
374                 free(cutstr);
375                 return(False);
376         }       
377
378         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",
379                         quotastat,
380                         gqr.getquota_rslt_u.gqr_rquota.rq_bsize,
381                         gqr.getquota_rslt_u.gqr_rquota.rq_active,
382                         gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit,
383                         gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit,
384                         gqr.getquota_rslt_u.gqr_rquota.rq_curblocks));
385
386         *bsize = gqr.getquota_rslt_u.gqr_rquota.rq_bsize;
387         *dsize = D.dqb_bsoftlimit;
388
389         if (D.dqb_curblocks == D.dqb_curblocks == 1)
390                 *bsize = 512;
391
392         if (D.dqb_curblocks > D.dqb_bsoftlimit) {
393                 *dfree = 0;
394                 *dsize = D.dqb_curblocks;
395         } else
396                 *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
397
398         auth_destroy(clnt->cl_auth);
399         clnt_destroy(clnt);
400
401         DEBUG(5,("nfs_quotas: For path \"%s\" returning  bsize %.0f, dfree %.0f, dsize %.0f\n",args.gqa_pathp,(double)*bsize,(double)*dfree,(double)*dsize));
402
403         free(cutstr);
404         DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
405         return(True);
406 }
407 #endif
408
409 /****************************************************************************
410 try to get the disk space from disk quotas (SunOS & Solaris2 version)
411 Quota code by Peter Urbanec (amiga@cse.unsw.edu.au).
412 ****************************************************************************/
413
414 BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
415 {
416         uid_t euser_id;
417         int ret;
418         struct dqblk D;
419 #if defined(SUNOS5)
420         struct quotctl command;
421         int file;
422         static struct mnttab mnt;
423         static pstring name;
424         pstring devopt;
425 #else /* SunOS4 */
426         struct mntent *mnt;
427         static pstring name;
428 #endif
429         FILE *fd;
430         SMB_STRUCT_STAT sbuf;
431         SMB_DEV_T devno ;
432         static SMB_DEV_T devno_cached = 0 ;
433         static int found ;
434
435         euser_id = geteuid();
436   
437         if ( sys_stat(path,&sbuf) == -1 )
438                 return(False) ;
439   
440         devno = sbuf.st_dev ;
441         DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n", path,devno));
442         if ( devno != devno_cached ) {
443                 devno_cached = devno ;
444 #if defined(SUNOS5)
445                 if ((fd = sys_fopen(MNTTAB, "r")) == NULL)
446                         return(False) ;
447     
448                 found = False ;
449                 slprintf(devopt, sizeof(devopt) - 1, "dev=%x", devno);
450                 while (getmntent(fd, &mnt) == 0) {
451                         if( !hasmntopt(&mnt, devopt) )
452                                 continue;
453
454                         DEBUG(5,("disk_quotas: testing \"%s\" %s\n", mnt.mnt_mountp,devopt));
455
456                         /* quotas are only on vxfs, UFS or NFS */
457                         if ( strcmp( mnt.mnt_fstype, MNTTYPE_UFS ) == 0 ||
458                                 strcmp( mnt.mnt_fstype, "nfs" ) == 0    ||
459                                 strcmp( mnt.mnt_fstype, "vxfs" ) == 0  ) { 
460                                         found = True ;
461                                         break;
462                         }
463                 }
464     
465                 pstrcpy(name,mnt.mnt_mountp) ;
466                 pstrcat(name,"/quotas") ;
467                 fclose(fd) ;
468 #else /* SunOS4 */
469                 if ((fd = setmntent(MOUNTED, "r")) == NULL)
470                         return(False) ;
471     
472                 found = False ;
473                 while ((mnt = getmntent(fd)) != NULL) {
474                         if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
475                                 continue ;
476                         DEBUG(5,("disk_quotas: testing \"%s\" devno=%o\n", mnt->mnt_dir,sbuf.st_dev));
477                         if (sbuf.st_dev == devno) {
478                                 found = True ;
479                                 break;
480                         }
481                 }
482     
483                 pstrcpy(name,mnt->mnt_fsname) ;
484                 endmntent(fd) ;
485 #endif
486         }
487
488         if ( ! found )
489                 return(False) ;
490
491         save_re_uid();
492         set_effective_uid(0);
493
494 #if defined(SUNOS5)
495         if ( strcmp( mnt.mnt_fstype, "nfs" ) == 0) {
496                 BOOL retval;
497                 DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n", mnt.mnt_special));
498                 retval = nfs_quotas(mnt.mnt_special, euser_id, bsize, dfree, dsize);
499                 restore_re_uid();
500                 return retval;
501         }
502
503         DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name));
504         if((file=sys_open(name, O_RDONLY,0))<0) {
505                 restore_re_uid();
506                 return(False);
507         }
508         command.op = Q_GETQUOTA;
509         command.uid = euser_id;
510         command.addr = (caddr_t) &D;
511         ret = ioctl(file, Q_QUOTACTL, &command);
512         close(file);
513 #else
514         DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name));
515         ret = quotactl(Q_GETQUOTA, name, euser_id, &D);
516 #endif
517
518         restore_re_uid();
519
520         if (ret < 0) {
521                 DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n", strerror(errno) ));
522
523 #if defined(SUNOS5) && defined(VXFS_QUOTA)
524                 /* If normal quotactl() fails, try vxfs private calls */
525                 set_effective_uid(euser_id);
526                 DEBUG(5,("disk_quotas: mount type \"%s\"\n", mnt.mnt_fstype));
527                 if ( 0 == strcmp ( mnt.mnt_fstype, "vxfs" )) {
528                         BOOL retval;
529                         retval = disk_quotas_vxfs(name, path, bsize, dfree, dsize);
530                         return(retval);
531                 }
532 #else
533                 return(False);
534 #endif
535         }
536
537         /* If softlimit is zero, set it equal to hardlimit.
538          */
539   
540         if (D.dqb_bsoftlimit==0)
541                 D.dqb_bsoftlimit = D.dqb_bhardlimit;
542
543         /* Use softlimit to determine disk space. A user exceeding the quota is told
544          * that there's no space left. Writes might actually work for a bit if the
545          * hardlimit is set higher than softlimit. Effectively the disk becomes
546          * made of rubber latex and begins to expand to accommodate the user :-)
547          */
548
549         if (D.dqb_bsoftlimit==0)
550                 return(False);
551         *bsize = DEV_BSIZE;
552         *dsize = D.dqb_bsoftlimit;
553
554         if (D.dqb_curblocks > D.dqb_bsoftlimit) {
555                 *dfree = 0;
556                 *dsize = D.dqb_curblocks;
557         } else
558                 *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
559       
560         DEBUG(5,("disk_quotas for path \"%s\" returning  bsize %.0f, dfree %.0f, dsize %.0f\n",
561                 path,(double)*bsize,(double)*dfree,(double)*dsize));
562
563         return(True);
564 }
565
566
567 #elif defined(OSF1)
568 #include <ufs/quota.h>
569
570 /****************************************************************************
571 try to get the disk space from disk quotas - OSF1 version
572 ****************************************************************************/
573
574 BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
575 {
576   int r, save_errno;
577   struct dqblk D;
578   SMB_STRUCT_STAT S;
579   uid_t euser_id;
580
581   /*
582    * This code presumes that OSF1 will only
583    * give out quota info when the real uid 
584    * matches the effective uid. JRA.
585    */
586   euser_id = geteuid();
587   save_re_uid();
588   if (set_re_uid() != 0) return False;
589
590   r= quotactl(path,QCMD(Q_GETQUOTA, USRQUOTA),euser_id,(char *) &D);
591   if (r) {
592      save_errno = errno;
593   }
594
595   restore_re_uid();
596
597   *bsize = DEV_BSIZE;
598
599   if (r)
600   {
601       if (save_errno == EDQUOT)   /* disk quota exceeded */
602       {
603          *dfree = 0;
604          *dsize = D.dqb_curblocks;
605          return (True);
606       }
607       else
608          return (False);  
609   }
610
611   /* If softlimit is zero, set it equal to hardlimit.
612    */
613
614   if (D.dqb_bsoftlimit==0)
615     D.dqb_bsoftlimit = D.dqb_bhardlimit;
616
617   /* Use softlimit to determine disk space, except when it has been exceeded */
618
619   if (D.dqb_bsoftlimit==0)
620     return(False);
621
622   if ((D.dqb_curblocks>D.dqb_bsoftlimit)) {
623     *dfree = 0;
624     *dsize = D.dqb_curblocks;
625   } else {
626     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
627     *dsize = D.dqb_bsoftlimit;
628   }
629   return (True);
630 }
631
632 #elif defined (IRIX6)
633 /****************************************************************************
634 try to get the disk space from disk quotas (IRIX 6.2 version)
635 ****************************************************************************/
636
637 #include <sys/quota.h>
638 #include <mntent.h>
639
640 BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
641 {
642   uid_t euser_id;
643   int r;
644   struct dqblk D;
645   struct fs_disk_quota        F;
646   SMB_STRUCT_STAT S;
647   FILE *fp;
648   struct mntent *mnt;
649   SMB_DEV_T devno;
650   int found;
651   
652   /* find the block device file */
653   
654   if ( sys_stat(path, &S) == -1 ) {
655     return(False) ;
656   }
657
658   devno = S.st_dev ;
659   
660   fp = setmntent(MOUNTED,"r");
661   found = False ;
662   
663   while ((mnt = getmntent(fp))) {
664     if ( sys_stat(mnt->mnt_dir,&S) == -1 )
665       continue ;
666     if (S.st_dev == devno) {
667       found = True ;
668       break ;
669     }
670   }
671   endmntent(fp) ;
672   
673   if (!found) {
674     return(False);
675   }
676
677   euser_id=geteuid();
678   save_re_uid();
679   set_effective_uid(0);  
680
681   /* Use softlimit to determine disk space, except when it has been exceeded */
682
683   *bsize = 512;
684
685   if ( 0 == strcmp ( mnt->mnt_type, "efs" ))
686   {
687     r=quotactl (Q_GETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &D);
688
689     restore_re_uid();
690
691     if (r==-1)
692       return(False);
693         
694     /* Use softlimit to determine disk space, except when it has been exceeded */
695     if (
696         (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) ||
697         (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) ||
698         (D.dqb_fsoftlimit && D.dqb_curfiles>=D.dqb_fsoftlimit) ||
699         (D.dqb_fhardlimit && D.dqb_curfiles>=D.dqb_fhardlimit)
700        )
701     {
702       *dfree = 0;
703       *dsize = D.dqb_curblocks;
704     }
705     else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0)
706     {
707       return(False);
708     }
709     else 
710     {
711       *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
712       *dsize = D.dqb_bsoftlimit;
713     }
714
715   }
716   else if ( 0 == strcmp ( mnt->mnt_type, "xfs" ))
717   {
718     r=quotactl (Q_XGETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &F);
719
720     restore_re_uid();
721
722     if (r==-1)
723       return(False);
724         
725     /* Use softlimit to determine disk space, except when it has been exceeded */
726     if (
727         (F.d_blk_softlimit && F.d_bcount>=F.d_blk_softlimit) ||
728         (F.d_blk_hardlimit && F.d_bcount>=F.d_blk_hardlimit) ||
729         (F.d_ino_softlimit && F.d_icount>=F.d_ino_softlimit) ||
730         (F.d_ino_hardlimit && F.d_icount>=F.d_ino_hardlimit)
731        )
732     {
733       *dfree = 0;
734       *dsize = F.d_bcount;
735     }
736     else if (F.d_blk_softlimit==0 && F.d_blk_hardlimit==0)
737     {
738       return(False);
739     }
740     else 
741     {
742       *dfree = (F.d_blk_softlimit - F.d_bcount);
743       *dsize = F.d_blk_softlimit;
744     }
745
746   }
747   else
748   {
749           restore_re_uid();
750           return(False);
751   }
752
753   return (True);
754
755 }
756
757 #else
758
759 #if    defined(__FreeBSD__) || defined(__OpenBSD__)
760 #include <ufs/ufs/quota.h>
761 #include <machine/param.h>
762 #elif         AIX
763 /* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
764 #include <jfs/quota.h>
765 /* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
766 #define dqb_curfiles dqb_curinodes
767 #define dqb_fhardlimit dqb_ihardlimit
768 #define dqb_fsoftlimit dqb_isoftlimit
769 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
770 #include <sys/quota.h>
771 #include <devnm.h>
772 #endif
773
774 /****************************************************************************
775 try to get the disk space from disk quotas - default version
776 ****************************************************************************/
777
778 BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
779 {
780   int r;
781   struct dqblk D;
782   uid_t euser_id;
783 #if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__)
784   char dev_disk[256];
785   SMB_STRUCT_STAT S;
786   /* find the block device file */
787   if ((sys_stat(path, &S)<0) ||
788       (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False);
789 #endif
790
791   euser_id = geteuid();
792
793 #ifdef HPUX
794   /* for HPUX, real uid must be same as euid to execute quotactl for euid */
795   save_re_uid();
796   if (set_re_uid() != 0) return False;
797   
798   r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
799
800   restore_re_uid();
801 #else 
802 #if defined(__FreeBSD__) || defined(__OpenBSD__)
803   {
804     /* FreeBSD patches from Marty Moll <martym@arbor.edu> */
805     gid_t egrp_id;
806  
807     save_re_uid();
808     set_effective_uid(0);
809
810     egrp_id = getegid();
811     r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
812
813     /* As FreeBSD has group quotas, if getting the user
814        quota fails, try getting the group instead. */
815     if (r) {
816             r= quotactl(path,QCMD(Q_GETQUOTA,GRPQUOTA),egrp_id,(char *) &D);
817     }
818
819     restore_re_uid();
820   }
821 #elif defined(AIX)
822   /* AIX has both USER and GROUP quotas: 
823      Get the USER quota (ohnielse@fysik.dtu.dk) */
824   r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
825 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
826   r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
827 #endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
828 #endif /* HPUX */
829
830   /* Use softlimit to determine disk space, except when it has been exceeded */
831 #if defined(__FreeBSD__) || defined(__OpenBSD__)
832   *bsize = DEV_BSIZE;
833 #else /* !__FreeBSD__ && !__OpenBSD__ */
834   *bsize = 1024;
835 #endif /*!__FreeBSD__ && !__OpenBSD__ */
836
837   if (r)
838     {
839       if (errno == EDQUOT) 
840         {
841           *dfree =0;
842           *dsize =D.dqb_curblocks;
843           return (True);
844         }
845       else return(False);
846     }
847
848   /* If softlimit is zero, set it equal to hardlimit.
849    */
850
851   if (D.dqb_bsoftlimit==0)
852     D.dqb_bsoftlimit = D.dqb_bhardlimit;
853
854   if (D.dqb_bsoftlimit==0)
855     return(False);
856   /* Use softlimit to determine disk space, except when it has been exceeded */
857   if ((D.dqb_curblocks>D.dqb_bsoftlimit)
858 #if !defined(__FreeBSD__) && !defined(__OpenBSD__)
859 ||((D.dqb_curfiles>D.dqb_fsoftlimit) && (D.dqb_fsoftlimit != 0))
860 #endif
861     ) {
862       *dfree = 0;
863       *dsize = D.dqb_curblocks;
864     }
865   else {
866     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
867     *dsize = D.dqb_bsoftlimit;
868   }
869   return (True);
870 }
871
872 #endif
873
874 #if defined(VXFS_QUOTA)
875
876 /****************************************************************************
877 Try to get the disk space from Veritas disk quotas.
878     David Lee <T.D.Lee@durham.ac.uk> August 1999.
879
880 Background assumptions:
881     Potentially under many Operating Systems.  Initially Solaris 2.
882
883     My guess is that Veritas is largely, though not entirely,
884     independent of OS.  So I have separated it out.
885
886     There may be some details.  For example, OS-specific "include" files.
887
888     It is understood that HPUX 10 somehow gets Veritas quotas without
889     any special effort; if so, this routine need not be compiled in.
890         Dirk De Wachter <Dirk.DeWachter@rug.ac.be>
891
892 Warning:
893     It is understood that Veritas do not publicly support this ioctl interface.
894     Rather their preference would be for the user (us) to call the native
895     OS and then for the OS itself to call through to the VxFS filesystem.
896     Presumably HPUX 10, see above, does this.
897
898 Hints for porting:
899     Add your OS to "IFLIST" below.
900     Get it to compile successfully:
901         Almost certainly "include"s require attention: see SUNOS5.
902     In the main code above, arrange for it to be called: see SUNOS5.
903     Test!
904     
905 ****************************************************************************/
906
907 /* "IFLIST"
908  * This "if" is a list of ports:
909  *      if defined(OS1) || defined(OS2) || ...
910  */
911 #if defined(SUNOS5)
912
913 #if defined(SUNOS5)
914 #include <sys/fs/vx_solaris.h>
915 #endif
916 #include <sys/fs/vx_machdep.h>
917 #include <sys/fs/vx_layout.h>
918 #include <sys/fs/vx_quota.h>
919 #include <sys/fs/vx_aioctl.h>
920 #include <sys/fs/vx_ioctl.h>
921
922 BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
923 {
924   uid_t user_id, euser_id;
925   int ret;
926   struct vx_dqblk D;
927   struct vx_quotctl quotabuf;
928   struct vx_genioctl genbuf;
929   pstring qfname;
930   int file;
931
932   /*
933    * "name" may or may not include a trailing "/quotas".
934    * Arranging consistency of calling here in "quotas.c" may not be easy and
935    * it might be easier to examine and adjust it here.
936    * Fortunately, VxFS seems not to mind at present.
937    */
938   pstrcpy(qfname, name) ;
939   /* pstrcat(qfname, "/quotas") ; */    /* possibly examine and adjust "name" */
940
941   euser_id = geteuid();
942   set_effective_uid(0);
943
944   DEBUG(5,("disk_quotas: looking for VxFS quotas file \"%s\"\n", qfname));
945   if((file=sys_open(qfname, O_RDONLY,0))<0) {
946     set_effective_uid(euser_id);
947     return(False);
948   }
949   genbuf.ioc_cmd = VX_QUOTACTL;
950   genbuf.ioc_up = (void *) &quotabuf;
951
952   quotabuf.cmd = VX_GETQUOTA;
953   quotabuf.uid = euser_id;
954   quotabuf.addr = (caddr_t) &D;
955   ret = ioctl(file, VX_ADMIN_IOCTL, &genbuf);
956   close(file);
957
958   set_effective_uid(euser_id);
959
960   if (ret < 0) {
961     DEBUG(5,("disk_quotas ioctl (VxFS) failed. Error = %s\n", strerror(errno) ));
962     return(False);
963   }
964
965   /* If softlimit is zero, set it equal to hardlimit.
966    */
967
968   if (D.dqb_bsoftlimit==0)
969     D.dqb_bsoftlimit = D.dqb_bhardlimit;
970
971   /* Use softlimit to determine disk space. A user exceeding the quota is told
972    * that there's no space left. Writes might actually work for a bit if the
973    * hardlimit is set higher than softlimit. Effectively the disk becomes
974    * made of rubber latex and begins to expand to accommodate the user :-)
975    */
976   DEBUG(5,("disk_quotas for path \"%s\" block c/s/h %ld/%ld/%ld; file c/s/h %ld/%ld/%ld\n",
977          path, D.dqb_curblocks, D.dqb_bsoftlimit, D.dqb_bhardlimit,
978          D.dqb_curfiles, D.dqb_fsoftlimit, D.dqb_fhardlimit));
979
980   if (D.dqb_bsoftlimit==0)
981     return(False);
982   *bsize = DEV_BSIZE;
983   *dsize = D.dqb_bsoftlimit;
984
985   if (D.dqb_curblocks > D.dqb_bsoftlimit) {
986      *dfree = 0;
987      *dsize = D.dqb_curblocks;
988   } else
989     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
990       
991   DEBUG(5,("disk_quotas for path \"%s\" returning  bsize %.0f, dfree %.0f, dsize %.0f\n",
992          path,(double)*bsize,(double)*dfree,(double)*dsize));
993
994   return(True);
995 }
996
997 #endif /* SUNOS5 || ... */
998
999 #endif /* VXFS_QUOTA */