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