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, "=") + 1;
 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, &quota);
 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 }