Makefile.in configure configure.in include/proto.h smbd/noquotas.c smbd/quotas.c:
[jra/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 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, int *bsize, int *dfree, int *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 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
134 {
135   struct mntent *mnt;
136   FILE *fd;
137   SMB_STRUCT_STAT sbuf;
138   SMB_DEV_T devno ;
139   static SMB_DEV_T devno_cached = 0 ;
140   static pstring name;
141   struct q_request request ;
142   struct qf_header header ;
143   static int quota_default = 0 ;
144   int found ;
145   
146   if ( sys_stat(path,&sbuf) == -1 )
147     return(False) ;
148   
149   devno = sbuf.st_dev ;
150   
151   if ( devno != devno_cached ) {
152     
153     devno_cached = devno ;
154     
155     if ((fd = setmntent(KMTAB)) == NULL)
156       return(False) ;
157     
158     found = False ;
159     
160     while ((mnt = getmntent(fd)) != NULL) {
161       
162       if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
163         continue ;
164       
165       if (sbuf.st_dev == devno) {
166         
167         found = True ;
168         break ;
169         
170       }
171       
172     }
173     
174     pstrcpy(name,mnt->mnt_dir) ;
175     endmntent(fd) ;
176     
177     if ( ! found )
178       return(False) ;
179   }
180   
181   request.qf_magic = QF_MAGIC ;
182   request.qf_entry.id = geteuid() ;
183   
184   if (quotactl(name, Q_GETQUOTA, &request) == -1)
185     return(False) ;
186   
187   if ( ! request.user )
188     return(False) ;
189   
190   if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) {
191     
192     if ( ! quota_default ) {
193       
194       if ( quotactl(name, Q_GETHEADER, &header) == -1 )
195         return(False) ;
196       else
197         quota_default = header.user_h.def_fq ;
198     }
199     
200     *dfree = quota_default ;
201     
202   }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) {
203     
204     *dfree = 0 ;
205     
206   }else{
207     
208     *dfree = request.qf_entry.user_q.f_quota ;
209     
210   }
211   
212   *dsize = request.qf_entry.user_q.f_use ;
213   
214   if ( *dfree )
215     *dfree -= *dsize ;
216   
217   if ( *dfree < 0 )
218     *dfree = 0 ;
219   
220   *bsize = 4096 ;  /* Cray blocksize */
221   
222   return(True) ;
223   
224 }
225
226
227 #elif defined(SUNOS5) || defined(SUNOS4)
228
229 #include <fcntl.h>
230 #if defined(SUNOS5)
231 #include <sys/fs/ufs_quota.h>
232 #include <sys/mnttab.h>
233 #else /* defined(SUNOS4) */
234 #include <ufs/quota.h>
235 #include <mntent.h>
236 #endif
237
238 /****************************************************************************
239 try to get the disk space from disk quotas (solaris 2 version)
240 ****************************************************************************/
241 /* Quota code by Peter Urbanec (amiga@cse.unsw.edu.au) */
242 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
243 {
244   uid_t user_id, euser_id;
245   int ret;
246   struct dqblk D;
247 #if defined(SUNOS5)
248   struct quotctl command;
249   int file;
250   struct mnttab mnt;
251   static pstring name;
252 #else
253   struct mntent *mnt;
254   static pstring name;
255 #endif
256   FILE *fd;
257   SMB_STRUCT_STAT sbuf;
258   SMB_DEV_T devno ;
259   static SMB_DEV_T devno_cached = 0 ;
260   int found ;
261   
262   if ( sys_stat(path,&sbuf) == -1 )
263     return(False) ;
264   
265   devno = sbuf.st_dev ;
266   DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%o\n", path,devno));
267   if ( devno != devno_cached ) {
268     devno_cached = devno ;
269 #if defined(SUNOS5)
270     if ((fd = fopen(MNTTAB, "r")) == NULL)
271       return(False) ;
272     
273     found = False ;
274     while (getmntent(fd, &mnt) == 0) {
275       if ( sys_stat(mnt.mnt_mountp,&sbuf) == -1 )
276         continue ;
277       DEBUG(5,("disk_quotas: testing \"%s\" devno=%o\n", 
278                mnt.mnt_mountp,sbuf.st_dev));
279       if (sbuf.st_dev == devno) {
280         found = True ;
281         break ;
282       }
283     }
284     
285     pstrcpy(name,mnt.mnt_mountp) ;
286     pstrcat(name,"/quotas") ;
287     fclose(fd) ;
288 #else
289     if ((fd = setmntent(MOUNTED, "r")) == NULL)
290       return(False) ;
291     
292     found = False ;
293     while ((mnt = getmntent(fd)) != NULL) {
294       if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
295         continue ;
296       DEBUG(5,("disk_quotas: testing \"%s\" devno=%o\n", 
297                mnt->mnt_dir,sbuf.st_dev));
298       if (sbuf.st_dev == devno) {
299         found = True ;
300         break ;
301       }
302     }
303     
304     pstrcpy(name,mnt->mnt_fsname) ;
305     endmntent(fd) ;
306 #endif
307     
308     if ( ! found )
309       return(False) ;
310   }
311
312   euser_id = geteuid();
313   user_id = getuid();
314
315   setuid(0);  /* Solaris seems to want to give info only to super-user */
316   seteuid(0);
317
318 #if defined(SUNOS5)
319   DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name));
320   if((file=open(name, O_RDONLY))<0) {
321     setuid(user_id);  /* Restore the original UID status */
322     seteuid(euser_id);
323     return(False);
324   }
325   command.op = Q_GETQUOTA;
326   command.uid = euser_id;
327   command.addr = (caddr_t) &D;
328   ret = ioctl(file, Q_QUOTACTL, &command);
329   close(file);
330 #else
331   DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name));
332   ret = quotactl(Q_GETQUOTA, name, euser_id, &D);
333 #endif
334
335   setuid(user_id); /* Restore the original uid status. */
336   seteuid(euser_id);
337
338   if (ret < 0) {
339     DEBUG(2,("disk_quotas ioctl (Solaris) failed\n"));
340     return(False);
341   }
342
343
344   /* Use softlimit to determine disk space. A user exceeding the quota is told
345    * that there's no space left. Writes might actually work for a bit if the
346    * hardlimit is set higher than softlimit. Effectively the disk becomes
347    * made of rubber latex and begins to expand to accommodate the user :-)
348    */
349
350   if (D.dqb_bsoftlimit==0)
351     return(False);
352   *bsize = 512;
353   *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
354   *dsize = D.dqb_bsoftlimit;
355   if(*dfree < 0)
356     {
357      *dfree = 0;
358      *dsize = D.dqb_curblocks;
359     }
360       
361 DEBUG(5,("disk_quotas for path \"%s\" returning  bsize %d, dfree %d, dsize %d\n",
362          path,*bsize,*dfree,*dsize));
363
364       return(True);
365 }
366
367
368 #elif defined(OSF1)
369 #include <ufs/quota.h>
370
371 /****************************************************************************
372 try to get the disk space from disk quotas - OFS1 version
373 ****************************************************************************/
374 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
375 {
376   uid_t user_id, euser_id;
377   int r, save_errno;
378   struct dqblk D;
379   SMB_STRUCT_STAT S;
380
381   euser_id = geteuid();
382   user_id = getuid();
383
384   setreuid(euser_id, -1);
385   r= quotactl(path,QCMD(Q_GETQUOTA, USRQUOTA),euser_id,(char *) &D);
386   if (r)
387      save_errno = errno;
388
389   if (setreuid(user_id, -1) == -1)
390     DEBUG(5,("Unable to reset uid to %d\n", user_id));
391
392   *bsize = DEV_BSIZE;
393
394   if (r)
395   {
396       if (save_errno == EDQUOT)   // disk quota exceeded
397       {
398          *dfree = 0;
399          *dsize = D.dqb_curblocks;
400          return (True);
401       }
402       else
403          return (False);  
404   }
405
406   /* Use softlimit to determine disk space, except when it has been exceeded */
407
408   if (D.dqb_bsoftlimit==0)
409     return(False);
410
411   if ((D.dqb_curblocks>D.dqb_bsoftlimit)) {
412     *dfree = 0;
413     *dsize = D.dqb_curblocks;
414   } else {
415     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
416     *dsize = D.dqb_bsoftlimit;
417   }
418   return (True);
419 }
420
421 #elif defined (SGI6)
422 /****************************************************************************
423 try to get the disk space from disk quotas (IRIX 6.2 version)
424 ****************************************************************************/
425
426 #include <sys/quota.h>
427 #include <mntent.h>
428
429 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
430 {
431   uid_t euser_id;
432   int r;
433   struct dqblk D;
434   struct fs_disk_quota        F;
435   SMB_STRUCT_STAT S;
436   FILE *fp;
437   struct mntent *mnt;
438   SMB_DEV_T devno;
439   int found;
440   
441   /* find the block device file */
442   
443   if ( sys_stat(path, &S) == -1 ) {
444     return(False) ;
445   }
446
447   devno = S.st_dev ;
448   
449   fp = setmntent(MOUNTED,"r");
450   found = False ;
451   
452   while ((mnt = getmntent(fp))) {
453     if ( sys_stat(mnt->mnt_dir,&S) == -1 )
454       continue ;
455     if (S.st_dev == devno) {
456       found = True ;
457       break ;
458     }
459   }
460   endmntent(fp) ;
461   
462   if (!found) {
463     return(False);
464   }
465
466   euser_id=geteuid();
467   seteuid(0);  
468
469   /* Use softlimit to determine disk space, except when it has been exceeded */
470
471   *bsize = 512;
472
473   if ( 0 == strcmp ( mnt->mnt_type, "efs" ))
474   {
475     r=quotactl (Q_GETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &D);
476
477     seteuid(euser_id); /* Restore the original uid status. */
478
479     if (r==-1)
480       return(False);
481         
482     /* Use softlimit to determine disk space, except when it has been exceeded */
483     if (
484         (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) ||
485         (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) ||
486         (D.dqb_fsoftlimit && D.dqb_curfiles>=D.dqb_fsoftlimit) ||
487         (D.dqb_fhardlimit && D.dqb_curfiles>=D.dqb_fhardlimit)
488        )
489     {
490       *dfree = 0;
491       *dsize = D.dqb_curblocks;
492     }
493     else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0)
494     {
495       return(False);
496     }
497     else 
498     {
499       *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
500       *dsize = D.dqb_bsoftlimit;
501     }
502
503   }
504   else if ( 0 == strcmp ( mnt->mnt_type, "xfs" ))
505   {
506     r=quotactl (Q_XGETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &F);
507
508     seteuid(euser_id); /* Restore the original uid status. */
509
510     if (r==-1)
511       return(False);
512         
513     /* Use softlimit to determine disk space, except when it has been exceeded */
514     if (
515         (F.d_blk_softlimit && F.d_bcount>=F.d_blk_softlimit) ||
516         (F.d_blk_hardlimit && F.d_bcount>=F.d_blk_hardlimit) ||
517         (F.d_ino_softlimit && F.d_icount>=F.d_ino_softlimit) ||
518         (F.d_ino_hardlimit && F.d_icount>=F.d_ino_hardlimit)
519        )
520     {
521       /*
522        * Fixme!: these are __uint64_t, this may truncate values
523        */
524       *dfree = 0;
525       *dsize = (int) F.d_bcount;
526     }
527     else if (F.d_blk_softlimit==0 && F.d_blk_hardlimit==0)
528     {
529       return(False);
530     }
531     else 
532     {
533       *dfree = (int)(F.d_blk_softlimit - F.d_bcount);
534       *dsize = (int)F.d_blk_softlimit;
535     }
536
537   }
538   else
539   {
540     seteuid(euser_id); /* Restore the original uid status. */
541     return(False);
542   }
543
544   return (True);
545
546 }
547
548 #else
549
550 #if    defined(__FreeBSD__) || defined(__OpenBSD__)
551 #include <ufs/ufs/quota.h>
552 #include <machine/param.h>
553 #elif         AIX
554 /* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
555 #include <jfs/quota.h>
556 /* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
557 #define dqb_curfiles dqb_curinodes
558 #define dqb_fhardlimit dqb_ihardlimit
559 #define dqb_fsoftlimit dqb_isoftlimit
560 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
561 #include <sys/quota.h>
562 #include <devnm.h>
563 #endif
564
565 /****************************************************************************
566 try to get the disk space from disk quotas - default version
567 ****************************************************************************/
568 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
569 {
570   uid_t euser_id;
571   int r;
572   struct dqblk D;
573 #if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__)
574   char dev_disk[256];
575   SMB_STRUCT_STAT S;
576   /* find the block device file */
577   if ((sys_stat(path, &S)<0) ||
578       (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False);
579 #endif
580
581   euser_id = geteuid();
582
583 #ifdef HPUX
584   {
585     uid_t user_id;
586
587     /* for HPUX, real uid must be same as euid to execute quotactl for euid */
588     user_id = getuid();
589     setresuid(euser_id,-1,-1);
590     r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
591     if (setresuid(user_id,-1,-1))
592       DEBUG(5,("Unable to reset uid to %d\n", user_id));
593   }
594 #else 
595 #if defined(__FreeBSD__) || defined(__OpenBSD__)
596   {
597     /* FreeBSD patches from Marty Moll <martym@arbor.edu> */
598     uid_t user_id;
599     gid_t egrp_id;
600  
601     /* Need to be root to get quotas in FreeBSD */
602     user_id = getuid();
603     egrp_id = getegid();
604     setuid(0);
605     seteuid(0);
606     r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
607
608     /* As FreeBSD has group quotas, if getting the user
609        quota fails, try getting the group instead. */
610     if (r)
611       r= quotactl(path,QCMD(Q_GETQUOTA,GRPQUOTA),egrp_id,(char *) &D);
612     setuid(user_id);
613     seteuid(euser_id);
614   }
615 #elif defined(AIX)
616   /* AIX has both USER and GROUP quotas: 
617      Get the USER quota (ohnielse@fysik.dtu.dk) */
618   r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
619 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
620   r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
621 #endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
622 #endif /* HAVE_SETRES */
623
624   /* Use softlimit to determine disk space, except when it has been exceeded */
625 #if defined(__FreeBSD__) || defined(__OpenBSD__)
626   *bsize = DEV_BSIZE;
627 #else /* !__FreeBSD__ && !__OpenBSD__ */
628   *bsize = 1024;
629 #endif /*!__FreeBSD__ && !__OpenBSD__ */
630
631   if (r)
632     {
633       if (errno == EDQUOT) 
634         {
635           *dfree =0;
636           *dsize =D.dqb_curblocks;
637           return (True);
638         }
639       else return(False);
640     }
641   if (D.dqb_bsoftlimit==0)
642     return(False);
643   /* Use softlimit to determine disk space, except when it has been exceeded */
644   if ((D.dqb_curblocks>D.dqb_bsoftlimit)
645 #if !defined(__FreeBSD__) && !defined(__OpenBSD__)
646 ||((D.dqb_curfiles>D.dqb_fsoftlimit) && (D.dqb_fsoftlimit != 0))
647 #endif
648     ) {
649       *dfree = 0;
650       *dsize = D.dqb_curblocks;
651     }
652   else {
653     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
654     *dsize = D.dqb_bsoftlimit;
655   }
656   return (True);
657 }
658
659 #endif