small patch to makesure we fallback to <xfs/xqm.h> if <linux/xqm.h> doesn't exist...
[metze/samba/wip.git] / source3 / smbd / quotas.c
1 /* 
2    Unix SMB/CIFS implementation.
3    support for quotas
4    Copyright (C) Andrew Tridgell 1992-1998
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21
22 /* 
23  * This is one of the most system dependent parts of Samba, and its
24  * done a litle differently. Each system has its own way of doing 
25  * things :-(
26  */
27
28 #include "includes.h"
29
30 #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(sizeof(char) * len );
417         if (!cutstr)
418                 return False;
419
420         host = strncat(cutstr,mnttype, sizeof(char) * len );
421         DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr));
422         DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype));
423         testpath=strchr_m(mnttype, ':');
424         args.gqa_pathp = testpath+1;
425         args.gqa_uid = uid;
426
427         DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host, RQUOTAPROG, RQUOTAVERS, "udp"));
428
429         if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) == NULL) {
430                 ret = False;
431                 goto out;
432         }
433
434         clnt->cl_auth = authunix_create_default();
435         DEBUG(9,("nfs_quotas: auth_success\n"));
436
437         clnt_stat=clnt_call(clnt, RQUOTAPROC_GETQUOTA, xdr_getquota_args, (caddr_t)&args, xdr_getquota_rslt, (caddr_t)&gqr, timeout);
438
439         if (clnt_stat != RPC_SUCCESS) {
440                 DEBUG(9,("nfs_quotas: clnt_call fail\n"));
441                 ret = False;
442                 goto out;
443         }
444
445         /* 
446          * quotastat returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is
447          * no quota set, and 3 if no permission to get the quota.  If 0 or 3 return
448          * something sensible.
449          */   
450
451         switch ( quotastat ) {
452         case 0:
453                 DEBUG(9,("nfs_quotas: Remote Quotas Failed!  Error \"%i\" \n", quotastat ));
454                 ret = False;
455                 goto out;
456
457         case 1:
458                 DEBUG(9,("nfs_quotas: Good quota data\n"));
459                 D.dqb_bsoftlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit;
460                 D.dqb_bhardlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit;
461                 D.dqb_curblocks = gqr.getquota_rslt_u.gqr_rquota.rq_curblocks;
462                 break;
463
464         case 2:
465         case 3:
466                 D.dqb_bsoftlimit = 1;
467                 D.dqb_curblocks = 1;
468                 DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", quotastat ));
469                 break;
470
471         default:
472                 DEBUG(9,("nfs_quotas: Remote Quotas Questionable!  Error \"%i\" \n", quotastat ));
473                 break;
474         }
475
476         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",
477                         quotastat,
478                         gqr.getquota_rslt_u.gqr_rquota.rq_bsize,
479                         gqr.getquota_rslt_u.gqr_rquota.rq_active,
480                         gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit,
481                         gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit,
482                         gqr.getquota_rslt_u.gqr_rquota.rq_curblocks));
483
484         *bsize = gqr.getquota_rslt_u.gqr_rquota.rq_bsize;
485         *dsize = D.dqb_bsoftlimit;
486
487         if (D.dqb_curblocks == D.dqb_curblocks == 1)
488                 *bsize = 512;
489
490         if (D.dqb_curblocks > D.dqb_bsoftlimit) {
491                 *dfree = 0;
492                 *dsize = D.dqb_curblocks;
493         } else
494                 *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
495
496   out:
497
498         if (clnt) {
499                 if (clnt->cl_auth)
500                         auth_destroy(clnt->cl_auth);
501                 clnt_destroy(clnt);
502         }
503
504         DEBUG(5,("nfs_quotas: For path \"%s\" returning  bsize %.0f, dfree %.0f, dsize %.0f\n",args.gqa_pathp,(double)*bsize,(double)*dfree,(double)*dsize));
505
506         SAFE_FREE(cutstr);
507         DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
508         return ret;
509 }
510 #endif
511
512 /****************************************************************************
513 try to get the disk space from disk quotas (SunOS & Solaris2 version)
514 Quota code by Peter Urbanec (amiga@cse.unsw.edu.au).
515 ****************************************************************************/
516
517 BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
518 {
519         uid_t euser_id;
520         int ret;
521         struct dqblk D;
522 #if defined(SUNOS5)
523         struct quotctl command;
524         int file;
525         static struct mnttab mnt;
526         static pstring name;
527         pstring devopt;
528 #else /* SunOS4 */
529         struct mntent *mnt;
530         static pstring name;
531 #endif
532         FILE *fd;
533         SMB_STRUCT_STAT sbuf;
534         SMB_DEV_T devno ;
535         static SMB_DEV_T devno_cached = 0 ;
536         static int found ;
537
538         euser_id = geteuid();
539   
540         if ( sys_stat(path,&sbuf) == -1 )
541                 return(False) ;
542   
543         devno = sbuf.st_dev ;
544         DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n", path,(unsigned int)devno));
545         if ( devno != devno_cached ) {
546                 devno_cached = devno ;
547 #if defined(SUNOS5)
548                 if ((fd = sys_fopen(MNTTAB, "r")) == NULL)
549                         return(False) ;
550     
551                 found = False ;
552                 slprintf(devopt, sizeof(devopt) - 1, "dev=%x", (unsigned int)devno);
553                 while (getmntent(fd, &mnt) == 0) {
554                         if( !hasmntopt(&mnt, devopt) )
555                                 continue;
556
557                         DEBUG(5,("disk_quotas: testing \"%s\" %s\n", mnt.mnt_mountp,devopt));
558
559                         /* quotas are only on vxfs, UFS or NFS */
560                         if ( strcmp( mnt.mnt_fstype, MNTTYPE_UFS ) == 0 ||
561                                 strcmp( mnt.mnt_fstype, "nfs" ) == 0    ||
562                                 strcmp( mnt.mnt_fstype, "vxfs" ) == 0  ) { 
563                                         found = True ;
564                                         break;
565                         }
566                 }
567     
568                 pstrcpy(name,mnt.mnt_mountp) ;
569                 pstrcat(name,"/quotas") ;
570                 fclose(fd) ;
571 #else /* SunOS4 */
572                 if ((fd = setmntent(MOUNTED, "r")) == NULL)
573                         return(False) ;
574     
575                 found = False ;
576                 while ((mnt = getmntent(fd)) != NULL) {
577                         if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
578                                 continue ;
579                         DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n", mnt->mnt_dir,(unsigned int)sbuf.st_dev));
580                         if (sbuf.st_dev == devno) {
581                                 found = True ;
582                                 break;
583                         }
584                 }
585     
586                 pstrcpy(name,mnt->mnt_fsname) ;
587                 endmntent(fd) ;
588 #endif
589         }
590
591         if ( ! found )
592                 return(False) ;
593
594         save_re_uid();
595         set_effective_uid(0);
596
597 #if defined(SUNOS5)
598         if ( strcmp( mnt.mnt_fstype, "nfs" ) == 0) {
599                 BOOL retval;
600                 DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n", mnt.mnt_special));
601                 retval = nfs_quotas(mnt.mnt_special, euser_id, bsize, dfree, dsize);
602                 restore_re_uid();
603                 return retval;
604         }
605
606         DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name));
607         if((file=sys_open(name, O_RDONLY,0))<0) {
608                 restore_re_uid();
609                 return(False);
610         }
611         command.op = Q_GETQUOTA;
612         command.uid = euser_id;
613         command.addr = (caddr_t) &D;
614         ret = ioctl(file, Q_QUOTACTL, &command);
615         close(file);
616 #else
617         DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name));
618         ret = quotactl(Q_GETQUOTA, name, euser_id, &D);
619 #endif
620
621         restore_re_uid();
622
623         if (ret < 0) {
624                 DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n", strerror(errno) ));
625
626 #if defined(SUNOS5) && defined(VXFS_QUOTA)
627                 /* If normal quotactl() fails, try vxfs private calls */
628                 set_effective_uid(euser_id);
629                 DEBUG(5,("disk_quotas: mount type \"%s\"\n", mnt.mnt_fstype));
630                 if ( 0 == strcmp ( mnt.mnt_fstype, "vxfs" )) {
631                         BOOL retval;
632                         retval = disk_quotas_vxfs(name, path, bsize, dfree, dsize);
633                         return(retval);
634                 }
635 #else
636                 return(False);
637 #endif
638         }
639
640         /* If softlimit is zero, set it equal to hardlimit.
641          */
642   
643         if (D.dqb_bsoftlimit==0)
644                 D.dqb_bsoftlimit = D.dqb_bhardlimit;
645
646         /* Use softlimit to determine disk space. A user exceeding the quota is told
647          * that there's no space left. Writes might actually work for a bit if the
648          * hardlimit is set higher than softlimit. Effectively the disk becomes
649          * made of rubber latex and begins to expand to accommodate the user :-)
650          */
651
652         if (D.dqb_bsoftlimit==0)
653                 return(False);
654         *bsize = DEV_BSIZE;
655         *dsize = D.dqb_bsoftlimit;
656
657         if (D.dqb_curblocks > D.dqb_bsoftlimit) {
658                 *dfree = 0;
659                 *dsize = D.dqb_curblocks;
660         } else
661                 *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
662       
663         DEBUG(5,("disk_quotas for path \"%s\" returning  bsize %.0f, dfree %.0f, dsize %.0f\n",
664                 path,(double)*bsize,(double)*dfree,(double)*dsize));
665
666         return(True);
667 }
668
669
670 #elif defined(OSF1)
671 #include <ufs/quota.h>
672
673 /****************************************************************************
674 try to get the disk space from disk quotas - OSF1 version
675 ****************************************************************************/
676
677 BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
678 {
679   int r, save_errno;
680   struct dqblk D;
681   SMB_STRUCT_STAT S;
682   uid_t euser_id;
683
684   /*
685    * This code presumes that OSF1 will only
686    * give out quota info when the real uid 
687    * matches the effective uid. JRA.
688    */
689   euser_id = geteuid();
690   save_re_uid();
691   if (set_re_uid() != 0) return False;
692
693   r= quotactl(path,QCMD(Q_GETQUOTA, USRQUOTA),euser_id,(char *) &D);
694   if (r) {
695      save_errno = errno;
696   }
697
698   restore_re_uid();
699
700   *bsize = DEV_BSIZE;
701
702   if (r)
703   {
704       if (save_errno == EDQUOT)   /* disk quota exceeded */
705       {
706          *dfree = 0;
707          *dsize = D.dqb_curblocks;
708          return (True);
709       }
710       else
711          return (False);  
712   }
713
714   /* If softlimit is zero, set it equal to hardlimit.
715    */
716
717   if (D.dqb_bsoftlimit==0)
718     D.dqb_bsoftlimit = D.dqb_bhardlimit;
719
720   /* Use softlimit to determine disk space, except when it has been exceeded */
721
722   if (D.dqb_bsoftlimit==0)
723     return(False);
724
725   if ((D.dqb_curblocks>D.dqb_bsoftlimit)) {
726     *dfree = 0;
727     *dsize = D.dqb_curblocks;
728   } else {
729     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
730     *dsize = D.dqb_bsoftlimit;
731   }
732   return (True);
733 }
734
735 #elif defined (IRIX6)
736 /****************************************************************************
737 try to get the disk space from disk quotas (IRIX 6.2 version)
738 ****************************************************************************/
739
740 #include <sys/quota.h>
741 #include <mntent.h>
742
743 BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
744 {
745   uid_t euser_id;
746   int r;
747   struct dqblk D;
748   struct fs_disk_quota        F;
749   SMB_STRUCT_STAT S;
750   FILE *fp;
751   struct mntent *mnt;
752   SMB_DEV_T devno;
753   int found;
754   
755   /* find the block device file */
756   
757   if ( sys_stat(path, &S) == -1 ) {
758     return(False) ;
759   }
760
761   devno = S.st_dev ;
762   
763   fp = setmntent(MOUNTED,"r");
764   found = False ;
765   
766   while ((mnt = getmntent(fp))) {
767     if ( sys_stat(mnt->mnt_dir,&S) == -1 )
768       continue ;
769     if (S.st_dev == devno) {
770       found = True ;
771       break ;
772     }
773   }
774   endmntent(fp) ;
775   
776   if (!found) {
777     return(False);
778   }
779
780   euser_id=geteuid();
781   save_re_uid();
782   set_effective_uid(0);  
783
784   /* Use softlimit to determine disk space, except when it has been exceeded */
785
786   *bsize = 512;
787
788   if ( 0 == strcmp ( mnt->mnt_type, "efs" ))
789   {
790     r=quotactl (Q_GETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &D);
791
792     restore_re_uid();
793
794     if (r==-1)
795       return(False);
796         
797     /* Use softlimit to determine disk space, except when it has been exceeded */
798     if (
799         (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) ||
800         (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) ||
801         (D.dqb_fsoftlimit && D.dqb_curfiles>=D.dqb_fsoftlimit) ||
802         (D.dqb_fhardlimit && D.dqb_curfiles>=D.dqb_fhardlimit)
803        )
804     {
805       *dfree = 0;
806       *dsize = D.dqb_curblocks;
807     }
808     else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0)
809     {
810       return(False);
811     }
812     else 
813     {
814       *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
815       *dsize = D.dqb_bsoftlimit;
816     }
817
818   }
819   else if ( 0 == strcmp ( mnt->mnt_type, "xfs" ))
820   {
821     r=quotactl (Q_XGETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &F);
822
823     restore_re_uid();
824
825     if (r==-1)
826       return(False);
827         
828     /* Use softlimit to determine disk space, except when it has been exceeded */
829     if (
830         (F.d_blk_softlimit && F.d_bcount>=F.d_blk_softlimit) ||
831         (F.d_blk_hardlimit && F.d_bcount>=F.d_blk_hardlimit) ||
832         (F.d_ino_softlimit && F.d_icount>=F.d_ino_softlimit) ||
833         (F.d_ino_hardlimit && F.d_icount>=F.d_ino_hardlimit)
834        )
835     {
836       *dfree = 0;
837       *dsize = F.d_bcount;
838     }
839     else if (F.d_blk_softlimit==0 && F.d_blk_hardlimit==0)
840     {
841       return(False);
842     }
843     else 
844     {
845       *dfree = (F.d_blk_softlimit - F.d_bcount);
846       *dsize = F.d_blk_softlimit;
847     }
848
849   }
850   else
851   {
852           restore_re_uid();
853           return(False);
854   }
855
856   return (True);
857
858 }
859
860 #else
861
862 #if    defined(__FreeBSD__) || defined(__OpenBSD__)
863 #include <ufs/ufs/quota.h>
864 #include <machine/param.h>
865 #elif         AIX
866 /* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
867 #include <jfs/quota.h>
868 /* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
869 #define dqb_curfiles dqb_curinodes
870 #define dqb_fhardlimit dqb_ihardlimit
871 #define dqb_fsoftlimit dqb_isoftlimit
872 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
873 #include <sys/quota.h>
874 #include <devnm.h>
875 #endif
876
877 /****************************************************************************
878 try to get the disk space from disk quotas - default version
879 ****************************************************************************/
880
881 BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
882 {
883   int r;
884   struct dqblk D;
885   uid_t euser_id;
886 #if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__)
887   char dev_disk[256];
888   SMB_STRUCT_STAT S;
889   /* find the block device file */
890   if ((sys_stat(path, &S)<0) ||
891       (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False);
892 #endif
893
894   euser_id = geteuid();
895
896 #ifdef HPUX
897   /* for HPUX, real uid must be same as euid to execute quotactl for euid */
898   save_re_uid();
899   if (set_re_uid() != 0) return False;
900   
901   r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
902
903   restore_re_uid();
904 #else 
905 #if defined(__FreeBSD__) || defined(__OpenBSD__)
906   {
907     /* FreeBSD patches from Marty Moll <martym@arbor.edu> */
908     gid_t egrp_id;
909  
910     save_re_uid();
911     set_effective_uid(0);
912
913     egrp_id = getegid();
914     r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
915
916     /* As FreeBSD has group quotas, if getting the user
917        quota fails, try getting the group instead. */
918     if (r) {
919             r= quotactl(path,QCMD(Q_GETQUOTA,GRPQUOTA),egrp_id,(char *) &D);
920     }
921
922     restore_re_uid();
923   }
924 #elif defined(AIX)
925   /* AIX has both USER and GROUP quotas: 
926      Get the USER quota (ohnielse@fysik.dtu.dk) */
927   r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
928 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
929   r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
930 #endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
931 #endif /* HPUX */
932
933   /* Use softlimit to determine disk space, except when it has been exceeded */
934 #if defined(__FreeBSD__) || defined(__OpenBSD__)
935   *bsize = DEV_BSIZE;
936 #else /* !__FreeBSD__ && !__OpenBSD__ */
937   *bsize = 1024;
938 #endif /*!__FreeBSD__ && !__OpenBSD__ */
939
940   if (r)
941     {
942       if (errno == EDQUOT) 
943         {
944           *dfree =0;
945           *dsize =D.dqb_curblocks;
946           return (True);
947         }
948       else return(False);
949     }
950
951   /* If softlimit is zero, set it equal to hardlimit.
952    */
953
954   if (D.dqb_bsoftlimit==0)
955     D.dqb_bsoftlimit = D.dqb_bhardlimit;
956
957   if (D.dqb_bsoftlimit==0)
958     return(False);
959   /* Use softlimit to determine disk space, except when it has been exceeded */
960   if ((D.dqb_curblocks>D.dqb_bsoftlimit)
961 #if !defined(__FreeBSD__) && !defined(__OpenBSD__)
962 ||((D.dqb_curfiles>D.dqb_fsoftlimit) && (D.dqb_fsoftlimit != 0))
963 #endif
964     ) {
965       *dfree = 0;
966       *dsize = D.dqb_curblocks;
967     }
968   else {
969     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
970     *dsize = D.dqb_bsoftlimit;
971   }
972   return (True);
973 }
974
975 #endif
976
977 #if defined(VXFS_QUOTA)
978
979 /****************************************************************************
980 Try to get the disk space from Veritas disk quotas.
981     David Lee <T.D.Lee@durham.ac.uk> August 1999.
982
983 Background assumptions:
984     Potentially under many Operating Systems.  Initially Solaris 2.
985
986     My guess is that Veritas is largely, though not entirely,
987     independent of OS.  So I have separated it out.
988
989     There may be some details.  For example, OS-specific "include" files.
990
991     It is understood that HPUX 10 somehow gets Veritas quotas without
992     any special effort; if so, this routine need not be compiled in.
993         Dirk De Wachter <Dirk.DeWachter@rug.ac.be>
994
995 Warning:
996     It is understood that Veritas do not publicly support this ioctl interface.
997     Rather their preference would be for the user (us) to call the native
998     OS and then for the OS itself to call through to the VxFS filesystem.
999     Presumably HPUX 10, see above, does this.
1000
1001 Hints for porting:
1002     Add your OS to "IFLIST" below.
1003     Get it to compile successfully:
1004         Almost certainly "include"s require attention: see SUNOS5.
1005     In the main code above, arrange for it to be called: see SUNOS5.
1006     Test!
1007     
1008 ****************************************************************************/
1009
1010 /* "IFLIST"
1011  * This "if" is a list of ports:
1012  *      if defined(OS1) || defined(OS2) || ...
1013  */
1014 #if defined(SUNOS5)
1015
1016 #if defined(SUNOS5)
1017 #include <sys/fs/vx_solaris.h>
1018 #endif
1019 #include <sys/fs/vx_machdep.h>
1020 #include <sys/fs/vx_layout.h>
1021 #include <sys/fs/vx_quota.h>
1022 #include <sys/fs/vx_aioctl.h>
1023 #include <sys/fs/vx_ioctl.h>
1024
1025 BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
1026 {
1027   uid_t user_id, euser_id;
1028   int ret;
1029   struct vx_dqblk D;
1030   struct vx_quotctl quotabuf;
1031   struct vx_genioctl genbuf;
1032   pstring qfname;
1033   int file;
1034
1035   /*
1036    * "name" may or may not include a trailing "/quotas".
1037    * Arranging consistency of calling here in "quotas.c" may not be easy and
1038    * it might be easier to examine and adjust it here.
1039    * Fortunately, VxFS seems not to mind at present.
1040    */
1041   pstrcpy(qfname, name) ;
1042   /* pstrcat(qfname, "/quotas") ; */    /* possibly examine and adjust "name" */
1043
1044   euser_id = geteuid();
1045   set_effective_uid(0);
1046
1047   DEBUG(5,("disk_quotas: looking for VxFS quotas file \"%s\"\n", qfname));
1048   if((file=sys_open(qfname, O_RDONLY,0))<0) {
1049     set_effective_uid(euser_id);
1050     return(False);
1051   }
1052   genbuf.ioc_cmd = VX_QUOTACTL;
1053   genbuf.ioc_up = (void *) &quotabuf;
1054
1055   quotabuf.cmd = VX_GETQUOTA;
1056   quotabuf.uid = euser_id;
1057   quotabuf.addr = (caddr_t) &D;
1058   ret = ioctl(file, VX_ADMIN_IOCTL, &genbuf);
1059   close(file);
1060
1061   set_effective_uid(euser_id);
1062
1063   if (ret < 0) {
1064     DEBUG(5,("disk_quotas ioctl (VxFS) failed. Error = %s\n", strerror(errno) ));
1065     return(False);
1066   }
1067
1068   /* If softlimit is zero, set it equal to hardlimit.
1069    */
1070
1071   if (D.dqb_bsoftlimit==0)
1072     D.dqb_bsoftlimit = D.dqb_bhardlimit;
1073
1074   /* Use softlimit to determine disk space. A user exceeding the quota is told
1075    * that there's no space left. Writes might actually work for a bit if the
1076    * hardlimit is set higher than softlimit. Effectively the disk becomes
1077    * made of rubber latex and begins to expand to accommodate the user :-)
1078    */
1079   DEBUG(5,("disk_quotas for path \"%s\" block c/s/h %ld/%ld/%ld; file c/s/h %ld/%ld/%ld\n",
1080          path, D.dqb_curblocks, D.dqb_bsoftlimit, D.dqb_bhardlimit,
1081          D.dqb_curfiles, D.dqb_fsoftlimit, D.dqb_fhardlimit));
1082
1083   if (D.dqb_bsoftlimit==0)
1084     return(False);
1085   *bsize = DEV_BSIZE;
1086   *dsize = D.dqb_bsoftlimit;
1087
1088   if (D.dqb_curblocks > D.dqb_bsoftlimit) {
1089      *dfree = 0;
1090      *dsize = D.dqb_curblocks;
1091   } else
1092     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
1093       
1094   DEBUG(5,("disk_quotas for path \"%s\" returning  bsize %.0f, dfree %.0f, dsize %.0f\n",
1095          path,(double)*bsize,(double)*dfree,(double)*dsize));
1096
1097   return(True);
1098 }
1099
1100 #endif /* SUNOS5 || ... */
1101
1102 #endif /* VXFS_QUOTA */