| /* |
| * Copyright (c) 1980, 1990 Regents of the University of California. |
| * Copyright (C) 2000, 2001 Silicon Graphics, Inc. [SGI] |
| * All rights reserved. |
| * |
| * [Extensions to support XFS are copyright SGI] |
| * |
| * 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. All advertising |
| * materials mentioning features or use of this software must display the |
| * following acknowledgement: This product includes software developed by the |
| * University of California, Berkeley and its contributors. 4. 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 "config.h" |
| |
| #include <sys/stat.h> |
| #include <sys/param.h> |
| #include <sys/ioctl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <time.h> |
| #include <utmp.h> |
| #include <pwd.h> |
| #include <grp.h> |
| |
| #include "pot.h" |
| #include "quot.h" |
| #include "common.h" |
| #include "mntopt.h" |
| #include "bylabel.h" |
| #include "quotasys.h" |
| |
| #define TSIZE 500 |
| static __uint64_t sizes[TSIZE]; |
| static __uint64_t overflow; |
| |
| static int aflag; |
| static int cflag; |
| static int fflag; |
| static int gflag; |
| static int uflag; |
| static int vflag; |
| static int iflag; |
| static int qflag; |
| static int Tflag; |
| static time_t now; |
| char *progname; |
| |
| static void mounttable(void); |
| static char *idname(__uint32_t, int); |
| static void report(const char *, const char *, int); |
| static void creport(const char *, const char *); |
| |
| static void usage(void) |
| { |
| errstr(_("Usage: %s [-acfugvViTq] [filesystem...]\n"), progname); |
| exit(1); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| int c; |
| |
| now = time(0); |
| progname = basename(argv[0]); |
| |
| while ((c = getopt(argc, argv, "acfguvVTq")) != -1) { |
| switch (c) { |
| case 'a': |
| aflag++; |
| break; |
| case 'c': |
| cflag++; |
| break; |
| case 'f': |
| fflag++; |
| break; |
| case 'g': |
| gflag++; |
| break; |
| case 'u': |
| uflag++; |
| break; |
| case 'v': |
| vflag++; |
| break; |
| case 'i': |
| iflag++; |
| break; |
| case 'q': |
| qflag++; |
| break; |
| case 'T': |
| Tflag++; |
| break; |
| case 'V': |
| version(); |
| exit(0); |
| default: |
| usage(); |
| } |
| } |
| if ((aflag && optind != argc) || (!aflag && optind == argc)) |
| usage(); |
| if (!uflag && !gflag) |
| uflag++; |
| if (init_mounts_scan(aflag ? 0 : argc - optind, argv + optind, (iflag ? MS_NO_AUTOFS : 0)) < 0) |
| return 1; |
| mounttable(); |
| end_mounts_scan(); |
| return 0; |
| } |
| |
| static void mounttable(void) |
| { |
| int doit = 0; |
| struct mount_entry *mntp; |
| |
| while ((mntp = get_next_mount())) { |
| /* Currently, only XFS is implemented... */ |
| if (strcmp(mntp->me_type, MNTTYPE_XFS) == 0) { |
| checkXFS(mntp->me_devname, mntp->me_dir); |
| doit = 1; |
| } |
| /* ...additional filesystems types here. */ |
| |
| if (doit) { |
| if (cflag) creport(mntp->me_devname, mntp->me_dir); |
| if (!cflag && uflag) report(mntp->me_devname, mntp->me_dir, 0); |
| if (!cflag && gflag) report(mntp->me_devname, mntp->me_dir, 1); |
| } |
| } |
| } |
| |
| static int qcmp(du_t * p1, du_t * p2) |
| { |
| if (p1->blocks > p2->blocks) |
| return -1; |
| if (p1->blocks < p2->blocks) |
| return 1; |
| if (p1->id > p2->id) |
| return 1; |
| else if (p1->id < p2->id) |
| return -1; |
| return 0; |
| } |
| |
| static void creport(const char *file, const char *fsdir) |
| { |
| int i; |
| __uint64_t t = 0; |
| |
| printf(_("%s (%s):\n"), file, fsdir); |
| for (i = 0; i < TSIZE - 1; i++) |
| if (sizes[i] > 0) { |
| t += sizes[i] * i; |
| printf(_("%d\t%llu\t%llu\n"), i, |
| (unsigned long long) sizes[i], |
| (unsigned long long) t); |
| } |
| printf(_("%d\t%llu\t%llu\n"), TSIZE - 1, |
| (unsigned long long) sizes[TSIZE - 1], |
| (unsigned long long) (overflow + t)); |
| } |
| |
| static void report(const char *file, const char *fsdir, int type) |
| { |
| du_t *dp; |
| |
| printf(_("%s (%s) %ss:\n"), file, fsdir, _(type2name(type))); |
| if (!qflag) |
| qsort(du[type], ndu[type], sizeof(du[type][0]), (int (*)(const void *, const void *))qcmp); |
| for (dp = du[type]; dp < &du[type][ndu[type]]; dp++) { |
| char *cp; |
| |
| if (dp->blocks == 0) |
| return; |
| printf(_("%8llu "), (unsigned long long) dp->blocks); |
| if (fflag) |
| printf(_("%8llu "), (unsigned long long) dp->nfiles); |
| if ((cp = idname(dp->id, type)) != NULL) { |
| if (Tflag) |
| printf(_("%s"), cp); |
| else |
| printf(_("%-8.8s"), cp); |
| } else |
| printf(_("#%-7d"), dp->id); |
| if (vflag) |
| printf(_(" %8llu %8llu %8llu"), |
| (unsigned long long) dp->blocks30, |
| (unsigned long long) dp->blocks60, |
| (unsigned long long) dp->blocks90); |
| putchar('\n'); |
| } |
| } |
| |
| static idcache_t *getnextent(int type, __uint32_t id, int byid) |
| { |
| struct passwd *pw; |
| struct group *gr; |
| static idcache_t idc; |
| |
| if (type) { /* /etc/group */ |
| if ((gr = byid? getgrgid(id) : getgrent()) == NULL) |
| return NULL; |
| idc.id = gr->gr_gid; |
| strncpy(idc.name, gr->gr_name, UT_NAMESIZE); |
| return &idc; |
| } |
| /* /etc/passwd */ |
| if ((pw = byid? getpwuid(id) : getpwent()) == NULL) |
| return NULL; |
| idc.id = pw->pw_uid; |
| strncpy(idc.name, pw->pw_name, UT_NAMESIZE); |
| return &idc; |
| } |
| |
| static char *idname(__uint32_t id, int type) |
| { |
| idcache_t *ncp, *idp; |
| static idcache_t nc[2][NID]; |
| static int entriesleft[2] = { NID, NID }; |
| |
| /* check cache for name first */ |
| ncp = &nc[type][id & IDMASK]; |
| if (ncp->id == id && ncp->name[0]) |
| return ncp->name; |
| if (entriesleft[type]) { |
| /* |
| * If we haven't gone through the passwd/group file |
| * then fill the cache while seaching for name. |
| * This lets us run through passwd/group serially. |
| */ |
| if (entriesleft[type] == NID) |
| type? setgrent() : setpwent(); |
| while (((idp = getnextent(type, id, 0)) != NULL) && entriesleft[type]) { |
| entriesleft[type]--; |
| ncp = &nc[type][idp->id & IDMASK]; |
| if (ncp->name[0] == '\0' || idp->id == id) |
| memcpy(ncp, idp, sizeof(idcache_t)); |
| if (idp->id == id) |
| return ncp->name; |
| } |
| type? endgrent() : endpwent(); |
| entriesleft[type] = 0; |
| ncp = &nc[type][id & IDMASK]; |
| } |
| |
| /* Not cached - do it the slow way & insert into cache */ |
| if ((idp = getnextent(type, id, 1)) == NULL) |
| return NULL; |
| memcpy(ncp, idp, sizeof(idcache_t)); |
| return ncp->name; |
| } |
| |
| /* |
| * === XFS specific code follows === |
| */ |
| |
| static void acctXFS(xfs_bstat_t *p) |
| { |
| register du_t *dp; |
| du_t **hp; |
| __uint64_t size; |
| __uint32_t i, id; |
| |
| if ((p->bs_mode & S_IFMT) == 0) |
| return; |
| size = howmany((p->bs_blocks * p->bs_blksize), 0x400ULL); |
| |
| if (cflag) { |
| if (!(S_ISDIR(p->bs_mode) || S_ISREG(p->bs_mode))) |
| return; |
| if (size >= TSIZE) { |
| overflow += size; |
| size = TSIZE - 1; |
| } |
| sizes[(int)size]++; |
| return; |
| } |
| for (i = 0; i < 2; i++) { |
| id = (i == 0)? p->bs_uid : p->bs_gid; |
| hp = &duhash[i][id % DUHASH]; |
| for (dp = *hp; dp; dp = dp->next) |
| if (dp->id == id) |
| break; |
| if (dp == 0) { |
| if (ndu[i] >= NDU) |
| return; |
| dp = &du[i][(ndu[i]++)]; |
| dp->next = *hp; |
| *hp = dp; |
| dp->id = id; |
| dp->nfiles = 0; |
| dp->blocks = 0; |
| dp->blocks30 = 0; |
| dp->blocks60 = 0; |
| dp->blocks90 = 0; |
| } |
| dp->blocks += size; |
| |
| if (now - p->bs_atime.tv_sec > 30 * SEC24HR) |
| dp->blocks30 += size; |
| if (now - p->bs_atime.tv_sec > 60 * SEC24HR) |
| dp->blocks60 += size; |
| if (now - p->bs_atime.tv_sec > 90 * SEC24HR) |
| dp->blocks90 += size; |
| dp->nfiles++; |
| } |
| } |
| |
| static void checkXFS(const char *file, const char *fsdir) |
| { |
| xfs_fsop_bulkreq_t bulkreq; |
| __u64 last = 0; |
| __s32 count; |
| int i; |
| int sts; |
| int fsfd; |
| du_t **dp; |
| xfs_bstat_t *buf; |
| |
| /* |
| * Initialize tables between checks; because of the qsort |
| * in report() the hash tables must be rebuilt each time. |
| */ |
| for (sts = 0; sts < TSIZE; sts++) |
| sizes[sts] = 0; |
| overflow = 0; |
| for (i = 0; i < 2; i++) |
| for (dp = duhash[i]; dp < &duhash[i][DUHASH]; dp++) |
| *dp = 0; |
| ndu[0] = ndu[1] = 0; |
| |
| fsfd = open(fsdir, O_RDONLY); |
| if (fsfd < 0) { |
| errstr(_("cannot open %s: %s\n"), fsdir, strerror(errno)); |
| exit(1); |
| } |
| sync(); |
| |
| buf = (xfs_bstat_t *) smalloc(NBSTAT * sizeof(xfs_bstat_t)); |
| memset(buf, 0, NBSTAT * sizeof(xfs_bstat_t)); |
| |
| bulkreq.lastip = &last; |
| bulkreq.icount = NBSTAT; |
| bulkreq.ubuffer = buf; |
| bulkreq.ocount = &count; |
| |
| while ((sts = ioctl(fsfd, XFS_IOC_FSBULKSTAT, &bulkreq)) == 0) { |
| if (count == 0) |
| break; |
| for (i = 0; i < count; i++) |
| acctXFS(&buf[i]); |
| } |
| if (sts < 0) { |
| errstr(_("XFS_IOC_FSBULKSTAT ioctl failed: %s\n"), |
| strerror(errno)); |
| exit(1); |
| } |
| free(buf); |
| close(fsfd); |
| } |