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