Makefile: Added quoata changes for Linux from Thorvald Natvig
[tprouty/samba.git] / source / 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   char dev_disk[256];
54   struct dqblk D;
55   struct stat S;
56   FILE *fp;
57   struct mntent *mnt;
58   int devno;
59   int found;
60   
61   /* find the block device file */
62   
63   if ( stat(path, &S) == -1 ) {
64     return(False) ;
65   }
66
67   devno = S.st_dev ;
68   
69   fp = setmntent(MOUNTED,"r");
70   found = False ;
71   
72   while ((mnt = getmntent(fp))) {
73     if ( stat(mnt->mnt_dir,&S) == -1 )
74       continue ;
75     if (S.st_dev == devno) {
76       found = True ;
77       break ;
78     }
79   }
80   endmntent(fp) ;
81   
82   if (!found) {
83       return(False);
84     }
85
86   euser_id=geteuid();
87   seteuid(0);  
88   r=quotactl(QCMD(Q_GETQUOTA,USRQUOTA), mnt->mnt_fsname, euser_id, (caddr_t)&D);
89       seteuid(euser_id);
90
91   /* Use softlimit to determine disk space, except when it has been exceeded */
92   *bsize = 1024;
93   if (r)
94     {
95       if (errno == EDQUOT) 
96        {
97          *dfree =0;
98          *dsize =D.dqb_curblocks;
99          return (True);
100     }
101       else return(False);
102   }
103   /* Use softlimit to determine disk space, except when it has been exceeded */
104   if (
105       (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) ||
106       (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) ||
107       (D.dqb_isoftlimit && D.dqb_curinodes>=D.dqb_isoftlimit) ||
108       (D.dqb_ihardlimit && D.dqb_curinodes>=D.dqb_ihardlimit)
109      )
110     {
111       *dfree = 0;
112       *dsize = D.dqb_curblocks;
113     }
114   else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0)
115     {
116       return(False);
117     }
118   else {
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   struct stat sbuf;
138   dev_t devno ;
139   static dev_t devno_cached = 0 ;
140   static char name[MNTMAXSTR] ;
141   struct q_request request ;
142   struct qf_header header ;
143   static int quota_default = 0 ;
144   int found ;
145   
146   if ( 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 ( 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     strcpy(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 char name[MNT_LINE_MAX] ;
252 #else
253   struct mntent *mnt;
254   static char name[MNTMAXSTR] ;
255 #endif
256   FILE *fd;
257   struct stat sbuf;
258   dev_t devno ;
259   static dev_t devno_cached = 0 ;
260   int found ;
261   
262   if ( 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 ( 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     strcpy(name,mnt.mnt_mountp) ;
286     strcat(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 ( 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     strcpy(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 #else
368
369 #ifdef        __FreeBSD__
370 #include <ufs/ufs/quota.h>
371 #else
372 #include <sys/quota.h>
373 #include <devnm.h>
374 #endif
375
376 /****************************************************************************
377 try to get the disk space from disk quotas - default version
378 ****************************************************************************/
379 BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
380 {
381   uid_t user_id, euser_id;
382   int r;
383   char dev_disk[256];
384   struct dqblk D;
385   struct stat S;
386 #ifndef __FreeBSD__
387   /* find the block device file */
388   if ((stat(path, &S)<0) ||
389       (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False);
390 #endif
391
392   euser_id = geteuid();
393
394 #ifdef USE_SETRES
395   /* for HPUX, real uid must be same as euid to execute quotactl for euid */
396   user_id = getuid();
397   setresuid(euser_id,-1,-1);
398   r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
399   if (setresuid(user_id,-1,-1))
400     DEBUG(5,("Unable to reset uid to %d\n", user_id));
401 #else
402 #if defined(__FreeBSD__)
403   r= quotactl(path,Q_GETQUOTA,euser_id,(char *) &D);
404 #else
405   r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
406 #endif
407 #endif
408
409   /* Use softlimit to determine disk space, except when it has been exceeded */
410   *bsize = 1024;
411   if (r)
412     {
413       if (errno == EDQUOT) 
414         {
415           *dfree =0;
416           *dsize =D.dqb_curblocks;
417           return (True);
418         }
419       else return(False);
420     }
421   if (D.dqb_bsoftlimit==0)
422     return(False);
423   /* Use softlimit to determine disk space, except when it has been exceeded */
424   if ((D.dqb_curblocks>D.dqb_bsoftlimit)
425 #if !defined(__FreeBSD__)
426 ||(D.dqb_curfiles>D.dqb_fsoftlimit)
427 #endif
428     ) {
429       *dfree = 0;
430       *dsize = D.dqb_curblocks;
431     }
432   else {
433     *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
434     *dsize = D.dqb_bsoftlimit;
435   }
436   return (True);
437 }
438
439 #endif
440
441 #else
442 /* this keeps fussy compilers happy */
443  void quotas_dummy(void) {}
444 #endif /* QUOTAS */
445