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
load_libzfs(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
main(int argc,char * argv[])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
showuid(uid_t uid)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
showname(char * name)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
showquotas(uid_t uid,char * name)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, "=") + 1;
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
warn(struct mnttab * mntp,struct dqblk * dqp)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
heading(uid_t uid,char * name)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
prquota(struct mnttab * mntp,struct dqblk * dqp)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
fmttime(char * buf,long time)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
alldigits(char * s)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
getdiskquota(struct mnttab * mntp,uid_t uid,struct dqblk * dqp)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
quotactl(int cmd,char * mountp,uid_t uid,caddr_t addr)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, "a);
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
hasopt(char * opt,char * optlist)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
getnfsquota(char * hostp,char * path,uid_t uid,struct dqblk * dqp)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
callaurpc(char * host,int prognum,int versnum,int procnum,xdrproc_t inproc,char * in,xdrproc_t outproc,char * out)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
getzfsquota(char * user,char * dataset,struct dqblk * zq)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
zexit(int n)903 zexit(int n)
904 {
905 if (g_zfs != NULL)
906 _libzfs_fini(g_zfs);
907 exit(n);
908 }
909