d4f746c9e36a4923b04f3c52a0f91da139b79ced
[kai/samba.git] / source3 / smbd / quotas.c
1 #ifdef QUOTAS
2 /* 
3    Unix SMB/Netbios implementation.
4    Version 1.9.
5    support for quotas
6    Copyright (C) Andrew Tridgell 1992-1997
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23
24 /* 
25  * This is one of the most system dependent parts of Samba, and its
26  * done a litle differently. Each system has its own way of doing 
27  * things :-(
28  */
29
30 #include "includes.h"
31
32 extern int DEBUGLEVEL;
33
34 #ifdef LINUX
35
36 #include <sys/types.h>
37 #include <asm/types.h>
38 #include <sys/quota.h>
39
40 #include <mntent.h>
41 #include <linux/unistd.h>
42
43 _syscall4(int, quotactl, int, cmd, const char *, special, int, id, caddr_t, addr);
44
45 /****************************************************************************
46 try to get the disk space from disk quotas (LINUX version)
47 ****************************************************************************/
48
49 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
50 {
51   uid_t euser_id;
52   int r;
53   struct dqblk D;
54   struct stat S;
55   FILE *fp;
56   struct mntent *mnt;
57   int devno;
58   int found;
59   
60   /* find the block device file */
61   
62   if ( stat(path, &S) == -1 ) {
63     return(False) ;
64   }
65
66   devno = S.st_dev ;
67   
68   fp = setmntent(MOUNTED,"r");
69   found = False ;
70   
71   while ((mnt = getmntent(fp))) {
72     if ( stat(mnt->mnt_dir,&S) == -1 )
73       continue ;
74     if (S.st_dev == devno) {
75       found = True ;
76       break ;
77     }
78   }
79   endmntent(fp) ;
80   
81   if (!found) {
82       return(False);
83     }
84
85   euser_id=geteuid();
86   seteuid(0);  
87   r=quotactl(QCMD(Q_GETQUOTA,USRQUOTA), mnt->mnt_fsname, euser_id, (caddr_t)&D);
88       seteuid(euser_id);
89
90   /* Use softlimit to determine disk space, except when it has been exceeded */
91   *bsize = 1024;
92   if (r)
93     {
94       if (errno == EDQUOT) 
95        {
96          *dfree =0;
97          *dsize =D.dqb_curblocks;
98          return (True);
99     }
100       else return(False);
101   }
102   /* Use softlimit to determine disk space, except when it has been exceeded */
103   if (
104       (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) ||
105       (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) ||
106       (D.dqb_isoftlimit && D.dqb_curinodes>=D.dqb_isoftlimit) ||
107       (D.dqb_ihardlimit && D.dqb_curinodes>=D.dqb_ihardlimit)
108      )
109     {
110       *dfree = 0;
111       *dsize = D.dqb_curblocks;
112     }
113   else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0)
114     {
115       return(False);
116     }
117   else {
118     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
119     *dsize = D.dqb_bsoftlimit;
120   }
121   return (True);
122 }
123
124 #elif defined(CRAY)
125
126 #include <sys/quota.h>
127 #include <mntent.h>
128
129 /****************************************************************************
130 try to get the disk space from disk quotas (CRAY VERSION)
131 ****************************************************************************/
132 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
133 {
134   struct mntent *mnt;
135   FILE *fd;
136   struct stat sbuf;
137   dev_t devno ;
138   static dev_t devno_cached = 0 ;
139   static char name[MNTMAXSTR] ;
140   struct q_request request ;
141   struct qf_header header ;
142   static int quota_default = 0 ;
143   int found ;
144   
145   if ( stat(path,&sbuf) == -1 )
146     return(False) ;
147   
148   devno = sbuf.st_dev ;
149   
150   if ( devno != devno_cached ) {
151     
152     devno_cached = devno ;
153     
154     if ((fd = setmntent(KMTAB)) == NULL)
155       return(False) ;
156     
157     found = False ;
158     
159     while ((mnt = getmntent(fd)) != NULL) {
160       
161       if ( stat(mnt->mnt_dir,&sbuf) == -1 )
162         continue ;
163       
164       if (sbuf.st_dev == devno) {
165         
166         found = True ;
167         break ;
168         
169       }
170       
171     }
172     
173     strcpy(name,mnt->mnt_dir) ;
174     endmntent(fd) ;
175     
176     if ( ! found )
177       return(False) ;
178   }
179   
180   request.qf_magic = QF_MAGIC ;
181   request.qf_entry.id = geteuid() ;
182   
183   if (quotactl(name, Q_GETQUOTA, &request) == -1)
184     return(False) ;
185   
186   if ( ! request.user )
187     return(False) ;
188   
189   if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) {
190     
191     if ( ! quota_default ) {
192       
193       if ( quotactl(name, Q_GETHEADER, &header) == -1 )
194         return(False) ;
195       else
196         quota_default = header.user_h.def_fq ;
197     }
198     
199     *dfree = quota_default ;
200     
201   }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) {
202     
203     *dfree = 0 ;
204     
205   }else{
206     
207     *dfree = request.qf_entry.user_q.f_quota ;
208     
209   }
210   
211   *dsize = request.qf_entry.user_q.f_use ;
212   
213   if ( *dfree )
214     *dfree -= *dsize ;
215   
216   if ( *dfree < 0 )
217     *dfree = 0 ;
218   
219   *bsize = 4096 ;  /* Cray blocksize */
220   
221   return(True) ;
222   
223 }
224
225
226 #elif defined(SUNOS5) || defined(SUNOS4)
227
228 #include <fcntl.h>
229 #if defined(SUNOS5)
230 #include <sys/fs/ufs_quota.h>
231 #include <sys/mnttab.h>
232 #else /* defined(SUNOS4) */
233 #include <ufs/quota.h>
234 #include <mntent.h>
235 #endif
236
237 /****************************************************************************
238 try to get the disk space from disk quotas (solaris 2 version)
239 ****************************************************************************/
240 /* Quota code by Peter Urbanec (amiga@cse.unsw.edu.au) */
241 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
242 {
243   uid_t user_id, euser_id;
244   int ret;
245   struct dqblk D;
246 #if defined(SUNOS5)
247   struct quotctl command;
248   int file;
249   struct mnttab mnt;
250   static char name[MNT_LINE_MAX] ;
251 #else
252   struct mntent *mnt;
253   static char name[MNTMAXSTR] ;
254 #endif
255   FILE *fd;
256   struct stat sbuf;
257   dev_t devno ;
258   static dev_t devno_cached = 0 ;
259   int found ;
260   
261   if ( stat(path,&sbuf) == -1 )
262     return(False) ;
263   
264   devno = sbuf.st_dev ;
265   DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%o\n", path,devno));
266   if ( devno != devno_cached ) {
267     devno_cached = devno ;
268 #if defined(SUNOS5)
269     if ((fd = fopen(MNTTAB, "r")) == NULL)
270       return(False) ;
271     
272     found = False ;
273     while (getmntent(fd, &mnt) == 0) {
274       if ( stat(mnt.mnt_mountp,&sbuf) == -1 )
275         continue ;
276       DEBUG(5,("disk_quotas: testing \"%s\" devno=%o\n", 
277                mnt.mnt_mountp,sbuf.st_dev));
278       if (sbuf.st_dev == devno) {
279         found = True ;
280         break ;
281       }
282     }
283     
284     strcpy(name,mnt.mnt_mountp) ;
285     strcat(name,"/quotas") ;
286     fclose(fd) ;
287 #else
288     if ((fd = setmntent(MOUNTED, "r")) == NULL)
289       return(False) ;
290     
291     found = False ;
292     while ((mnt = getmntent(fd)) != NULL) {
293       if ( stat(mnt->mnt_dir,&sbuf) == -1 )
294         continue ;
295       DEBUG(5,("disk_quotas: testing \"%s\" devno=%o\n", 
296                mnt->mnt_dir,sbuf.st_dev));
297       if (sbuf.st_dev == devno) {
298         found = True ;
299         break ;
300       }
301     }
302     
303     strcpy(name,mnt->mnt_fsname) ;
304     endmntent(fd) ;
305 #endif
306     
307     if ( ! found )
308       return(False) ;
309   }
310
311   euser_id = geteuid();
312   user_id = getuid();
313
314   setuid(0);  /* Solaris seems to want to give info only to super-user */
315   seteuid(0);
316
317 #if defined(SUNOS5)
318   DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name));
319   if((file=open(name, O_RDONLY))<0) {
320     setuid(user_id);  /* Restore the original UID status */
321     seteuid(euser_id);
322     return(False);
323   }
324   command.op = Q_GETQUOTA;
325   command.uid = euser_id;
326   command.addr = (caddr_t) &D;
327   ret = ioctl(file, Q_QUOTACTL, &command);
328   close(file);
329 #else
330   DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name));
331   ret = quotactl(Q_GETQUOTA, name, euser_id, &D);
332 #endif
333
334   setuid(user_id); /* Restore the original uid status. */
335   seteuid(euser_id);
336
337   if (ret < 0) {
338     DEBUG(2,("disk_quotas ioctl (Solaris) failed\n"));
339     return(False);
340   }
341
342
343   /* Use softlimit to determine disk space. A user exceeding the quota is told
344    * that there's no space left. Writes might actually work for a bit if the
345    * hardlimit is set higher than softlimit. Effectively the disk becomes
346    * made of rubber latex and begins to expand to accommodate the user :-)
347    */
348
349   if (D.dqb_bsoftlimit==0)
350     return(False);
351   *bsize = 512;
352   *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
353   *dsize = D.dqb_bsoftlimit;
354   if(*dfree < 0)
355     {
356      *dfree = 0;
357      *dsize = D.dqb_curblocks;
358     }
359       
360 DEBUG(5,("disk_quotas for path \"%s\" returning  bsize %d, dfree %d, dsize %d\n",
361          path,*bsize,*dfree,*dsize));
362
363       return(True);
364 }
365
366
367 #elif defined(OSF1)
368 #include <ufs/quota.h>
369
370 /****************************************************************************
371 try to get the disk space from disk quotas - OFS1 version
372 ****************************************************************************/
373 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
374 {
375   uid_t user_id, euser_id;
376   int r, save_errno;
377   struct dqblk D;
378   struct stat S;
379
380   euser_id = geteuid();
381   user_id = getuid();
382
383   setreuid(euser_id, -1);
384   r= quotactl(path,QCMD(Q_GETQUOTA, USRQUOTA),euser_id,(char *) &D);
385   if (r)
386      save_errno = errno;
387
388   if (setreuid(user_id, -1) == -1)
389     DEBUG(5,("Unable to reset uid to %d\n", user_id));
390
391   *bsize = DEV_BSIZE;
392
393   if (r)
394   {
395       if (save_errno == EDQUOT)   // disk quota exceeded
396       {
397          *dfree = 0;
398          *dsize = D.dqb_curblocks;
399          return (True);
400       }
401       else
402          return (False);  
403   }
404
405   /* Use softlimit to determine disk space, except when it has been exceeded */
406
407   if (D.dqb_bsoftlimit==0)
408     return(False);
409
410   if ((D.dqb_curblocks>D.dqb_bsoftlimit)) {
411     *dfree = 0;
412     *dsize = D.dqb_curblocks;
413   } else {
414     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
415     *dsize = D.dqb_bsoftlimit;
416   }
417   return (True);
418 }
419 #else
420
421 #ifdef        __FreeBSD__
422 #include <ufs/ufs/quota.h>
423 #elif         AIX
424 /* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
425 #include <jfs/quota.h>
426 /* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
427 #define dqb_curfiles dqb_curinodes
428 #define dqb_fhardlimit dqb_ihardlimit
429 #define dqb_fsoftlimit dqb_isoftlimit
430 #else /* !__FreeBSD__ && !AIX */
431 #include <sys/quota.h>
432 #include <devnm.h>
433 #endif
434
435 /****************************************************************************
436 try to get the disk space from disk quotas - default version
437 ****************************************************************************/
438 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
439 {
440   uid_t euser_id;
441   int r;
442   struct dqblk D;
443 #if !defined(__FreeBSD__) && !defined(AIX)
444   char dev_disk[256];
445   struct stat S;
446   /* find the block device file */
447   if ((stat(path, &S)<0) ||
448       (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False);
449 #endif
450
451   euser_id = geteuid();
452
453 #ifdef USE_SETRES
454   {
455     uid_t user_id;
456
457     /* for HPUX, real uid must be same as euid to execute quotactl for euid */
458     user_id = getuid();
459     setresuid(euser_id,-1,-1);
460     r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
461     if (setresuid(user_id,-1,-1))
462       DEBUG(5,("Unable to reset uid to %d\n", user_id));
463   }
464 #else /* USE_SETRES */
465 #if defined(__FreeBSD__)
466   r= quotactl(path,Q_GETQUOTA,euser_id,(char *) &D);
467 #elif defined(AIX)
468   /* AIX has both USER and GROUP quotas: 
469      Get the USER quota (ohnielse@fysik.dtu.dk) */
470   r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
471 #else /* !__FreeBSD__ && !AIX */
472   r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
473 #endif /* !__FreeBSD__ && !AIX */
474 #endif /* USE_SETRES */
475
476   /* Use softlimit to determine disk space, except when it has been exceeded */
477   *bsize = 1024;
478   if (r)
479     {
480       if (errno == EDQUOT) 
481         {
482           *dfree =0;
483           *dsize =D.dqb_curblocks;
484           return (True);
485         }
486       else return(False);
487     }
488   if (D.dqb_bsoftlimit==0)
489     return(False);
490   /* Use softlimit to determine disk space, except when it has been exceeded */
491   if ((D.dqb_curblocks>D.dqb_bsoftlimit)
492 #if !defined(__FreeBSD__)
493 ||((D.dqb_curfiles>D.dqb_fsoftlimit) && (D.dqb_fsoftlimit != 0))
494 #endif
495     ) {
496       *dfree = 0;
497       *dsize = D.dqb_curblocks;
498     }
499   else {
500     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
501     *dsize = D.dqb_bsoftlimit;
502   }
503   return (True);
504 }
505
506 #endif
507
508 #else
509 /* this keeps fussy compilers happy */
510  void quotas_dummy(void) {}
511 #endif /* QUOTAS */
512