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