diff --git a/usr.sbin/quot/Makefile b/usr.sbin/quot/Makefile new file mode 100644 index 000000000000..aca581b820d3 --- /dev/null +++ b/usr.sbin/quot/Makefile @@ -0,0 +1,6 @@ +# $Id: Makefile,v 1.3 1994/12/22 11:39:03 cgd Exp $ + +PROG= quot +MAN= quot.8 + +.include diff --git a/usr.sbin/quot/quot.8 b/usr.sbin/quot/quot.8 index b1aefdebe668..7b964d944c33 100644 --- a/usr.sbin/quot/quot.8 +++ b/usr.sbin/quot/quot.8 @@ -1,5 +1,6 @@ -.\" Copyright (c) 1980, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. +.\" Copyright (C) 1994 Wolfgang Solfrank. +.\" Copyright (C) 1994 TooLs GmbH. +.\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -11,74 +12,85 @@ .\" 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 product includes software developed by TooLs GmbH. +.\" 4. The name of TooLs GmbH may not 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. +.\" THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. .\" -.\" @(#)quot.8 8.1 (Berkeley) 6/6/93 +.\" $Id: quot.8,v 1.3 1994/03/19 07:59:47 cgd Exp $ .\" -.Dd June 6, 1993 +.Dd February 8, 1994 .Dt QUOT 8 .Os BSD 4 .Sh NAME .Nm quot -.Nd display total block usage per user for a file system +.Nd display disk space occupied by each user .Sh SYNOPSIS .Nm quot -.Op Fl ncf -.Op Ar filesystem Ar ... +.Op Fl acfhknv +.Op Ar filesystem ... .Sh DESCRIPTION -The -.Nm quot -command -displays the total count of blocks owned by a user for the filesystem -.Ar filesystem . -If the filesystem option -.Ar filesystem -is not specified, -the file systems listed in -.Pa /etc/fstab -are used. +.Nm Quot +is used to gather statistics about the disk usage for each local user. +.Pp The following options are available: .Bl -tag -width Ds +.It Fl a +Include statistics for all mounted filesystems. .It Fl c -Display information on file size and block usage. The file sizes -are listed in the first column, the second column contains a count -of how many files of that size were found and the third column -lists the cumulative block usage for the displayed size and all smaller -files. +Display three columns containing number of blocks per file, +number of files in this category, and aggregate total of +blocks in files with this or lower size. .It Fl f -For each user, -the number of files (inodes) owned is displayed in addition -to the block usage. -.It Fl n -A list sorted by block usage is displayed. -This is the result of -.Nm quot -executing the following command: -.Bd -literal -offset indent -ncheck filesystem \&| sort +0n \&| quot \-n filesystem -.Ed -.El -.Sh SEE ALSO -.Xr ls 1 , -.Xr du 1 -.Sh HISTORY +For each user, display count of files and space occupied. +.It Fl h +Estimate the number of blocks in each file based on its size. +Despite that this doesn't give the correct resuls (it doesn't +account for the holes in files), this option isn't any faster +and thus is discouraged. +.It Fl k +By default, all sizes are reported in 512-byte block counts. The -.Nm -command appeared in -.At 32v . +.Fl k +options causes the numbers to be reported in kilobyte counts. +.It Fl n +Given a list of inodes (plus some optional data on each line) +in the standard input, for each file print out the owner (plus +the remainder of the input line). This is traditionally used +in the pipe: +.Bd -literal -offset indent +ncheck filesystem | sort +0n | quot -n filesystem +.Ed +.Pp +to get a report of files and their owners. +.It Fl v +In addition to the default output, display the number of files +not accessed within 30, 60 and 90 days. +.El +.Sh ENVIRONMENTAL VARIABLES +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environmental variable +.Ev BLOCKSIZE +is set, and the +.Gl k +option is not specified, the block counts will be displayed in units of that +size block. +.El +.\".Sh BUGS +.Sh SEE ALSO +.Xr df 1 , +.Xr quota 1 , +.Xr getmntinfo 3 , +.Xr fstab 5 , +.Xr mount 8 , diff --git a/usr.sbin/quot/quot.c b/usr.sbin/quot/quot.c new file mode 100644 index 000000000000..e5f2daad68c7 --- /dev/null +++ b/usr.sbin/quot/quot.c @@ -0,0 +1,583 @@ +/* + * Copyright (C) 1991, 1994 Wolfgang Solfrank. + * Copyright (C) 1991, 1994 TooLs GmbH. + * All rights reserved. + * + * 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 TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. + */ + +#ifndef lint +static char rcsid[] = "$Id: quot.c,v 1.6.4.1 1995/11/01 00:06:41 jtc Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* some flags of what to do: */ +static char estimate; +static char count; +static char unused; +static int (*func)(); +static long blocksize; +static char *header; +static int headerlen; + +/* + * Original BSD quot doesn't round to number of frags/blocks, + * doesn't account for indirection blocks and gets it totally + * wrong if the size is a multiple of the blocksize. + * The new code always counts the number of 512 byte blocks + * instead of the number of kilobytes and converts them to + * kByte when done (on request). + */ +#ifdef COMPAT +#define SIZE(n) (n) +#else +#define SIZE(n) (((n) * 512 + blocksize - 1)/blocksize) +#endif + +#define INOCNT(fs) ((fs)->fs_ipg) +#define INOSZ(fs) (sizeof(struct dinode) * INOCNT(fs)) + +static struct dinode *get_inode(fd,super,ino) + struct fs *super; + ino_t ino; +{ + static struct dinode *ip; + static ino_t last; + + if (fd < 0) { /* flush cache */ + if (ip) { + free(ip); + ip = 0; + } + return 0; + } + + if (!ip || ino < last || ino >= last + INOCNT(super)) { + if (!ip + && !(ip = (struct dinode *)malloc(INOSZ(super)))) { + perror("allocate inodes"); + exit(1); + } + last = (ino / INOCNT(super)) * INOCNT(super); + if (lseek(fd,ino_to_fsba(super,last) << super->fs_fshift,0) < 0 + || read(fd,ip,INOSZ(super)) != INOSZ(super)) { + perror("read inodes"); + exit(1); + } + } + + return ip + ino % INOCNT(super); +} + +#ifdef COMPAT +#define actualblocks(super,ip) ((ip)->di_blocks/2) +#else +#define actualblocks(super,ip) ((ip)->di_blocks) +#endif + +static virtualblocks(super,ip) + struct fs *super; + struct dinode *ip; +{ + register off_t nblk, sz; + + sz = ip->di_size; +#ifdef COMPAT + if (lblkno(super,sz) >= NDADDR) { + nblk = blkroundup(super,sz); + if (sz == nblk) + nblk += super->fs_bsize; + } + + return sz / 1024; + +#else /* COMPAT */ + + if (lblkno(super,sz) >= NDADDR) { + nblk = blkroundup(super,sz); + sz = lblkno(super,nblk); + sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super); + while (sz > 0) { + nblk += sz * super->fs_bsize; + /* sz - 1 rounded up */ + sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super); + } + } else + nblk = fragroundup(super,sz); + + return nblk / 512; +#endif /* COMPAT */ +} + +static isfree(ip) + struct dinode *ip; +{ +#ifdef COMPAT + return (ip->di_mode&IFMT) == 0; +#else /* COMPAT */ + + switch (ip->di_mode&IFMT) { + case IFIFO: + case IFLNK: /* should check FASTSYMLINK? */ + case IFDIR: + case IFREG: + return 0; + default: + return 1; + } +#endif +} + +static struct user { + uid_t uid; + char *name; + daddr_t space; + long count; + daddr_t spc30; + daddr_t spc60; + daddr_t spc90; +} *users; +static int nusers; + +static inituser() +{ + register i; + register struct user *usr; + + if (!nusers) { + nusers = 8; + if (!(users = + (struct user *)calloc(nusers,sizeof(struct user)))) { + perror("allocate users"); + exit(1); + } + } else { + for (usr = users, i = nusers; --i >= 0; usr++) { + usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0; + usr->count = 0; + } + } +} + +static usrrehash() +{ + register i; + register struct user *usr, *usrn; + struct user *svusr; + + svusr = users; + nusers <<= 1; + if (!(users = (struct user *)calloc(nusers,sizeof(struct user)))) { + perror("allocate users"); + exit(1); + } + for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) { + for (usrn = users + (usr->uid&(nusers - 1)); usrn->name; + usrn--) { + if (usrn <= users) + usrn = users + nusers; + } + *usrn = *usr; + } +} + +static struct user *user(uid) + uid_t uid; +{ + register struct user *usr; + register i; + struct passwd *pwd; + + while (1) { + for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0; + usr--) { + if (!usr->name) { + usr->uid = uid; + + if (!(pwd = getpwuid(uid))) { + if (usr->name = (char *)malloc(7)) + sprintf(usr->name,"#%d",uid); + } else { + if (usr->name = (char *) + malloc(strlen(pwd->pw_name) + 1)) + strcpy(usr->name,pwd->pw_name); + } + if (!usr->name) { + perror("allocate users"); + exit(1); + } + + return usr; + + } else if (usr->uid == uid) + return usr; + + if (usr <= users) + usr = users + nusers; + } + usrrehash(); + } +} + +static cmpusers(u1,u2) + struct user *u1, *u2; +{ + return u2->space - u1->space; +} + +#define sortusers(users) (qsort((users),nusers,sizeof(struct user), \ + cmpusers)) + +static uses(uid,blks,act) + uid_t uid; + daddr_t blks; + time_t act; +{ + static time_t today; + register struct user *usr; + + if (!today) + time(&today); + + usr = user(uid); + usr->count++; + usr->space += blks; + + if (today - act > 90L * 24L * 60L * 60L) + usr->spc90 += blks; + if (today - act > 60L * 24L * 60L * 60L) + usr->spc60 += blks; + if (today - act > 30L * 24L * 60L * 60L) + usr->spc30 += blks; +} + +#ifdef COMPAT +#define FSZCNT 500 +#else +#define FSZCNT 512 +#endif +struct fsizes { + struct fsizes *fsz_next; + daddr_t fsz_first, fsz_last; + ino_t fsz_count[FSZCNT]; + daddr_t fsz_sz[FSZCNT]; +} *fsizes; + +static initfsizes() +{ + register struct fsizes *fp; + register i; + + for (fp = fsizes; fp; fp = fp->fsz_next) { + for (i = FSZCNT; --i >= 0;) { + fp->fsz_count[i] = 0; + fp->fsz_sz[i] = 0; + } + } +} + +static dofsizes(fd,super,name) + struct fs *super; + char *name; +{ + ino_t inode, maxino; + struct dinode *ip; + daddr_t sz, ksz; + struct fsizes *fp, **fsp; + register i; + + maxino = super->fs_ncg * super->fs_ipg - 1; +#ifdef COMPAT + if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes)))) { + perror("alloc fsize structure"); + exit(1); + } +#endif /* COMPAT */ + for (inode = 0; inode < maxino; inode++) { + errno = 0; + if ((ip = get_inode(fd,super,inode)) +#ifdef COMPAT + && ((ip->di_mode&IFMT) == IFREG + || (ip->di_mode&IFMT) == IFDIR) +#else /* COMPAT */ + && !isfree(ip) +#endif /* COMPAT */ + ) { + sz = estimate ? virtualblocks(super,ip) : + actualblocks(super,ip); +#ifdef COMPAT + if (sz >= FSZCNT) { + fsizes->fsz_count[FSZCNT-1]++; + fsizes->fsz_sz[FSZCNT-1] += sz; + } else { + fsizes->fsz_count[sz]++; + fsizes->fsz_sz[sz] += sz; + } +#else /* COMPAT */ + ksz = SIZE(sz); + for (fsp = &fsizes; fp = *fsp; fsp = &fp->fsz_next) { + if (ksz < fp->fsz_last) + break; + } + if (!fp || ksz < fp->fsz_first) { + if (!(fp = (struct fsizes *) + malloc(sizeof(struct fsizes)))) { + perror("alloc fsize structure"); + exit(1); + } + fp->fsz_next = *fsp; + *fsp = fp; + fp->fsz_first = (ksz / FSZCNT) * FSZCNT; + fp->fsz_last = fp->fsz_first + FSZCNT; + for (i = FSZCNT; --i >= 0;) { + fp->fsz_count[i] = 0; + fp->fsz_sz[i] = 0; + } + } + fp->fsz_count[ksz % FSZCNT]++; + fp->fsz_sz[ksz % FSZCNT] += sz; +#endif /* COMPAT */ + } else if (errno) { + perror(name); + exit(1); + } + } + sz = 0; + for (fp = fsizes; fp; fp = fp->fsz_next) { + for (i = 0; i < FSZCNT; i++) { + if (fp->fsz_count[i]) + printf("%d\t%d\t%d\n",fp->fsz_first + i, + fp->fsz_count[i], + SIZE(sz += fp->fsz_sz[i])); + } + } +} + +static douser(fd,super,name) + struct fs *super; + char *name; +{ + ino_t inode, maxino; + struct user *usr, *usrs; + struct dinode *ip; + register n; + + maxino = super->fs_ncg * super->fs_ipg - 1; + for (inode = 0; inode < maxino; inode++) { + errno = 0; + if ((ip = get_inode(fd,super,inode)) + && !isfree(ip)) + uses(ip->di_uid, + estimate ? virtualblocks(super,ip) : + actualblocks(super,ip), + ip->di_atime); + else if (errno) { + perror(name); + exit(1); + } + } + if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user)))) { + perror("allocate users"); + exit(1); + } + bcopy(users,usrs,nusers * sizeof(struct user)); + sortusers(usrs); + for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { + printf("%5d",SIZE(usr->space)); + if (count) + printf("\t%5d",usr->count); + printf("\t%-8s",usr->name); + if (unused) + printf("\t%5d\t%5d\t%5d", + SIZE(usr->spc30), + SIZE(usr->spc60), + SIZE(usr->spc90)); + printf("\n"); + } + free(usrs); +} + +static donames(fd,super,name) + struct fs *super; + char *name; +{ + int c; + ino_t inode, inode1; + ino_t maxino; + struct dinode *ip; + + maxino = super->fs_ncg * super->fs_ipg - 1; + /* first skip the name of the filesystem */ + while ((c = getchar()) != EOF && (c < '0' || c > '9')) + while ((c = getchar()) != EOF && c != '\n'); + ungetc(c,stdin); + inode1 = -1; + while (scanf("%d",&inode) == 1) { + if (inode < 0 || inode > maxino) { + fprintf(stderr,"illegal inode %d\n",inode); + return; + } + errno = 0; + if ((ip = get_inode(fd,super,inode)) + && !isfree(ip)) { + printf("%s\t",user(ip->di_uid)->name); + /* now skip whitespace */ + while ((c = getchar()) == ' ' || c == '\t'); + /* and print out the remainder of the input line */ + while (c != EOF && c != '\n') { + putchar(c); + c = getchar(); + } + putchar('\n'); + inode1 = inode; + } else { + if (errno) { + perror(name); + exit(1); + } + /* skip this line */ + while ((c = getchar()) != EOF && c != '\n'); + } + if (c == EOF) + break; + } +} + +static usage() +{ +#ifdef COMPAT + fprintf(stderr,"Usage: quot [-nfcvha] [filesystem ...]\n"); +#else /* COMPAT */ + fprintf(stderr,"Usage: quot [ -acfhknv ] [ filesystem ... ]\n"); +#endif /* COMPAT */ + exit(1); +} + +static char superblock[SBSIZE]; + +quot(name,mp) + char *name, *mp; +{ + int fd; + + get_inode(-1); /* flush cache */ + inituser(); + initfsizes(); + if ((fd = open(name,0)) < 0 + || lseek(fd,SBOFF,0) != SBOFF + || read(fd,superblock,SBSIZE) != SBSIZE) { + perror(name); + close(fd); + return; + } + if (((struct fs *)superblock)->fs_magic != FS_MAGIC) { + fprintf(stderr,"%s: not a BSD filesystem\n",name); + close(fd); + return; + } + printf("%s:",name); + if (mp) + printf(" (%s)",mp); + putchar('\n'); + (*func)(fd,superblock,name); + close(fd); +} + +int main(argc,argv) + char **argv; +{ + int fd; + char all = 0; + FILE *fp; + struct statfs *mp; + char dev[MNAMELEN + 1]; + char *nm; + int cnt; + + func = douser; +#ifndef COMPAT + header = getbsize(&headerlen,&blocksize); +#endif + while (--argc > 0 && **++argv == '-') { + while (*++*argv) { + switch (**argv) { + case 'n': + func = donames; + break; + case 'c': + func = dofsizes; + break; + case 'a': + all = 1; + break; + case 'f': + count = 1; + break; + case 'h': + estimate = 1; + break; +#ifndef COMPAT + case 'k': + blocksize = 1024; + break; +#endif /* COMPAT */ + case 'v': + unused = 1; + break; + default: + usage(); + } + } + } + if (all) { + cnt = getmntinfo(&mp,MNT_NOWAIT); + for (; --cnt >= 0; mp++) { + if (!strncmp(mp->f_fstypename, MOUNT_FFS, MFSNAMELEN)) { + if (nm = strrchr(mp->f_mntfromname,'/')) { + sprintf(dev,"/dev/r%s",nm + 1); + nm = dev; + } else + nm = mp->f_mntfromname; + quot(nm,mp->f_mntonname); + } + } + } + while (--argc >= 0) + quot(*argv++,0); + return 0; +}