Added the same open()/fopen()/creat()/mmap() -> sys_XXX calls.
[tprouty/samba.git] / source / smbd / quotas.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    support for quotas
5    Copyright (C) Andrew Tridgell 1992-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22
23 /* 
24  * This is one of the most system dependent parts of Samba, and its
25  * done a litle differently. Each system has its own way of doing 
26  * things :-(
27  */
28
29 #include "includes.h"
30
31 extern int DEBUGLEVEL;
32
33 #ifdef LINUX
34
35 #include <sys/types.h>
36 #include <asm/types.h>
37 #include <sys/quota.h>
38
39 #include <mntent.h>
40 #include <linux/unistd.h>
41
42 _syscall4(int, quotactl, int, cmd, const char *, special, int, id, caddr_t, addr);
43
44 /****************************************************************************
45 try to get the disk space from disk quotas (LINUX version)
46 ****************************************************************************/
47
48 BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
49 {
50   uid_t euser_id;
51   int r;
52   struct dqblk D;
53   SMB_STRUCT_STAT S;
54   FILE *fp;
55   struct mntent *mnt;
56   SMB_DEV_T devno;
57   int found;
58   
59   /* find the block device file */
60   
61   if ( sys_stat(path, &S) == -1 ) {
62     return(False) ;
63   }
64
65   devno = S.st_dev ;
66   
67   fp = setmntent(MOUNTED,"r");
68   found = False ;
69   
70   while ((mnt = getmntent(fp))) {
71     if ( sys_stat(mnt->mnt_dir,&S) == -1 )
72       continue ;
73     if (S.st_dev == devno) {
74       found = True ;
75       break ;
76     }
77   }
78   endmntent(fp) ;
79   
80   if (!found) {
81       return(False);
82     }
83
84   euser_id=geteuid();
85   seteuid(0);  
86   r=quotactl(QCMD(Q_GETQUOTA,USRQUOTA), mnt->mnt_fsname, euser_id, (caddr_t)&D);
87       seteuid(euser_id);
88
89   /* Use softlimit to determine disk space, except when it has been exceeded */
90   *bsize = 1024;
91   if (r)
92     {
93       if (errno == EDQUOT) 
94        {
95          *dfree =0;
96          *dsize =D.dqb_curblocks;
97          return (True);
98     }
99       else return(False);
100   }
101   /* Use softlimit to determine disk space, except when it has been exceeded */
102   if (
103       (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) ||
104       (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) ||
105       (D.dqb_isoftlimit && D.dqb_curinodes>=D.dqb_isoftlimit) ||
106       (D.dqb_ihardlimit && D.dqb_curinodes>=D.dqb_ihardlimit)
107      )
108     {
109       *dfree = 0;
110       *dsize = D.dqb_curblocks;
111     }
112   else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0)
113     {
114       return(False);
115     }
116   else {
117     if (D.dqb_bsoftlimit == 0)
118       D.dqb_bsoftlimit = D.dqb_bhardlimit;
119     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
120     *dsize = D.dqb_bsoftlimit;
121   }
122   return (True);
123 }
124
125 #elif defined(CRAY)
126
127 #include <sys/quota.h>
128 #include <mntent.h>
129
130 /****************************************************************************
131 try to get the disk space from disk quotas (CRAY VERSION)
132 ****************************************************************************/
133
134 BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
135 {
136   struct mntent *mnt;
137   FILE *fd;
138   SMB_STRUCT_STAT sbuf;
139   SMB_DEV_T devno ;
140   static SMB_DEV_T devno_cached = 0 ;
141   static pstring name;
142   struct q_request request ;
143   struct qf_header header ;
144   static int quota_default = 0 ;
145   int found ;
146   
147   if ( sys_stat(path,&sbuf) == -1 )
148     return(False) ;
149   
150   devno = sbuf.st_dev ;
151   
152   if ( devno != devno_cached ) {
153     
154     devno_cached = devno ;
155     
156     if ((fd = setmntent(KMTAB)) == NULL)
157       return(False) ;
158     
159     found = False ;
160     
161     while ((mnt = getmntent(fd)) != NULL) {
162       
163       if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
164         continue ;
165       
166       if (sbuf.st_dev == devno) {
167         
168         found = True ;
169         break ;
170         
171       }
172       
173     }
174     
175     pstrcpy(name,mnt->mnt_dir) ;
176     endmntent(fd) ;
177     
178     if ( ! found )
179       return(False) ;
180   }
181   
182   request.qf_magic = QF_MAGIC ;
183   request.qf_entry.id = geteuid() ;
184   
185   if (quotactl(name, Q_GETQUOTA, &request) == -1)
186     return(False) ;
187   
188   if ( ! request.user )
189     return(False) ;
190   
191   if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) {
192     
193     if ( ! quota_default ) {
194       
195       if ( quotactl(name, Q_GETHEADER, &header) == -1 )
196         return(False) ;
197       else
198         quota_default = header.user_h.def_fq ;
199     }
200     
201     *dfree = quota_default ;
202     
203   }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) {
204     
205     *dfree = 0 ;
206     
207   }else{
208     
209     *dfree = request.qf_entry.user_q.f_quota ;
210     
211   }
212   
213   *dsize = request.qf_entry.user_q.f_use ;
214   
215   if ( *dfree )
216     *dfree -= *dsize ;
217   
218   if ( *dfree < 0 )
219     *dfree = 0 ;
220   
221   *bsize = 4096 ;  /* Cray blocksize */
222   
223   return(True) ;
224   
225 }
226
227
228 #elif defined(SUNOS5) || defined(SUNOS4)
229
230 #include <fcntl.h>
231 #include <sys/param.h>
232 #if defined(SUNOS5)
233 #include <sys/fs/ufs_quota.h>
234 #include <sys/mnttab.h>
235 #else /* defined(SUNOS4) */
236 #include <ufs/quota.h>
237 #include <mntent.h>
238 #endif
239
240 /****************************************************************************
241 try to get the disk space from disk quotas (SunOS & Solaris2 version)
242 Quota code by Peter Urbanec (amiga@cse.unsw.edu.au).
243 ****************************************************************************/
244
245 BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
246 {
247   uid_t user_id, euser_id;
248   int ret;
249   struct dqblk D;
250 #if defined(SUNOS5)
251   struct quotctl command;
252   int file;
253   struct mnttab mnt;
254   static pstring name;
255 #else /* SunOS4 */
256   struct mntent *mnt;
257   static pstring name;
258 #endif
259   FILE *fd;
260   SMB_STRUCT_STAT sbuf;
261   SMB_DEV_T devno ;
262   static SMB_DEV_T devno_cached = 0 ;
263   int found ;
264   
265   if ( sys_stat(path,&sbuf) == -1 )
266     return(False) ;
267   
268   devno = sbuf.st_dev ;
269   DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%o\n", path,devno));
270   if ( devno != devno_cached ) {
271     devno_cached = devno ;
272 #if defined(SUNOS5)
273     if ((fd = sys_fopen(MNTTAB, "r")) == NULL)
274       return(False) ;
275     
276     found = False ;
277     while (getmntent(fd, &mnt) == 0) {
278       if ( sys_stat(mnt.mnt_mountp,&sbuf) == -1 )
279         continue ;
280       DEBUG(5,("disk_quotas: testing \"%s\" devno=%o\n", 
281                mnt.mnt_mountp,sbuf.st_dev));
282       if (sbuf.st_dev == devno) {
283         found = True ;
284         break ;
285       }
286     }
287     
288     pstrcpy(name,mnt.mnt_mountp) ;
289     pstrcat(name,"/quotas") ;
290     fclose(fd) ;
291 #else /* SunOS4 */
292     if ((fd = setmntent(MOUNTED, "r")) == NULL)
293       return(False) ;
294     
295     found = False ;
296     while ((mnt = getmntent(fd)) != NULL) {
297       if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
298         continue ;
299       DEBUG(5,("disk_quotas: testing \"%s\" devno=%o\n", 
300                mnt->mnt_dir,sbuf.st_dev));
301       if (sbuf.st_dev == devno) {
302         found = True ;
303         break ;
304       }
305     }
306     
307     pstrcpy(name,mnt->mnt_fsname) ;
308     endmntent(fd) ;
309 #endif
310     
311     if ( ! found )
312       return(False) ;
313   }
314
315   euser_id = geteuid();
316   user_id = getuid();
317
318   setuid(0);  /* Solaris seems to want to give info only to super-user */
319   seteuid(0);
320
321 #if defined(SUNOS5)
322   DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name));
323   if((file=sys_open(name, O_RDONLY,0))<0) {
324     setuid(user_id);  /* Restore the original UID status */
325     seteuid(euser_id);
326     return(False);
327   }
328   command.op = Q_GETQUOTA;
329   command.uid = euser_id;
330   command.addr = (caddr_t) &D;
331   ret = ioctl(file, Q_QUOTACTL, &command);
332   close(file);
333 #else
334   DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name));
335   ret = quotactl(Q_GETQUOTA, name, euser_id, &D);
336 #endif
337
338   setuid(user_id); /* Restore the original uid status. */
339   seteuid(euser_id);
340
341   if (ret < 0) {
342     DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n", strerror(errno) ));
343     return(False);
344   }
345
346
347   /* Use softlimit to determine disk space. A user exceeding the quota is told
348    * that there's no space left. Writes might actually work for a bit if the
349    * hardlimit is set higher than softlimit. Effectively the disk becomes
350    * made of rubber latex and begins to expand to accommodate the user :-)
351    */
352
353   if (D.dqb_bsoftlimit==0)
354     return(False);
355   *bsize = DEV_BSIZE;
356   *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
357   *dsize = D.dqb_bsoftlimit;
358
359   if(*dfree < 0)
360     {
361      *dfree = 0;
362      *dsize = D.dqb_curblocks;
363     }
364       
365   DEBUG(5,("disk_quotas for path \"%s\" returning  bsize %.0f, dfree %.0f, dsize %.0f\n",
366          path,(double)*bsize,(double)*dfree,(double)*dsize));
367
368       return(True);
369 }
370
371
372 #elif defined(OSF1)
373 #include <ufs/quota.h>
374
375 /****************************************************************************
376 try to get the disk space from disk quotas - OSF1 version
377 ****************************************************************************/
378
379 BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
380 {
381   uid_t user_id, euser_id;
382   int r, save_errno;
383   struct dqblk D;
384   SMB_STRUCT_STAT S;
385
386   euser_id = geteuid();
387   user_id = getuid();
388
389   setreuid(euser_id, -1);
390   r= quotactl(path,QCMD(Q_GETQUOTA, USRQUOTA),euser_id,(char *) &D);
391   if (r)
392      save_errno = errno;
393
394   if (setreuid(user_id, -1) == -1)
395     DEBUG(5,("Unable to reset uid to %d\n", user_id));
396
397   *bsize = DEV_BSIZE;
398
399   if (r)
400   {
401       if (save_errno == EDQUOT)   // disk quota exceeded
402       {
403          *dfree = 0;
404          *dsize = D.dqb_curblocks;
405          return (True);
406       }
407       else
408          return (False);  
409   }
410
411   /* Use softlimit to determine disk space, except when it has been exceeded */
412
413   if (D.dqb_bsoftlimit==0)
414     return(False);
415
416   if ((D.dqb_curblocks>D.dqb_bsoftlimit)) {
417     *dfree = 0;
418     *dsize = D.dqb_curblocks;
419   } else {
420     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
421     *dsize = D.dqb_bsoftlimit;
422   }
423   return (True);
424 }
425
426 #elif defined (SGI6)
427 /****************************************************************************
428 try to get the disk space from disk quotas (IRIX 6.2 version)
429 ****************************************************************************/
430
431 #include <sys/quota.h>
432 #include <mntent.h>
433
434 BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
435 {
436   uid_t euser_id;
437   int r;
438   struct dqblk D;
439   struct fs_disk_quota        F;
440   SMB_STRUCT_STAT S;
441   FILE *fp;
442   struct mntent *mnt;
443   SMB_DEV_T devno;
444   int found;
445   
446   /* find the block device file */
447   
448   if ( sys_stat(path, &S) == -1 ) {
449     return(False) ;
450   }
451
452   devno = S.st_dev ;
453   
454   fp = setmntent(MOUNTED,"r");
455   found = False ;
456   
457   while ((mnt = getmntent(fp))) {
458     if ( sys_stat(mnt->mnt_dir,&S) == -1 )
459       continue ;
460     if (S.st_dev == devno) {
461       found = True ;
462       break ;
463     }
464   }
465   endmntent(fp) ;
466   
467   if (!found) {
468     return(False);
469   }
470
471   euser_id=geteuid();
472   seteuid(0);  
473
474   /* Use softlimit to determine disk space, except when it has been exceeded */
475
476   *bsize = 512;
477
478   if ( 0 == strcmp ( mnt->mnt_type, "efs" ))
479   {
480     r=quotactl (Q_GETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &D);
481
482     seteuid(euser_id); /* Restore the original uid status. */
483
484     if (r==-1)
485       return(False);
486         
487     /* Use softlimit to determine disk space, except when it has been exceeded */
488     if (
489         (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) ||
490         (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) ||
491         (D.dqb_fsoftlimit && D.dqb_curfiles>=D.dqb_fsoftlimit) ||
492         (D.dqb_fhardlimit && D.dqb_curfiles>=D.dqb_fhardlimit)
493        )
494     {
495       *dfree = 0;
496       *dsize = D.dqb_curblocks;
497     }
498     else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0)
499     {
500       return(False);
501     }
502     else 
503     {
504       *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
505       *dsize = D.dqb_bsoftlimit;
506     }
507
508   }
509   else if ( 0 == strcmp ( mnt->mnt_type, "xfs" ))
510   {
511     r=quotactl (Q_XGETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &F);
512
513     seteuid(euser_id); /* Restore the original uid status. */
514
515     if (r==-1)
516       return(False);
517         
518     /* Use softlimit to determine disk space, except when it has been exceeded */
519     if (
520         (F.d_blk_softlimit && F.d_bcount>=F.d_blk_softlimit) ||
521         (F.d_blk_hardlimit && F.d_bcount>=F.d_blk_hardlimit) ||
522         (F.d_ino_softlimit && F.d_icount>=F.d_ino_softlimit) ||
523         (F.d_ino_hardlimit && F.d_icount>=F.d_ino_hardlimit)
524        )
525     {
526       *dfree = 0;
527       *dsize = F.d_bcount;
528     }
529     else if (F.d_blk_softlimit==0 && F.d_blk_hardlimit==0)
530     {
531       return(False);
532     }
533     else 
534     {
535       *dfree = (F.d_blk_softlimit - F.d_bcount);
536       *dsize = F.d_blk_softlimit;
537     }
538
539   }
540   else
541   {
542     seteuid(euser_id); /* Restore the original uid status. */
543     return(False);
544   }
545
546   return (True);
547
548 }
549
550 #else
551
552 #if    defined(__FreeBSD__) || defined(__OpenBSD__)
553 #include <ufs/ufs/quota.h>
554 #include <machine/param.h>
555 #elif         AIX
556 /* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
557 #include <jfs/quota.h>
558 /* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
559 #define dqb_curfiles dqb_curinodes
560 #define dqb_fhardlimit dqb_ihardlimit
561 #define dqb_fsoftlimit dqb_isoftlimit
562 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
563 #include <sys/quota.h>
564 #include <devnm.h>
565 #endif
566
567 /****************************************************************************
568 try to get the disk space from disk quotas - default version
569 ****************************************************************************/
570
571 BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
572 {
573   uid_t euser_id;
574   int r;
575   struct dqblk D;
576 #if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__)
577   char dev_disk[256];
578   SMB_STRUCT_STAT S;
579   /* find the block device file */
580   if ((sys_stat(path, &S)<0) ||
581       (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False);
582 #endif
583
584   euser_id = geteuid();
585
586 #ifdef HPUX
587   {
588     uid_t user_id;
589
590     /* for HPUX, real uid must be same as euid to execute quotactl for euid */
591     user_id = getuid();
592     setresuid(euser_id,-1,-1);
593     r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
594     if (setresuid(user_id,-1,-1))
595       DEBUG(5,("Unable to reset uid to %d\n", user_id));
596   }
597 #else 
598 #if defined(__FreeBSD__) || defined(__OpenBSD__)
599   {
600     /* FreeBSD patches from Marty Moll <martym@arbor.edu> */
601     uid_t user_id;
602     gid_t egrp_id;
603  
604     /* Need to be root to get quotas in FreeBSD */
605     user_id = getuid();
606     egrp_id = getegid();
607     setuid(0);
608     seteuid(0);
609     r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
610
611     /* As FreeBSD has group quotas, if getting the user
612        quota fails, try getting the group instead. */
613     if (r)
614       r= quotactl(path,QCMD(Q_GETQUOTA,GRPQUOTA),egrp_id,(char *) &D);
615     setuid(user_id);
616     seteuid(euser_id);
617   }
618 #elif defined(AIX)
619   /* AIX has both USER and GROUP quotas: 
620      Get the USER quota (ohnielse@fysik.dtu.dk) */
621   r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
622 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
623   r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
624 #endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
625 #endif /* HAVE_SETRES */
626
627   /* Use softlimit to determine disk space, except when it has been exceeded */
628 #if defined(__FreeBSD__) || defined(__OpenBSD__)
629   *bsize = DEV_BSIZE;
630 #else /* !__FreeBSD__ && !__OpenBSD__ */
631   *bsize = 1024;
632 #endif /*!__FreeBSD__ && !__OpenBSD__ */
633
634   if (r)
635     {
636       if (errno == EDQUOT) 
637         {
638           *dfree =0;
639           *dsize =D.dqb_curblocks;
640           return (True);
641         }
642       else return(False);
643     }
644   if (D.dqb_bsoftlimit==0)
645     return(False);
646   /* Use softlimit to determine disk space, except when it has been exceeded */
647   if ((D.dqb_curblocks>D.dqb_bsoftlimit)
648 #if !defined(__FreeBSD__) && !defined(__OpenBSD__)
649 ||((D.dqb_curfiles>D.dqb_fsoftlimit) && (D.dqb_fsoftlimit != 0))
650 #endif
651     ) {
652       *dfree = 0;
653       *dsize = D.dqb_curblocks;
654     }
655   else {
656     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
657     *dsize = D.dqb_bsoftlimit;
658   }
659   return (True);
660 }
661
662 #endif