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