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 2017 Joyent Inc
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
  27 /* All Rights Reserved */
  28 
  29 #include <stdio.h>
  30 #include <stdlib.h>
  31 #include <signal.h>
  32 #include <syslog.h>
  33 #include <string.h>
  34 #include <stropts.h>
  35 #include <errno.h>
  36 #include <sys/netconfig.h>
  37 #include <sys/mntent.h>
  38 #include <sys/mnttab.h>
  39 #include <sys/param.h>
  40 #include <sys/time.h>
  41 #include <sys/debug.h>
  42 #ifdef notdef
  43 #include <netconfig.h>
  44 #endif
  45 #include <sys/stat.h>
  46 #include <sys/file.h>
  47 #include <sys/fs/ufs_quota.h>
  48 #include <netdir.h>
  49 #include <rpc/rpc.h>
  50 #include <rpcsvc/rquota.h>
  51 #include <tiuser.h>
  52 #include <unistd.h>
  53 #include <dlfcn.h>
  54 #include <libzfs.h>
  55 
  56 #define QFNAME          "quotas"        /* name of quota file */
  57 #define RPCSVC_CLOSEDOWN 120            /* 2 minutes */
  58 
  59 struct fsquot {
  60         char *fsq_fstype;
  61         struct fsquot *fsq_next;
  62         char *fsq_dir;
  63         char *fsq_devname;
  64         dev_t fsq_dev;
  65 };
  66 
  67 struct fsquot *fsqlist = NULL;
  68 
  69 typedef struct authunix_parms *authp;
  70 
  71 static int request_pending;             /* Request in progress ? */
  72 
  73 void closedown();
  74 void dispatch();
  75 struct fsquot *findfsq();
  76 void freefs();
  77 int  getdiskquota();
  78 void getquota();
  79 int  hasquota();
  80 void log_cant_reply();
  81 void setupfs();
  82 static void zexit(int) __NORETURN;
  83 
  84 static libzfs_handle_t *(*_libzfs_init)(void);
  85 static void (*_libzfs_fini)(libzfs_handle_t *);
  86 static zfs_handle_t *(*_zfs_open)(libzfs_handle_t *, const char *, int);
  87 static void (*_zfs_close)(zfs_handle_t *);
  88 static int (*_zfs_prop_get_userquota_int)(zfs_handle_t *, const char *,
  89     uint64_t *);
  90 static libzfs_handle_t *g_zfs = NULL;
  91 
  92 /*
  93  * Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs
  94  * packages.  'rquotad' supports zfs as an option.
  95  */
  96 static void
  97 load_libzfs(void)
  98 {
  99         void *hdl;
 100 
 101         if (g_zfs != NULL)
 102                 return;
 103 
 104         if ((hdl = dlopen("libzfs.so", RTLD_LAZY)) != NULL) {
 105                 _libzfs_init = (libzfs_handle_t *(*)(void))dlsym(hdl,
 106                     "libzfs_init");
 107                 _libzfs_fini = (void (*)())dlsym(hdl, "libzfs_fini");
 108                 _zfs_open = (zfs_handle_t *(*)())dlsym(hdl, "zfs_open");
 109                 _zfs_close = (void (*)())dlsym(hdl, "zfs_close");
 110                 _zfs_prop_get_userquota_int = (int (*)())
 111                     dlsym(hdl, "zfs_prop_get_userquota_int");
 112 
 113                 if (_libzfs_init && _libzfs_fini && _zfs_open &&
 114                     _zfs_close && _zfs_prop_get_userquota_int)
 115                         g_zfs = _libzfs_init();
 116         }
 117 }
 118 
 119 /*ARGSUSED*/
 120 int
 121 main(int argc, char *argv[])
 122 {
 123         register SVCXPRT *transp;
 124 
 125         load_libzfs();
 126 
 127         /*
 128          * If stdin looks like a TLI endpoint, we assume
 129          * that we were started by a port monitor. If
 130          * t_getstate fails with TBADF, this is not a
 131          * TLI endpoint.
 132          */
 133         if (t_getstate(0) != -1 || t_errno != TBADF) {
 134                 char *netid;
 135                 struct netconfig *nconf = NULL;
 136 
 137                 openlog("rquotad", LOG_PID, LOG_DAEMON);
 138 
 139                 if ((netid = getenv("NLSPROVIDER")) == NULL) {
 140                         struct t_info tinfo;
 141 
 142                         if (t_sync(0) == -1) {
 143                                 syslog(LOG_ERR, "could not do t_sync");
 144                                 zexit(1);
 145                         }
 146                         if (t_getinfo(0, &tinfo) == -1) {
 147                                 syslog(LOG_ERR, "t_getinfo failed");
 148                                 zexit(1);
 149                         }
 150                         if (tinfo.servtype == T_CLTS) {
 151                                 if (tinfo.addr == INET_ADDRSTRLEN)
 152                                         netid = "udp";
 153                                 else
 154                                         netid = "udp6";
 155                         } else {
 156                                 syslog(LOG_ERR, "wrong transport");
 157                                 zexit(1);
 158                         }
 159                 }
 160                 if ((nconf = getnetconfigent(netid)) == NULL) {
 161                         syslog(LOG_ERR, "cannot get transport info");
 162                 }
 163 
 164                 if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
 165                         syslog(LOG_ERR, "cannot create server handle");
 166                         zexit(1);
 167                 }
 168                 if (nconf)
 169                         freenetconfigent(nconf);
 170 
 171                 if (!svc_reg(transp, RQUOTAPROG, RQUOTAVERS, dispatch, 0)) {
 172                         syslog(LOG_ERR,
 173                             "unable to register (RQUOTAPROG, RQUOTAVERS).");
 174                         zexit(1);
 175                 }
 176 
 177                 (void) sigset(SIGALRM, (void(*)(int)) closedown);
 178                 (void) alarm(RPCSVC_CLOSEDOWN);
 179 
 180                 svc_run();
 181                 zexit(1);
 182                 /* NOTREACHED */
 183         }
 184 
 185         /*
 186          * Started from a shell - fork the daemon.
 187          */
 188 
 189         switch (fork()) {
 190         case 0:         /* child */
 191                 break;
 192         case -1:
 193                 perror("rquotad: can't fork");
 194                 zexit(1);
 195         default:        /* parent */
 196                 zexit(0);
 197         }
 198 
 199         /*
 200          * Close existing file descriptors, open "/dev/null" as
 201          * standard input, output, and error, and detach from
 202          * controlling terminal.
 203          */
 204         closefrom(0);
 205         (void) open("/dev/null", O_RDONLY);
 206         (void) open("/dev/null", O_WRONLY);
 207         (void) dup(1);
 208         (void) setsid();
 209 
 210         openlog("rquotad", LOG_PID, LOG_DAEMON);
 211 
 212         /*
 213          * Create datagram service
 214          */
 215         if (svc_create(dispatch, RQUOTAPROG, RQUOTAVERS, "datagram_v") == 0) {
 216                 syslog(LOG_ERR, "couldn't register datagram_v service");
 217                 zexit(1);
 218         }
 219 
 220         /*
 221          * Start serving
 222          */
 223         svc_run();
 224         syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
 225         return (1);
 226 }
 227 
 228 void
 229 dispatch(rqstp, transp)
 230         register struct svc_req *rqstp;
 231         register SVCXPRT *transp;
 232 {
 233 
 234         request_pending = 1;
 235 
 236         switch (rqstp->rq_proc) {
 237         case NULLPROC:
 238                 errno = 0;
 239                 if (!svc_sendreply(transp, xdr_void, 0))
 240                         log_cant_reply(transp);
 241                 break;
 242 
 243         case RQUOTAPROC_GETQUOTA:
 244         case RQUOTAPROC_GETACTIVEQUOTA:
 245                 getquota(rqstp, transp);
 246                 break;
 247 
 248         default:
 249                 svcerr_noproc(transp);
 250                 break;
 251         }
 252 
 253         request_pending = 0;
 254 }
 255 
 256 void
 257 closedown()
 258 {
 259         if (!request_pending) {
 260                 int i, openfd;
 261                 struct t_info tinfo;
 262 
 263                 if (!t_getinfo(0, &tinfo) && (tinfo.servtype == T_CLTS))
 264                         zexit(0);
 265 
 266                 for (i = 0, openfd = 0; i < svc_max_pollfd && openfd < 2; i++) {
 267                         if (svc_pollfd[i].fd >= 0)
 268                                 openfd++;
 269                 }
 270 
 271                 if (openfd <= 1)
 272                         zexit(0);
 273         }
 274         (void) alarm(RPCSVC_CLOSEDOWN);
 275 }
 276 
 277 static int
 278 getzfsquota(uid_t user, char *dataset, struct dqblk *zq)
 279 {
 280         zfs_handle_t *zhp = NULL;
 281         char propname[ZFS_MAXPROPLEN];
 282         uint64_t userquota, userused;
 283 
 284         if (g_zfs == NULL)
 285                 return (1);
 286 
 287         if ((zhp = _zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL) {
 288                 syslog(LOG_ERR, "can not open zfs dataset %s", dataset);
 289                 return (1);
 290         }
 291 
 292         (void) snprintf(propname, sizeof (propname), "userquota@%u", user);
 293         if (_zfs_prop_get_userquota_int(zhp, propname, &userquota) != 0) {
 294                 _zfs_close(zhp);
 295                 return (1);
 296         }
 297 
 298         (void) snprintf(propname, sizeof (propname), "userused@%u", user);
 299         if (_zfs_prop_get_userquota_int(zhp, propname, &userused) != 0) {
 300                 _zfs_close(zhp);
 301                 return (1);
 302         }
 303 
 304         zq->dqb_bhardlimit = userquota / DEV_BSIZE;
 305         zq->dqb_bsoftlimit = userquota / DEV_BSIZE;
 306         zq->dqb_curblocks = userused / DEV_BSIZE;
 307         _zfs_close(zhp);
 308         return (0);
 309 }
 310 
 311 void
 312 getquota(rqstp, transp)
 313         register struct svc_req *rqstp;
 314         register SVCXPRT *transp;
 315 {
 316         struct getquota_args gqa;
 317         struct getquota_rslt gqr;
 318         struct dqblk dqblk;
 319         struct fsquot *fsqp;
 320         struct timeval tv;
 321         bool_t qactive;
 322 
 323         gqa.gqa_pathp = NULL;           /* let xdr allocate the storage */
 324         if (!svc_getargs(transp, xdr_getquota_args, (caddr_t)&gqa)) {
 325                 svcerr_decode(transp);
 326                 return;
 327         }
 328         /*
 329          * This authentication is really bogus with the current rpc
 330          * authentication scheme. One day we will have something for real.
 331          */
 332         CTASSERT(sizeof (authp) <= RQCRED_SIZE);
 333         if (rqstp->rq_cred.oa_flavor != AUTH_UNIX ||
 334             (((authp) rqstp->rq_clntcred)->aup_uid != 0 &&
 335                 ((authp) rqstp->rq_clntcred)->aup_uid != (uid_t)gqa.gqa_uid)) {
 336                 gqr.status = Q_EPERM;
 337                 goto sendreply;
 338         }
 339         fsqp = findfsq(gqa.gqa_pathp);
 340         if (fsqp == NULL) {
 341                 gqr.status = Q_NOQUOTA;
 342                 goto sendreply;
 343         }
 344 
 345         bzero(&dqblk, sizeof (dqblk));
 346         if (strcmp(fsqp->fsq_fstype, MNTTYPE_ZFS) == 0) {
 347                 if (getzfsquota(gqa.gqa_uid, fsqp->fsq_devname, &dqblk)) {
 348                         gqr.status = Q_NOQUOTA;
 349                         goto sendreply;
 350                 }
 351                 qactive = TRUE;
 352         } else {
 353                 if (quotactl(Q_GETQUOTA, fsqp->fsq_dir,
 354                     (uid_t)gqa.gqa_uid, &dqblk) != 0) {
 355                         qactive = FALSE;
 356                         if ((errno == ENOENT) ||
 357                             (rqstp->rq_proc != RQUOTAPROC_GETQUOTA)) {
 358                                 gqr.status = Q_NOQUOTA;
 359                                 goto sendreply;
 360                         }
 361 
 362                         /*
 363                          * If there is no quotas file, don't bother to sync it.
 364                          */
 365                         if (errno != ENOENT) {
 366                                 if (quotactl(Q_ALLSYNC, fsqp->fsq_dir,
 367                                     (uid_t)gqa.gqa_uid, &dqblk) < 0 &&
 368                                     errno == EINVAL)
 369                                         syslog(LOG_WARNING,
 370                                             "Quotas are not compiled "
 371                                             "into this kernel");
 372                                 if (getdiskquota(fsqp, (uid_t)gqa.gqa_uid,
 373                                     &dqblk) == 0) {
 374                                         gqr.status = Q_NOQUOTA;
 375                                         goto sendreply;
 376                                 }
 377                         }
 378                 } else {
 379                         qactive = TRUE;
 380                 }
 381                 /*
 382                  * We send the remaining time instead of the absolute time
 383                  * because clock skew between machines should be much greater
 384                  * than rpc delay.
 385                  */
 386 #define gqrslt getquota_rslt_u.gqr_rquota
 387 
 388                 gettimeofday(&tv, NULL);
 389                 gqr.gqrslt.rq_btimeleft = dqblk.dqb_btimelimit - tv.tv_sec;
 390                 gqr.gqrslt.rq_ftimeleft = dqblk.dqb_ftimelimit - tv.tv_sec;
 391         }
 392 
 393         gqr.status = Q_OK;
 394         gqr.gqrslt.rq_active    = qactive;
 395         gqr.gqrslt.rq_bsize     = DEV_BSIZE;
 396         gqr.gqrslt.rq_bhardlimit = dqblk.dqb_bhardlimit;
 397         gqr.gqrslt.rq_bsoftlimit = dqblk.dqb_bsoftlimit;
 398         gqr.gqrslt.rq_curblocks = dqblk.dqb_curblocks;
 399         gqr.gqrslt.rq_fhardlimit = dqblk.dqb_fhardlimit;
 400         gqr.gqrslt.rq_fsoftlimit = dqblk.dqb_fsoftlimit;
 401         gqr.gqrslt.rq_curfiles  = dqblk.dqb_curfiles;
 402 sendreply:
 403         errno = 0;
 404         if (!svc_sendreply(transp, xdr_getquota_rslt, (caddr_t)&gqr))
 405                 log_cant_reply(transp);
 406 }
 407 
 408 int
 409 quotactl(cmd, mountp, uid, dqp)
 410         int     cmd;
 411         char    *mountp;
 412         uid_t   uid;
 413         struct dqblk *dqp;
 414 {
 415         int             fd;
 416         int             status;
 417         struct quotctl  quota;
 418         char            mountpoint[256];
 419         FILE            *fstab;
 420         struct mnttab   mntp;
 421 
 422         if ((mountp == NULL) && (cmd == Q_ALLSYNC)) {
 423                 /*
 424                  * Find the mount point of any ufs file system. this is
 425                  * because the ioctl that implements the quotactl call has
 426                  * to go to a real file, and not to the block device.
 427                  */
 428                 if ((fstab = fopen(MNTTAB, "r")) == NULL) {
 429                         syslog(LOG_ERR, "can not open %s: %m ", MNTTAB);
 430                         return (-1);
 431                 }
 432                 fd = -1;
 433                 while ((status = getmntent(fstab, &mntp)) == NULL) {
 434                         if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 ||
 435                                 !(hasmntopt(&mntp, MNTOPT_RQ) ||
 436                                 hasmntopt(&mntp, MNTOPT_QUOTA)))
 437                                 continue;
 438                         (void) strlcpy(mountpoint, mntp.mnt_mountp,
 439                             sizeof (mountpoint));
 440                         strcat(mountpoint, "/quotas");
 441                         if ((fd = open64(mountpoint, O_RDWR)) >= 0)
 442                                 break;
 443                 }
 444                 fclose(fstab);
 445                 if (fd == -1) {
 446                         errno = ENOENT;
 447                         return (-1);
 448                 }
 449         } else {
 450                 if (mountp == NULL || mountp[0] == '\0') {
 451                         errno = ENOENT;
 452                         return (-1);
 453                 }
 454                 (void) strlcpy(mountpoint, mountp, sizeof (mountpoint));
 455                 strcat(mountpoint, "/quotas");
 456 
 457                 if ((fd = open64(mountpoint, O_RDONLY)) < 0) {
 458                         errno = ENOENT;
 459                         syslog(LOG_ERR, "can not open %s: %m ", mountpoint);
 460                         return (-1);
 461                 }
 462         }
 463         quota.op = cmd;
 464         quota.uid = uid;
 465         quota.addr = (caddr_t)dqp;
 466 
 467         status = ioctl(fd, Q_QUOTACTL, &quota);
 468 
 469         close(fd);
 470         return (status);
 471 }
 472 
 473 /*
 474  * Return the quota information for the given path.  Returns NULL if none
 475  * was found.
 476  */
 477 
 478 struct fsquot *
 479 findfsq(char *dir)
 480 {
 481         struct stat sb;
 482         struct fsquot *fsqp;
 483         static time_t lastmtime = 0;    /* mount table's previous mtime */
 484 
 485         /*
 486          * If we've never looked at the mount table, or it has changed
 487          * since the last time, rebuild the list of quota'd file systems
 488          * and remember the current mod time for the mount table.
 489          */
 490 
 491         if (stat(MNTTAB, &sb) < 0) {
 492                 syslog(LOG_ERR, "can't stat %s: %m", MNTTAB);
 493                 return (NULL);
 494         }
 495         if (lastmtime == 0 || sb.st_mtime != lastmtime) {
 496                 freefs();
 497                 setupfs();
 498                 lastmtime = sb.st_mtime;
 499         }
 500 
 501         /*
 502          * Try to find the given path in the list of file systems with
 503          * quotas.
 504          */
 505 
 506         if (fsqlist == NULL)
 507                 return (NULL);
 508         if (stat(dir, &sb) < 0)
 509                 return (NULL);
 510 
 511         for (fsqp = fsqlist; fsqp != NULL; fsqp = fsqp->fsq_next) {
 512                 if (sb.st_dev == fsqp->fsq_dev)
 513                         return (fsqp);
 514         }
 515 
 516         return (NULL);
 517 }
 518 
 519 static void
 520 setup_zfs(struct mnttab *mp)
 521 {
 522         struct fsquot *fsqp;
 523         struct stat sb;
 524 
 525         if (stat(mp->mnt_mountp, &sb) < 0)
 526                 return;
 527 
 528         fsqp = malloc(sizeof (struct fsquot));
 529         if (fsqp == NULL) {
 530                 syslog(LOG_ERR, "out of memory");
 531                 zexit(1);
 532         }
 533         fsqp->fsq_dir = strdup(mp->mnt_mountp);
 534         fsqp->fsq_devname = strdup(mp->mnt_special);
 535         if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) {
 536                 syslog(LOG_ERR, "out of memory");
 537                 zexit(1);
 538         }
 539 
 540         fsqp->fsq_fstype = MNTTYPE_ZFS;
 541         fsqp->fsq_dev = sb.st_dev;
 542         fsqp->fsq_next = fsqlist;
 543         fsqlist = fsqp;
 544 }
 545 
 546 void
 547 setupfs()
 548 {
 549         struct fsquot *fsqp;
 550         FILE *mt;
 551         struct mnttab m;
 552         struct stat sb;
 553         char qfilename[MAXPATHLEN];
 554 
 555         mt = fopen(MNTTAB, "r");
 556         if (mt == NULL) {
 557                 syslog(LOG_ERR, "can't read %s: %m", MNTTAB);
 558                 return;
 559         }
 560 
 561         while (getmntent(mt, &m) == 0) {
 562                 if (strcmp(m.mnt_fstype, MNTTYPE_ZFS) == 0) {
 563                         setup_zfs(&m);
 564                         continue;
 565                 }
 566 
 567                 if (strcmp(m.mnt_fstype, MNTTYPE_UFS) != 0)
 568                         continue;
 569                 if (!hasquota(m.mnt_mntopts)) {
 570                         snprintf(qfilename, sizeof (qfilename), "%s/%s",
 571                             m.mnt_mountp, QFNAME);
 572                         if (access(qfilename, F_OK) < 0)
 573                                 continue;
 574                 }
 575                 if (stat(m.mnt_special, &sb) < 0 ||
 576                     (sb.st_mode & S_IFMT) != S_IFBLK)
 577                         continue;
 578                 fsqp = malloc(sizeof (struct fsquot));
 579                 if (fsqp == NULL) {
 580                         syslog(LOG_ERR, "out of memory");
 581                         zexit(1);
 582                 }
 583                 fsqp->fsq_dir = strdup(m.mnt_mountp);
 584                 fsqp->fsq_devname = strdup(m.mnt_special);
 585                 if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) {
 586                         syslog(LOG_ERR, "out of memory");
 587                         zexit(1);
 588                 }
 589                 fsqp->fsq_fstype = MNTTYPE_UFS;
 590                 fsqp->fsq_dev = sb.st_rdev;
 591                 fsqp->fsq_next = fsqlist;
 592                 fsqlist = fsqp;
 593         }
 594         (void) fclose(mt);
 595 }
 596 
 597 /*
 598  * Free the memory used by the current list of quota'd file systems.  Nulls
 599  * out the list.
 600  */
 601 
 602 void
 603 freefs()
 604 {
 605         register struct fsquot *fsqp;
 606 
 607         while ((fsqp = fsqlist) != NULL) {
 608                 fsqlist = fsqp->fsq_next;
 609                 free(fsqp->fsq_dir);
 610                 free(fsqp->fsq_devname);
 611                 free(fsqp);
 612         }
 613 }
 614 
 615 int
 616 getdiskquota(fsqp, uid, dqp)
 617         struct fsquot *fsqp;
 618         uid_t uid;
 619         struct dqblk *dqp;
 620 {
 621         int fd;
 622         char qfilename[MAXPATHLEN];
 623 
 624         snprintf(qfilename, sizeof (qfilename), "%s/%s", fsqp->fsq_dir, QFNAME);
 625         if ((fd = open64(qfilename, O_RDONLY)) < 0)
 626                 return (0);
 627         (void) llseek(fd, (offset_t)dqoff(uid), L_SET);
 628         if (read(fd, dqp, sizeof (struct dqblk)) != sizeof (struct dqblk)) {
 629                 close(fd);
 630                 return (0);
 631         }
 632         close(fd);
 633         if (dqp->dqb_bhardlimit == 0 && dqp->dqb_bsoftlimit == 0 &&
 634             dqp->dqb_fhardlimit == 0 && dqp->dqb_fsoftlimit == 0) {
 635                 return (0);
 636         }
 637         return (1);
 638 }
 639 
 640 /*
 641  * Get the client's hostname from the transport handle
 642  * If the name is not available then return "(anon)".
 643  */
 644 struct nd_hostservlist *
 645 getclientsnames(transp)
 646         SVCXPRT *transp;
 647 {
 648         struct netbuf *nbuf;
 649         struct netconfig *nconf;
 650         static struct nd_hostservlist   *serv;
 651         static struct nd_hostservlist   anon_hsl;
 652         static struct nd_hostserv       anon_hs;
 653         static char anon_hname[] = "(anon)";
 654         static char anon_sname[] = "";
 655 
 656         /* Set up anonymous client */
 657         anon_hs.h_host = anon_hname;
 658         anon_hs.h_serv = anon_sname;
 659         anon_hsl.h_cnt = 1;
 660         anon_hsl.h_hostservs = &anon_hs;
 661 
 662         if (serv) {
 663                 netdir_free((char *)serv, ND_HOSTSERVLIST);
 664                 serv = NULL;
 665         }
 666         nconf = getnetconfigent(transp->xp_netid);
 667         if (nconf == NULL) {
 668                 syslog(LOG_ERR, "%s: getnetconfigent failed",
 669                         transp->xp_netid);
 670                 return (&anon_hsl);
 671         }
 672 
 673         nbuf = svc_getrpccaller(transp);
 674         if (nbuf == NULL) {
 675                 freenetconfigent(nconf);
 676                 return (&anon_hsl);
 677         }
 678         if (netdir_getbyaddr(nconf, &serv, nbuf)) {
 679                 freenetconfigent(nconf);
 680                 return (&anon_hsl);
 681         }
 682         freenetconfigent(nconf);
 683         return (serv);
 684 }
 685 
 686 void
 687 log_cant_reply(transp)
 688         SVCXPRT *transp;
 689 {
 690         int saverrno;
 691         struct nd_hostservlist *clnames;
 692         register char *name;
 693 
 694         saverrno = errno;       /* save error code */
 695         clnames = getclientsnames(transp);
 696         if (clnames == NULL)
 697                 return;
 698         name = clnames->h_hostservs->h_host;
 699 
 700         errno = saverrno;
 701         if (errno == 0)
 702                 syslog(LOG_ERR, "couldn't send reply to %s", name);
 703         else
 704                 syslog(LOG_ERR, "couldn't send reply to %s: %m", name);
 705 }
 706 
 707 char *mntopts[] = { MNTOPT_QUOTA, NULL };
 708 #define QUOTA    0
 709 
 710 /*
 711  * Return 1 if "quota" appears in the options string
 712  */
 713 int
 714 hasquota(opts)
 715         char *opts;
 716 {
 717         char *value;
 718 
 719         if (opts == NULL)
 720                 return (0);
 721         while (*opts != '\0') {
 722                 if (getsubopt(&opts, mntopts, &value) == QUOTA)
 723                         return (1);
 724         }
 725 
 726         return (0);
 727 }
 728 
 729 static void
 730 zexit(int n)
 731 {
 732         if (g_zfs != NULL)
 733                 _libzfs_fini(g_zfs);
 734         exit(n);
 735 }