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