/* $NetBSD: quota.c,v 1.50 2014/07/13 01:46:04 dholland Exp $ */ /* * Copyright (c) 1980, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Robert Elz at The University of Melbourne. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifndef lint __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\ The Regents of the University of California. All rights reserved."); #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)quota.c 8.4 (Berkeley) 4/28/95"; #else __RCSID("$NetBSD: quota.c,v 1.50 2014/07/13 01:46:04 dholland Exp $"); #endif #endif /* not lint */ /* * Disk quota reporting program. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "printquota.h" struct quotause { struct quotause *next; uid_t id; struct quotaval *qvs; unsigned numqvs; char fsname[MAXPATHLEN + 1]; struct quotahandle *qh; }; static int anyusage(struct quotaval *, unsigned); static int anyover(struct quotaval *, unsigned, time_t); static const char *getovermsg(struct quotaval *, const char *, time_t); static struct quotause *getprivs(id_t, int); static void heading(int, const char *, id_t, const char *, const char *); static int isover(struct quotaval *qv, time_t now); static void printqv(struct quotaval *, int, time_t); static void showgid(gid_t); static void showgrpname(const char *); static void showonequota(int, const char *, id_t, const char *, struct quotause *); static void showquotas(int, const char *, id_t, const char *); static void showuid(uid_t); static void showusrname(const char *); static int unlimited(struct quotaval *qvs, unsigned numqvs); static void usage(void) __dead; static int qflag = 0; static int vflag = 0; static int hflag = 0; static int dflag = 0; static uid_t myuid; static int needheading; int main(int argc, char *argv[]) { int ngroups; gid_t mygid, gidset[NGROUPS]; int i, gflag = 0, uflag = 0; int ch; myuid = getuid(); while ((ch = getopt(argc, argv, "dhugvq")) != -1) { switch(ch) { case 'g': gflag++; break; case 'u': uflag++; break; case 'v': vflag++; break; case 'q': qflag++; break; case 'h': hflag++; break; case 'd': dflag++; break; default: usage(); } } argc -= optind; argv += optind; if (!uflag && !gflag) uflag++; if (dflag) { #if 0 if (myuid != 0) errx(1, "-d: permission denied"); #endif if (uflag) showquotas(QUOTA_IDTYPE_USER, "user", 0, ""); if (gflag) showquotas(QUOTA_IDTYPE_GROUP, "group", 0, ""); return 0; } if (argc == 0) { if (uflag) showuid(myuid); if (gflag) { if (dflag) showgid(0); else { mygid = getgid(); ngroups = getgroups(NGROUPS, gidset); if (ngroups < 0) err(1, "getgroups"); showgid(mygid); for (i = 0; i < ngroups; i++) if (gidset[i] != mygid) showgid(gidset[i]); } } return 0; } if (uflag && gflag) usage(); if (uflag) { for (; argc > 0; argc--, argv++) { if (alldigits(*argv)) showuid((uid_t)atoi(*argv)); else showusrname(*argv); } return 0; } if (gflag) { for (; argc > 0; argc--, argv++) { if (alldigits(*argv)) showgid((gid_t)atoi(*argv)); else showgrpname(*argv); } return 0; } /* NOTREACHED */ return 0; } static void usage(void) { const char *p = getprogname(); fprintf(stderr, "Usage: %s [-Dhguqv]\n" "\t%s [-Dhqv] -u username ...\n" "\t%s [-Dhqv] -g groupname ...\n" "\t%s -d [-Dhguqv]\n", p, p, p, p); exit(1); } /* * Print out quotas for a specified user identifier. */ static void showuid(uid_t uid) { struct passwd *pwd = getpwuid(uid); const char *name; if (pwd == NULL) name = "(no account)"; else name = pwd->pw_name; if (uid != myuid && myuid != 0) { warnx("%s (uid %d): permission denied", name, uid); return; } showquotas(QUOTA_IDTYPE_USER, "user", uid, name); } /* * Print out quotas for a specified user name. */ static void showusrname(const char *name) { struct passwd *pwd = getpwnam(name); if (pwd == NULL) { warnx("%s: unknown user", name); return; } if (pwd->pw_uid != myuid && myuid != 0) { warnx("%s (uid %d): permission denied", name, pwd->pw_uid); return; } showquotas(QUOTA_IDTYPE_USER, "user", pwd->pw_uid, name); } /* * Print out quotas for a specified group identifier. */ static void showgid(gid_t gid) { struct group *grp = getgrgid(gid); int ngroups; gid_t mygid, gidset[NGROUPS]; int i; const char *name; if (grp == NULL) name = "(no entry)"; else name = grp->gr_name; mygid = getgid(); ngroups = getgroups(NGROUPS, gidset); if (ngroups < 0) { warn("getgroups"); return; } if (gid != mygid) { for (i = 0; i < ngroups; i++) if (gid == gidset[i]) break; if (i >= ngroups && myuid != 0) { warnx("%s (gid %d): permission denied", name, gid); return; } } showquotas(QUOTA_IDTYPE_GROUP, "group", gid, name); } /* * Print out quotas for a specified group name. */ static void showgrpname(const char *name) { struct group *grp = getgrnam(name); int ngroups; gid_t mygid, gidset[NGROUPS]; int i; if (grp == NULL) { warnx("%s: unknown group", name); return; } mygid = getgid(); ngroups = getgroups(NGROUPS, gidset); if (ngroups < 0) { warn("getgroups"); return; } if (grp->gr_gid != mygid) { for (i = 0; i < ngroups; i++) if (grp->gr_gid == gidset[i]) break; if (i >= ngroups && myuid != 0) { warnx("%s (gid %d): permission denied", name, grp->gr_gid); return; } } showquotas(QUOTA_IDTYPE_GROUP, "group", grp->gr_gid, name); } static void showquotas(int idtype, const char *idtypename, id_t id, const char *idname) { struct quotause *qup; struct quotause *quplist; needheading = 1; quplist = getprivs(id, idtype); for (qup = quplist; qup; qup = qup->next) { showonequota(idtype, idtypename, id, idname, qup); } if (!qflag) { /* In case nothing printed, issue a header saying "none" */ heading(idtype, idtypename, id, idname, "none"); } } static void showonequota(int idtype, const char *idtypename, id_t id, const char *idname, struct quotause *qup) { static time_t now; struct quotaval *qvs; unsigned numqvs, i; const char *msg; qvs = qup->qvs; numqvs = qup->numqvs; if (now == 0) { time(&now); } if (!vflag && unlimited(qvs, numqvs)) { return; } if (qflag) { for (i=0; iqh, i), now); if (msg != NULL) { heading(idtype, idtypename, id, idname, ""); printf("\t%s %s\n", msg, qup->fsname); } } return; } /* * XXX this behavior appears to be demanded by the ATF tests, * although it seems to be at variance with the preexisting * logic in quota.c. */ if (unlimited(qvs, numqvs) && !anyusage(qvs, numqvs)) { return; } /* * XXX: anyover can in fact be true if anyusage is not true, * if there's a quota of zero set on some volume. This is * because the check we do checks if adding one more thing * will go over. That is reasonable, I suppose, but arguably * the resulting behavior with usage 0 is a bug. (Also, what * reason do we have to believe that the reported grace expire * time is valid if we aren't in fact over yet?) */ if (vflag || dflag || anyover(qvs, numqvs, now) || anyusage(qvs, numqvs)) { heading(idtype, idtypename, id, idname, ""); if (strlen(qup->fsname) > 4) { printf("%s\n", qup->fsname); printf("%12s", ""); } else { printf("%12s", qup->fsname); } for (i=0; iqh, i), now); } printf("\n"); } } static void heading(int idtype, const char *idtypename, id_t id, const char *idname, const char *tag) { if (needheading == 0) return; needheading = 0; if (dflag) printf("Default %s disk quotas: %s\n", idtypename, tag); else printf("Disk quotas for %s %s (%cid %u): %s\n", idtypename, idname, idtypename[0], id, tag); if (!qflag && tag[0] == '\0') { printf("%12s%9s %8s%9s%8s%8s %7s%8s%8s\n" , "Filesystem" , "blocks" , "quota" , "limit" , "grace" , "files" , "quota" , "limit" , "grace" ); } } static void printqv(struct quotaval *qv, int isbytes, time_t now) { char buf[20]; const char *str; int intprtflags, over, width; /* * The assorted finagling of width is to match the previous * open-coded formatting for exactly two quota object types, * which was chosen to make the default report fit in 80 * columns. */ width = isbytes ? 9 : 8; intprtflags = isbytes ? HN_B : 0; over = isover(qv, now); str = intprt(buf, width, qv->qv_usage, intprtflags, hflag); printf("%*s", width, str); printf("%c", over ? '*' : ' '); str = intprt(buf, width, qv->qv_softlimit, intprtflags, hflag); printf("%*s", width-1, str); str = intprt(buf, width, qv->qv_hardlimit, intprtflags, hflag); printf("%*s", width, str); if (over) { str = timeprt(buf, 9, now, qv->qv_expiretime); } else if (vflag && qv->qv_grace != QUOTA_NOTIME) { str = timeprt(buf, 9, 0, qv->qv_grace); } else { str = ""; } printf("%8s", str); } /* * Collect the requested quota information. */ static struct quotause * getprivs(id_t id, int idtype) { struct quotause *qup, *quptail; struct quotause *quphead; struct statvfs *fst; struct quotakey qk; int nfst, i; unsigned j; qup = quphead = quptail = NULL; nfst = getmntinfo(&fst, MNT_WAIT); if (nfst == 0) errx(2, "no filesystems mounted!"); for (i = 0; i < nfst; i++) { if (qup == NULL) { if ((qup = malloc(sizeof *qup)) == NULL) err(1, "Out of memory"); } qup->qh = quota_open(fst[i].f_mntonname); if (qup->qh == NULL) { if (errno == EOPNOTSUPP || errno == ENXIO) { continue; } err(1, "%s: quota_open", fst[i].f_mntonname); } qup->numqvs = quota_getnumidtypes(qup->qh); qup->qvs = malloc(qup->numqvs * sizeof(qup->qvs[0])); if (qup->qvs == NULL) { err(1, "Out of memory"); } qk.qk_idtype = idtype; if (dflag) { qk.qk_id = QUOTA_DEFAULTID; } else { qk.qk_id = id; } for (j=0; jnumqvs; j++) { qk.qk_objtype = j; if (quota_get(qup->qh, &qk, &qup->qvs[j]) < 0) { if (errno != ENOENT && errno != ENODEV) { warn("%s: quota_get (objtype %u)", fst[i].f_mntonname, j); } quotaval_clear(&qup->qvs[j]); } } (void)strlcpy(qup->fsname, fst[i].f_mntonname, sizeof(qup->fsname)); if (quphead == NULL) quphead = qup; else quptail->next = qup; quptail = qup; quptail->next = 0; qup = NULL; } free(qup); return quphead; } static int unlimited(struct quotaval *qvs, unsigned numqvs) { unsigned i; for (i=0; i 0) { return 1; } } return 0; } static int anyover(struct quotaval *qvs, unsigned numqvs, time_t now) { unsigned i; for (i=0; iqv_usage >= qv->qv_hardlimit || qv->qv_usage >= qv->qv_softlimit); } static const char * getovermsg(struct quotaval *qv, const char *what, time_t now) { static char buf[64]; if (qv->qv_usage >= qv->qv_hardlimit) { snprintf(buf, sizeof(buf), "%c%s limit reached on", toupper((unsigned char)what[0]), what+1); return buf; } if (qv->qv_usage < qv->qv_softlimit) { /* Ok */ return NULL; } if (now > qv->qv_expiretime) { snprintf(buf, sizeof(buf), "Over %s quota on", what); return buf; } snprintf(buf, sizeof(buf), "In %s grace period on", what); return buf; }