883c2c050de6e2683d4459e66c99333189683885
[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   seteuid(euser_id); /* Restore the original uid status. */
335   setuid(user_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 #else
367
368 #ifdef        __FreeBSD__
369 #include <ufs/ufs/quota.h>
370 #elif         AIX
371 /* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
372 #include <jfs/quota.h>
373 /* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
374 #define dqb_curfiles dqb_curinodes
375 #define dqb_fhardlimit dqb_ihardlimit
376 #define dqb_fsoftlimit dqb_isoftlimit
377 #else /* !__FreeBSD__ && !AIX */
378 #include <sys/quota.h>
379 #include <devnm.h>
380 #endif
381
382 /****************************************************************************
383 try to get the disk space from disk quotas - default version
384 ****************************************************************************/
385 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
386 {
387   uid_t euser_id;
388   int r;
389   struct dqblk D;
390 #if !defined(__FreeBSD__) && !defined(AIX)
391   char dev_disk[256];
392   struct stat S;
393   /* find the block device file */
394   if ((stat(path, &S)<0) ||
395       (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False);
396 #endif
397
398   euser_id = geteuid();
399
400 #ifdef USE_SETRES
401   {
402     uid_t user_id;
403
404     /* for HPUX, real uid must be same as euid to execute quotactl for euid */
405     user_id = getuid();
406     setresuid(euser_id,-1,-1);
407     r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
408     if (setresuid(user_id,-1,-1))
409       DEBUG(5,("Unable to reset uid to %d\n", user_id));
410   }
411 #else /* USE_SETRES */
412 #if defined(__FreeBSD__)
413   r= quotactl(path,Q_GETQUOTA,euser_id,(char *) &D);
414 #elif defined(AIX)
415   /* AIX has both USER and GROUP quotas: 
416      Get the USER quota (ohnielse@fysik.dtu.dk) */
417   r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
418 #else /* !__FreeBSD__ && !AIX */
419   r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
420 #endif /* !__FreeBSD__ && !AIX */
421 #endif /* USE_SETRES */
422
423   /* Use softlimit to determine disk space, except when it has been exceeded */
424   *bsize = 1024;
425   if (r)
426     {
427       if (errno == EDQUOT) 
428         {
429           *dfree =0;
430           *dsize =D.dqb_curblocks;
431           return (True);
432         }
433       else return(False);
434     }
435   if (D.dqb_bsoftlimit==0)
436     return(False);
437   /* Use softlimit to determine disk space, except when it has been exceeded */
438   if ((D.dqb_curblocks>D.dqb_bsoftlimit)
439 #if !defined(__FreeBSD__)
440 ||((D.dqb_curfiles>D.dqb_fsoftlimit) && (D.dqb_fsoftlimit != 0))
441 #endif
442     ) {
443       *dfree = 0;
444       *dsize = D.dqb_curblocks;
445     }
446   else {
447     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
448     *dsize = D.dqb_bsoftlimit;
449   }
450   return (True);
451 }
452
453 #endif
454
455 #else
456 /* this keeps fussy compilers happy */
457  void quotas_dummy(void) {}
458 #endif /* QUOTAS */
459