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