]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/quota.c
Integrate talloc from Samba, cleanup configure functions checks
[netatalk.git] / etc / afpd / quota.c
1 /*
2  * $Id: quota.c,v 1.35 2010-04-03 07:11:35 franklahm Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #ifndef NO_QUOTA_SUPPORT
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <sys/types.h>
18 #include <string.h>
19 #include <sys/stat.h>
20 #include <sys/time.h>
21 #include <sys/param.h>
22 #ifdef HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif /* HAVE_UNISTD_H */
25 #ifdef HAVE_FCNTL_H
26 #include <fcntl.h>
27 #endif /* HAVE_FCNTL_H */
28
29 #include <atalk/logger.h>
30 #include <atalk/afp.h>
31 #include <atalk/compat.h>
32
33 #include "auth.h"
34 #include "volume.h"
35 #include "unix.h"
36
37 /*
38 #define DEBUG_QUOTA 0
39 */
40
41 #define WANT_USER_QUOTA 0
42 #define WANT_GROUP_QUOTA 1
43
44 #ifdef NEED_QUOTACTL_WRAPPER
45 int quotactl(int cmd, const char *special, int id, caddr_t addr)
46 {
47     return syscall(__NR_quotactl, cmd, special, id, addr);
48 }
49 #endif /* NEED_QUOTACTL_WRAPPER */
50
51 static int overquota( struct dqblk *);
52
53 #ifdef linux
54
55 #ifdef HAVE_LINUX_XQM_H
56 #include <linux/xqm.h>
57 #else
58 #ifdef HAVE_XFS_XQM_H
59 #include <xfs/xqm.h>
60 #define HAVE_LINUX_XQM_H
61 #else
62 #ifdef  HAVE_LINUX_DQBLK_XFS_H
63 #include <linux/dqblk_xfs.h>
64 #define HAVE_LINUX_XQM_H
65 #endif /* HAVE_LINUX_DQBLK_XFS_H */
66 #endif /* HAVE_XFS_XQM_H */
67 #endif /* HAVE_LINUX_XQM_H */
68
69 #include <linux/unistd.h>
70
71 static int is_xfs = 0;
72
73 static int get_linux_xfs_quota(int, char*, uid_t, struct dqblk *);
74 static int get_linux_fs_quota(int, char*, uid_t, struct dqblk *);
75
76 /* format supported by current kernel */
77 static int kernel_iface = IFACE_UNSET;
78
79 /*
80 **  Check kernel quota version
81 **  Taken from quota-tools 3.08 by Jan Kara <jack@suse.cz>
82 */
83 static void linuxquota_get_api( void )
84 {
85 #ifndef LINUX_API_VERSION
86     struct stat st;
87
88     if (stat("/proc/sys/fs/quota", &st) == 0) {
89         kernel_iface = IFACE_GENERIC;
90     }
91     else {
92         struct dqstats_v2 v2_stats;
93         struct sigaction  sig;
94         struct sigaction  oldsig;
95
96         /* This signal handling is needed because old kernels send us SIGSEGV as they try to resolve the device */
97         sig.sa_handler   = SIG_IGN;
98         sig.sa_sigaction = NULL;
99         sig.sa_flags     = 0;
100         sigemptyset(&sig.sa_mask);
101         if (sigaction(SIGSEGV, &sig, &oldsig) < 0) {
102             LOG( log_error, logtype_afpd, "cannot set SEGV signal handler: %s", strerror(errno));
103             goto failure;
104         }
105         if (quotactl(QCMD(Q_V2_GETSTATS, 0), NULL, 0, (void *)&v2_stats) >= 0) {
106             kernel_iface = IFACE_VFSV0;
107         }
108         else if (errno != ENOSYS && errno != ENOTSUP) {
109             /* RedHat 7.1 (2.4.2-2) newquota check 
110              * Q_V2_GETSTATS in it's old place, Q_GETQUOTA in the new place
111              * (they haven't moved Q_GETSTATS to its new value) */
112             int err_stat = 0;
113             int err_quota = 0;
114             char tmp[1024];         /* Just temporary buffer */
115
116             if (quotactl(QCMD(Q_V1_GETSTATS, 0), NULL, 0, tmp))
117                 err_stat = errno;
118             if (quotactl(QCMD(Q_V1_GETQUOTA, 0), "/dev/null", 0, tmp))
119                 err_quota = errno;
120
121             /* On a RedHat 2.4.2-2      we expect 0, EINVAL
122              * On a 2.4.x               we expect 0, ENOENT
123              * On a 2.4.x-ac    we wont get here */
124             if (err_stat == 0 && err_quota == EINVAL) {
125                 kernel_iface = IFACE_VFSV0;
126             }
127             else {
128                 kernel_iface = IFACE_VFSOLD;
129             }
130         }
131         else {
132             /* This branch is *not* in quota-tools 3.08
133             ** but without it quota version is not correctly
134             ** identified for the original SuSE 8.0 kernel */
135             unsigned int vers_no;
136             FILE * qf;
137
138             if ((qf = fopen("/proc/fs/quota", "r"))) {
139                 if (fscanf(qf, "Version %u", &vers_no) == 1) {
140                     if ( (vers_no == (6*10000 + 5*100 + 0)) ||
141                          (vers_no == (6*10000 + 5*100 + 1)) ) {
142                         kernel_iface = IFACE_VFSV0;
143                     }
144                 }
145                 fclose(qf);
146             }
147         }
148         if (sigaction(SIGSEGV, &oldsig, NULL) < 0) {
149             LOG(log_error, logtype_afpd, "cannot reset signal handler: %s", strerror(errno));
150             goto failure;
151         }
152     }
153
154 failure:
155     if (kernel_iface == IFACE_UNSET)
156        kernel_iface = IFACE_VFSOLD;
157
158 #else /* defined LINUX_API_VERSION */
159     kernel_iface = LINUX_API_VERSION;
160 #endif
161 }
162
163 /****************************************************************************/
164
165 static int get_linux_quota(int what, char *path, uid_t euser_id, struct dqblk *dp)
166 {
167         int r; /* result */
168
169         if ( is_xfs )
170                 r=get_linux_xfs_quota(what, path, euser_id, dp);
171         else
172                 r=get_linux_fs_quota(what, path, euser_id, dp);
173     
174         return r;
175 }
176
177 /****************************************************************************
178  Abstract out the XFS Quota Manager quota get call.
179 ****************************************************************************/
180
181 static int get_linux_xfs_quota(int what, char *path, uid_t euser_id, struct dqblk *dqb)
182 {
183         int ret = -1;
184 #ifdef HAVE_LINUX_XQM_H
185         struct fs_disk_quota D;
186         
187         memset (&D, 0, sizeof(D));
188
189         if ((ret = quotactl(QCMD(Q_XGETQUOTA,(what ? GRPQUOTA : USRQUOTA)), path, euser_id, (caddr_t)&D)))
190                return ret;
191
192         dqb->bsize = (u_int64_t)512;
193         dqb->dqb_bsoftlimit  = (u_int64_t)D.d_blk_softlimit;
194         dqb->dqb_bhardlimit  = (u_int64_t)D.d_blk_hardlimit;
195         dqb->dqb_ihardlimit  = (u_int64_t)D.d_ino_hardlimit;
196         dqb->dqb_isoftlimit  = (u_int64_t)D.d_ino_softlimit;
197         dqb->dqb_curinodes   = (u_int64_t)D.d_icount;
198         dqb->dqb_curblocks   = (u_int64_t)D.d_bcount; 
199 #endif
200        return ret;
201 }
202
203 /*
204 ** Wrapper for the quotactl(GETQUOTA) call.
205 ** For API v2 the results are copied back into a v1 structure.
206 ** Taken from quota-1.4.8 perl module
207 */
208 static int get_linux_fs_quota(int what, char *path, uid_t euser_id, struct dqblk *dqb)
209 {
210         int ret;
211
212         if (kernel_iface == IFACE_UNSET)
213                 linuxquota_get_api();
214
215         if (kernel_iface == IFACE_GENERIC)
216         {
217                 struct dqblk_v3 dqb3;
218
219                 ret = quotactl(QCMD(Q_V3_GETQUOTA, (what ? GRPQUOTA : USRQUOTA)), path, euser_id, (caddr_t) &dqb3);
220                 if (ret == 0)
221                 {
222                         dqb->dqb_bhardlimit = dqb3.dqb_bhardlimit;
223                         dqb->dqb_bsoftlimit = dqb3.dqb_bsoftlimit;
224                         dqb->dqb_curblocks  = dqb3.dqb_curspace / DEV_QBSIZE;
225                         dqb->dqb_ihardlimit = dqb3.dqb_ihardlimit;
226                         dqb->dqb_isoftlimit = dqb3.dqb_isoftlimit;
227                         dqb->dqb_curinodes  = dqb3.dqb_curinodes;
228                         dqb->dqb_btime      = dqb3.dqb_btime;
229                         dqb->dqb_itime      = dqb3.dqb_itime;
230                         dqb->bsize          = DEV_QBSIZE;
231                 }
232         }
233         else if (kernel_iface == IFACE_VFSV0)
234         {
235                 struct dqblk_v2 dqb2;
236
237                 ret = quotactl(QCMD(Q_V2_GETQUOTA, (what ? GRPQUOTA : USRQUOTA)), path, euser_id, (caddr_t) &dqb2);
238                 if (ret == 0)
239                 {
240                         dqb->dqb_bhardlimit = dqb2.dqb_bhardlimit;
241                         dqb->dqb_bsoftlimit = dqb2.dqb_bsoftlimit;
242                         dqb->dqb_curblocks  = dqb2.dqb_curspace / DEV_QBSIZE;
243                         dqb->dqb_ihardlimit = dqb2.dqb_ihardlimit;
244                         dqb->dqb_isoftlimit = dqb2.dqb_isoftlimit;
245                         dqb->dqb_curinodes  = dqb2.dqb_curinodes;
246                         dqb->dqb_btime      = dqb2.dqb_btime;
247                         dqb->dqb_itime      = dqb2.dqb_itime;
248                         dqb->bsize          = DEV_QBSIZE;
249                 }
250         }
251         else /* if (kernel_iface == IFACE_VFSOLD) */
252         {
253                 struct dqblk_v1 dqb1;
254
255                 ret = quotactl(QCMD(Q_V1_GETQUOTA, (what ? GRPQUOTA : USRQUOTA)), path, euser_id, (caddr_t) &dqb1);
256                 if (ret == 0)
257                 {
258                         dqb->dqb_bhardlimit = dqb1.dqb_bhardlimit;
259                         dqb->dqb_bsoftlimit = dqb1.dqb_bsoftlimit;
260                         dqb->dqb_curblocks  = dqb1.dqb_curblocks;
261                         dqb->dqb_ihardlimit = dqb1.dqb_ihardlimit;
262                         dqb->dqb_isoftlimit = dqb1.dqb_isoftlimit;
263                         dqb->dqb_curinodes  = dqb1.dqb_curinodes;
264                         dqb->dqb_btime      = dqb1.dqb_btime;
265                         dqb->dqb_itime      = dqb1.dqb_itime;
266                         dqb->bsize          = DEV_QBSIZE;
267                 }
268         }
269         return ret;
270 }
271
272 #endif /* linux */
273
274 #if defined(HAVE_SYS_MNTTAB_H) || defined(__svr4__)
275 /*
276  * Return the mount point associated with the filesystem
277  * on which "file" resides.  Returns NULL on failure.
278  */
279 static char *
280 mountp( char *file, int *nfs)
281 {
282     struct stat                 sb;
283     FILE                        *mtab;
284     dev_t                       devno;
285     static struct mnttab        mnt;
286
287     if ( lstat( file, &sb ) < 0 ) {
288         return( NULL );
289     }
290     devno = sb.st_dev;
291
292     if (( mtab = fopen( "/etc/mnttab", "r" )) == NULL ) {
293         return( NULL );
294     }
295
296     while ( getmntent( mtab, &mnt ) == 0 ) {
297         /* local fs */
298         if ( (lstat( mnt.mnt_special, &sb ) == 0) && (devno == sb.st_rdev)) {
299             fclose( mtab );
300             return mnt.mnt_mountp;
301         }
302
303         /* check for nfs. we probably should use
304          * strcmp(mnt.mnt_fstype, MNTTYPE_NFS), but that's not as fast. */
305         if ((lstat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev) &&
306                 strchr(mnt.mnt_special, ':')) {
307             *nfs = 1;
308             fclose( mtab );
309             return mnt.mnt_special;
310         }
311     }
312
313     fclose( mtab );
314     return( NULL );
315 }
316
317 #else /* __svr4__ */
318 #ifdef ultrix
319 /*
320 * Return the block-special device name associated with the filesystem
321 * on which "file" resides.  Returns NULL on failure.
322 */
323
324 static char *
325 special( char *file, int *nfs)
326 {
327     static struct fs_data       fsd;
328
329     if ( getmnt(0, &fsd, 0, STAT_ONE, file ) < 0 ) {
330         LOG(log_info, logtype_afpd, "special: getmnt %s: %s", file, strerror(errno) );
331         return( NULL );
332     }
333
334     /* XXX: does this really detect an nfs mounted fs? */
335     if (strchr(fsd.fd_req.devname, ':'))
336         *nfs = 1;
337     return( fsd.fd_req.devname );
338 }
339
340 #else /* ultrix */
341 #if (defined(HAVE_SYS_MOUNT_H) && !defined(__linux__)) || defined(BSD4_4) || defined(_IBMR2)
342
343 static char *
344 special(char *file, int *nfs)
345 {
346     static struct statfs        sfs;
347
348     if ( statfs( file, &sfs ) < 0 ) {
349         return( NULL );
350     }
351
352 #ifdef TRU64
353     /* Digital UNIX: The struct sfs contains a field sfs.f_type,
354      * the MOUNT_* constants are defined in <sys/mount.h> */
355     if ((sfs.f_type == MOUNT_NFS)||(sfs.f_type == MOUNT_NFS3))
356 #else /* TRU64 */
357     /* XXX: make sure this really detects an nfs mounted fs */
358     if (strchr(sfs.f_mntfromname, ':'))
359 #endif /* TRU64 */
360         *nfs = 1;
361     return( sfs.f_mntfromname );
362 }
363
364 #else /* BSD4_4 */
365
366 static char *
367 special(char *file, int *nfs)
368 {
369     struct stat         sb;
370     FILE                *mtab;
371     dev_t               devno;
372     struct mntent       *mnt;
373     int                 found=0;
374
375     if ( lstat( file, &sb ) < 0 ) {
376         return( NULL );
377     }
378     devno = sb.st_dev;
379
380     if (( mtab = setmntent( "/etc/mtab", "r" )) == NULL ) {
381         return( NULL );
382     }
383
384     while (( mnt = getmntent( mtab )) != NULL ) {
385         /* check for local fs */
386         if ( (lstat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
387             found = 1;
388             break;
389         }
390
391         /* check for an nfs mount entry. the alternative is to use
392         * strcmp(mnt->mnt_type, MNTTYPE_NFS) instead of the strchr. */
393         if ((lstat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
394                 strchr(mnt->mnt_fsname, ':')) {
395             *nfs = 1;
396             found = 1;
397             break;
398         }
399     }
400
401     endmntent( mtab );
402
403     if (!found)
404         return (NULL);
405 #ifdef linux
406     if (strcmp(mnt->mnt_type, "xfs") == 0)
407         is_xfs = 1;
408 #endif
409         
410     return( mnt->mnt_fsname );
411 }
412
413 #endif /* BSD4_4 */
414 #endif /* ultrix */
415 #endif /* __svr4__ */
416
417
418 static int getfsquota(struct vol *vol, const int uid, struct dqblk *dq)
419
420 {
421         struct dqblk dqg;
422
423 #ifdef __svr4__
424     struct quotctl      qc;
425 #endif
426
427     memset(&dqg, 0, sizeof(dqg));
428         
429 #ifdef __svr4__
430     qc.op = Q_GETQUOTA;
431     qc.uid = uid;
432     qc.addr = (caddr_t)dq;
433     if ( ioctl( vol->v_qfd, Q_QUOTACTL, &qc ) < 0 ) {
434         return( AFPERR_PARAM );
435     }
436
437 #else /* __svr4__ */
438 #ifdef ultrix
439     if ( quota( Q_GETDLIM, uid, vol->v_gvs, dq ) != 0 ) {
440         return( AFPERR_PARAM );
441     }
442 #else /* ultrix */
443
444 #ifndef USRQUOTA
445 #define USRQUOTA   0
446 #endif
447
448 #ifndef QCMD
449 #define QCMD(a,b)  (a)
450 #endif
451
452 #ifndef TRU64
453     /* for group quotas. we only use these if the user belongs
454     * to one group. */
455 #endif /* TRU64 */
456
457 #ifdef BSD4_4
458     if ( seteuid( getuid() ) == 0 ) {
459         if ( quotactl( vol->v_path, QCMD(Q_GETQUOTA,USRQUOTA),
460                        uid, (char *)dq ) != 0 ) {
461             /* try group quotas */
462             if (ngroups >= 1) {
463                 if ( quotactl(vol->v_path, QCMD(Q_GETQUOTA, GRPQUOTA),
464                               groups[0], (char *) &dqg) != 0 ) {
465                     seteuid( uid );
466                     return( AFPERR_PARAM );
467                 }
468             }
469         }
470         seteuid( uid );
471     }
472
473 #elif defined(TRU64)
474     if ( seteuid( getuid() ) == 0 ) {
475         if ( quotactl( vol->v_path, QCMD(Q_GETQUOTA, USRQUOTA),
476                        uid, (char *)dq ) != 0 ) {
477             seteuid( uid );
478             return ( AFPERR_PARAM );
479         }
480         seteuid( uid );
481     }
482
483 #else /* BSD4_4 */
484     if (get_linux_quota (WANT_USER_QUOTA, vol->v_gvs, uid, dq) !=0) {
485         return( AFPERR_PARAM );
486     }
487
488     if (get_linux_quota(WANT_GROUP_QUOTA, vol->v_gvs, getegid(),  &dqg) != 0) {
489 #ifdef DEBUG_QUOTA
490         LOG(log_debug, logtype_afpd, "group quota did not work!" );
491 #endif /* DEBUG_QUOTA */
492
493         return AFP_OK; /* no need to check user vs group quota */
494     }
495 #endif  /* BSD4_4 */
496
497
498 #ifndef TRU64
499     /* return either the group quota entry or user quota entry,
500        whichever has the least amount of space remaining
501     */
502
503     /* if user space remaining > group space remaining */
504     if( 
505         /* if overquota, free space is 0 otherwise hard-current */
506         ( overquota( dq ) ? 0 : ( dq->dqb_bhardlimit ? dq->dqb_bhardlimit - 
507                                   dq->dqb_curblocks : ~((u_int64_t) 0) ) )
508
509       >
510         
511         ( overquota( &dqg ) ? 0 : ( dqg.dqb_bhardlimit ? dqg.dqb_bhardlimit - 
512                                     dqg.dqb_curblocks : ~((u_int64_t) 0) ) )
513
514       ) /* if */
515     {
516         /* use group quota limits rather than user limits */
517         dq->dqb_curblocks = dqg.dqb_curblocks;
518         dq->dqb_bhardlimit = dqg.dqb_bhardlimit;
519         dq->dqb_bsoftlimit = dqg.dqb_bsoftlimit;
520         dq->dqb_btimelimit = dqg.dqb_btimelimit;
521     } /* if */
522
523 #endif /* TRU64 */
524
525 #endif /* ultrix */
526 #endif /* __svr4__ */
527
528     return AFP_OK;
529 }
530
531
532 static int getquota( struct vol *vol, struct dqblk *dq, const u_int32_t bsize)
533 {
534     char *p;
535
536 #ifdef __svr4__
537     char                buf[ MAXPATHLEN + 1];
538
539     if ( vol->v_qfd == -1 && vol->v_gvs == NULL) {
540         if (( p = mountp( vol->v_path, &vol->v_nfs)) == NULL ) {
541             LOG(log_info, logtype_afpd, "getquota: mountp %s fails", vol->v_path );
542             return( AFPERR_PARAM );
543         }
544
545         if (vol->v_nfs) {
546             if (( vol->v_gvs = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
547                 LOG(log_error, logtype_afpd, "getquota: malloc: %s", strerror(errno) );
548                 return AFPERR_MISC;
549             }
550             strcpy( vol->v_gvs, p );
551
552         } else {
553             sprintf( buf, "%s/quotas", p );
554             if (( vol->v_qfd = open( buf, O_RDONLY, 0 )) < 0 ) {
555                 LOG(log_info, logtype_afpd, "open %s: %s", buf, strerror(errno) );
556                 return( AFPERR_PARAM );
557             }
558         }
559
560     }
561 #else
562     if ( vol->v_gvs == NULL ) {
563         if (( p = special( vol->v_path, &vol->v_nfs )) == NULL ) {
564             LOG(log_info, logtype_afpd, "getquota: special %s fails", vol->v_path );
565             return( AFPERR_PARAM );
566         }
567
568         if (( vol->v_gvs = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
569             LOG(log_error, logtype_afpd, "getquota: malloc: %s", strerror(errno) );
570             return AFPERR_MISC;
571         }
572         strcpy( vol->v_gvs, p );
573     }
574 #endif
575
576 #ifdef TRU64
577     /* Digital UNIX: Two forms of specifying an NFS filesystem are possible,
578        either 'hostname:path' or 'path@hostname' (Ultrix heritage) */
579     if (vol->v_nfs) {
580         char *hostpath;
581         char pathstring[MNAMELEN];
582         /* MNAMELEN ist defined in <sys/mount.h> */
583         int result;
584         
585         if ((hostpath = strchr(vol->v_gvs,'@')) != NULL ) {
586             /* convert 'path@hostname' to 'hostname:path',
587              * call getnfsquota(),
588              * convert 'hostname:path' back to 'path@hostname' */
589             *hostpath = '\0';
590             sprintf(pathstring,"%s:%s",hostpath+1,vol->v_gvs);
591             strcpy(vol->v_gvs,pathstring);
592             
593             result = getnfsquota(vol, uuid, bsize, dq);
594             
595             hostpath = strchr(vol->v_gvs,':');
596             *hostpath = '\0';
597             sprintf(pathstring,"%s@%s",hostpath+1,vol->v_gvs);
598             strcpy(vol->v_gvs,pathstring);
599             
600             return result;
601         }
602         else
603             /* vol->v_gvs is of the form 'hostname:path' */
604             return getnfsquota(vol, uuid, bsize, dq);
605     } else
606         /* local filesystem */
607         return getfsquota(vol, uuid, dq);
608            
609 #else /* TRU64 */
610     return vol->v_nfs ? getnfsquota(vol, uuid, bsize, dq) :
611            getfsquota(vol, uuid, dq);
612 #endif /* TRU64 */
613 }
614
615 static int overquota( struct dqblk *dqblk)
616 {
617     struct timeval      tv;
618
619     if ( dqblk->dqb_curblocks > dqblk->dqb_bhardlimit &&
620          dqblk->dqb_bhardlimit != 0 ) {
621         return( 1 );
622     }
623
624     if ( dqblk->dqb_curblocks < dqblk->dqb_bsoftlimit ||
625          dqblk->dqb_bsoftlimit == 0 ) {
626         return( 0 );
627     }
628 #ifdef ultrix
629     if ( dqblk->dqb_bwarn ) {
630         return( 0 );
631     }
632 #else /* ultrix */
633     if ( gettimeofday( &tv, NULL ) < 0 ) {
634         LOG(log_error, logtype_afpd, "overquota: gettimeofday: %s", strerror(errno) );
635         return( AFPERR_PARAM );
636     }
637     if ( dqblk->dqb_btimelimit && dqblk->dqb_btimelimit > tv.tv_sec ) {
638         return( 0 );
639     }
640 #endif /* ultrix */
641     return( 1 );
642 }
643
644 /*
645  * This next bit is basically for linux -- everything is fine
646  * if you use 1k blocks... but if you try (for example) to mount
647  * a volume via nfs from a netapp (which might use 4k blocks) everything
648  * gets reported improperly.  I have no idea about dbtob on other
649  * platforms.
650  */
651
652 #ifdef HAVE_BROKEN_DBTOB
653 #undef dbtob
654 #define dbtob(a, b)     ((VolSpace)((VolSpace)(a) * (VolSpace)(b)))
655 #define HAVE_2ARG_DBTOB
656 #endif
657
658 #ifndef dbtob
659 #define dbtob(a)       ((a) << 10)
660 #endif
661
662 /* i do the cast to VolSpace here to make sure that 64-bit shifts
663    work */
664 #ifdef HAVE_2ARG_DBTOB
665 #define tobytes(a, b)  dbtob((VolSpace) (a), (VolSpace) (b))
666 #else 
667 #define tobytes(a, b)  dbtob((VolSpace) (a))
668 #endif
669
670 int uquota_getvolspace( struct vol *vol, VolSpace *bfree, VolSpace *btotal, const u_int32_t bsize)
671 {
672         u_int64_t this_bsize;
673         struct dqblk dqblk;
674
675         this_bsize = bsize;
676                         
677         if (getquota( vol, &dqblk, bsize) != 0 ) {
678                 return( AFPERR_PARAM );
679         }
680
681 #ifdef linux
682         this_bsize = dqblk.bsize;
683 #endif
684
685 #ifdef DEBUG_QUOTA
686         LOG(log_debug, logtype_afpd, "after calling getquota in uquota_getvolspace!" );
687         LOG(log_debug, logtype_afpd, "dqb_ihardlimit: %u", dqblk.dqb_ihardlimit );
688         LOG(log_debug, logtype_afpd, "dqb_isoftlimit: %u", dqblk.dqb_isoftlimit );
689         LOG(log_debug, logtype_afpd, "dqb_curinodes : %u", dqblk.dqb_curinodes );
690         LOG(log_debug, logtype_afpd, "dqb_bhardlimit: %u", dqblk.dqb_bhardlimit );
691         LOG(log_debug, logtype_afpd, "dqb_bsoftlimit: %u", dqblk.dqb_bsoftlimit );
692         LOG(log_debug, logtype_afpd, "dqb_curblocks : %u", dqblk.dqb_curblocks );
693         LOG(log_debug, logtype_afpd, "dqb_btime     : %u", dqblk.dqb_btime );
694         LOG(log_debug, logtype_afpd, "dqb_itime     : %u", dqblk.dqb_itime );
695         LOG(log_debug, logtype_afpd, "bsize/this_bsize : %u/%u", bsize, this_bsize );
696         LOG(log_debug, logtype_afpd, "dqblk.dqb_bhardlimit size: %u", tobytes( dqblk.dqb_bhardlimit, this_bsize ));
697         LOG(log_debug, logtype_afpd, "dqblk.dqb_bsoftlimit size: %u", tobytes( dqblk.dqb_bsoftlimit, this_bsize ));
698         LOG(log_debug, logtype_afpd, "dqblk.dqb_curblocks  size: %u", tobytes( dqblk.dqb_curblocks, this_bsize ));
699 #endif /* DEBUG_QUOTA */ 
700
701         /* no limit set for this user. it might be set in the future. */
702         if (dqblk.dqb_bsoftlimit == 0 && dqblk.dqb_bhardlimit == 0) {
703                 *btotal = *bfree = ~((VolSpace) 0);
704         } else if ( overquota( &dqblk )) {
705                 if ( tobytes( dqblk.dqb_curblocks, this_bsize ) > tobytes( dqblk.dqb_bsoftlimit, this_bsize ) ) {
706                         *btotal = tobytes( dqblk.dqb_curblocks, this_bsize );
707                         *bfree = 0;
708                 }
709                 else {
710                         *btotal = tobytes( dqblk.dqb_bsoftlimit, this_bsize );
711                         *bfree  = tobytes( dqblk.dqb_bsoftlimit, this_bsize ) -
712                                   tobytes( dqblk.dqb_curblocks, this_bsize );
713                 }
714         } else {
715                 *btotal = tobytes( dqblk.dqb_bhardlimit, this_bsize );
716                 *bfree  = tobytes( dqblk.dqb_bhardlimit, this_bsize  ) -
717                           tobytes( dqblk.dqb_curblocks, this_bsize );
718         }
719
720 #ifdef DEBUG_QUOTA
721         LOG(log_debug, logtype_afpd, "bfree          : %u", *bfree );
722         LOG(log_debug, logtype_afpd, "btotal         : %u", *btotal );
723         LOG(log_debug, logtype_afpd, "bfree          : %uKB", *bfree/1024 );
724         LOG(log_debug, logtype_afpd, "btotal         : %uKB", *btotal/1024 );
725 #endif
726
727         return( AFP_OK );
728 }
729 #endif