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
37 static int
getfreespace(const AFPObj * obj,struct vol * vol,VolSpace * bfree,VolSpace * btotal,uid_t uid,const char * classq)38 getfreespace(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
uquota_getvolspace(const AFPObj * obj,struct vol * vol,VolSpace * bfree,VolSpace * btotal,const u_int32_t bsize)81 int 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
quotactl(int cmd,const char * special,int id,caddr_t addr)131 int 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
137 static 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
157 static int is_xfs = 0;
158
159 static int get_linux_xfs_quota(int, char*, uid_t, struct dqblk *);
160 static int get_linux_fs_quota(int, char*, uid_t, struct dqblk *);
161
162 /* format supported by current kernel */
163 static 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 */
linuxquota_get_api(void)169 static 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
240 failure:
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
get_linux_quota(int what,char * path,uid_t euser_id,struct dqblk * dp)251 static 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
get_linux_xfs_quota(int what,char * path,uid_t euser_id,struct dqblk * dqb)267 static 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 */
get_linux_fs_quota(int what,char * path,uid_t euser_id,struct dqblk * dqb)294 static 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 */
365 static char *
mountp(char * file,int * nfs)366 mountp( 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. */
390 if ((stat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev)) {
391 *nfs = strcmp(mnt.mnt_fstype, MNTTYPE_NFS) == 0 ? 1 : 0;
392 fclose( mtab );
393 return mnt.mnt_special;
394 }
395 }
396
397 fclose( mtab );
398 return( NULL );
399 }
400
401 #else /* __svr4__ */
402 #ifdef ultrix
403 /*
404 * Return the block-special device name associated with the filesystem
405 * on which "file" resides. Returns NULL on failure.
406 */
407
408 static char *
special(char * file,int * nfs)409 special( char *file, int *nfs)
410 {
411 static struct fs_data fsd;
412
413 if ( getmnt(0, &fsd, 0, STAT_ONE, file ) < 0 ) {
414 LOG(log_info, logtype_afpd, "special: getmnt %s: %s", file, strerror(errno) );
415 return( NULL );
416 }
417
418 /* XXX: does this really detect an nfs mounted fs? */
419 if (strchr(fsd.fd_req.devname, ':'))
420 *nfs = 1;
421 return( fsd.fd_req.devname );
422 }
423
424 #else /* ultrix */
425 #if (defined(HAVE_SYS_MOUNT_H) && !defined(__linux__)) || defined(BSD4_4) || defined(_IBMR2)
426
427 static char *
special(char * file,int * nfs)428 special(char *file, int *nfs)
429 {
430 static struct statfs sfs;
431
432 if ( statfs( file, &sfs ) < 0 ) {
433 return( NULL );
434 }
435
436 #ifdef TRU64
437 /* Digital UNIX: The struct sfs contains a field sfs.f_type,
438 * the MOUNT_* constants are defined in <sys/mount.h> */
439 if ((sfs.f_type == MOUNT_NFS)||(sfs.f_type == MOUNT_NFS3))
440 #else /* TRU64 */
441 /* XXX: make sure this really detects an nfs mounted fs */
442 if (strchr(sfs.f_mntfromname, ':'))
443 #endif /* TRU64 */
444 *nfs = 1;
445 return( sfs.f_mntfromname );
446 }
447
448 #else /* BSD4_4 */
449
450 static char *
special(char * file,int * nfs)451 special(char *file, int *nfs)
452 {
453 struct stat sb;
454 FILE *mtab;
455 dev_t devno;
456 struct mntent *mnt;
457 int found=0;
458
459 if (stat(file, &sb) < 0 ) {
460 return( NULL );
461 }
462 devno = sb.st_dev;
463
464 if (( mtab = setmntent( "/etc/mtab", "r" )) == NULL ) {
465 return( NULL );
466 }
467
468 while (( mnt = getmntent( mtab )) != NULL ) {
469 /* check for local fs */
470 if ( (stat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
471 found = 1;
472 break;
473 }
474
475 /* check for an nfs mount entry. the alternative is to use
476 * strcmp(mnt->mnt_type, MNTTYPE_NFS) instead of the strchr. */
477 if ((stat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
478 strchr(mnt->mnt_fsname, ':')) {
479 *nfs = 1;
480 found = 1;
481 break;
482 }
483 }
484
485 endmntent( mtab );
486
487 if (!found)
488 return (NULL);
489 #ifdef linux
490 if (strcmp(mnt->mnt_type, "xfs") == 0)
491 is_xfs = 1;
492 #endif
493
494 return( mnt->mnt_fsname );
495 }
496
497 #endif /* BSD4_4 */
498 #endif /* ultrix */
499 #endif /* __svr4__ */
500
501
getfsquota(const AFPObj * obj,struct vol * vol,const int uid,struct dqblk * dq)502 static int getfsquota(const AFPObj *obj, struct vol *vol, const int uid, struct dqblk *dq)
503
504 {
505 struct dqblk dqg;
506
507 #ifdef __svr4__
508 struct quotctl qc;
509 #endif
510
511 memset(dq, 0, sizeof(struct dqblk));
512 memset(&dqg, 0, sizeof(dqg));
513
514 #ifdef __svr4__
515 qc.op = Q_GETQUOTA;
516 qc.uid = uid;
517 qc.addr = (caddr_t)dq;
518 if ( ioctl( vol->v_qfd, Q_QUOTACTL, &qc ) < 0 ) {
519 return( AFPERR_PARAM );
520 }
521
522 #else /* __svr4__ */
523 #ifdef ultrix
524 if ( quota( Q_GETDLIM, uid, vol->v_gvs, dq ) != 0 ) {
525 return( AFPERR_PARAM );
526 }
527 #else /* ultrix */
528
529 #ifndef USRQUOTA
530 #define USRQUOTA 0
531 #endif
532
533 #ifndef QCMD
534 #define QCMD(a,b) (a)
535 #endif
536
537 #ifndef TRU64
538 /* for group quotas. we only use these if the user belongs
539 * to one group. */
540 #endif /* TRU64 */
541
542 #ifdef BSD4_4
543 become_root();
544 if ( quotactl( vol->v_path, QCMD(Q_GETQUOTA,USRQUOTA),
545 uid, (char *)dq ) != 0 ) {
546 /* try group quotas */
547 if (obj->ngroups >= 1) {
548 if ( quotactl(vol->v_path, QCMD(Q_GETQUOTA, GRPQUOTA),
549 obj->groups[0], (char *) &dqg) != 0 ) {
550 unbecome_root();
551 return( AFPERR_PARAM );
552 }
553 }
554 }
555 unbecome_root();
556 }
557
558 #else /* BSD4_4 */
559 if (get_linux_quota (WANT_USER_QUOTA, vol->v_gvs, uid, dq) !=0) {
560 #ifdef DEBUG_QUOTA
561 LOG(log_debug, logtype_afpd, "user quota did not work!" );
562 #endif /* DEBUG_QUOTA */
563 }
564
565 if (get_linux_quota(WANT_GROUP_QUOTA, vol->v_gvs, getegid(), &dqg) != 0) {
566 #ifdef DEBUG_QUOTA
567 LOG(log_debug, logtype_afpd, "group quota did not work!" );
568 #endif /* DEBUG_QUOTA */
569
570 return AFP_OK; /* no need to check user vs group quota */
571 }
572 #endif /* BSD4_4 */
573
574
575 #ifndef TRU64
576 /* return either the group quota entry or user quota entry,
577 whichever has the least amount of space remaining
578 */
579
580 /* if user space remaining > group space remaining */
581 if(
582 /* if overquota, free space is 0 otherwise hard-current */
583 ( overquota( dq ) ? 0 : ( dq->dqb_bhardlimit ? dq->dqb_bhardlimit -
584 dq->dqb_curblocks : ~((uint64_t) 0) ) )
585
586 >
587
588 ( overquota( &dqg ) ? 0 : ( dqg.dqb_bhardlimit ? dqg.dqb_bhardlimit -
589 dqg.dqb_curblocks : ~((uint64_t) 0) ) )
590
591 ) /* if */
592 {
593 /* use group quota limits rather than user limits */
594 dq->dqb_bhardlimit = dqg.dqb_bhardlimit;
595 dq->dqb_bsoftlimit = dqg.dqb_bsoftlimit;
596 dq->dqb_curblocks = dqg.dqb_curblocks;
597 dq->dqb_ihardlimit = dqg.dqb_ihardlimit;
598 dq->dqb_isoftlimit = dqg.dqb_isoftlimit;
599 dq->dqb_curinodes = dqg.dqb_curinodes;
600 dq->dqb_btime = dqg.dqb_btime;
601 dq->dqb_itime = dqg.dqb_itime;
602 dq->bsize = dqg.bsize;
603 } /* if */
604
605 #endif /* TRU64 */
606
607 #endif /* ultrix */
608 #endif /* __svr4__ */
609
610 return AFP_OK;
611 }
612
613
614 static int getquota(const AFPObj *obj, struct vol *vol, struct dqblk *dq, const uint32_t bsize)
615 {
616 char *p;
617
618 #ifdef __svr4__
619 char buf[ MAXPATHLEN + 1];
620
621 if ( vol->v_qfd == -1 && vol->v_gvs == NULL) {
622 if (( p = mountp( vol->v_path, &vol->v_nfs)) == NULL ) {
623 LOG(log_info, logtype_afpd, "getquota: mountp %s fails", vol->v_path );
624 return( AFPERR_PARAM );
625 }
626
627 if (vol->v_nfs) {
628 if (( vol->v_gvs = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
629 LOG(log_error, logtype_afpd, "getquota: malloc: %s", strerror(errno) );
630 return AFPERR_MISC;
631 }
632 strcpy( vol->v_gvs, p );
633
634 } else {
635 sprintf( buf, "%s/quotas", p );
636 if (( vol->v_qfd = open( buf, O_RDONLY, 0 )) < 0 ) {
637 LOG(log_info, logtype_afpd, "open %s: %s", buf, strerror(errno) );
638 return( AFPERR_PARAM );
639 }
640 }
641
642 }
643 #else
644 if ( vol->v_gvs == NULL ) {
645 if (( p = special( vol->v_path, &vol->v_nfs )) == NULL ) {
646 LOG(log_info, logtype_afpd, "getquota: special %s fails", vol->v_path );
647 return( AFPERR_PARAM );
648 }
649
650 if (( vol->v_gvs = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
651 LOG(log_error, logtype_afpd, "getquota: malloc: %s", strerror(errno) );
652 return AFPERR_MISC;
653 }
654 strcpy( vol->v_gvs, p );
655 }
656 #endif
657
658 #ifdef TRU64
659 /* Digital UNIX: Two forms of specifying an NFS filesystem are possible,
660 either 'hostname:path' or 'path@hostname' (Ultrix heritage) */
661 if (vol->v_nfs) {
662 char *hostpath;
663 char pathstring[MNAMELEN];
664 /* MNAMELEN ist defined in <sys/mount.h> */
665 int result;
666
667 if ((hostpath = strchr(vol->v_gvs,'@')) != NULL ) {
668 /* convert 'path@hostname' to 'hostname:path',
669 * call getnfsquota(),
670 * convert 'hostname:path' back to 'path@hostname' */
671 *hostpath = '\0';
672 sprintf(pathstring,"%s:%s",hostpath+1,vol->v_gvs);
673 strcpy(vol->v_gvs,pathstring);
674
675 result = getnfsquota(vol, uuid, bsize, dq);
676
677 hostpath = strchr(vol->v_gvs,':');
678 *hostpath = '\0';
679 sprintf(pathstring,"%s@%s",hostpath+1,vol->v_gvs);
680 strcpy(vol->v_gvs,pathstring);
681
682 return result;
683 }
684 else
685 /* vol->v_gvs is of the form 'hostname:path' */
686 return getnfsquota(vol, uuid, bsize, dq);
687 } else
688 /* local filesystem */
689 return getfsquota(obj, vol, obj->uid, dq);
690
691 #else /* TRU64 */
692 return vol->v_nfs ? getnfsquota(vol, obj->uid, bsize, dq) :
693 getfsquota(obj, vol, obj->uid, dq);
694 #endif /* TRU64 */
695 }
696
697 static int overquota( struct dqblk *dqblk)
698 {
699 struct timeval tv;
700
701 if ( dqblk->dqb_curblocks > dqblk->dqb_bhardlimit &&
702 dqblk->dqb_bhardlimit != 0 ) {
703 return( 1 );
704 }
705
706 if ( dqblk->dqb_curblocks < dqblk->dqb_bsoftlimit ||
707 dqblk->dqb_bsoftlimit == 0 ) {
708 return( 0 );
709 }
710 #ifdef ultrix
711 if ( dqblk->dqb_bwarn ) {
712 return( 0 );
713 }
714 #else /* ultrix */
715 if ( gettimeofday( &tv, NULL ) < 0 ) {
716 LOG(log_error, logtype_afpd, "overquota: gettimeofday: %s", strerror(errno) );
717 return( AFPERR_PARAM );
718 }
719 if ( dqblk->dqb_btimelimit && dqblk->dqb_btimelimit > tv.tv_sec ) {
720 return( 0 );
721 }
722 #endif /* ultrix */
723 return( 1 );
724 }
725
726 /*
727 * This next bit is basically for linux -- everything is fine
728 * if you use 1k blocks... but if you try (for example) to mount
729 * a volume via nfs from a netapp (which might use 4k blocks) everything
730 * gets reported improperly. I have no idea about dbtob on other
731 * platforms.
732 */
733
734 #ifdef HAVE_BROKEN_DBTOB
735 #undef dbtob
736 #define dbtob(a, b) ((VolSpace)((VolSpace)(a) * (VolSpace)(b)))
737 #define HAVE_2ARG_DBTOB
738 #endif
739
740 #ifndef dbtob
741 #define dbtob(a) ((a) << 10)
742 #endif
743
744 /* i do the cast to VolSpace here to make sure that 64-bit shifts
745 work */
746 #ifdef HAVE_2ARG_DBTOB
747 #define tobytes(a, b) dbtob((VolSpace) (a), (VolSpace) (b))
748 #else
749 #define tobytes(a, b) dbtob((VolSpace) (a))
750 #endif
751
752 int uquota_getvolspace(const AFPObj *obj, struct vol *vol, VolSpace *bfree, VolSpace *btotal, const uint32_t bsize)
753 {
754 uint64_t this_bsize;
755 struct dqblk dqblk;
756
757 this_bsize = bsize;
758
759 if (getquota(obj, vol, &dqblk, bsize) != 0 ) {
760 return( AFPERR_PARAM );
761 }
762
763 #ifdef linux
764 this_bsize = dqblk.bsize;
765 #endif
766
767 #ifdef DEBUG_QUOTA
768 LOG(log_debug, logtype_afpd, "after calling getquota in uquota_getvolspace!" );
769 LOG(log_debug, logtype_afpd, "dqb_ihardlimit: %u", dqblk.dqb_ihardlimit );
770 LOG(log_debug, logtype_afpd, "dqb_isoftlimit: %u", dqblk.dqb_isoftlimit );
771 LOG(log_debug, logtype_afpd, "dqb_curinodes : %u", dqblk.dqb_curinodes );
772 LOG(log_debug, logtype_afpd, "dqb_bhardlimit: %u", dqblk.dqb_bhardlimit );
773 LOG(log_debug, logtype_afpd, "dqb_bsoftlimit: %u", dqblk.dqb_bsoftlimit );
774 LOG(log_debug, logtype_afpd, "dqb_curblocks : %u", dqblk.dqb_curblocks );
775 LOG(log_debug, logtype_afpd, "dqb_btime : %u", dqblk.dqb_btime );
776 LOG(log_debug, logtype_afpd, "dqb_itime : %u", dqblk.dqb_itime );
777 LOG(log_debug, logtype_afpd, "bsize/this_bsize : %u/%u", bsize, this_bsize );
778 LOG(log_debug, logtype_afpd, "dqblk.dqb_bhardlimit size: %u", tobytes( dqblk.dqb_bhardlimit, this_bsize ));
779 LOG(log_debug, logtype_afpd, "dqblk.dqb_bsoftlimit size: %u", tobytes( dqblk.dqb_bsoftlimit, this_bsize ));
780 LOG(log_debug, logtype_afpd, "dqblk.dqb_curblocks size: %u", tobytes( dqblk.dqb_curblocks, this_bsize ));
781 #endif /* DEBUG_QUOTA */
782
783 /* no limit set for this user. it might be set in the future. */
784 if (dqblk.dqb_bsoftlimit == 0 && dqblk.dqb_bhardlimit == 0) {
785 *btotal = *bfree = ~((VolSpace) 0);
786 } else if ( overquota( &dqblk )) {
787 if ( tobytes( dqblk.dqb_curblocks, this_bsize ) > tobytes( dqblk.dqb_bsoftlimit, this_bsize ) ) {
788 *btotal = tobytes( dqblk.dqb_curblocks, this_bsize );
789 *bfree = 0;
790 }
791 else {
792 *btotal = tobytes( dqblk.dqb_bsoftlimit, this_bsize );
793 *bfree = tobytes( dqblk.dqb_bsoftlimit, this_bsize ) -
794 tobytes( dqblk.dqb_curblocks, this_bsize );
795 }
796 } else {
797 *btotal = tobytes( dqblk.dqb_bhardlimit, this_bsize );
798 *bfree = tobytes( dqblk.dqb_bhardlimit, this_bsize ) -
799 tobytes( dqblk.dqb_curblocks, this_bsize );
800 }
801
802 #ifdef DEBUG_QUOTA
803 LOG(log_debug, logtype_afpd, "bfree : %u", *bfree );
804 LOG(log_debug, logtype_afpd, "btotal : %u", *btotal );
805 LOG(log_debug, logtype_afpd, "bfree : %uKB", *bfree/1024 );
806 LOG(log_debug, logtype_afpd, "btotal : %uKB", *btotal/1024 );
807 #endif
808
809 return( AFP_OK );
810 }
811 #endif /* HAVE_LIBQUOTA */
812 #endif
813