xref: /illumos-gate/usr/src/cmd/fs.d/ufs/quota/quota.c (revision e753f464d28e02e23aa93bd7d51d39fc56f79897)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * University Copyright- Copyright (c) 1982, 1986, 1988
31  * The Regents of the University of California
32  * All Rights Reserved
33  *
34  * University Acknowledgment- Portions of this document are derived from
35  * software developed by the University of California, Berkeley, and its
36  * contributors.
37  */
38 
39 /*
40  * Disk quota reporting program.
41  */
42 #include <stdio.h>
43 #include <sys/mnttab.h>
44 #include <ctype.h>
45 #include <pwd.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <memory.h>
49 #include <sys/time.h>
50 #include <sys/param.h>
51 #include <sys/types.h>
52 #include <sys/sysmacros.h>
53 #include <sys/mntent.h>
54 #include <sys/file.h>
55 #include <sys/stat.h>
56 #include <sys/fs/ufs_quota.h>
57 #include <priv_utils.h>
58 #include <locale.h>
59 #include <rpc/rpc.h>
60 #include <netdb.h>
61 #include <rpcsvc/rquota.h>
62 #include <zone.h>
63 #include "../../nfs/lib/replica.h"
64 #include <dlfcn.h>
65 #include <libzfs.h>
66 
67 int	vflag;
68 int	nolocalquota;
69 
70 extern int	optind;
71 extern char	*optarg;
72 
73 #define	QFNAME	"quotas"
74 
75 #if DEV_BSIZE < 1024
76 #define	kb(x)	((x) / (1024 / DEV_BSIZE))
77 #else
78 #define	kb(x)	((x) * (DEV_BSIZE / 1024))
79 #endif
80 
81 #if	!defined(TEXT_DOMAIN)   /* Should be defined by cc -D */
82 #define	TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't */
83 #endif
84 
85 static void zexit(int);
86 static int getzfsquota(char *, char *, struct dqblk *);
87 static int getnfsquota(char *, char *, uid_t, struct dqblk *);
88 static void showuid(uid_t);
89 static void showquotas(uid_t, char *);
90 static void warn(struct mnttab *, struct dqblk *);
91 static void heading(uid_t, char *);
92 static void prquota(struct mnttab *, struct dqblk *);
93 static void fmttime(char *, long);
94 
95 static libzfs_handle_t *(*_libzfs_init)(void);
96 static void (*_libzfs_fini)(libzfs_handle_t *);
97 static zfs_handle_t *(*_zfs_open)(libzfs_handle_t *, const char *, int);
98 static void (*_zfs_close)(zfs_handle_t *);
99 static int (*_zfs_prop_get_userquota_int)(zfs_handle_t *, const char *,
100     uint64_t *);
101 static libzfs_handle_t *g_zfs = NULL;
102 
103 /*
104  * Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs
105  * packages.  'quota' utility supports zfs as an option.
106  */
107 static void
108 load_libzfs(void)
109 {
110 	void *hdl;
111 
112 	if (g_zfs != NULL)
113 		return;
114 
115 	if ((hdl = dlopen("libzfs.so", RTLD_LAZY)) != NULL) {
116 		_libzfs_init = (libzfs_handle_t *(*)(void))dlsym(hdl,
117 		    "libzfs_init");
118 		_libzfs_fini = (void (*)())dlsym(hdl, "libzfs_fini");
119 		_zfs_open = (zfs_handle_t *(*)())dlsym(hdl, "zfs_open");
120 		_zfs_close = (void (*)())dlsym(hdl, "zfs_close");
121 		_zfs_prop_get_userquota_int = (int (*)())
122 		    dlsym(hdl, "zfs_prop_get_userquota_int");
123 
124 		if (_libzfs_init && _libzfs_fini && _zfs_open &&
125 		    _zfs_close && _zfs_prop_get_userquota_int)
126 			g_zfs = _libzfs_init();
127 	}
128 }
129 
130 int
131 main(int argc, char *argv[])
132 {
133 	int	opt;
134 	int	i;
135 	int	status = 0;
136 
137 	(void) setlocale(LC_ALL, "");
138 	(void) textdomain(TEXT_DOMAIN);
139 
140 	/*
141 	 * PRIV_FILE_DAC_READ is needed to read the QFNAME file
142 	 * Clear all other privleges from the limit set, and add
143 	 * the required privilege to the bracketed set.
144 	 */
145 
146 	if (__init_suid_priv(PU_CLEARLIMITSET, PRIV_FILE_DAC_READ,
147 	    NULL) == -1) {
148 		(void) fprintf(stderr,
149 		    gettext("Insufficient privileges, "
150 		    "quota must be set-uid root or have "
151 		    "file_dac_read privileges\n"));
152 
153 		exit(1);
154 	}
155 
156 	load_libzfs();
157 
158 	while ((opt = getopt(argc, argv, "vV")) != EOF) {
159 		switch (opt) {
160 
161 		case 'v':
162 			vflag++;
163 			break;
164 
165 		case 'V':		/* Print command line */
166 			{
167 			char	*opt_text;
168 			int	opt_count;
169 
170 			(void) fprintf(stdout, "quota -F UFS ");
171 			for (opt_count = 1; opt_count < argc; opt_count++) {
172 				opt_text = argv[opt_count];
173 				if (opt_text)
174 					(void) fprintf(stdout, " %s ",
175 					    opt_text);
176 			}
177 			(void) fprintf(stdout, "\n");
178 			}
179 			break;
180 
181 		case '?':
182 			fprintf(stderr, "usage: quota [-v] [username]\n");
183 			zexit(32);
184 		}
185 	}
186 	if (quotactl(Q_ALLSYNC, NULL, (uid_t)0, NULL) < 0 && errno == EINVAL) {
187 		if (vflag)
188 			fprintf(stderr, "There are no quotas on this system\n");
189 		nolocalquota++;
190 	}
191 	if (argc == optind) {
192 		showuid(getuid());
193 		zexit(0);
194 	}
195 	for (i = optind; i < argc; i++) {
196 		if (alldigits(argv[i])) {
197 			showuid((uid_t)atoi(argv[i]));
198 		} else
199 			status |= showname(argv[i]);
200 	}
201 	__priv_relinquish();
202 	return (status);
203 }
204 
205 static void
206 showuid(uid_t uid)
207 {
208 	struct passwd *pwd = getpwuid(uid);
209 
210 	if (uid == 0) {
211 		if (vflag)
212 			printf("no disk quota for uid 0\n");
213 		return;
214 	}
215 	if (pwd == NULL)
216 		showquotas(uid, "(no account)");
217 	else
218 		showquotas(uid, pwd->pw_name);
219 }
220 
221 int
222 showname(char *name)
223 {
224 	struct passwd *pwd = getpwnam(name);
225 
226 	if (pwd == NULL) {
227 		fprintf(stderr, "quota: %s: unknown user\n", name);
228 		return (32);
229 	}
230 	if (pwd->pw_uid == 0) {
231 		if (vflag)
232 			printf("no disk quota for %s (uid 0)\n", name);
233 		return (0);
234 	}
235 	showquotas(pwd->pw_uid, name);
236 	return (0);
237 }
238 
239 static void
240 showquotas(uid_t uid, char *name)
241 {
242 	struct mnttab mnt;
243 	FILE *mtab;
244 	struct dqblk dqblk;
245 	uid_t myuid;
246 	struct failed_srv {
247 		char *serv_name;
248 		struct failed_srv *next;
249 	};
250 	struct failed_srv *failed_srv_list = NULL;
251 	int	rc;
252 	char	my_zonename[ZONENAME_MAX];
253 	zoneid_t my_zoneid = getzoneid();
254 
255 	myuid = getuid();
256 	if (uid != myuid && myuid != 0) {
257 		printf("quota: %s (uid %d): permission denied\n", name, uid);
258 		zexit(32);
259 	}
260 
261 	memset(my_zonename, '\0', ZONENAME_MAX);
262 	getzonenamebyid(my_zoneid, my_zonename, ZONENAME_MAX);
263 
264 	if (vflag)
265 		heading(uid, name);
266 	mtab = fopen(MNTTAB, "r");
267 	while (getmntent(mtab, &mnt) == NULL) {
268 		if (strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) == 0) {
269 			bzero(&dqblk, sizeof (dqblk));
270 			if (getzfsquota(name, mnt.mnt_special, &dqblk))
271 				continue;
272 		} else if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) == 0) {
273 			if (nolocalquota ||
274 			    (quotactl(Q_GETQUOTA,
275 			    mnt.mnt_mountp, uid, &dqblk) != 0 &&
276 			    !(vflag && getdiskquota(&mnt, uid, &dqblk))))
277 				continue;
278 		} else if (strcmp(mnt.mnt_fstype, MNTTYPE_NFS) == 0) {
279 
280 			struct replica *rl;
281 			int count;
282 			char *mntopt = NULL;
283 
284 			/*
285 			 * Skip checking quotas for file systems mounted
286 			 * in other zones. Zone names will be passed in
287 			 * following format from hasmntopt():
288 			 * "zone=<zone-name>,<mnt options...>"
289 			 */
290 			if ((mntopt = hasmntopt(&mnt, MNTOPT_ZONE)) &&
291 			    (my_zonename[0] != '\0')) {
292 				mntopt += strcspn(mntopt, "=");
293 				if (strncmp(++mntopt, my_zonename,
294 				    strcspn(mntopt, ",")) != 0)
295 					continue;
296 			}
297 
298 			if (hasopt(MNTOPT_NOQUOTA, mnt.mnt_mntopts))
299 				continue;
300 
301 			/*
302 			 * Skip quota processing if mounted with public
303 			 * option. We are not likely to be able to pierce
304 			 * a fire wall to contact the quota server.
305 			 */
306 			if (hasopt(MNTOPT_PUBLIC, mnt.mnt_mntopts))
307 				continue;
308 
309 			rl = parse_replica(mnt.mnt_special, &count);
310 
311 			if (rl == NULL) {
312 
313 				if (count < 0)
314 					fprintf(stderr, "cannot find hostname "
315 					    "and/or pathname for %s\n",
316 					    mnt.mnt_mountp);
317 				else
318 					fprintf(stderr, "no memory to parse "
319 					    "mnttab entry for %s\n",
320 					    mnt.mnt_mountp);
321 				continue;
322 			}
323 
324 			/*
325 			 * We skip quota reporting on mounts with replicas
326 			 * for the following reasons:
327 			 *
328 			 * (1) Very little point in reporting quotas on
329 			 * a set of read-only replicas ... how will the
330 			 * user correct the problem?
331 			 *
332 			 * (2) Which replica would we report the quota
333 			 * for? If we pick the current replica, what
334 			 * happens when a fail over event occurs? The
335 			 * next time quota is run, the quota will look
336 			 * all different, or there won't even be one.
337 			 * This has the potential to break scripts.
338 			 *
339 			 * If we prnt quouta for all replicas, how do
340 			 * we present the output without breaking scripts?
341 			 */
342 
343 			if (count > 1) {
344 				free_replica(rl, count);
345 				continue;
346 			}
347 
348 			/*
349 			 * Skip file systems mounted using public fh.
350 			 * We are not likely to be able to pierce
351 			 * a fire wall to contact the quota server.
352 			 */
353 			if (strcmp(rl[0].host, "nfs") == 0 &&
354 			    strncmp(rl[0].path, "//", 2) == 0) {
355 				free_replica(rl, count);
356 				continue;
357 			}
358 
359 			/*
360 			 * Skip getting quotas from failing servers
361 			 */
362 			if (failed_srv_list != NULL) {
363 				struct failed_srv *tmp_list;
364 				int found_failed = 0;
365 				size_t len = strlen(rl[0].host);
366 
367 				tmp_list = failed_srv_list;
368 				do {
369 					if (strncasecmp(rl[0].host,
370 					    tmp_list->serv_name, len) == 0) {
371 						found_failed = 1;
372 						break;
373 					}
374 				} while ((tmp_list = tmp_list->next) != NULL);
375 				if (found_failed) {
376 					free_replica(rl, count);
377 					continue;
378 				}
379 			}
380 
381 			rc = getnfsquota(rl[0].host, rl[0].path, uid, &dqblk);
382 			if (rc != RPC_SUCCESS) {
383 				size_t len;
384 				struct failed_srv *tmp_srv;
385 
386 				/*
387 				 * Failed to get quota from this server. Add
388 				 * this server to failed_srv_list and skip
389 				 * getting quotas for other mounted filesystems
390 				 * from this server.
391 				 */
392 				if (rc == RPC_TIMEDOUT || rc == RPC_CANTSEND) {
393 					len = strlen(rl[0].host);
394 					tmp_srv = (struct failed_srv *)malloc(
395 					    sizeof (struct failed_srv));
396 					tmp_srv->serv_name = (char *)malloc(
397 					    len * sizeof (char) + 1);
398 					strncpy(tmp_srv->serv_name, rl[0].host,
399 					    len);
400 					tmp_srv->serv_name[len] = '\0';
401 
402 					tmp_srv->next = failed_srv_list;
403 					failed_srv_list = tmp_srv;
404 				}
405 
406 				free_replica(rl, count);
407 				continue;
408 			}
409 
410 			free_replica(rl, count);
411 		} else {
412 			continue;
413 		}
414 		if (dqblk.dqb_bsoftlimit == 0 && dqblk.dqb_bhardlimit == 0 &&
415 		    dqblk.dqb_fsoftlimit == 0 && dqblk.dqb_fhardlimit == 0)
416 			continue;
417 		if (vflag)
418 			prquota(&mnt, &dqblk);
419 		else
420 			warn(&mnt, &dqblk);
421 	}
422 
423 	/*
424 	 * Free list of failed servers
425 	 */
426 	while (failed_srv_list != NULL) {
427 		struct failed_srv *tmp_srv = failed_srv_list;
428 
429 		failed_srv_list = failed_srv_list->next;
430 		free(tmp_srv->serv_name);
431 		free(tmp_srv);
432 	}
433 
434 	fclose(mtab);
435 }
436 
437 static void
438 warn(struct mnttab *mntp, struct dqblk *dqp)
439 {
440 	struct timeval tv;
441 
442 	time(&(tv.tv_sec));
443 	tv.tv_usec = 0;
444 	if (dqp->dqb_bhardlimit &&
445 	    dqp->dqb_curblocks >= dqp->dqb_bhardlimit) {
446 		printf("Block limit reached on %s\n", mntp->mnt_mountp);
447 	} else if (dqp->dqb_bsoftlimit &&
448 	    dqp->dqb_curblocks >= dqp->dqb_bsoftlimit) {
449 		if (dqp->dqb_btimelimit == 0) {
450 			printf("Over disk quota on %s, remove %luK\n",
451 			    mntp->mnt_mountp,
452 			    kb(dqp->dqb_curblocks - dqp->dqb_bsoftlimit + 1));
453 		} else if (dqp->dqb_btimelimit > tv.tv_sec) {
454 			char btimeleft[80];
455 
456 			fmttime(btimeleft, dqp->dqb_btimelimit - tv.tv_sec);
457 			printf("Over disk quota on %s, remove %luK within %s\n",
458 			    mntp->mnt_mountp,
459 			    kb(dqp->dqb_curblocks - dqp->dqb_bsoftlimit + 1),
460 			    btimeleft);
461 		} else {
462 			printf(
463 		"Over disk quota on %s, time limit has expired, remove %luK\n",
464 			    mntp->mnt_mountp,
465 			    kb(dqp->dqb_curblocks - dqp->dqb_bsoftlimit + 1));
466 		}
467 	}
468 	if (dqp->dqb_fhardlimit &&
469 	    dqp->dqb_curfiles >= dqp->dqb_fhardlimit) {
470 		printf("File count limit reached on %s\n", mntp->mnt_mountp);
471 	} else if (dqp->dqb_fsoftlimit &&
472 	    dqp->dqb_curfiles >= dqp->dqb_fsoftlimit) {
473 		if (dqp->dqb_ftimelimit == 0) {
474 			printf("Over file quota on %s, remove %lu file%s\n",
475 			    mntp->mnt_mountp,
476 			    dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1,
477 			    ((dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1) > 1 ?
478 			    "s" : ""));
479 		} else if (dqp->dqb_ftimelimit > tv.tv_sec) {
480 			char ftimeleft[80];
481 
482 			fmttime(ftimeleft, dqp->dqb_ftimelimit - tv.tv_sec);
483 			printf(
484 "Over file quota on %s, remove %lu file%s within %s\n",
485 			    mntp->mnt_mountp,
486 			    dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1,
487 			    ((dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1) > 1 ?
488 			    "s" : ""), ftimeleft);
489 		} else {
490 			printf(
491 "Over file quota on %s, time limit has expired, remove %lu file%s\n",
492 			    mntp->mnt_mountp,
493 			    dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1,
494 			    ((dqp->dqb_curfiles - dqp->dqb_fsoftlimit + 1) > 1 ?
495 			    "s" : ""));
496 		}
497 	}
498 }
499 
500 static void
501 heading(uid_t uid, char *name)
502 {
503 	printf("Disk quotas for %s (uid %ld):\n", name, (long)uid);
504 	printf("%-12s %7s%7s%7s%12s%7s%7s%7s%12s\n",
505 	    "Filesystem",
506 	    "usage",
507 	    "quota",
508 	    "limit",
509 	    "timeleft",
510 	    "files",
511 	    "quota",
512 	    "limit",
513 	    "timeleft");
514 }
515 
516 static void
517 prquota(struct mnttab *mntp, struct dqblk *dqp)
518 {
519 	struct timeval tv;
520 	char ftimeleft[80], btimeleft[80];
521 	char *cp;
522 
523 	time(&(tv.tv_sec));
524 	tv.tv_usec = 0;
525 	if (dqp->dqb_bsoftlimit && dqp->dqb_curblocks >= dqp->dqb_bsoftlimit) {
526 		if (dqp->dqb_btimelimit == 0) {
527 			strlcpy(btimeleft, "NOT STARTED", sizeof (btimeleft));
528 		} else if (dqp->dqb_btimelimit > tv.tv_sec) {
529 			fmttime(btimeleft, dqp->dqb_btimelimit - tv.tv_sec);
530 		} else {
531 			strlcpy(btimeleft, "EXPIRED", sizeof (btimeleft));
532 		}
533 	} else {
534 		btimeleft[0] = '\0';
535 	}
536 	if (dqp->dqb_fsoftlimit && dqp->dqb_curfiles >= dqp->dqb_fsoftlimit) {
537 		if (dqp->dqb_ftimelimit == 0) {
538 			strlcpy(ftimeleft, "NOT STARTED", sizeof (ftimeleft));
539 		} else if (dqp->dqb_ftimelimit > tv.tv_sec) {
540 			fmttime(ftimeleft, dqp->dqb_ftimelimit - tv.tv_sec);
541 		} else {
542 			strlcpy(ftimeleft, "EXPIRED", sizeof (ftimeleft));
543 		}
544 	} else {
545 		ftimeleft[0] = '\0';
546 	}
547 	if (strlen(mntp->mnt_mountp) > 12) {
548 		printf("%s\n", mntp->mnt_mountp);
549 		cp = "";
550 	} else {
551 		cp = mntp->mnt_mountp;
552 	}
553 
554 	if (dqp->dqb_curfiles == 0 &&
555 	    dqp->dqb_fsoftlimit == 0 && dqp->dqb_fhardlimit == 0) {
556 		printf("%-12.12s %7d %6d %6d %11s %6s %6s %6s %11s\n",
557 		    cp,
558 		    kb(dqp->dqb_curblocks),
559 		    kb(dqp->dqb_bsoftlimit),
560 		    kb(dqp->dqb_bhardlimit),
561 		    "-",
562 		    "-",
563 		    "-",
564 		    "-",
565 		    "-");
566 	} else {
567 		printf("%-12.12s %7d %6d %6d %11s %6d %6d %6d %11s\n",
568 		    cp,
569 		    kb(dqp->dqb_curblocks),
570 		    kb(dqp->dqb_bsoftlimit),
571 		    kb(dqp->dqb_bhardlimit),
572 		    btimeleft,
573 		    dqp->dqb_curfiles,
574 		    dqp->dqb_fsoftlimit,
575 		    dqp->dqb_fhardlimit,
576 		    ftimeleft);
577 	}
578 }
579 
580 static void
581 fmttime(char *buf, long time)
582 {
583 	int i;
584 	static struct {
585 		int c_secs;		/* conversion units in secs */
586 		char *c_str;		/* unit string */
587 	} cunits [] = {
588 		{60*60*24*28, "months"},
589 		{60*60*24*7, "weeks"},
590 		{60*60*24, "days"},
591 		{60*60, "hours"},
592 		{60, "mins"},
593 		{1, "secs"}
594 	};
595 
596 	if (time <= 0) {
597 		strlcpy(buf, "EXPIRED", sizeof (*buf));
598 		return;
599 	}
600 	for (i = 0; i < sizeof (cunits)/sizeof (cunits[0]); i++) {
601 		if (time >= cunits[i].c_secs)
602 			break;
603 	}
604 	snprintf(buf, sizeof (*buf), "%.1f %s",
605 	    (double)time/cunits[i].c_secs, cunits[i].c_str);
606 }
607 
608 int
609 alldigits(char *s)
610 {
611 	int c;
612 
613 	c = *s++;
614 	do {
615 		if (!isdigit(c))
616 			return (0);
617 	} while (c = *s++);
618 	return (1);
619 }
620 
621 int
622 getdiskquota(struct mnttab *mntp, uid_t uid, struct dqblk *dqp)
623 {
624 	int fd;
625 	dev_t fsdev;
626 	struct stat64 statb;
627 	char qfilename[MAXPATHLEN];
628 
629 	if (stat64(mntp->mnt_special, &statb) < 0 ||
630 	    (statb.st_mode & S_IFMT) != S_IFBLK)
631 		return (0);
632 	fsdev = statb.st_rdev;
633 	(void) snprintf(qfilename, sizeof (qfilename), "%s/%s",
634 	    mntp->mnt_mountp, QFNAME);
635 	if (stat64(qfilename, &statb) < 0 || statb.st_dev != fsdev)
636 		return (0);
637 	(void) __priv_bracket(PRIV_ON);
638 	fd = open64(qfilename, O_RDONLY);
639 	(void) __priv_bracket(PRIV_OFF);
640 	if (fd < 0)
641 		return (0);
642 	(void) llseek(fd, (offset_t)dqoff(uid), L_SET);
643 	switch (read(fd, dqp, sizeof (struct dqblk))) {
644 	case 0:				/* EOF */
645 		/*
646 		 * Convert implicit 0 quota (EOF)
647 		 * into an explicit one (zero'ed dqblk).
648 		 */
649 		memset((caddr_t)dqp, 0, sizeof (struct dqblk));
650 		break;
651 
652 	case sizeof (struct dqblk):	/* OK */
653 		break;
654 
655 	default:			/* ERROR */
656 		close(fd);
657 		return (0);
658 	}
659 	close(fd);
660 	return (1);
661 }
662 
663 int
664 quotactl(int cmd, char *mountp, uid_t uid, caddr_t addr)
665 {
666 	int		fd;
667 	int		status;
668 	struct quotctl	quota;
669 	char		qfile[MAXPATHLEN];
670 
671 	FILE		*fstab;
672 	struct mnttab	mnt;
673 
674 
675 	if ((mountp == NULL) && (cmd == Q_ALLSYNC)) {
676 	/*
677 	 * Find the mount point of any mounted file system. This is
678 	 * because the ioctl that implements the quotactl call has
679 	 * to go to a real file, and not to the block device.
680 	 */
681 		if ((fstab = fopen(MNTTAB, "r")) == NULL) {
682 			fprintf(stderr, "%s: ", MNTTAB);
683 			perror("open");
684 			zexit(32);
685 		}
686 		fd = -1;
687 		while ((status = getmntent(fstab, &mnt)) == NULL) {
688 			if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) != 0 ||
689 			    hasopt(MNTOPT_RO, mnt.mnt_mntopts))
690 				continue;
691 			if ((strlcpy(qfile, mnt.mnt_mountp,
692 			    sizeof (qfile)) >= sizeof (qfile)) ||
693 			    (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >=
694 			    sizeof (qfile))) {
695 				continue;
696 			}
697 			(void) __priv_bracket(PRIV_ON);
698 			fd = open64(qfile, O_RDONLY);
699 			(void) __priv_bracket(PRIV_OFF);
700 			if (fd != -1)
701 				break;
702 		}
703 		fclose(fstab);
704 		if (fd == -1) {
705 			errno = ENOENT;
706 			return (-1);
707 		}
708 	} else {
709 		if (mountp == NULL || mountp[0] == '\0') {
710 			errno = ENOENT;
711 			return (-1);
712 		}
713 		if ((strlcpy(qfile, mountp, sizeof (qfile)) >= sizeof
714 		    (qfile)) ||
715 		    (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >= sizeof
716 		    (qfile))) {
717 			errno = ENOENT;
718 			return (-1);
719 		}
720 		(void) __priv_bracket(PRIV_ON);
721 		fd = open64(qfile, O_RDONLY);
722 		(void) __priv_bracket(PRIV_OFF);
723 		if (fd < 0)
724 			return (-1);
725 	}	/* else */
726 	quota.op = cmd;
727 	quota.uid = uid;
728 	quota.addr = addr;
729 	status = ioctl(fd, Q_QUOTACTL, &quota);
730 	if (fd != 0)
731 		close(fd);
732 	return (status);
733 }
734 
735 
736 /*
737  * Return 1 if opt appears in optlist
738  */
739 int
740 hasopt(char *opt, char *optlist)
741 {
742 	char *value;
743 	char *opts[2];
744 
745 	opts[0] = opt;
746 	opts[1] = NULL;
747 
748 	if (optlist == NULL)
749 		return (0);
750 	while (*optlist != '\0') {
751 		if (getsubopt(&optlist, opts, &value) == 0)
752 			return (1);
753 	}
754 	return (0);
755 }
756 
757 /*
758  * If there are no quotas available, then getnfsquota() returns
759  * RPC_SYSTEMERROR to caller.
760  */
761 static int
762 getnfsquota(char *hostp, char *path, uid_t uid, struct dqblk *dqp)
763 {
764 	struct getquota_args gq_args;
765 	struct getquota_rslt gq_rslt;
766 	struct rquota *rquota;
767 	extern char *strchr();
768 	int	rpc_err;
769 
770 	gq_args.gqa_pathp = path;
771 	gq_args.gqa_uid = uid;
772 	rpc_err = callaurpc(hostp, RQUOTAPROG, RQUOTAVERS,
773 	    (vflag? RQUOTAPROC_GETQUOTA: RQUOTAPROC_GETACTIVEQUOTA),
774 	    xdr_getquota_args, &gq_args, xdr_getquota_rslt, &gq_rslt);
775 	if (rpc_err != RPC_SUCCESS) {
776 		return (rpc_err);
777 	}
778 	switch (gq_rslt.status) {
779 	case Q_OK:
780 		{
781 		struct timeval tv;
782 		u_longlong_t limit;
783 
784 		rquota = &gq_rslt.getquota_rslt_u.gqr_rquota;
785 
786 		if (!vflag && rquota->rq_active == FALSE) {
787 			return (RPC_SYSTEMERROR);
788 		}
789 		gettimeofday(&tv, NULL);
790 		limit = (u_longlong_t)(rquota->rq_bhardlimit) *
791 		    rquota->rq_bsize / DEV_BSIZE;
792 		dqp->dqb_bhardlimit = limit;
793 		limit = (u_longlong_t)(rquota->rq_bsoftlimit) *
794 		    rquota->rq_bsize / DEV_BSIZE;
795 		dqp->dqb_bsoftlimit = limit;
796 		limit = (u_longlong_t)(rquota->rq_curblocks) *
797 		    rquota->rq_bsize / DEV_BSIZE;
798 		dqp->dqb_curblocks = limit;
799 		dqp->dqb_fhardlimit = rquota->rq_fhardlimit;
800 		dqp->dqb_fsoftlimit = rquota->rq_fsoftlimit;
801 		dqp->dqb_curfiles = rquota->rq_curfiles;
802 		dqp->dqb_btimelimit =
803 		    tv.tv_sec + rquota->rq_btimeleft;
804 		dqp->dqb_ftimelimit =
805 		    tv.tv_sec + rquota->rq_ftimeleft;
806 		return (RPC_SUCCESS);
807 		}
808 
809 	case Q_NOQUOTA:
810 		return (RPC_SYSTEMERROR);
811 
812 	case Q_EPERM:
813 		fprintf(stderr, "quota permission error, host: %s\n", hostp);
814 		return (RPC_AUTHERROR);
815 
816 	default:
817 		fprintf(stderr, "bad rpc result, host: %s\n",  hostp);
818 		return (RPC_CANTDECODEARGS);
819 	}
820 
821 	/* NOTREACHED */
822 }
823 
824 int
825 callaurpc(char *host, int prognum, int versnum, int procnum,
826 		xdrproc_t inproc, char *in, xdrproc_t outproc, char *out)
827 {
828 	static enum clnt_stat clnt_stat;
829 	struct timeval tottimeout = {20, 0};
830 
831 	static CLIENT *cl = NULL;
832 	static int oldprognum, oldversnum;
833 	static char oldhost[MAXHOSTNAMELEN+1];
834 
835 	/*
836 	 * Cache the client handle in case there are lots
837 	 * of entries in the /etc/mnttab for the same
838 	 * server. If the server returns an error, don't
839 	 * make further calls.
840 	 */
841 	if (cl == NULL || oldprognum != prognum || oldversnum != versnum ||
842 	    strcmp(oldhost, host) != 0) {
843 		if (cl) {
844 			clnt_destroy(cl);
845 			cl = NULL;
846 		}
847 		cl = clnt_create_timed(host, prognum, versnum, "udp",
848 		    &tottimeout);
849 		if (cl == NULL)
850 			return ((int)RPC_TIMEDOUT);
851 		if ((cl->cl_auth = authunix_create_default()) == NULL) {
852 			clnt_destroy(cl);
853 			return (RPC_CANTSEND);
854 		}
855 		oldprognum = prognum;
856 		oldversnum = versnum;
857 		(void) strlcpy(oldhost, host, sizeof (oldhost));
858 		clnt_stat = RPC_SUCCESS;
859 	}
860 
861 	if (clnt_stat != RPC_SUCCESS)
862 		return ((int)clnt_stat);	/* don't bother retrying */
863 
864 	clnt_stat = clnt_call(cl, procnum, inproc, in,
865 	    outproc, out, tottimeout);
866 
867 	return ((int)clnt_stat);
868 }
869 
870 static int
871 getzfsquota(char *user, char *dataset, struct dqblk *zq)
872 {
873 	zfs_handle_t *zhp = NULL;
874 	char propname[ZFS_MAXPROPLEN];
875 	uint64_t userquota, userused;
876 
877 	if (g_zfs == NULL)
878 		return (1);
879 
880 	if ((zhp = _zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL)
881 		return (1);
882 
883 	(void) snprintf(propname, sizeof (propname), "userquota@%s", user);
884 	if (_zfs_prop_get_userquota_int(zhp, propname, &userquota) != 0) {
885 		_zfs_close(zhp);
886 		return (1);
887 	}
888 
889 	(void) snprintf(propname, sizeof (propname), "userused@%s", user);
890 	if (_zfs_prop_get_userquota_int(zhp, propname, &userused) != 0) {
891 		_zfs_close(zhp);
892 		return (1);
893 	}
894 
895 	zq->dqb_bhardlimit = userquota / DEV_BSIZE;
896 	zq->dqb_bsoftlimit = userquota / DEV_BSIZE;
897 	zq->dqb_curblocks = userused / DEV_BSIZE;
898 	_zfs_close(zhp);
899 	return (0);
900 }
901 
902 static void
903 zexit(int n)
904 {
905 	if (g_zfs != NULL)
906 		_libzfs_fini(g_zfs);
907 	exit(n);
908 }
909