1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * University Copyright- Copyright (c) 1982, 1986, 1988 31 * The Regents of the University of California 32 * All Rights Reserved 33 * 34 * University Acknowledgment- Portions of this document are derived from 35 * software developed by the University of California, Berkeley, and its 36 * contributors. 37 */ 38 39 /* 40 * Disk quota reporting program. 41 */ 42 #include <stdio.h> 43 #include <sys/mnttab.h> 44 #include <ctype.h> 45 #include <pwd.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <memory.h> 49 #include <sys/time.h> 50 #include <sys/param.h> 51 #include <sys/types.h> 52 #include <sys/sysmacros.h> 53 #include <sys/mntent.h> 54 #include <sys/file.h> 55 #include <sys/stat.h> 56 #include <sys/fs/ufs_quota.h> 57 #include <priv_utils.h> 58 #include <locale.h> 59 #include <rpc/rpc.h> 60 #include <netdb.h> 61 #include <rpcsvc/rquota.h> 62 #include <zone.h> 63 #include "../../nfs/lib/replica.h" 64 #include <dlfcn.h> 65 #include <libzfs.h> 66 67 int vflag; 68 int nolocalquota; 69 70 extern int optind; 71 extern char *optarg; 72 73 #define QFNAME "quotas" 74 75 #if DEV_BSIZE < 1024 76 #define kb(x) ((x) / (1024 / DEV_BSIZE)) 77 #else 78 #define kb(x) ((x) * (DEV_BSIZE / 1024)) 79 #endif 80 81 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 82 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 83 #endif 84 85 static void zexit(int); 86 static int getzfsquota(char *, char *, struct dqblk *); 87 static int getnfsquota(char *, char *, uid_t, struct dqblk *); 88 static void showuid(uid_t); 89 static void showquotas(uid_t, char *); 90 static void warn(struct mnttab *, struct dqblk *); 91 static void heading(uid_t, char *); 92 static void prquota(struct mnttab *, struct dqblk *); 93 static void fmttime(char *, long); 94 95 static libzfs_handle_t *(*_libzfs_init)(void); 96 static void (*_libzfs_fini)(libzfs_handle_t *); 97 static zfs_handle_t *(*_zfs_open)(libzfs_handle_t *, const char *, int); 98 static void (*_zfs_close)(zfs_handle_t *); 99 static int (*_zfs_prop_get_userquota_int)(zfs_handle_t *, const char *, 100 uint64_t *); 101 static libzfs_handle_t *g_zfs = NULL; 102 103 /* 104 * Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs 105 * packages. 'quota' utility supports zfs as an option. 106 */ 107 static void 108 load_libzfs(void) 109 { 110 void *hdl; 111 112 if (g_zfs != NULL) 113 return; 114 115 if ((hdl = dlopen("libzfs.so", RTLD_LAZY)) != NULL) { 116 _libzfs_init = (libzfs_handle_t *(*)(void))dlsym(hdl, 117 "libzfs_init"); 118 _libzfs_fini = (void (*)())dlsym(hdl, "libzfs_fini"); 119 _zfs_open = (zfs_handle_t *(*)())dlsym(hdl, "zfs_open"); 120 _zfs_close = (void (*)())dlsym(hdl, "zfs_close"); 121 _zfs_prop_get_userquota_int = (int (*)()) 122 dlsym(hdl, "zfs_prop_get_userquota_int"); 123 124 if (_libzfs_init && _libzfs_fini && _zfs_open && 125 _zfs_close && _zfs_prop_get_userquota_int) 126 g_zfs = _libzfs_init(); 127 } 128 } 129 130 int 131 main(int argc, char *argv[]) 132 { 133 int opt; 134 int i; 135 int status = 0; 136 137 (void) setlocale(LC_ALL, ""); 138 (void) textdomain(TEXT_DOMAIN); 139 140 /* 141 * PRIV_FILE_DAC_READ is needed to read the QFNAME file 142 * Clear all other privleges from the limit set, and add 143 * the required privilege to the bracketed set. 144 */ 145 146 if (__init_suid_priv(PU_CLEARLIMITSET, PRIV_FILE_DAC_READ, 147 NULL) == -1) { 148 (void) fprintf(stderr, 149 gettext("Insufficient privileges, " 150 "quota must be set-uid root or have " 151 "file_dac_read privileges\n")); 152 153 exit(1); 154 } 155 156 load_libzfs(); 157 158 while ((opt = getopt(argc, argv, "vV")) != EOF) { 159 switch (opt) { 160 161 case 'v': 162 vflag++; 163 break; 164 165 case 'V': /* Print command line */ 166 { 167 char *opt_text; 168 int opt_count; 169 170 (void) fprintf(stdout, "quota -F UFS "); 171 for (opt_count = 1; opt_count < argc; opt_count++) { 172 opt_text = argv[opt_count]; 173 if (opt_text) 174 (void) fprintf(stdout, " %s ", 175 opt_text); 176 } 177 (void) fprintf(stdout, "\n"); 178 } 179 break; 180 181 case '?': 182 fprintf(stderr, "usage: quota [-v] [username]\n"); 183 zexit(32); 184 } 185 } 186 if (quotactl(Q_ALLSYNC, NULL, (uid_t)0, NULL) < 0 && errno == EINVAL) { 187 if (vflag) 188 fprintf(stderr, "There are no quotas on this system\n"); 189 nolocalquota++; 190 } 191 if (argc == optind) { 192 showuid(getuid()); 193 zexit(0); 194 } 195 for (i = optind; i < argc; i++) { 196 if (alldigits(argv[i])) { 197 showuid((uid_t)atoi(argv[i])); 198 } else 199 status |= showname(argv[i]); 200 } 201 __priv_relinquish(); 202 return (status); 203 } 204 205 static void 206 showuid(uid_t uid) 207 { 208 struct passwd *pwd = getpwuid(uid); 209 210 if (uid == 0) { 211 if (vflag) 212 printf("no disk quota for uid 0\n"); 213 return; 214 } 215 if (pwd == NULL) 216 showquotas(uid, "(no account)"); 217 else 218 showquotas(uid, pwd->pw_name); 219 } 220 221 int 222 showname(char *name) 223 { 224 struct passwd *pwd = getpwnam(name); 225 226 if (pwd == NULL) { 227 fprintf(stderr, "quota: %s: unknown user\n", name); 228 return (32); 229 } 230 if (pwd->pw_uid == 0) { 231 if (vflag) 232 printf("no disk quota for %s (uid 0)\n", name); 233 return (0); 234 } 235 showquotas(pwd->pw_uid, name); 236 return (0); 237 } 238 239 static void 240 showquotas(uid_t uid, char *name) 241 { 242 struct mnttab mnt; 243 FILE *mtab; 244 struct dqblk dqblk; 245 uid_t myuid; 246 struct failed_srv { 247 char *serv_name; 248 struct failed_srv *next; 249 }; 250 struct failed_srv *failed_srv_list = NULL; 251 int rc; 252 char my_zonename[ZONENAME_MAX]; 253 zoneid_t my_zoneid = getzoneid(); 254 255 myuid = getuid(); 256 if (uid != myuid && myuid != 0) { 257 printf("quota: %s (uid %d): permission denied\n", name, uid); 258 zexit(32); 259 } 260 261 memset(my_zonename, '\0', ZONENAME_MAX); 262 getzonenamebyid(my_zoneid, my_zonename, ZONENAME_MAX); 263 264 if (vflag) 265 heading(uid, name); 266 mtab = fopen(MNTTAB, "r"); 267 while (getmntent(mtab, &mnt) == NULL) { 268 if (strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) == 0) { 269 bzero(&dqblk, sizeof (dqblk)); 270 if (getzfsquota(name, mnt.mnt_special, &dqblk)) 271 continue; 272 } else if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) == 0) { 273 if (nolocalquota || 274 (quotactl(Q_GETQUOTA, 275 mnt.mnt_mountp, uid, &dqblk) != 0 && 276 !(vflag && getdiskquota(&mnt, uid, &dqblk)))) 277 continue; 278 } else if (strcmp(mnt.mnt_fstype, MNTTYPE_NFS) == 0) { 279 280 struct replica *rl; 281 int count; 282 char *mntopt = NULL; 283 284 /* 285 * Skip checking quotas for file systems mounted 286 * in other zones. Zone names will be passed in 287 * following format from hasmntopt(): 288 * "zone=<zone-name>,<mnt options...>" 289 */ 290 if ((mntopt = hasmntopt(&mnt, MNTOPT_ZONE)) && 291 (my_zonename[0] != '\0')) { 292 mntopt += strcspn(mntopt, "="); 293 if (strncmp(++mntopt, my_zonename, 294 strcspn(mntopt, ",")) != 0) 295 continue; 296 } 297 298 if (hasopt(MNTOPT_NOQUOTA, mnt.mnt_mntopts)) 299 continue; 300 301 /* 302 * Skip quota processing if mounted with public 303 * option. We are not likely to be able to pierce 304 * a fire wall to contact the quota server. 305 */ 306 if (hasopt(MNTOPT_PUBLIC, mnt.mnt_mntopts)) 307 continue; 308 309 rl = parse_replica(mnt.mnt_special, &count); 310 311 if (rl == NULL) { 312 313 if (count < 0) 314 fprintf(stderr, "cannot find hostname " 315 "and/or pathname for %s\n", 316 mnt.mnt_mountp); 317 else 318 fprintf(stderr, "no memory to parse " 319 "mnttab entry for %s\n", 320 mnt.mnt_mountp); 321 continue; 322 } 323 324 /* 325 * We skip quota reporting on mounts with replicas 326 * for the following reasons: 327 * 328 * (1) Very little point in reporting quotas on 329 * a set of read-only replicas ... how will the 330 * user correct the problem? 331 * 332 * (2) Which replica would we report the quota 333 * for? If we pick the current replica, what 334 * happens when a fail over event occurs? The 335 * next time quota is run, the quota will look 336 * all different, or there won't even be one. 337 * This has the potential to break scripts. 338 * 339 * If we prnt quouta for all replicas, how do 340 * we present the output without breaking scripts? 341 */ 342 343 if (count > 1) { 344 free_replica(rl, count); 345 continue; 346 } 347 348 /* 349 * Skip file systems mounted using public fh. 350 * We are not likely to be able to pierce 351 * a fire wall to contact the quota server. 352 */ 353 if (strcmp(rl[0].host, "nfs") == 0 && 354 strncmp(rl[0].path, "//", 2) == 0) { 355 free_replica(rl, count); 356 continue; 357 } 358 359 /* 360 * Skip getting quotas from failing servers 361 */ 362 if (failed_srv_list != NULL) { 363 struct failed_srv *tmp_list; 364 int found_failed = 0; 365 size_t len = strlen(rl[0].host); 366 367 tmp_list = failed_srv_list; 368 do { 369 if (strncasecmp(rl[0].host, 370 tmp_list->serv_name, len) == 0) { 371 found_failed = 1; 372 break; 373 } 374 } while ((tmp_list = tmp_list->next) != NULL); 375 if (found_failed) { 376 free_replica(rl, count); 377 continue; 378 } 379 } 380 381 rc = getnfsquota(rl[0].host, rl[0].path, uid, &dqblk); 382 if (rc != RPC_SUCCESS) { 383 size_t len; 384 struct failed_srv *tmp_srv; 385 386 /* 387 * Failed to get quota from this server. Add 388 * this server to failed_srv_list and skip 389 * getting quotas for other mounted filesystems 390 * from this server. 391 */ 392 if (rc == RPC_TIMEDOUT || rc == RPC_CANTSEND) { 393 len = strlen(rl[0].host); 394 tmp_srv = (struct failed_srv *)malloc( 395 sizeof (struct failed_srv)); 396 tmp_srv->serv_name = (char *)malloc( 397 len * sizeof (char) + 1); 398 strncpy(tmp_srv->serv_name, rl[0].host, 399 len); 400 tmp_srv->serv_name[len] = '\0'; 401 402 tmp_srv->next = failed_srv_list; 403 failed_srv_list = tmp_srv; 404 } 405 406 free_replica(rl, count); 407 continue; 408 } 409 410 free_replica(rl, count); 411 } else { 412 continue; 413 } 414 if (dqblk.dqb_bsoftlimit == 0 && dqblk.dqb_bhardlimit == 0 && 415 dqblk.dqb_fsoftlimit == 0 && dqblk.dqb_fhardlimit == 0) 416 continue; 417 if (vflag) 418 prquota(&mnt, &dqblk); 419 else 420 warn(&mnt, &dqblk); 421 } 422 423 /* 424 * Free list of failed servers 425 */ 426 while (failed_srv_list != NULL) { 427 struct failed_srv *tmp_srv = failed_srv_list; 428 429 failed_srv_list = failed_srv_list->next; 430 free(tmp_srv->serv_name); 431 free(tmp_srv); 432 } 433 434 fclose(mtab); 435 } 436 437 static void 438 warn(struct mnttab *mntp, struct dqblk *dqp) 439 { 440 struct timeval tv; 441 442 time(&(tv.tv_sec)); 443 tv.tv_usec = 0; 444 if (dqp->dqb_bhardlimit && 445 dqp->dqb_curblocks >= dqp->dqb_bhardlimit) { 446 printf("Block limit reached on %s\n", mntp->mnt_mountp); 447 } else if (dqp->dqb_bsoftlimit && 448 dqp->dqb_curblocks >= dqp->dqb_bsoftlimit) { 449 if (dqp->dqb_btimelimit == 0) { 450 printf("Over disk quota on %s, remove %luK\n", 451 mntp->mnt_mountp, 452 kb(dqp->dqb_curblocks - dqp->dqb_bsoftlimit + 1)); 453 } else if (dqp->dqb_btimelimit > tv.tv_sec) { 454 char btimeleft[80]; 455 456 fmttime(btimeleft, dqp->dqb_btimelimit - tv.tv_sec); 457 printf("Over disk quota on %s, remove %luK within %s\n", 458 mntp->mnt_mountp, 459 kb(dqp->dqb_curblocks - dqp->dqb_bsoftlimit + 1), 460 btimeleft); 461 } else { 462 printf( 463 "Over disk quota on %s, time limit has expired, remove %luK\n", 464 mntp->mnt_mountp, 465 kb(dqp->dqb_curblocks - dqp->dqb_bsoftlimit + 1)); 466 } 467 } 468 if (dqp->dqb_fhardlimit && 469 dqp->dqb_curfiles >= dqp->dqb_fhardlimit) { 470 printf("File count limit reached on %s\n", mntp->mnt_mountp); 471 } else if (dqp->dqb_fsoftlimit && 472 dqp->dqb_curfiles >= dqp->dqb_fsoftlimit) { 473 if (dqp->dqb_ftimelimit == 0) { 474 printf("Over file quota on %s, remove %lu file%s\n", 475 mntp->mnt_mountp, 476 dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1, 477 ((dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1) > 1 ? 478 "s" : "")); 479 } else if (dqp->dqb_ftimelimit > tv.tv_sec) { 480 char ftimeleft[80]; 481 482 fmttime(ftimeleft, dqp->dqb_ftimelimit - tv.tv_sec); 483 printf( 484 "Over file quota on %s, remove %lu file%s within %s\n", 485 mntp->mnt_mountp, 486 dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1, 487 ((dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1) > 1 ? 488 "s" : ""), ftimeleft); 489 } else { 490 printf( 491 "Over file quota on %s, time limit has expired, remove %lu file%s\n", 492 mntp->mnt_mountp, 493 dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1, 494 ((dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1) > 1 ? 495 "s" : "")); 496 } 497 } 498 } 499 500 static void 501 heading(uid_t uid, char *name) 502 { 503 printf("Disk quotas for %s (uid %ld):\n", name, (long)uid); 504 printf("%-12s %7s%7s%7s%12s%7s%7s%7s%12s\n", 505 "Filesystem", 506 "usage", 507 "quota", 508 "limit", 509 "timeleft", 510 "files", 511 "quota", 512 "limit", 513 "timeleft"); 514 } 515 516 static void 517 prquota(struct mnttab *mntp, struct dqblk *dqp) 518 { 519 struct timeval tv; 520 char ftimeleft[80], btimeleft[80]; 521 char *cp; 522 523 time(&(tv.tv_sec)); 524 tv.tv_usec = 0; 525 if (dqp->dqb_bsoftlimit && dqp->dqb_curblocks >= dqp->dqb_bsoftlimit) { 526 if (dqp->dqb_btimelimit == 0) { 527 strlcpy(btimeleft, "NOT STARTED", sizeof (btimeleft)); 528 } else if (dqp->dqb_btimelimit > tv.tv_sec) { 529 fmttime(btimeleft, dqp->dqb_btimelimit - tv.tv_sec); 530 } else { 531 strlcpy(btimeleft, "EXPIRED", sizeof (btimeleft)); 532 } 533 } else { 534 btimeleft[0] = '\0'; 535 } 536 if (dqp->dqb_fsoftlimit && dqp->dqb_curfiles >= dqp->dqb_fsoftlimit) { 537 if (dqp->dqb_ftimelimit == 0) { 538 strlcpy(ftimeleft, "NOT STARTED", sizeof (ftimeleft)); 539 } else if (dqp->dqb_ftimelimit > tv.tv_sec) { 540 fmttime(ftimeleft, dqp->dqb_ftimelimit - tv.tv_sec); 541 } else { 542 strlcpy(ftimeleft, "EXPIRED", sizeof (ftimeleft)); 543 } 544 } else { 545 ftimeleft[0] = '\0'; 546 } 547 if (strlen(mntp->mnt_mountp) > 12) { 548 printf("%s\n", mntp->mnt_mountp); 549 cp = ""; 550 } else { 551 cp = mntp->mnt_mountp; 552 } 553 554 if (dqp->dqb_curfiles == 0 && 555 dqp->dqb_fsoftlimit == 0 && dqp->dqb_fhardlimit == 0) { 556 printf("%-12.12s %7d %6d %6d %11s %6s %6s %6s %11s\n", 557 cp, 558 kb(dqp->dqb_curblocks), 559 kb(dqp->dqb_bsoftlimit), 560 kb(dqp->dqb_bhardlimit), 561 "-", 562 "-", 563 "-", 564 "-", 565 "-"); 566 } else { 567 printf("%-12.12s %7d %6d %6d %11s %6d %6d %6d %11s\n", 568 cp, 569 kb(dqp->dqb_curblocks), 570 kb(dqp->dqb_bsoftlimit), 571 kb(dqp->dqb_bhardlimit), 572 btimeleft, 573 dqp->dqb_curfiles, 574 dqp->dqb_fsoftlimit, 575 dqp->dqb_fhardlimit, 576 ftimeleft); 577 } 578 } 579 580 static void 581 fmttime(char *buf, long time) 582 { 583 int i; 584 static struct { 585 int c_secs; /* conversion units in secs */ 586 char *c_str; /* unit string */ 587 } cunits [] = { 588 {60*60*24*28, "months"}, 589 {60*60*24*7, "weeks"}, 590 {60*60*24, "days"}, 591 {60*60, "hours"}, 592 {60, "mins"}, 593 {1, "secs"} 594 }; 595 596 if (time <= 0) { 597 strlcpy(buf, "EXPIRED", sizeof (*buf)); 598 return; 599 } 600 for (i = 0; i < sizeof (cunits)/sizeof (cunits[0]); i++) { 601 if (time >= cunits[i].c_secs) 602 break; 603 } 604 snprintf(buf, sizeof (*buf), "%.1f %s", 605 (double)time/cunits[i].c_secs, cunits[i].c_str); 606 } 607 608 int 609 alldigits(char *s) 610 { 611 int c; 612 613 c = *s++; 614 do { 615 if (!isdigit(c)) 616 return (0); 617 } while (c = *s++); 618 return (1); 619 } 620 621 int 622 getdiskquota(struct mnttab *mntp, uid_t uid, struct dqblk *dqp) 623 { 624 int fd; 625 dev_t fsdev; 626 struct stat64 statb; 627 char qfilename[MAXPATHLEN]; 628 629 if (stat64(mntp->mnt_special, &statb) < 0 || 630 (statb.st_mode & S_IFMT) != S_IFBLK) 631 return (0); 632 fsdev = statb.st_rdev; 633 (void) snprintf(qfilename, sizeof (qfilename), "%s/%s", 634 mntp->mnt_mountp, QFNAME); 635 if (stat64(qfilename, &statb) < 0 || statb.st_dev != fsdev) 636 return (0); 637 (void) __priv_bracket(PRIV_ON); 638 fd = open64(qfilename, O_RDONLY); 639 (void) __priv_bracket(PRIV_OFF); 640 if (fd < 0) 641 return (0); 642 (void) llseek(fd, (offset_t)dqoff(uid), L_SET); 643 switch (read(fd, dqp, sizeof (struct dqblk))) { 644 case 0: /* EOF */ 645 /* 646 * Convert implicit 0 quota (EOF) 647 * into an explicit one (zero'ed dqblk). 648 */ 649 memset((caddr_t)dqp, 0, sizeof (struct dqblk)); 650 break; 651 652 case sizeof (struct dqblk): /* OK */ 653 break; 654 655 default: /* ERROR */ 656 close(fd); 657 return (0); 658 } 659 close(fd); 660 return (1); 661 } 662 663 int 664 quotactl(int cmd, char *mountp, uid_t uid, caddr_t addr) 665 { 666 int fd; 667 int status; 668 struct quotctl quota; 669 char qfile[MAXPATHLEN]; 670 671 FILE *fstab; 672 struct mnttab mnt; 673 674 675 if ((mountp == NULL) && (cmd == Q_ALLSYNC)) { 676 /* 677 * Find the mount point of any mounted file system. This is 678 * because the ioctl that implements the quotactl call has 679 * to go to a real file, and not to the block device. 680 */ 681 if ((fstab = fopen(MNTTAB, "r")) == NULL) { 682 fprintf(stderr, "%s: ", MNTTAB); 683 perror("open"); 684 zexit(32); 685 } 686 fd = -1; 687 while ((status = getmntent(fstab, &mnt)) == NULL) { 688 if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) != 0 || 689 hasopt(MNTOPT_RO, mnt.mnt_mntopts)) 690 continue; 691 if ((strlcpy(qfile, mnt.mnt_mountp, 692 sizeof (qfile)) >= sizeof (qfile)) || 693 (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >= 694 sizeof (qfile))) { 695 continue; 696 } 697 (void) __priv_bracket(PRIV_ON); 698 fd = open64(qfile, O_RDONLY); 699 (void) __priv_bracket(PRIV_OFF); 700 if (fd != -1) 701 break; 702 } 703 fclose(fstab); 704 if (fd == -1) { 705 errno = ENOENT; 706 return (-1); 707 } 708 } else { 709 if (mountp == NULL || mountp[0] == '\0') { 710 errno = ENOENT; 711 return (-1); 712 } 713 if ((strlcpy(qfile, mountp, sizeof (qfile)) >= sizeof 714 (qfile)) || 715 (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >= sizeof 716 (qfile))) { 717 errno = ENOENT; 718 return (-1); 719 } 720 (void) __priv_bracket(PRIV_ON); 721 fd = open64(qfile, O_RDONLY); 722 (void) __priv_bracket(PRIV_OFF); 723 if (fd < 0) 724 return (-1); 725 } /* else */ 726 quota.op = cmd; 727 quota.uid = uid; 728 quota.addr = addr; 729 status = ioctl(fd, Q_QUOTACTL, "a); 730 if (fd != 0) 731 close(fd); 732 return (status); 733 } 734 735 736 /* 737 * Return 1 if opt appears in optlist 738 */ 739 int 740 hasopt(char *opt, char *optlist) 741 { 742 char *value; 743 char *opts[2]; 744 745 opts[0] = opt; 746 opts[1] = NULL; 747 748 if (optlist == NULL) 749 return (0); 750 while (*optlist != '\0') { 751 if (getsubopt(&optlist, opts, &value) == 0) 752 return (1); 753 } 754 return (0); 755 } 756 757 /* 758 * If there are no quotas available, then getnfsquota() returns 759 * RPC_SYSTEMERROR to caller. 760 */ 761 static int 762 getnfsquota(char *hostp, char *path, uid_t uid, struct dqblk *dqp) 763 { 764 struct getquota_args gq_args; 765 struct getquota_rslt gq_rslt; 766 struct rquota *rquota; 767 extern char *strchr(); 768 int rpc_err; 769 770 gq_args.gqa_pathp = path; 771 gq_args.gqa_uid = uid; 772 rpc_err = callaurpc(hostp, RQUOTAPROG, RQUOTAVERS, 773 (vflag? RQUOTAPROC_GETQUOTA: RQUOTAPROC_GETACTIVEQUOTA), 774 xdr_getquota_args, &gq_args, xdr_getquota_rslt, &gq_rslt); 775 if (rpc_err != RPC_SUCCESS) { 776 return (rpc_err); 777 } 778 switch (gq_rslt.status) { 779 case Q_OK: 780 { 781 struct timeval tv; 782 u_longlong_t limit; 783 784 rquota = &gq_rslt.getquota_rslt_u.gqr_rquota; 785 786 if (!vflag && rquota->rq_active == FALSE) { 787 return (RPC_SYSTEMERROR); 788 } 789 gettimeofday(&tv, NULL); 790 limit = (u_longlong_t)(rquota->rq_bhardlimit) * 791 rquota->rq_bsize / DEV_BSIZE; 792 dqp->dqb_bhardlimit = limit; 793 limit = (u_longlong_t)(rquota->rq_bsoftlimit) * 794 rquota->rq_bsize / DEV_BSIZE; 795 dqp->dqb_bsoftlimit = limit; 796 limit = (u_longlong_t)(rquota->rq_curblocks) * 797 rquota->rq_bsize / DEV_BSIZE; 798 dqp->dqb_curblocks = limit; 799 dqp->dqb_fhardlimit = rquota->rq_fhardlimit; 800 dqp->dqb_fsoftlimit = rquota->rq_fsoftlimit; 801 dqp->dqb_curfiles = rquota->rq_curfiles; 802 dqp->dqb_btimelimit = 803 tv.tv_sec + rquota->rq_btimeleft; 804 dqp->dqb_ftimelimit = 805 tv.tv_sec + rquota->rq_ftimeleft; 806 return (RPC_SUCCESS); 807 } 808 809 case Q_NOQUOTA: 810 return (RPC_SYSTEMERROR); 811 812 case Q_EPERM: 813 fprintf(stderr, "quota permission error, host: %s\n", hostp); 814 return (RPC_AUTHERROR); 815 816 default: 817 fprintf(stderr, "bad rpc result, host: %s\n", hostp); 818 return (RPC_CANTDECODEARGS); 819 } 820 821 /* NOTREACHED */ 822 } 823 824 int 825 callaurpc(char *host, int prognum, int versnum, int procnum, 826 xdrproc_t inproc, char *in, xdrproc_t outproc, char *out) 827 { 828 static enum clnt_stat clnt_stat; 829 struct timeval tottimeout = {20, 0}; 830 831 static CLIENT *cl = NULL; 832 static int oldprognum, oldversnum; 833 static char oldhost[MAXHOSTNAMELEN+1]; 834 835 /* 836 * Cache the client handle in case there are lots 837 * of entries in the /etc/mnttab for the same 838 * server. If the server returns an error, don't 839 * make further calls. 840 */ 841 if (cl == NULL || oldprognum != prognum || oldversnum != versnum || 842 strcmp(oldhost, host) != 0) { 843 if (cl) { 844 clnt_destroy(cl); 845 cl = NULL; 846 } 847 cl = clnt_create_timed(host, prognum, versnum, "udp", 848 &tottimeout); 849 if (cl == NULL) 850 return ((int)RPC_TIMEDOUT); 851 if ((cl->cl_auth = authunix_create_default()) == NULL) { 852 clnt_destroy(cl); 853 return (RPC_CANTSEND); 854 } 855 oldprognum = prognum; 856 oldversnum = versnum; 857 (void) strlcpy(oldhost, host, sizeof (oldhost)); 858 clnt_stat = RPC_SUCCESS; 859 } 860 861 if (clnt_stat != RPC_SUCCESS) 862 return ((int)clnt_stat); /* don't bother retrying */ 863 864 clnt_stat = clnt_call(cl, procnum, inproc, in, 865 outproc, out, tottimeout); 866 867 return ((int)clnt_stat); 868 } 869 870 static int 871 getzfsquota(char *user, char *dataset, struct dqblk *zq) 872 { 873 zfs_handle_t *zhp = NULL; 874 char propname[ZFS_MAXPROPLEN]; 875 uint64_t userquota, userused; 876 877 if (g_zfs == NULL) 878 return (1); 879 880 if ((zhp = _zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL) 881 return (1); 882 883 (void) snprintf(propname, sizeof (propname), "userquota@%s", user); 884 if (_zfs_prop_get_userquota_int(zhp, propname, &userquota) != 0) { 885 _zfs_close(zhp); 886 return (1); 887 } 888 889 (void) snprintf(propname, sizeof (propname), "userused@%s", user); 890 if (_zfs_prop_get_userquota_int(zhp, propname, &userused) != 0) { 891 _zfs_close(zhp); 892 return (1); 893 } 894 895 zq->dqb_bhardlimit = userquota / DEV_BSIZE; 896 zq->dqb_bsoftlimit = userquota / DEV_BSIZE; 897 zq->dqb_curblocks = userused / DEV_BSIZE; 898 _zfs_close(zhp); 899 return (0); 900 } 901 902 static void 903 zexit(int n) 904 { 905 if (g_zfs != NULL) 906 _libzfs_fini(g_zfs); 907 exit(n); 908 } 909