17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5edea4b55SLin Ling * Common Development and Distribution License (the "License").
6edea4b55SLin Ling * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22b1cdc720SAlex Wilson * Copyright 2017 Joyent Inc
23edea4b55SLin Ling * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
247c478bd9Sstevel@tonic-gate * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
277c478bd9Sstevel@tonic-gate /* All Rights Reserved */
287c478bd9Sstevel@tonic-gate
297c478bd9Sstevel@tonic-gate #include <stdio.h>
307c478bd9Sstevel@tonic-gate #include <stdlib.h>
317c478bd9Sstevel@tonic-gate #include <signal.h>
327c478bd9Sstevel@tonic-gate #include <syslog.h>
337c478bd9Sstevel@tonic-gate #include <string.h>
347c478bd9Sstevel@tonic-gate #include <stropts.h>
357c478bd9Sstevel@tonic-gate #include <errno.h>
367c478bd9Sstevel@tonic-gate #include <sys/netconfig.h>
377c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
387c478bd9Sstevel@tonic-gate #include <sys/mnttab.h>
397c478bd9Sstevel@tonic-gate #include <sys/param.h>
407c478bd9Sstevel@tonic-gate #include <sys/time.h>
41b1cdc720SAlex Wilson #include <sys/debug.h>
427c478bd9Sstevel@tonic-gate #ifdef notdef
437c478bd9Sstevel@tonic-gate #include <netconfig.h>
447c478bd9Sstevel@tonic-gate #endif
457c478bd9Sstevel@tonic-gate #include <sys/stat.h>
467c478bd9Sstevel@tonic-gate #include <sys/file.h>
477c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_quota.h>
487c478bd9Sstevel@tonic-gate #include <netdir.h>
497c478bd9Sstevel@tonic-gate #include <rpc/rpc.h>
507c478bd9Sstevel@tonic-gate #include <rpcsvc/rquota.h>
517c478bd9Sstevel@tonic-gate #include <tiuser.h>
527c478bd9Sstevel@tonic-gate #include <unistd.h>
53edea4b55SLin Ling #include <dlfcn.h>
54edea4b55SLin Ling #include <libzfs.h>
557c478bd9Sstevel@tonic-gate
567c478bd9Sstevel@tonic-gate #define QFNAME "quotas" /* name of quota file */
577c478bd9Sstevel@tonic-gate #define RPCSVC_CLOSEDOWN 120 /* 2 minutes */
587c478bd9Sstevel@tonic-gate
597c478bd9Sstevel@tonic-gate struct fsquot {
60edea4b55SLin Ling char *fsq_fstype;
617c478bd9Sstevel@tonic-gate struct fsquot *fsq_next;
627c478bd9Sstevel@tonic-gate char *fsq_dir;
637c478bd9Sstevel@tonic-gate char *fsq_devname;
647c478bd9Sstevel@tonic-gate dev_t fsq_dev;
657c478bd9Sstevel@tonic-gate };
667c478bd9Sstevel@tonic-gate
677c478bd9Sstevel@tonic-gate struct fsquot *fsqlist = NULL;
687c478bd9Sstevel@tonic-gate
697c478bd9Sstevel@tonic-gate typedef struct authunix_parms *authp;
707c478bd9Sstevel@tonic-gate
717c478bd9Sstevel@tonic-gate static int request_pending; /* Request in progress ? */
727c478bd9Sstevel@tonic-gate
737c478bd9Sstevel@tonic-gate void closedown();
747c478bd9Sstevel@tonic-gate void dispatch();
757c478bd9Sstevel@tonic-gate struct fsquot *findfsq();
767c478bd9Sstevel@tonic-gate void freefs();
777c478bd9Sstevel@tonic-gate int getdiskquota();
787c478bd9Sstevel@tonic-gate void getquota();
797c478bd9Sstevel@tonic-gate int hasquota();
807c478bd9Sstevel@tonic-gate void log_cant_reply();
817c478bd9Sstevel@tonic-gate void setupfs();
826685d298SToomas Soome static void zexit(int) __NORETURN;
83edea4b55SLin Ling
84edea4b55SLin Ling static libzfs_handle_t *(*_libzfs_init)(void);
85edea4b55SLin Ling static void (*_libzfs_fini)(libzfs_handle_t *);
86edea4b55SLin Ling static zfs_handle_t *(*_zfs_open)(libzfs_handle_t *, const char *, int);
87edea4b55SLin Ling static void (*_zfs_close)(zfs_handle_t *);
88edea4b55SLin Ling static int (*_zfs_prop_get_userquota_int)(zfs_handle_t *, const char *,
89edea4b55SLin Ling uint64_t *);
90edea4b55SLin Ling static libzfs_handle_t *g_zfs = NULL;
91edea4b55SLin Ling
92edea4b55SLin Ling /*
93edea4b55SLin Ling * Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs
94edea4b55SLin Ling * packages. 'rquotad' supports zfs as an option.
95edea4b55SLin Ling */
96edea4b55SLin Ling static void
load_libzfs(void)97edea4b55SLin Ling load_libzfs(void)
98edea4b55SLin Ling {
99edea4b55SLin Ling void *hdl;
100edea4b55SLin Ling
101edea4b55SLin Ling if (g_zfs != NULL)
102edea4b55SLin Ling return;
103edea4b55SLin Ling
104edea4b55SLin Ling if ((hdl = dlopen("libzfs.so", RTLD_LAZY)) != NULL) {
105edea4b55SLin Ling _libzfs_init = (libzfs_handle_t *(*)(void))dlsym(hdl,
106edea4b55SLin Ling "libzfs_init");
107edea4b55SLin Ling _libzfs_fini = (void (*)())dlsym(hdl, "libzfs_fini");
108edea4b55SLin Ling _zfs_open = (zfs_handle_t *(*)())dlsym(hdl, "zfs_open");
109edea4b55SLin Ling _zfs_close = (void (*)())dlsym(hdl, "zfs_close");
110edea4b55SLin Ling _zfs_prop_get_userquota_int = (int (*)())
111edea4b55SLin Ling dlsym(hdl, "zfs_prop_get_userquota_int");
112edea4b55SLin Ling
113edea4b55SLin Ling if (_libzfs_init && _libzfs_fini && _zfs_open &&
114edea4b55SLin Ling _zfs_close && _zfs_prop_get_userquota_int)
115edea4b55SLin Ling g_zfs = _libzfs_init();
116edea4b55SLin Ling }
117edea4b55SLin Ling }
1187c478bd9Sstevel@tonic-gate
1197c478bd9Sstevel@tonic-gate /*ARGSUSED*/
12011606941Sjwahlig int
main(int argc,char * argv[])12111606941Sjwahlig main(int argc, char *argv[])
1227c478bd9Sstevel@tonic-gate {
1237c478bd9Sstevel@tonic-gate register SVCXPRT *transp;
1247c478bd9Sstevel@tonic-gate
125edea4b55SLin Ling load_libzfs();
126edea4b55SLin Ling
1277c478bd9Sstevel@tonic-gate /*
1287c478bd9Sstevel@tonic-gate * If stdin looks like a TLI endpoint, we assume
1297c478bd9Sstevel@tonic-gate * that we were started by a port monitor. If
1307c478bd9Sstevel@tonic-gate * t_getstate fails with TBADF, this is not a
1317c478bd9Sstevel@tonic-gate * TLI endpoint.
1327c478bd9Sstevel@tonic-gate */
1337c478bd9Sstevel@tonic-gate if (t_getstate(0) != -1 || t_errno != TBADF) {
1347c478bd9Sstevel@tonic-gate char *netid;
1357c478bd9Sstevel@tonic-gate struct netconfig *nconf = NULL;
1367c478bd9Sstevel@tonic-gate
1377c478bd9Sstevel@tonic-gate openlog("rquotad", LOG_PID, LOG_DAEMON);
1387c478bd9Sstevel@tonic-gate
1397c478bd9Sstevel@tonic-gate if ((netid = getenv("NLSPROVIDER")) == NULL) {
1407c478bd9Sstevel@tonic-gate struct t_info tinfo;
1417c478bd9Sstevel@tonic-gate
1427c478bd9Sstevel@tonic-gate if (t_sync(0) == -1) {
1437c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "could not do t_sync");
144edea4b55SLin Ling zexit(1);
1457c478bd9Sstevel@tonic-gate }
1467c478bd9Sstevel@tonic-gate if (t_getinfo(0, &tinfo) == -1) {
1477c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "t_getinfo failed");
148edea4b55SLin Ling zexit(1);
1497c478bd9Sstevel@tonic-gate }
1507c478bd9Sstevel@tonic-gate if (tinfo.servtype == T_CLTS) {
1517c478bd9Sstevel@tonic-gate if (tinfo.addr == INET_ADDRSTRLEN)
1527c478bd9Sstevel@tonic-gate netid = "udp";
1537c478bd9Sstevel@tonic-gate else
1547c478bd9Sstevel@tonic-gate netid = "udp6";
1557c478bd9Sstevel@tonic-gate } else {
1567c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "wrong transport");
157edea4b55SLin Ling zexit(1);
1587c478bd9Sstevel@tonic-gate }
1597c478bd9Sstevel@tonic-gate }
1607c478bd9Sstevel@tonic-gate if ((nconf = getnetconfigent(netid)) == NULL) {
1617c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "cannot get transport info");
1627c478bd9Sstevel@tonic-gate }
1637c478bd9Sstevel@tonic-gate
1647c478bd9Sstevel@tonic-gate if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
1657c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "cannot create server handle");
166edea4b55SLin Ling zexit(1);
1677c478bd9Sstevel@tonic-gate }
1687c478bd9Sstevel@tonic-gate if (nconf)
1697c478bd9Sstevel@tonic-gate freenetconfigent(nconf);
1707c478bd9Sstevel@tonic-gate
1717c478bd9Sstevel@tonic-gate if (!svc_reg(transp, RQUOTAPROG, RQUOTAVERS, dispatch, 0)) {
1727c478bd9Sstevel@tonic-gate syslog(LOG_ERR,
173edea4b55SLin Ling "unable to register (RQUOTAPROG, RQUOTAVERS).");
174edea4b55SLin Ling zexit(1);
1757c478bd9Sstevel@tonic-gate }
1767c478bd9Sstevel@tonic-gate
1777c478bd9Sstevel@tonic-gate (void) sigset(SIGALRM, (void(*)(int)) closedown);
1787c478bd9Sstevel@tonic-gate (void) alarm(RPCSVC_CLOSEDOWN);
1797c478bd9Sstevel@tonic-gate
1807c478bd9Sstevel@tonic-gate svc_run();
181edea4b55SLin Ling zexit(1);
1827c478bd9Sstevel@tonic-gate /* NOTREACHED */
1837c478bd9Sstevel@tonic-gate }
1847c478bd9Sstevel@tonic-gate
1857c478bd9Sstevel@tonic-gate /*
1867c478bd9Sstevel@tonic-gate * Started from a shell - fork the daemon.
1877c478bd9Sstevel@tonic-gate */
1887c478bd9Sstevel@tonic-gate
1897c478bd9Sstevel@tonic-gate switch (fork()) {
1907c478bd9Sstevel@tonic-gate case 0: /* child */
1917c478bd9Sstevel@tonic-gate break;
1927c478bd9Sstevel@tonic-gate case -1:
1937c478bd9Sstevel@tonic-gate perror("rquotad: can't fork");
194edea4b55SLin Ling zexit(1);
1957c478bd9Sstevel@tonic-gate default: /* parent */
196edea4b55SLin Ling zexit(0);
1977c478bd9Sstevel@tonic-gate }
1987c478bd9Sstevel@tonic-gate
1997c478bd9Sstevel@tonic-gate /*
2007c478bd9Sstevel@tonic-gate * Close existing file descriptors, open "/dev/null" as
2017c478bd9Sstevel@tonic-gate * standard input, output, and error, and detach from
2027c478bd9Sstevel@tonic-gate * controlling terminal.
2037c478bd9Sstevel@tonic-gate */
2047c478bd9Sstevel@tonic-gate closefrom(0);
2057c478bd9Sstevel@tonic-gate (void) open("/dev/null", O_RDONLY);
2067c478bd9Sstevel@tonic-gate (void) open("/dev/null", O_WRONLY);
2077c478bd9Sstevel@tonic-gate (void) dup(1);
2087c478bd9Sstevel@tonic-gate (void) setsid();
2097c478bd9Sstevel@tonic-gate
2107c478bd9Sstevel@tonic-gate openlog("rquotad", LOG_PID, LOG_DAEMON);
2117c478bd9Sstevel@tonic-gate
2127c478bd9Sstevel@tonic-gate /*
2137c478bd9Sstevel@tonic-gate * Create datagram service
2147c478bd9Sstevel@tonic-gate */
2157c478bd9Sstevel@tonic-gate if (svc_create(dispatch, RQUOTAPROG, RQUOTAVERS, "datagram_v") == 0) {
2167c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "couldn't register datagram_v service");
217edea4b55SLin Ling zexit(1);
2187c478bd9Sstevel@tonic-gate }
2197c478bd9Sstevel@tonic-gate
2207c478bd9Sstevel@tonic-gate /*
2217c478bd9Sstevel@tonic-gate * Start serving
2227c478bd9Sstevel@tonic-gate */
2237c478bd9Sstevel@tonic-gate svc_run();
2247c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
22511606941Sjwahlig return (1);
2267c478bd9Sstevel@tonic-gate }
2277c478bd9Sstevel@tonic-gate
2287c478bd9Sstevel@tonic-gate void
dispatch(rqstp,transp)2297c478bd9Sstevel@tonic-gate dispatch(rqstp, transp)
2307c478bd9Sstevel@tonic-gate register struct svc_req *rqstp;
2317c478bd9Sstevel@tonic-gate register SVCXPRT *transp;
2327c478bd9Sstevel@tonic-gate {
2337c478bd9Sstevel@tonic-gate
2347c478bd9Sstevel@tonic-gate request_pending = 1;
2357c478bd9Sstevel@tonic-gate
2367c478bd9Sstevel@tonic-gate switch (rqstp->rq_proc) {
2377c478bd9Sstevel@tonic-gate case NULLPROC:
2387c478bd9Sstevel@tonic-gate errno = 0;
2397c478bd9Sstevel@tonic-gate if (!svc_sendreply(transp, xdr_void, 0))
2407c478bd9Sstevel@tonic-gate log_cant_reply(transp);
2417c478bd9Sstevel@tonic-gate break;
2427c478bd9Sstevel@tonic-gate
2437c478bd9Sstevel@tonic-gate case RQUOTAPROC_GETQUOTA:
2447c478bd9Sstevel@tonic-gate case RQUOTAPROC_GETACTIVEQUOTA:
2457c478bd9Sstevel@tonic-gate getquota(rqstp, transp);
2467c478bd9Sstevel@tonic-gate break;
2477c478bd9Sstevel@tonic-gate
2487c478bd9Sstevel@tonic-gate default:
2497c478bd9Sstevel@tonic-gate svcerr_noproc(transp);
2507c478bd9Sstevel@tonic-gate break;
2517c478bd9Sstevel@tonic-gate }
2527c478bd9Sstevel@tonic-gate
2537c478bd9Sstevel@tonic-gate request_pending = 0;
2547c478bd9Sstevel@tonic-gate }
2557c478bd9Sstevel@tonic-gate
2567c478bd9Sstevel@tonic-gate void
closedown()2577c478bd9Sstevel@tonic-gate closedown()
2587c478bd9Sstevel@tonic-gate {
2597c478bd9Sstevel@tonic-gate if (!request_pending) {
2607c478bd9Sstevel@tonic-gate int i, openfd;
2617c478bd9Sstevel@tonic-gate struct t_info tinfo;
2627c478bd9Sstevel@tonic-gate
2637c478bd9Sstevel@tonic-gate if (!t_getinfo(0, &tinfo) && (tinfo.servtype == T_CLTS))
264edea4b55SLin Ling zexit(0);
2657c478bd9Sstevel@tonic-gate
2667c478bd9Sstevel@tonic-gate for (i = 0, openfd = 0; i < svc_max_pollfd && openfd < 2; i++) {
2677c478bd9Sstevel@tonic-gate if (svc_pollfd[i].fd >= 0)
2687c478bd9Sstevel@tonic-gate openfd++;
2697c478bd9Sstevel@tonic-gate }
2707c478bd9Sstevel@tonic-gate
2717c478bd9Sstevel@tonic-gate if (openfd <= 1)
272edea4b55SLin Ling zexit(0);
2737c478bd9Sstevel@tonic-gate }
2747c478bd9Sstevel@tonic-gate (void) alarm(RPCSVC_CLOSEDOWN);
2757c478bd9Sstevel@tonic-gate }
2767c478bd9Sstevel@tonic-gate
277edea4b55SLin Ling static int
getzfsquota(uid_t user,char * dataset,struct dqblk * zq)278edea4b55SLin Ling getzfsquota(uid_t user, char *dataset, struct dqblk *zq)
279edea4b55SLin Ling {
280edea4b55SLin Ling zfs_handle_t *zhp = NULL;
281edea4b55SLin Ling char propname[ZFS_MAXPROPLEN];
282edea4b55SLin Ling uint64_t userquota, userused;
283edea4b55SLin Ling
284edea4b55SLin Ling if (g_zfs == NULL)
285edea4b55SLin Ling return (1);
286edea4b55SLin Ling
287edea4b55SLin Ling if ((zhp = _zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL) {
288edea4b55SLin Ling syslog(LOG_ERR, "can not open zfs dataset %s", dataset);
289edea4b55SLin Ling return (1);
290edea4b55SLin Ling }
291edea4b55SLin Ling
292edea4b55SLin Ling (void) snprintf(propname, sizeof (propname), "userquota@%u", user);
293edea4b55SLin Ling if (_zfs_prop_get_userquota_int(zhp, propname, &userquota) != 0) {
294edea4b55SLin Ling _zfs_close(zhp);
295edea4b55SLin Ling return (1);
296edea4b55SLin Ling }
297edea4b55SLin Ling
298edea4b55SLin Ling (void) snprintf(propname, sizeof (propname), "userused@%u", user);
299edea4b55SLin Ling if (_zfs_prop_get_userquota_int(zhp, propname, &userused) != 0) {
300edea4b55SLin Ling _zfs_close(zhp);
301edea4b55SLin Ling return (1);
302edea4b55SLin Ling }
303edea4b55SLin Ling
304edea4b55SLin Ling zq->dqb_bhardlimit = userquota / DEV_BSIZE;
305edea4b55SLin Ling zq->dqb_bsoftlimit = userquota / DEV_BSIZE;
306edea4b55SLin Ling zq->dqb_curblocks = userused / DEV_BSIZE;
307edea4b55SLin Ling _zfs_close(zhp);
308edea4b55SLin Ling return (0);
309edea4b55SLin Ling }
310edea4b55SLin Ling
3117c478bd9Sstevel@tonic-gate void
getquota(rqstp,transp)3127c478bd9Sstevel@tonic-gate getquota(rqstp, transp)
3137c478bd9Sstevel@tonic-gate register struct svc_req *rqstp;
3147c478bd9Sstevel@tonic-gate register SVCXPRT *transp;
3157c478bd9Sstevel@tonic-gate {
3167c478bd9Sstevel@tonic-gate struct getquota_args gqa;
3177c478bd9Sstevel@tonic-gate struct getquota_rslt gqr;
3187c478bd9Sstevel@tonic-gate struct dqblk dqblk;
3197c478bd9Sstevel@tonic-gate struct fsquot *fsqp;
3207c478bd9Sstevel@tonic-gate struct timeval tv;
3217c478bd9Sstevel@tonic-gate bool_t qactive;
3227c478bd9Sstevel@tonic-gate
3237c478bd9Sstevel@tonic-gate gqa.gqa_pathp = NULL; /* let xdr allocate the storage */
3247c478bd9Sstevel@tonic-gate if (!svc_getargs(transp, xdr_getquota_args, (caddr_t)&gqa)) {
3257c478bd9Sstevel@tonic-gate svcerr_decode(transp);
3267c478bd9Sstevel@tonic-gate return;
3277c478bd9Sstevel@tonic-gate }
3287c478bd9Sstevel@tonic-gate /*
3297c478bd9Sstevel@tonic-gate * This authentication is really bogus with the current rpc
3307c478bd9Sstevel@tonic-gate * authentication scheme. One day we will have something for real.
3317c478bd9Sstevel@tonic-gate */
332b1cdc720SAlex Wilson CTASSERT(sizeof (authp) <= RQCRED_SIZE);
3337c478bd9Sstevel@tonic-gate if (rqstp->rq_cred.oa_flavor != AUTH_UNIX ||
3347c478bd9Sstevel@tonic-gate (((authp) rqstp->rq_clntcred)->aup_uid != 0 &&
3357c478bd9Sstevel@tonic-gate ((authp) rqstp->rq_clntcred)->aup_uid != (uid_t)gqa.gqa_uid)) {
3367c478bd9Sstevel@tonic-gate gqr.status = Q_EPERM;
3377c478bd9Sstevel@tonic-gate goto sendreply;
3387c478bd9Sstevel@tonic-gate }
3397c478bd9Sstevel@tonic-gate fsqp = findfsq(gqa.gqa_pathp);
3407c478bd9Sstevel@tonic-gate if (fsqp == NULL) {
3417c478bd9Sstevel@tonic-gate gqr.status = Q_NOQUOTA;
3427c478bd9Sstevel@tonic-gate goto sendreply;
3437c478bd9Sstevel@tonic-gate }
3447c478bd9Sstevel@tonic-gate
345edea4b55SLin Ling bzero(&dqblk, sizeof (dqblk));
346edea4b55SLin Ling if (strcmp(fsqp->fsq_fstype, MNTTYPE_ZFS) == 0) {
347edea4b55SLin Ling if (getzfsquota(gqa.gqa_uid, fsqp->fsq_devname, &dqblk)) {
3487c478bd9Sstevel@tonic-gate gqr.status = Q_NOQUOTA;
3497c478bd9Sstevel@tonic-gate goto sendreply;
3507c478bd9Sstevel@tonic-gate }
351edea4b55SLin Ling qactive = TRUE;
352edea4b55SLin Ling } else {
353edea4b55SLin Ling if (quotactl(Q_GETQUOTA, fsqp->fsq_dir,
354edea4b55SLin Ling (uid_t)gqa.gqa_uid, &dqblk) != 0) {
355edea4b55SLin Ling qactive = FALSE;
356edea4b55SLin Ling if ((errno == ENOENT) ||
357edea4b55SLin Ling (rqstp->rq_proc != RQUOTAPROC_GETQUOTA)) {
3587c478bd9Sstevel@tonic-gate gqr.status = Q_NOQUOTA;
3597c478bd9Sstevel@tonic-gate goto sendreply;
3607c478bd9Sstevel@tonic-gate }
361edea4b55SLin Ling
362edea4b55SLin Ling /*
363edea4b55SLin Ling * If there is no quotas file, don't bother to sync it.
364edea4b55SLin Ling */
365edea4b55SLin Ling if (errno != ENOENT) {
366edea4b55SLin Ling if (quotactl(Q_ALLSYNC, fsqp->fsq_dir,
367edea4b55SLin Ling (uid_t)gqa.gqa_uid, &dqblk) < 0 &&
368edea4b55SLin Ling errno == EINVAL)
369edea4b55SLin Ling syslog(LOG_WARNING,
370edea4b55SLin Ling "Quotas are not compiled "
371edea4b55SLin Ling "into this kernel");
372edea4b55SLin Ling if (getdiskquota(fsqp, (uid_t)gqa.gqa_uid,
373edea4b55SLin Ling &dqblk) == 0) {
374edea4b55SLin Ling gqr.status = Q_NOQUOTA;
375edea4b55SLin Ling goto sendreply;
376edea4b55SLin Ling }
377edea4b55SLin Ling }
378edea4b55SLin Ling } else {
379edea4b55SLin Ling qactive = TRUE;
3807c478bd9Sstevel@tonic-gate }
381edea4b55SLin Ling /*
382edea4b55SLin Ling * We send the remaining time instead of the absolute time
383edea4b55SLin Ling * because clock skew between machines should be much greater
384edea4b55SLin Ling * than rpc delay.
385edea4b55SLin Ling */
3867c478bd9Sstevel@tonic-gate #define gqrslt getquota_rslt_u.gqr_rquota
3877c478bd9Sstevel@tonic-gate
388edea4b55SLin Ling gettimeofday(&tv, NULL);
389edea4b55SLin Ling gqr.gqrslt.rq_btimeleft = dqblk.dqb_btimelimit - tv.tv_sec;
390edea4b55SLin Ling gqr.gqrslt.rq_ftimeleft = dqblk.dqb_ftimelimit - tv.tv_sec;
391edea4b55SLin Ling }
392edea4b55SLin Ling
3937c478bd9Sstevel@tonic-gate gqr.status = Q_OK;
3947c478bd9Sstevel@tonic-gate gqr.gqrslt.rq_active = qactive;
3957c478bd9Sstevel@tonic-gate gqr.gqrslt.rq_bsize = DEV_BSIZE;
3967c478bd9Sstevel@tonic-gate gqr.gqrslt.rq_bhardlimit = dqblk.dqb_bhardlimit;
3977c478bd9Sstevel@tonic-gate gqr.gqrslt.rq_bsoftlimit = dqblk.dqb_bsoftlimit;
3987c478bd9Sstevel@tonic-gate gqr.gqrslt.rq_curblocks = dqblk.dqb_curblocks;
3997c478bd9Sstevel@tonic-gate gqr.gqrslt.rq_fhardlimit = dqblk.dqb_fhardlimit;
4007c478bd9Sstevel@tonic-gate gqr.gqrslt.rq_fsoftlimit = dqblk.dqb_fsoftlimit;
4017c478bd9Sstevel@tonic-gate gqr.gqrslt.rq_curfiles = dqblk.dqb_curfiles;
4027c478bd9Sstevel@tonic-gate sendreply:
4037c478bd9Sstevel@tonic-gate errno = 0;
4047c478bd9Sstevel@tonic-gate if (!svc_sendreply(transp, xdr_getquota_rslt, (caddr_t)&gqr))
4057c478bd9Sstevel@tonic-gate log_cant_reply(transp);
4067c478bd9Sstevel@tonic-gate }
4077c478bd9Sstevel@tonic-gate
40811606941Sjwahlig int
quotactl(int cmd,char * mountp,uid_t uid,struct dqblk * dqp)409*8509e9caSToomas Soome quotactl(int cmd, char *mountp, uid_t uid, struct dqblk *dqp)
4107c478bd9Sstevel@tonic-gate {
411*8509e9caSToomas Soome int fd;
412*8509e9caSToomas Soome int status;
413*8509e9caSToomas Soome struct quotctl quota;
4147c478bd9Sstevel@tonic-gate char mountpoint[256];
4157c478bd9Sstevel@tonic-gate FILE *fstab;
4167c478bd9Sstevel@tonic-gate struct mnttab mntp;
4177c478bd9Sstevel@tonic-gate
4187c478bd9Sstevel@tonic-gate if ((mountp == NULL) && (cmd == Q_ALLSYNC)) {
4197c478bd9Sstevel@tonic-gate /*
4207c478bd9Sstevel@tonic-gate * Find the mount point of any ufs file system. this is
4217c478bd9Sstevel@tonic-gate * because the ioctl that implements the quotactl call has
4227c478bd9Sstevel@tonic-gate * to go to a real file, and not to the block device.
4237c478bd9Sstevel@tonic-gate */
4247c478bd9Sstevel@tonic-gate if ((fstab = fopen(MNTTAB, "r")) == NULL) {
4257c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "can not open %s: %m ", MNTTAB);
4267c478bd9Sstevel@tonic-gate return (-1);
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate fd = -1;
429*8509e9caSToomas Soome while ((status = getmntent(fstab, &mntp)) == 0) {
4307c478bd9Sstevel@tonic-gate if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 ||
4317c478bd9Sstevel@tonic-gate !(hasmntopt(&mntp, MNTOPT_RQ) ||
4327c478bd9Sstevel@tonic-gate hasmntopt(&mntp, MNTOPT_QUOTA)))
4337c478bd9Sstevel@tonic-gate continue;
434edea4b55SLin Ling (void) strlcpy(mountpoint, mntp.mnt_mountp,
435edea4b55SLin Ling sizeof (mountpoint));
4367c478bd9Sstevel@tonic-gate strcat(mountpoint, "/quotas");
4377c478bd9Sstevel@tonic-gate if ((fd = open64(mountpoint, O_RDWR)) >= 0)
4387c478bd9Sstevel@tonic-gate break;
4397c478bd9Sstevel@tonic-gate }
4407c478bd9Sstevel@tonic-gate fclose(fstab);
4417c478bd9Sstevel@tonic-gate if (fd == -1) {
4427c478bd9Sstevel@tonic-gate errno = ENOENT;
4437c478bd9Sstevel@tonic-gate return (-1);
4447c478bd9Sstevel@tonic-gate }
4457c478bd9Sstevel@tonic-gate } else {
4467c478bd9Sstevel@tonic-gate if (mountp == NULL || mountp[0] == '\0') {
4477c478bd9Sstevel@tonic-gate errno = ENOENT;
4487c478bd9Sstevel@tonic-gate return (-1);
4497c478bd9Sstevel@tonic-gate }
450edea4b55SLin Ling (void) strlcpy(mountpoint, mountp, sizeof (mountpoint));
4517c478bd9Sstevel@tonic-gate strcat(mountpoint, "/quotas");
4527c478bd9Sstevel@tonic-gate
4537c478bd9Sstevel@tonic-gate if ((fd = open64(mountpoint, O_RDONLY)) < 0) {
4547c478bd9Sstevel@tonic-gate errno = ENOENT;
4557c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "can not open %s: %m ", mountpoint);
4567c478bd9Sstevel@tonic-gate return (-1);
4577c478bd9Sstevel@tonic-gate }
4587c478bd9Sstevel@tonic-gate }
4597c478bd9Sstevel@tonic-gate quota.op = cmd;
4607c478bd9Sstevel@tonic-gate quota.uid = uid;
4617c478bd9Sstevel@tonic-gate quota.addr = (caddr_t)dqp;
4627c478bd9Sstevel@tonic-gate
4637c478bd9Sstevel@tonic-gate status = ioctl(fd, Q_QUOTACTL, "a);
4647c478bd9Sstevel@tonic-gate
4657c478bd9Sstevel@tonic-gate close(fd);
4667c478bd9Sstevel@tonic-gate return (status);
4677c478bd9Sstevel@tonic-gate }
4687c478bd9Sstevel@tonic-gate
4697c478bd9Sstevel@tonic-gate /*
4707c478bd9Sstevel@tonic-gate * Return the quota information for the given path. Returns NULL if none
4717c478bd9Sstevel@tonic-gate * was found.
4727c478bd9Sstevel@tonic-gate */
4737c478bd9Sstevel@tonic-gate
4747c478bd9Sstevel@tonic-gate struct fsquot *
findfsq(char * dir)4752c151c63SLin Ling findfsq(char *dir)
4767c478bd9Sstevel@tonic-gate {
4777c478bd9Sstevel@tonic-gate struct stat sb;
4782c151c63SLin Ling struct fsquot *fsqp;
479*8509e9caSToomas Soome static time_t lastmtime = 0; /* mount table's previous mtime */
4807c478bd9Sstevel@tonic-gate
4817c478bd9Sstevel@tonic-gate /*
4827c478bd9Sstevel@tonic-gate * If we've never looked at the mount table, or it has changed
4837c478bd9Sstevel@tonic-gate * since the last time, rebuild the list of quota'd file systems
4847c478bd9Sstevel@tonic-gate * and remember the current mod time for the mount table.
4857c478bd9Sstevel@tonic-gate */
4867c478bd9Sstevel@tonic-gate
4877c478bd9Sstevel@tonic-gate if (stat(MNTTAB, &sb) < 0) {
4887c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "can't stat %s: %m", MNTTAB);
4897c478bd9Sstevel@tonic-gate return (NULL);
4907c478bd9Sstevel@tonic-gate }
4917c478bd9Sstevel@tonic-gate if (lastmtime == 0 || sb.st_mtime != lastmtime) {
4927c478bd9Sstevel@tonic-gate freefs();
4937c478bd9Sstevel@tonic-gate setupfs();
4947c478bd9Sstevel@tonic-gate lastmtime = sb.st_mtime;
4957c478bd9Sstevel@tonic-gate }
4967c478bd9Sstevel@tonic-gate
4977c478bd9Sstevel@tonic-gate /*
4987c478bd9Sstevel@tonic-gate * Try to find the given path in the list of file systems with
4997c478bd9Sstevel@tonic-gate * quotas.
5007c478bd9Sstevel@tonic-gate */
5017c478bd9Sstevel@tonic-gate
5027c478bd9Sstevel@tonic-gate if (fsqlist == NULL)
5037c478bd9Sstevel@tonic-gate return (NULL);
5047c478bd9Sstevel@tonic-gate if (stat(dir, &sb) < 0)
5057c478bd9Sstevel@tonic-gate return (NULL);
506be961672SLin Ling
5077c478bd9Sstevel@tonic-gate for (fsqp = fsqlist; fsqp != NULL; fsqp = fsqp->fsq_next) {
5082c151c63SLin Ling if (sb.st_dev == fsqp->fsq_dev)
5097c478bd9Sstevel@tonic-gate return (fsqp);
5107c478bd9Sstevel@tonic-gate }
5112c151c63SLin Ling
5127c478bd9Sstevel@tonic-gate return (NULL);
5137c478bd9Sstevel@tonic-gate }
5147c478bd9Sstevel@tonic-gate
515edea4b55SLin Ling static void
setup_zfs(struct mnttab * mp)516edea4b55SLin Ling setup_zfs(struct mnttab *mp)
517edea4b55SLin Ling {
518edea4b55SLin Ling struct fsquot *fsqp;
5192c151c63SLin Ling struct stat sb;
5202c151c63SLin Ling
5212c151c63SLin Ling if (stat(mp->mnt_mountp, &sb) < 0)
5222c151c63SLin Ling return;
523edea4b55SLin Ling
524edea4b55SLin Ling fsqp = malloc(sizeof (struct fsquot));
525edea4b55SLin Ling if (fsqp == NULL) {
526edea4b55SLin Ling syslog(LOG_ERR, "out of memory");
527edea4b55SLin Ling zexit(1);
528edea4b55SLin Ling }
529edea4b55SLin Ling fsqp->fsq_dir = strdup(mp->mnt_mountp);
530edea4b55SLin Ling fsqp->fsq_devname = strdup(mp->mnt_special);
531edea4b55SLin Ling if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) {
532edea4b55SLin Ling syslog(LOG_ERR, "out of memory");
533edea4b55SLin Ling zexit(1);
534edea4b55SLin Ling }
5352c151c63SLin Ling
536edea4b55SLin Ling fsqp->fsq_fstype = MNTTYPE_ZFS;
5372c151c63SLin Ling fsqp->fsq_dev = sb.st_dev;
538edea4b55SLin Ling fsqp->fsq_next = fsqlist;
539edea4b55SLin Ling fsqlist = fsqp;
540edea4b55SLin Ling }
541edea4b55SLin Ling
5427c478bd9Sstevel@tonic-gate void
setupfs()5437c478bd9Sstevel@tonic-gate setupfs()
5447c478bd9Sstevel@tonic-gate {
545edea4b55SLin Ling struct fsquot *fsqp;
5467c478bd9Sstevel@tonic-gate FILE *mt;
5477c478bd9Sstevel@tonic-gate struct mnttab m;
5487c478bd9Sstevel@tonic-gate struct stat sb;
5497c478bd9Sstevel@tonic-gate char qfilename[MAXPATHLEN];
5507c478bd9Sstevel@tonic-gate
5517c478bd9Sstevel@tonic-gate mt = fopen(MNTTAB, "r");
5527c478bd9Sstevel@tonic-gate if (mt == NULL) {
5537c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "can't read %s: %m", MNTTAB);
5547c478bd9Sstevel@tonic-gate return;
5557c478bd9Sstevel@tonic-gate }
5567c478bd9Sstevel@tonic-gate
5577c478bd9Sstevel@tonic-gate while (getmntent(mt, &m) == 0) {
558edea4b55SLin Ling if (strcmp(m.mnt_fstype, MNTTYPE_ZFS) == 0) {
559edea4b55SLin Ling setup_zfs(&m);
560edea4b55SLin Ling continue;
561edea4b55SLin Ling }
562edea4b55SLin Ling
5637c478bd9Sstevel@tonic-gate if (strcmp(m.mnt_fstype, MNTTYPE_UFS) != 0)
5647c478bd9Sstevel@tonic-gate continue;
5657c478bd9Sstevel@tonic-gate if (!hasquota(m.mnt_mntopts)) {
566edea4b55SLin Ling snprintf(qfilename, sizeof (qfilename), "%s/%s",
567edea4b55SLin Ling m.mnt_mountp, QFNAME);
5687c478bd9Sstevel@tonic-gate if (access(qfilename, F_OK) < 0)
5697c478bd9Sstevel@tonic-gate continue;
5707c478bd9Sstevel@tonic-gate }
5717c478bd9Sstevel@tonic-gate if (stat(m.mnt_special, &sb) < 0 ||
5727c478bd9Sstevel@tonic-gate (sb.st_mode & S_IFMT) != S_IFBLK)
5737c478bd9Sstevel@tonic-gate continue;
574edea4b55SLin Ling fsqp = malloc(sizeof (struct fsquot));
5757c478bd9Sstevel@tonic-gate if (fsqp == NULL) {
5767c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "out of memory");
577edea4b55SLin Ling zexit(1);
5787c478bd9Sstevel@tonic-gate }
579edea4b55SLin Ling fsqp->fsq_dir = strdup(m.mnt_mountp);
580edea4b55SLin Ling fsqp->fsq_devname = strdup(m.mnt_special);
5817c478bd9Sstevel@tonic-gate if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) {
5827c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "out of memory");
583edea4b55SLin Ling zexit(1);
5847c478bd9Sstevel@tonic-gate }
585edea4b55SLin Ling fsqp->fsq_fstype = MNTTYPE_UFS;
5867c478bd9Sstevel@tonic-gate fsqp->fsq_dev = sb.st_rdev;
587edea4b55SLin Ling fsqp->fsq_next = fsqlist;
5887c478bd9Sstevel@tonic-gate fsqlist = fsqp;
5897c478bd9Sstevel@tonic-gate }
5907c478bd9Sstevel@tonic-gate (void) fclose(mt);
5917c478bd9Sstevel@tonic-gate }
5927c478bd9Sstevel@tonic-gate
5937c478bd9Sstevel@tonic-gate /*
5947c478bd9Sstevel@tonic-gate * Free the memory used by the current list of quota'd file systems. Nulls
5957c478bd9Sstevel@tonic-gate * out the list.
5967c478bd9Sstevel@tonic-gate */
5977c478bd9Sstevel@tonic-gate
5987c478bd9Sstevel@tonic-gate void
freefs()5997c478bd9Sstevel@tonic-gate freefs()
6007c478bd9Sstevel@tonic-gate {
6017c478bd9Sstevel@tonic-gate register struct fsquot *fsqp;
6027c478bd9Sstevel@tonic-gate
6037c478bd9Sstevel@tonic-gate while ((fsqp = fsqlist) != NULL) {
6047c478bd9Sstevel@tonic-gate fsqlist = fsqp->fsq_next;
6057c478bd9Sstevel@tonic-gate free(fsqp->fsq_dir);
6067c478bd9Sstevel@tonic-gate free(fsqp->fsq_devname);
6077c478bd9Sstevel@tonic-gate free(fsqp);
6087c478bd9Sstevel@tonic-gate }
6097c478bd9Sstevel@tonic-gate }
6107c478bd9Sstevel@tonic-gate
6117c478bd9Sstevel@tonic-gate int
getdiskquota(fsqp,uid,dqp)6127c478bd9Sstevel@tonic-gate getdiskquota(fsqp, uid, dqp)
6137c478bd9Sstevel@tonic-gate struct fsquot *fsqp;
6147c478bd9Sstevel@tonic-gate uid_t uid;
6157c478bd9Sstevel@tonic-gate struct dqblk *dqp;
6167c478bd9Sstevel@tonic-gate {
6177c478bd9Sstevel@tonic-gate int fd;
6187c478bd9Sstevel@tonic-gate char qfilename[MAXPATHLEN];
6197c478bd9Sstevel@tonic-gate
620edea4b55SLin Ling snprintf(qfilename, sizeof (qfilename), "%s/%s", fsqp->fsq_dir, QFNAME);
6217c478bd9Sstevel@tonic-gate if ((fd = open64(qfilename, O_RDONLY)) < 0)
6227c478bd9Sstevel@tonic-gate return (0);
6237c478bd9Sstevel@tonic-gate (void) llseek(fd, (offset_t)dqoff(uid), L_SET);
6247c478bd9Sstevel@tonic-gate if (read(fd, dqp, sizeof (struct dqblk)) != sizeof (struct dqblk)) {
6257c478bd9Sstevel@tonic-gate close(fd);
6267c478bd9Sstevel@tonic-gate return (0);
6277c478bd9Sstevel@tonic-gate }
6287c478bd9Sstevel@tonic-gate close(fd);
6297c478bd9Sstevel@tonic-gate if (dqp->dqb_bhardlimit == 0 && dqp->dqb_bsoftlimit == 0 &&
6307c478bd9Sstevel@tonic-gate dqp->dqb_fhardlimit == 0 && dqp->dqb_fsoftlimit == 0) {
6317c478bd9Sstevel@tonic-gate return (0);
6327c478bd9Sstevel@tonic-gate }
6337c478bd9Sstevel@tonic-gate return (1);
6347c478bd9Sstevel@tonic-gate }
6357c478bd9Sstevel@tonic-gate
6367c478bd9Sstevel@tonic-gate /*
6377c478bd9Sstevel@tonic-gate * Get the client's hostname from the transport handle
6387c478bd9Sstevel@tonic-gate * If the name is not available then return "(anon)".
6397c478bd9Sstevel@tonic-gate */
6407c478bd9Sstevel@tonic-gate struct nd_hostservlist *
getclientsnames(transp)6417c478bd9Sstevel@tonic-gate getclientsnames(transp)
6427c478bd9Sstevel@tonic-gate SVCXPRT *transp;
6437c478bd9Sstevel@tonic-gate {
6447c478bd9Sstevel@tonic-gate struct netbuf *nbuf;
6457c478bd9Sstevel@tonic-gate struct netconfig *nconf;
6467c478bd9Sstevel@tonic-gate static struct nd_hostservlist *serv;
6477c478bd9Sstevel@tonic-gate static struct nd_hostservlist anon_hsl;
6487c478bd9Sstevel@tonic-gate static struct nd_hostserv anon_hs;
6497c478bd9Sstevel@tonic-gate static char anon_hname[] = "(anon)";
6507c478bd9Sstevel@tonic-gate static char anon_sname[] = "";
6517c478bd9Sstevel@tonic-gate
6527c478bd9Sstevel@tonic-gate /* Set up anonymous client */
6537c478bd9Sstevel@tonic-gate anon_hs.h_host = anon_hname;
6547c478bd9Sstevel@tonic-gate anon_hs.h_serv = anon_sname;
6557c478bd9Sstevel@tonic-gate anon_hsl.h_cnt = 1;
6567c478bd9Sstevel@tonic-gate anon_hsl.h_hostservs = &anon_hs;
6577c478bd9Sstevel@tonic-gate
6587c478bd9Sstevel@tonic-gate if (serv) {
6597c478bd9Sstevel@tonic-gate netdir_free((char *)serv, ND_HOSTSERVLIST);
6607c478bd9Sstevel@tonic-gate serv = NULL;
6617c478bd9Sstevel@tonic-gate }
6627c478bd9Sstevel@tonic-gate nconf = getnetconfigent(transp->xp_netid);
6637c478bd9Sstevel@tonic-gate if (nconf == NULL) {
6647c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "%s: getnetconfigent failed",
6657c478bd9Sstevel@tonic-gate transp->xp_netid);
6667c478bd9Sstevel@tonic-gate return (&anon_hsl);
6677c478bd9Sstevel@tonic-gate }
6687c478bd9Sstevel@tonic-gate
6697c478bd9Sstevel@tonic-gate nbuf = svc_getrpccaller(transp);
6707c478bd9Sstevel@tonic-gate if (nbuf == NULL) {
6717c478bd9Sstevel@tonic-gate freenetconfigent(nconf);
6727c478bd9Sstevel@tonic-gate return (&anon_hsl);
6737c478bd9Sstevel@tonic-gate }
6747c478bd9Sstevel@tonic-gate if (netdir_getbyaddr(nconf, &serv, nbuf)) {
6757c478bd9Sstevel@tonic-gate freenetconfigent(nconf);
6767c478bd9Sstevel@tonic-gate return (&anon_hsl);
6777c478bd9Sstevel@tonic-gate }
6787c478bd9Sstevel@tonic-gate freenetconfigent(nconf);
6797c478bd9Sstevel@tonic-gate return (serv);
6807c478bd9Sstevel@tonic-gate }
6817c478bd9Sstevel@tonic-gate
6827c478bd9Sstevel@tonic-gate void
log_cant_reply(transp)6837c478bd9Sstevel@tonic-gate log_cant_reply(transp)
6847c478bd9Sstevel@tonic-gate SVCXPRT *transp;
6857c478bd9Sstevel@tonic-gate {
6867c478bd9Sstevel@tonic-gate int saverrno;
6877c478bd9Sstevel@tonic-gate struct nd_hostservlist *clnames;
6887c478bd9Sstevel@tonic-gate register char *name;
6897c478bd9Sstevel@tonic-gate
6907c478bd9Sstevel@tonic-gate saverrno = errno; /* save error code */
6917c478bd9Sstevel@tonic-gate clnames = getclientsnames(transp);
6927c478bd9Sstevel@tonic-gate if (clnames == NULL)
6937c478bd9Sstevel@tonic-gate return;
6947c478bd9Sstevel@tonic-gate name = clnames->h_hostservs->h_host;
6957c478bd9Sstevel@tonic-gate
6967c478bd9Sstevel@tonic-gate errno = saverrno;
6977c478bd9Sstevel@tonic-gate if (errno == 0)
6987c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "couldn't send reply to %s", name);
6997c478bd9Sstevel@tonic-gate else
7007c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "couldn't send reply to %s: %m", name);
7017c478bd9Sstevel@tonic-gate }
7027c478bd9Sstevel@tonic-gate
7037c478bd9Sstevel@tonic-gate char *mntopts[] = { MNTOPT_QUOTA, NULL };
7047c478bd9Sstevel@tonic-gate #define QUOTA 0
7057c478bd9Sstevel@tonic-gate
7067c478bd9Sstevel@tonic-gate /*
7077c478bd9Sstevel@tonic-gate * Return 1 if "quota" appears in the options string
7087c478bd9Sstevel@tonic-gate */
7097c478bd9Sstevel@tonic-gate int
hasquota(opts)7107c478bd9Sstevel@tonic-gate hasquota(opts)
7117c478bd9Sstevel@tonic-gate char *opts;
7127c478bd9Sstevel@tonic-gate {
7137c478bd9Sstevel@tonic-gate char *value;
7147c478bd9Sstevel@tonic-gate
7157c478bd9Sstevel@tonic-gate if (opts == NULL)
7167c478bd9Sstevel@tonic-gate return (0);
7177c478bd9Sstevel@tonic-gate while (*opts != '\0') {
7187c478bd9Sstevel@tonic-gate if (getsubopt(&opts, mntopts, &value) == QUOTA)
7197c478bd9Sstevel@tonic-gate return (1);
7207c478bd9Sstevel@tonic-gate }
7217c478bd9Sstevel@tonic-gate
7227c478bd9Sstevel@tonic-gate return (0);
7237c478bd9Sstevel@tonic-gate }
724edea4b55SLin Ling
725edea4b55SLin Ling static void
zexit(int n)726edea4b55SLin Ling zexit(int n)
727edea4b55SLin Ling {
728edea4b55SLin Ling if (g_zfs != NULL)
729edea4b55SLin Ling _libzfs_fini(g_zfs);
730edea4b55SLin Ling exit(n);
731edea4b55SLin Ling }
732