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