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         CTASSERT(sizeof (authp) <= RQCRED_SIZE);
 324 
 325         gqa.gqa_pathp = NULL;           /* let xdr allocate the storage */
 326         if (!svc_getargs(transp, xdr_getquota_args, (caddr_t)&gqa)) {
 327                 svcerr_decode(transp);
 328                 return;
 329         }
 330         /*
 331          * This authentication is really bogus with the current rpc
 332          * authentication scheme. One day we will have something for real.
 333          */
 334         CTASSERT(sizeof (authp) <= RQCRED_SIZE);
 335         if (rqstp->rq_cred.oa_flavor != AUTH_UNIX ||
 336             (((authp) rqstp->rq_clntcred)->aup_uid != 0 &&
 337                 ((authp) rqstp->rq_clntcred)->aup_uid != (uid_t)gqa.gqa_uid)) {
 338                 gqr.status = Q_EPERM;
 339                 goto sendreply;
 340         }
 341         fsqp = findfsq(gqa.gqa_pathp);
 342         if (fsqp == NULL) {
 343                 gqr.status = Q_NOQUOTA;
 344                 goto sendreply;
 345         }
 346 
 347         bzero(&dqblk, sizeof (dqblk));
 348         if (strcmp(fsqp->fsq_fstype, MNTTYPE_ZFS) == 0) {
 349                 if (getzfsquota(gqa.gqa_uid, fsqp->fsq_devname, &dqblk)) {
 350                         gqr.status = Q_NOQUOTA;
 351                         goto sendreply;
 352                 }
 353                 qactive = TRUE;
 354         } else {
 355                 if (quotactl(Q_GETQUOTA, fsqp->fsq_dir,
 356                     (uid_t)gqa.gqa_uid, &dqblk) != 0) {
 357                         qactive = FALSE;
 358                         if ((errno == ENOENT) ||
 359                             (rqstp->rq_proc != RQUOTAPROC_GETQUOTA)) {
 360                                 gqr.status = Q_NOQUOTA;
 361                                 goto sendreply;
 362                         }
 363 
 364                         /*
 365                          * If there is no quotas file, don't bother to sync it.
 366                          */
 367                         if (errno != ENOENT) {
 368                                 if (quotactl(Q_ALLSYNC, fsqp->fsq_dir,
 369                                     (uid_t)gqa.gqa_uid, &dqblk) < 0 &&
 370                                     errno == EINVAL)
 371                                         syslog(LOG_WARNING,
 372                                             "Quotas are not compiled "
 373                                             "into this kernel");
 374                                 if (getdiskquota(fsqp, (uid_t)gqa.gqa_uid,
 375                                     &dqblk) == 0) {
 376                                         gqr.status = Q_NOQUOTA;
 377                                         goto sendreply;
 378                                 }
 379                         }
 380                 } else {
 381                         qactive = TRUE;
 382                 }
 383                 /*
 384                  * We send the remaining time instead of the absolute time
 385                  * because clock skew between machines should be much greater
 386                  * than rpc delay.
 387                  */
 388 #define gqrslt getquota_rslt_u.gqr_rquota
 389 
 390                 gettimeofday(&tv, NULL);
 391                 gqr.gqrslt.rq_btimeleft = dqblk.dqb_btimelimit - tv.tv_sec;
 392                 gqr.gqrslt.rq_ftimeleft = dqblk.dqb_ftimelimit - tv.tv_sec;
 393         }
 394 
 395         gqr.status = Q_OK;
 396         gqr.gqrslt.rq_active    = qactive;
 397         gqr.gqrslt.rq_bsize     = DEV_BSIZE;
 398         gqr.gqrslt.rq_bhardlimit = dqblk.dqb_bhardlimit;
 399         gqr.gqrslt.rq_bsoftlimit = dqblk.dqb_bsoftlimit;
 400         gqr.gqrslt.rq_curblocks = dqblk.dqb_curblocks;
 401         gqr.gqrslt.rq_fhardlimit = dqblk.dqb_fhardlimit;
 402         gqr.gqrslt.rq_fsoftlimit = dqblk.dqb_fsoftlimit;
 403         gqr.gqrslt.rq_curfiles  = dqblk.dqb_curfiles;
 404 sendreply:
 405         errno = 0;
 406         if (!svc_sendreply(transp, xdr_getquota_rslt, (caddr_t)&gqr))
 407                 log_cant_reply(transp);
 408 }
 409 
 410 int
 411 quotactl(cmd, mountp, uid, dqp)
 412         int     cmd;
 413         char    *mountp;
 414         uid_t   uid;
 415         struct dqblk *dqp;
 416 {
 417         int             fd;
 418         int             status;
 419         struct quotctl  quota;
 420         char            mountpoint[256];
 421         FILE            *fstab;
 422         struct mnttab   mntp;
 423 
 424         if ((mountp == NULL) && (cmd == Q_ALLSYNC)) {
 425                 /*
 426                  * Find the mount point of any ufs file system. this is
 427                  * because the ioctl that implements the quotactl call has
 428                  * to go to a real file, and not to the block device.
 429                  */
 430                 if ((fstab = fopen(MNTTAB, "r")) == NULL) {
 431                         syslog(LOG_ERR, "can not open %s: %m ", MNTTAB);
 432                         return (-1);
 433                 }
 434                 fd = -1;
 435                 while ((status = getmntent(fstab, &mntp)) == NULL) {
 436                         if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 ||
 437                                 !(hasmntopt(&mntp, MNTOPT_RQ) ||
 438                                 hasmntopt(&mntp, MNTOPT_QUOTA)))
 439                                 continue;
 440                         (void) strlcpy(mountpoint, mntp.mnt_mountp,
 441                             sizeof (mountpoint));
 442                         strcat(mountpoint, "/quotas");
 443                         if ((fd = open64(mountpoint, O_RDWR)) >= 0)
 444                                 break;
 445                 }
 446                 fclose(fstab);
 447                 if (fd == -1) {
 448                         errno = ENOENT;
 449                         return (-1);
 450                 }
 451         } else {
 452                 if (mountp == NULL || mountp[0] == '\0') {
 453                         errno = ENOENT;
 454                         return (-1);
 455                 }
 456                 (void) strlcpy(mountpoint, mountp, sizeof (mountpoint));
 457                 strcat(mountpoint, "/quotas");
 458 
 459                 if ((fd = open64(mountpoint, O_RDONLY)) < 0) {
 460                         errno = ENOENT;
 461                         syslog(LOG_ERR, "can not open %s: %m ", mountpoint);
 462                         return (-1);
 463                 }
 464         }
 465         quota.op = cmd;
 466         quota.uid = uid;
 467         quota.addr = (caddr_t)dqp;
 468 
 469         status = ioctl(fd, Q_QUOTACTL, &quota);
 470 
 471         close(fd);
 472         return (status);
 473 }
 474 
 475 /*
 476  * Return the quota information for the given path.  Returns NULL if none
 477  * was found.
 478  */
 479 
 480 struct fsquot *
 481 findfsq(char *dir)
 482 {
 483         struct stat sb;
 484         struct fsquot *fsqp;
 485         static time_t lastmtime = 0;    /* mount table's previous mtime */
 486 
 487         /*
 488          * If we've never looked at the mount table, or it has changed
 489          * since the last time, rebuild the list of quota'd file systems
 490          * and remember the current mod time for the mount table.
 491          */
 492 
 493         if (stat(MNTTAB, &sb) < 0) {
 494                 syslog(LOG_ERR, "can't stat %s: %m", MNTTAB);
 495                 return (NULL);
 496         }
 497         if (lastmtime == 0 || sb.st_mtime != lastmtime) {
 498                 freefs();
 499                 setupfs();
 500                 lastmtime = sb.st_mtime;
 501         }
 502 
 503         /*
 504          * Try to find the given path in the list of file systems with
 505          * quotas.
 506          */
 507 
 508         if (fsqlist == NULL)
 509                 return (NULL);
 510         if (stat(dir, &sb) < 0)
 511                 return (NULL);
 512 
 513         for (fsqp = fsqlist; fsqp != NULL; fsqp = fsqp->fsq_next) {
 514                 if (sb.st_dev == fsqp->fsq_dev)
 515                         return (fsqp);
 516         }
 517 
 518         return (NULL);
 519 }
 520 
 521 static void
 522 setup_zfs(struct mnttab *mp)
 523 {
 524         struct fsquot *fsqp;
 525         struct stat sb;
 526 
 527         if (stat(mp->mnt_mountp, &sb) < 0)
 528                 return;
 529 
 530         fsqp = malloc(sizeof (struct fsquot));
 531         if (fsqp == NULL) {
 532                 syslog(LOG_ERR, "out of memory");
 533                 zexit(1);
 534         }
 535         fsqp->fsq_dir = strdup(mp->mnt_mountp);
 536         fsqp->fsq_devname = strdup(mp->mnt_special);
 537         if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) {
 538                 syslog(LOG_ERR, "out of memory");
 539                 zexit(1);
 540         }
 541 
 542         fsqp->fsq_fstype = MNTTYPE_ZFS;
 543         fsqp->fsq_dev = sb.st_dev;
 544         fsqp->fsq_next = fsqlist;
 545         fsqlist = fsqp;
 546 }
 547 
 548 void
 549 setupfs()
 550 {
 551         struct fsquot *fsqp;
 552         FILE *mt;
 553         struct mnttab m;
 554         struct stat sb;
 555         char qfilename[MAXPATHLEN];
 556 
 557         mt = fopen(MNTTAB, "r");
 558         if (mt == NULL) {
 559                 syslog(LOG_ERR, "can't read %s: %m", MNTTAB);
 560                 return;
 561         }
 562 
 563         while (getmntent(mt, &m) == 0) {
 564                 if (strcmp(m.mnt_fstype, MNTTYPE_ZFS) == 0) {
 565                         setup_zfs(&m);
 566                         continue;
 567                 }
 568 
 569                 if (strcmp(m.mnt_fstype, MNTTYPE_UFS) != 0)
 570                         continue;
 571                 if (!hasquota(m.mnt_mntopts)) {
 572                         snprintf(qfilename, sizeof (qfilename), "%s/%s",
 573                             m.mnt_mountp, QFNAME);
 574                         if (access(qfilename, F_OK) < 0)
 575                                 continue;
 576                 }
 577                 if (stat(m.mnt_special, &sb) < 0 ||
 578                     (sb.st_mode & S_IFMT) != S_IFBLK)
 579                         continue;
 580                 fsqp = malloc(sizeof (struct fsquot));
 581                 if (fsqp == NULL) {
 582                         syslog(LOG_ERR, "out of memory");
 583                         zexit(1);
 584                 }
 585                 fsqp->fsq_dir = strdup(m.mnt_mountp);
 586                 fsqp->fsq_devname = strdup(m.mnt_special);
 587                 if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) {
 588                         syslog(LOG_ERR, "out of memory");
 589                         zexit(1);
 590                 }
 591                 fsqp->fsq_fstype = MNTTYPE_UFS;
 592                 fsqp->fsq_dev = sb.st_rdev;
 593                 fsqp->fsq_next = fsqlist;
 594                 fsqlist = fsqp;
 595         }
 596         (void) fclose(mt);
 597 }
 598 
 599 /*
 600  * Free the memory used by the current list of quota'd file systems.  Nulls
 601  * out the list.
 602  */
 603 
 604 void
 605 freefs()
 606 {
 607         register struct fsquot *fsqp;
 608 
 609         while ((fsqp = fsqlist) != NULL) {
 610                 fsqlist = fsqp->fsq_next;
 611                 free(fsqp->fsq_dir);
 612                 free(fsqp->fsq_devname);
 613                 free(fsqp);
 614         }
 615 }
 616 
 617 int
 618 getdiskquota(fsqp, uid, dqp)
 619         struct fsquot *fsqp;
 620         uid_t uid;
 621         struct dqblk *dqp;
 622 {
 623         int fd;
 624         char qfilename[MAXPATHLEN];
 625 
 626         snprintf(qfilename, sizeof (qfilename), "%s/%s", fsqp->fsq_dir, QFNAME);
 627         if ((fd = open64(qfilename, O_RDONLY)) < 0)
 628                 return (0);
 629         (void) llseek(fd, (offset_t)dqoff(uid), L_SET);
 630         if (read(fd, dqp, sizeof (struct dqblk)) != sizeof (struct dqblk)) {
 631                 close(fd);
 632                 return (0);
 633         }
 634         close(fd);
 635         if (dqp->dqb_bhardlimit == 0 && dqp->dqb_bsoftlimit == 0 &&
 636             dqp->dqb_fhardlimit == 0 && dqp->dqb_fsoftlimit == 0) {
 637                 return (0);
 638         }
 639         return (1);
 640 }
 641 
 642 /*
 643  * Get the client's hostname from the transport handle
 644  * If the name is not available then return "(anon)".
 645  */
 646 struct nd_hostservlist *
 647 getclientsnames(transp)
 648         SVCXPRT *transp;
 649 {
 650         struct netbuf *nbuf;
 651         struct netconfig *nconf;
 652         static struct nd_hostservlist   *serv;
 653         static struct nd_hostservlist   anon_hsl;
 654         static struct nd_hostserv       anon_hs;
 655         static char anon_hname[] = "(anon)";
 656         static char anon_sname[] = "";
 657 
 658         /* Set up anonymous client */
 659         anon_hs.h_host = anon_hname;
 660         anon_hs.h_serv = anon_sname;
 661         anon_hsl.h_cnt = 1;
 662         anon_hsl.h_hostservs = &anon_hs;
 663 
 664         if (serv) {
 665                 netdir_free((char *)serv, ND_HOSTSERVLIST);
 666                 serv = NULL;
 667         }
 668         nconf = getnetconfigent(transp->xp_netid);
 669         if (nconf == NULL) {
 670                 syslog(LOG_ERR, "%s: getnetconfigent failed",
 671                         transp->xp_netid);
 672                 return (&anon_hsl);
 673         }
 674 
 675         nbuf = svc_getrpccaller(transp);
 676         if (nbuf == NULL) {
 677                 freenetconfigent(nconf);
 678                 return (&anon_hsl);
 679         }
 680         if (netdir_getbyaddr(nconf, &serv, nbuf)) {
 681                 freenetconfigent(nconf);
 682                 return (&anon_hsl);
 683         }
 684         freenetconfigent(nconf);
 685         return (serv);
 686 }
 687 
 688 void
 689 log_cant_reply(transp)
 690         SVCXPRT *transp;
 691 {
 692         int saverrno;
 693         struct nd_hostservlist *clnames;
 694         register char *name;
 695 
 696         saverrno = errno;       /* save error code */
 697         clnames = getclientsnames(transp);
 698         if (clnames == NULL)
 699                 return;
 700         name = clnames->h_hostservs->h_host;
 701 
 702         errno = saverrno;
 703         if (errno == 0)
 704                 syslog(LOG_ERR, "couldn't send reply to %s", name);
 705         else
 706                 syslog(LOG_ERR, "couldn't send reply to %s: %m", name);
 707 }
 708 
 709 char *mntopts[] = { MNTOPT_QUOTA, NULL };
 710 #define QUOTA    0
 711 
 712 /*
 713  * Return 1 if "quota" appears in the options string
 714  */
 715 int
 716 hasquota(opts)
 717         char *opts;
 718 {
 719         char *value;
 720 
 721         if (opts == NULL)
 722                 return (0);
 723         while (*opts != '\0') {
 724                 if (getsubopt(&opts, mntopts, &value) == QUOTA)
 725                         return (1);
 726         }
 727 
 728         return (0);
 729 }
 730 
 731 static void
 732 zexit(int n)
 733 {
 734         if (g_zfs != NULL)
 735                 _libzfs_fini(g_zfs);
 736         exit(n);
 737 }