Home | History | Annotate | Download | only in quota
      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