]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/quota.c
Symlink patch from Anton Starikov
[netatalk.git] / etc / afpd / quota.c
1 /*
2  * $Id: quota.c,v 1.32.2.1 2010-01-02 10:22:32 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 */
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(int what, char *path, uid_t euser_id, struct dqblk *dqb)
194 {
195         int ret = -1;
196 #ifdef HAVE_LINUX_XQM_H
197         struct fs_disk_quota D;
198         
199         memset (&D, 0, sizeof(D));
200
201         if ((ret = quotactl(QCMD(Q_XGETQUOTA,(what ? GRPQUOTA : USRQUOTA)), path, euser_id, (caddr_t)&D)))
202                return ret;
203
204         dqb->bsize = (u_int64_t)512;
205         dqb->dqb_bsoftlimit  = (u_int64_t)D.d_blk_softlimit;
206         dqb->dqb_bhardlimit  = (u_int64_t)D.d_blk_hardlimit;
207         dqb->dqb_ihardlimit  = (u_int64_t)D.d_ino_hardlimit;
208         dqb->dqb_isoftlimit  = (u_int64_t)D.d_ino_softlimit;
209         dqb->dqb_curinodes   = (u_int64_t)D.d_icount;
210         dqb->dqb_curblocks   = (u_int64_t)D.d_bcount; 
211 #endif
212        return ret;
213 }
214
215 /*
216 ** Wrapper for the quotactl(GETQUOTA) call.
217 ** For API v2 the results are copied back into a v1 structure.
218 ** Taken from quota-1.4.8 perl module
219 */
220 static int get_linux_fs_quota(int what, char *path, uid_t euser_id, struct dqblk *dqb)
221 {
222         int ret;
223
224         if (kernel_iface == IFACE_UNSET)
225                 linuxquota_get_api();
226
227         if (kernel_iface == IFACE_GENERIC)
228         {
229                 struct dqblk_v3 dqb3;
230
231                 ret = quotactl(QCMD(Q_V3_GETQUOTA, (what ? GRPQUOTA : USRQUOTA)), path, euser_id, (caddr_t) &dqb3);
232                 if (ret == 0)
233                 {
234                         dqb->dqb_bhardlimit = dqb3.dqb_bhardlimit;
235                         dqb->dqb_bsoftlimit = dqb3.dqb_bsoftlimit;
236                         dqb->dqb_curblocks  = dqb3.dqb_curspace / DEV_QBSIZE;
237                         dqb->dqb_ihardlimit = dqb3.dqb_ihardlimit;
238                         dqb->dqb_isoftlimit = dqb3.dqb_isoftlimit;
239                         dqb->dqb_curinodes  = dqb3.dqb_curinodes;
240                         dqb->dqb_btime      = dqb3.dqb_btime;
241                         dqb->dqb_itime      = dqb3.dqb_itime;
242                         dqb->bsize          = DEV_QBSIZE;
243                 }
244         }
245         else if (kernel_iface == IFACE_VFSV0)
246         {
247                 struct dqblk_v2 dqb2;
248
249                 ret = quotactl(QCMD(Q_V2_GETQUOTA, (what ? GRPQUOTA : USRQUOTA)), path, euser_id, (caddr_t) &dqb2);
250                 if (ret == 0)
251                 {
252                         dqb->dqb_bhardlimit = dqb2.dqb_bhardlimit;
253                         dqb->dqb_bsoftlimit = dqb2.dqb_bsoftlimit;
254                         dqb->dqb_curblocks  = dqb2.dqb_curspace / DEV_QBSIZE;
255                         dqb->dqb_ihardlimit = dqb2.dqb_ihardlimit;
256                         dqb->dqb_isoftlimit = dqb2.dqb_isoftlimit;
257                         dqb->dqb_curinodes  = dqb2.dqb_curinodes;
258                         dqb->dqb_btime      = dqb2.dqb_btime;
259                         dqb->dqb_itime      = dqb2.dqb_itime;
260                         dqb->bsize          = DEV_QBSIZE;
261                 }
262         }
263         else /* if (kernel_iface == IFACE_VFSOLD) */
264         {
265                 struct dqblk_v1 dqb1;
266
267                 ret = quotactl(QCMD(Q_V1_GETQUOTA, (what ? GRPQUOTA : USRQUOTA)), path, euser_id, (caddr_t) &dqb1);
268                 if (ret == 0)
269                 {
270                         dqb->dqb_bhardlimit = dqb1.dqb_bhardlimit;
271                         dqb->dqb_bsoftlimit = dqb1.dqb_bsoftlimit;
272                         dqb->dqb_curblocks  = dqb1.dqb_curblocks;
273                         dqb->dqb_ihardlimit = dqb1.dqb_ihardlimit;
274                         dqb->dqb_isoftlimit = dqb1.dqb_isoftlimit;
275                         dqb->dqb_curinodes  = dqb1.dqb_curinodes;
276                         dqb->dqb_btime      = dqb1.dqb_btime;
277                         dqb->dqb_itime      = dqb1.dqb_itime;
278                         dqb->bsize          = DEV_QBSIZE;
279                 }
280         }
281         return ret;
282 }
283
284 #endif /* linux */
285
286 #if defined(HAVE_SYS_MNTTAB_H) || defined(__svr4__)
287 /*
288  * Return the mount point associated with the filesystem
289  * on which "file" resides.  Returns NULL on failure.
290  */
291 static char *
292 mountp( char *file, int *nfs)
293 {
294     struct stat                 sb;
295     FILE                        *mtab;
296     dev_t                       devno;
297     static struct mnttab        mnt;
298
299     if ( lstat( file, &sb ) < 0 ) {
300         return( NULL );
301     }
302     devno = sb.st_dev;
303
304     if (( mtab = fopen( "/etc/mnttab", "r" )) == NULL ) {
305         return( NULL );
306     }
307
308     while ( getmntent( mtab, &mnt ) == 0 ) {
309         /* local fs */
310         if ( (lstat( mnt.mnt_special, &sb ) == 0) && (devno == sb.st_rdev)) {
311             fclose( mtab );
312             return mnt.mnt_mountp;
313         }
314
315         /* check for nfs. we probably should use
316          * strcmp(mnt.mnt_fstype, MNTTYPE_NFS), but that's not as fast. */
317         if ((lstat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev) &&
318                 strchr(mnt.mnt_special, ':')) {
319             *nfs = 1;
320             fclose( mtab );
321             return mnt.mnt_special;
322         }
323     }
324
325     fclose( mtab );
326     return( NULL );
327 }
328
329 #else /* __svr4__ */
330 #ifdef ultrix
331 /*
332 * Return the block-special device name associated with the filesystem
333 * on which "file" resides.  Returns NULL on failure.
334 */
335
336 static char *
337 special( char *file, int *nfs)
338 {
339     static struct fs_data       fsd;
340
341     if ( getmnt(0, &fsd, 0, STAT_ONE, file ) < 0 ) {
342         LOG(log_info, logtype_afpd, "special: getmnt %s: %s", file, strerror(errno) );
343         return( NULL );
344     }
345
346     /* XXX: does this really detect an nfs mounted fs? */
347     if (strchr(fsd.fd_req.devname, ':'))
348         *nfs = 1;
349     return( fsd.fd_req.devname );
350 }
351
352 #else /* ultrix */
353 #if (defined(HAVE_SYS_MOUNT_H) && !defined(__linux__)) || defined(BSD4_4) || defined(_IBMR2)
354
355 static char *
356 special(char *file, int *nfs)
357 {
358     static struct statfs        sfs;
359
360     if ( statfs( file, &sfs ) < 0 ) {
361         return( NULL );
362     }
363
364 #ifdef TRU64
365     /* Digital UNIX: The struct sfs contains a field sfs.f_type,
366      * the MOUNT_* constants are defined in <sys/mount.h> */
367     if ((sfs.f_type == MOUNT_NFS)||(sfs.f_type == MOUNT_NFS3))
368 #else /* TRU64 */
369     /* XXX: make sure this really detects an nfs mounted fs */
370     if (strchr(sfs.f_mntfromname, ':'))
371 #endif /* TRU64 */
372         *nfs = 1;
373     return( sfs.f_mntfromname );
374 }
375
376 #else /* BSD4_4 */
377
378 static char *
379 special(char *file, int *nfs)
380 {
381     struct stat         sb;
382     FILE                *mtab;
383     dev_t               devno;
384     struct mntent       *mnt;
385     int                 found=0;
386
387     if ( lstat( file, &sb ) < 0 ) {
388         return( NULL );
389     }
390     devno = sb.st_dev;
391
392     if (( mtab = setmntent( "/etc/mtab", "r" )) == NULL ) {
393         return( NULL );
394     }
395
396     while (( mnt = getmntent( mtab )) != NULL ) {
397         /* check for local fs */
398         if ( (lstat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
399             found = 1;
400             break;
401         }
402
403         /* check for an nfs mount entry. the alternative is to use
404         * strcmp(mnt->mnt_type, MNTTYPE_NFS) instead of the strchr. */
405         if ((lstat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
406                 strchr(mnt->mnt_fsname, ':')) {
407             *nfs = 1;
408             found = 1;
409             break;
410         }
411     }
412
413     endmntent( mtab );
414
415     if (!found)
416         return (NULL);
417 #ifdef linux
418     if (strcmp(mnt->mnt_type, "xfs") == 0)
419         is_xfs = 1;
420 #endif
421         
422     return( mnt->mnt_fsname );
423 }
424
425 #endif /* BSD4_4 */
426 #endif /* ultrix */
427 #endif /* __svr4__ */
428
429
430 static int getfsquota(struct vol *vol, const int uid, struct dqblk *dq)
431
432 {
433         struct dqblk dqg;
434
435 #ifdef __svr4__
436     struct quotctl      qc;
437 #endif
438
439     memset(&dqg, 0, sizeof(dqg));
440         
441 #ifdef __svr4__
442     qc.op = Q_GETQUOTA;
443     qc.uid = uid;
444     qc.addr = (caddr_t)dq;
445     if ( ioctl( vol->v_qfd, Q_QUOTACTL, &qc ) < 0 ) {
446         return( AFPERR_PARAM );
447     }
448
449 #else /* __svr4__ */
450 #ifdef ultrix
451     if ( quota( Q_GETDLIM, uid, vol->v_gvs, dq ) != 0 ) {
452         return( AFPERR_PARAM );
453     }
454 #else /* ultrix */
455
456 #ifndef USRQUOTA
457 #define USRQUOTA   0
458 #endif
459
460 #ifndef QCMD
461 #define QCMD(a,b)  (a)
462 #endif
463
464 #ifndef TRU64
465     /* for group quotas. we only use these if the user belongs
466     * to one group. */
467 #endif /* TRU64 */
468
469 #ifdef BSD4_4
470     if ( seteuid( getuid() ) == 0 ) {
471         if ( quotactl( vol->v_path, QCMD(Q_GETQUOTA,USRQUOTA),
472                        uid, (char *)dq ) != 0 ) {
473             /* try group quotas */
474             if (ngroups >= 1) {
475                 if ( quotactl(vol->v_path, QCMD(Q_GETQUOTA, GRPQUOTA),
476                               groups[0], (char *) &dqg) != 0 ) {
477                     seteuid( uid );
478                     return( AFPERR_PARAM );
479                 }
480             }
481         }
482         seteuid( uid );
483     }
484
485 #elif defined(TRU64)
486     if ( seteuid( getuid() ) == 0 ) {
487         if ( quotactl( vol->v_path, QCMD(Q_GETQUOTA, USRQUOTA),
488                        uid, (char *)dq ) != 0 ) {
489             seteuid( uid );
490             return ( AFPERR_PARAM );
491         }
492         seteuid( uid );
493     }
494
495 #else /* BSD4_4 */
496     if (get_linux_quota (WANT_USER_QUOTA, vol->v_gvs, uid, dq) !=0) {
497         return( AFPERR_PARAM );
498     }
499
500     if (get_linux_quota(WANT_GROUP_QUOTA, vol->v_gvs, getegid(),  &dqg) != 0) {
501 #ifdef DEBUG_QUOTA
502         LOG(log_debug, logtype_afpd, "group quota did not work!" );
503 #endif /* DEBUG_QUOTA */
504
505         return AFP_OK; /* no need to check user vs group quota */
506     }
507 #endif  /* BSD4_4 */
508
509
510 #ifndef TRU64
511     /* return either the group quota entry or user quota entry,
512        whichever has the least amount of space remaining
513     */
514
515     /* if user space remaining > group space remaining */
516     if( 
517         /* if overquota, free space is 0 otherwise hard-current */
518         ( overquota( dq ) ? 0 : ( dq->dqb_bhardlimit ? dq->dqb_bhardlimit - 
519                                   dq->dqb_curblocks : ~((u_int64_t) 0) ) )
520
521       >
522         
523         ( overquota( &dqg ) ? 0 : ( dqg.dqb_bhardlimit ? dqg.dqb_bhardlimit - 
524                                     dqg.dqb_curblocks : ~((u_int64_t) 0) ) )
525
526       ) /* if */
527     {
528         /* use group quota limits rather than user limits */
529         dq->dqb_curblocks = dqg.dqb_curblocks;
530         dq->dqb_bhardlimit = dqg.dqb_bhardlimit;
531         dq->dqb_bsoftlimit = dqg.dqb_bsoftlimit;
532         dq->dqb_btimelimit = dqg.dqb_btimelimit;
533     } /* if */
534
535 #endif /* TRU64 */
536
537 #endif /* ultrix */
538 #endif /* __svr4__ */
539
540     return AFP_OK;
541 }
542
543
544 static int getquota( struct vol *vol, struct dqblk *dq, const u_int32_t bsize)
545 {
546     char *p;
547
548 #ifdef __svr4__
549     char                buf[ MAXPATHLEN + 1];
550
551     if ( vol->v_qfd == -1 && vol->v_gvs == NULL) {
552         if (( p = mountp( vol->v_path, &vol->v_nfs)) == NULL ) {
553             LOG(log_info, logtype_afpd, "getquota: mountp %s fails", vol->v_path );
554             return( AFPERR_PARAM );
555         }
556
557         if (vol->v_nfs) {
558             if (( vol->v_gvs = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
559                 LOG(log_error, logtype_afpd, "getquota: malloc: %s", strerror(errno) );
560                 return AFPERR_MISC;
561             }
562             strcpy( vol->v_gvs, p );
563
564         } else {
565             sprintf( buf, "%s/quotas", p );
566             if (( vol->v_qfd = open( buf, O_RDONLY, 0 )) < 0 ) {
567                 LOG(log_info, logtype_afpd, "open %s: %s", buf, strerror(errno) );
568                 return( AFPERR_PARAM );
569             }
570         }
571
572     }
573 #else
574     if ( vol->v_gvs == NULL ) {
575         if (( p = special( vol->v_path, &vol->v_nfs )) == NULL ) {
576             LOG(log_info, logtype_afpd, "getquota: special %s fails", vol->v_path );
577             return( AFPERR_PARAM );
578         }
579
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 #endif
587
588 #ifdef TRU64
589     /* Digital UNIX: Two forms of specifying an NFS filesystem are possible,
590        either 'hostname:path' or 'path@hostname' (Ultrix heritage) */
591     if (vol->v_nfs) {
592         char *hostpath;
593         char pathstring[MNAMELEN];
594         /* MNAMELEN ist defined in <sys/mount.h> */
595         int result;
596         
597         if ((hostpath = strchr(vol->v_gvs,'@')) != NULL ) {
598             /* convert 'path@hostname' to 'hostname:path',
599              * call getnfsquota(),
600              * convert 'hostname:path' back to 'path@hostname' */
601             *hostpath = '\0';
602             sprintf(pathstring,"%s:%s",hostpath+1,vol->v_gvs);
603             strcpy(vol->v_gvs,pathstring);
604             
605             result = getnfsquota(vol, uuid, bsize, dq);
606             
607             hostpath = strchr(vol->v_gvs,':');
608             *hostpath = '\0';
609             sprintf(pathstring,"%s@%s",hostpath+1,vol->v_gvs);
610             strcpy(vol->v_gvs,pathstring);
611             
612             return result;
613         }
614         else
615             /* vol->v_gvs is of the form 'hostname:path' */
616             return getnfsquota(vol, uuid, bsize, dq);
617     } else
618         /* local filesystem */
619         return getfsquota(vol, uuid, dq);
620            
621 #else /* TRU64 */
622     return vol->v_nfs ? getnfsquota(vol, uuid, bsize, dq) :
623            getfsquota(vol, uuid, dq);
624 #endif /* TRU64 */
625 }
626
627 static int overquota( struct dqblk *dqblk)
628 {
629     struct timeval      tv;
630
631     if ( dqblk->dqb_curblocks > dqblk->dqb_bhardlimit &&
632          dqblk->dqb_bhardlimit != 0 ) {
633         return( 1 );
634     }
635
636     if ( dqblk->dqb_curblocks < dqblk->dqb_bsoftlimit ||
637          dqblk->dqb_bsoftlimit == 0 ) {
638         return( 0 );
639     }
640 #ifdef ultrix
641     if ( dqblk->dqb_bwarn ) {
642         return( 0 );
643     }
644 #else /* ultrix */
645     if ( gettimeofday( &tv, NULL ) < 0 ) {
646         LOG(log_error, logtype_afpd, "overquota: gettimeofday: %s", strerror(errno) );
647         return( AFPERR_PARAM );
648     }
649     if ( dqblk->dqb_btimelimit && dqblk->dqb_btimelimit > tv.tv_sec ) {
650         return( 0 );
651     }
652 #endif /* ultrix */
653     return( 1 );
654 }
655
656 /*
657  * This next bit is basically for linux -- everything is fine
658  * if you use 1k blocks... but if you try (for example) to mount
659  * a volume via nfs from a netapp (which might use 4k blocks) everything
660  * gets reported improperly.  I have no idea about dbtob on other
661  * platforms.
662  */
663
664 #ifdef HAVE_BROKEN_DBTOB
665 #undef dbtob
666 #define dbtob(a, b)     ((VolSpace)((VolSpace)(a) * (VolSpace)(b)))
667 #define HAVE_2ARG_DBTOB
668 #endif
669
670 #ifndef dbtob
671 #define dbtob(a)       ((a) << 10)
672 #endif
673
674 /* i do the cast to VolSpace here to make sure that 64-bit shifts
675    work */
676 #ifdef HAVE_2ARG_DBTOB
677 #define tobytes(a, b)  dbtob((VolSpace) (a), (VolSpace) (b))
678 #else 
679 #define tobytes(a, b)  dbtob((VolSpace) (a))
680 #endif
681
682 int uquota_getvolspace( struct vol *vol, VolSpace *bfree, VolSpace *btotal, const u_int32_t bsize)
683 {
684         u_int64_t this_bsize;
685         struct dqblk dqblk;
686
687         this_bsize = bsize;
688                         
689         if (getquota( vol, &dqblk, bsize) != 0 ) {
690                 return( AFPERR_PARAM );
691         }
692
693 #ifdef linux
694         this_bsize = dqblk.bsize;
695 #endif
696
697 #ifdef DEBUG_QUOTA
698         LOG(log_info, logtype_afpd, "after calling getquota in uquota_getvolspace!" );
699         LOG(log_info, logtype_afpd, "dqb_ihardlimit: %u", dqblk.dqb_ihardlimit );
700         LOG(log_info, logtype_afpd, "dqb_isoftlimit: %u", dqblk.dqb_isoftlimit );
701         LOG(log_info, logtype_afpd, "dqb_curinodes : %u", dqblk.dqb_curinodes );
702         LOG(log_info, logtype_afpd, "dqb_bhardlimit: %u", dqblk.dqb_bhardlimit );
703         LOG(log_info, logtype_afpd, "dqb_bsoftlimit: %u", dqblk.dqb_bsoftlimit );
704         LOG(log_info, logtype_afpd, "dqb_curblocks : %u", dqblk.dqb_curblocks );
705         LOG(log_info, logtype_afpd, "dqb_btime     : %u", dqblk.dqb_btime );
706         LOG(log_info, logtype_afpd, "dqb_itime     : %u", dqblk.dqb_itime );
707         LOG(log_info, logtype_afpd, "bsize/this_bsize : %u/%u", bsize, this_bsize );
708         LOG(log_info, logtype_afpd, "dqblk.dqb_bhardlimit size: %u", tobytes( dqblk.dqb_bhardlimit, this_bsize ));
709         LOG(log_info, logtype_afpd, "dqblk.dqb_bsoftlimit size: %u", tobytes( dqblk.dqb_bsoftlimit, this_bsize ));
710         LOG(log_info, logtype_afpd, "dqblk.dqb_curblocks  size: %u", tobytes( dqblk.dqb_curblocks, this_bsize ));
711 #endif /* DEBUG_QUOTA */ 
712
713         /* no limit set for this user. it might be set in the future. */
714         if (dqblk.dqb_bsoftlimit == 0 && dqblk.dqb_bhardlimit == 0) {
715                 *btotal = *bfree = ~((VolSpace) 0);
716         } else if ( overquota( &dqblk )) {
717                 if ( tobytes( dqblk.dqb_curblocks, this_bsize ) > tobytes( dqblk.dqb_bsoftlimit, this_bsize ) ) {
718                         *btotal = tobytes( dqblk.dqb_curblocks, this_bsize );
719                         *bfree = 0;
720                 }
721                 else {
722                         *btotal = tobytes( dqblk.dqb_bsoftlimit, this_bsize );
723                         *bfree  = tobytes( dqblk.dqb_bsoftlimit, this_bsize ) -
724                                   tobytes( dqblk.dqb_curblocks, this_bsize );
725                 }
726         } else {
727                 *btotal = tobytes( dqblk.dqb_bhardlimit, this_bsize );
728                 *bfree  = tobytes( dqblk.dqb_bhardlimit, this_bsize  ) -
729                           tobytes( dqblk.dqb_curblocks, this_bsize );
730         }
731
732 #ifdef DEBUG_QUOTA
733         LOG(log_info, logtype_afpd, "bfree          : %u", *bfree );
734         LOG(log_info, logtype_afpd, "btotal         : %u", *btotal );
735         LOG(log_info, logtype_afpd, "bfree          : %uKB", *bfree/1024 );
736         LOG(log_info, logtype_afpd, "btotal         : %uKB", *btotal/1024 );
737 #endif
738
739         return( AFP_OK );
740 }
741 #endif