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