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