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