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