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