xref: /netbsd-src/usr.sbin/quot/quot.c (revision 87ba0e2a319653af510fae24a6d2975d3589735b)
1*87ba0e2aSchs /*	$NetBSD: quot.c,v 1.35 2022/11/17 06:40:41 chs Exp $	*/
202132669Sws 
366ffc9f8Sws /*
466ffc9f8Sws  * Copyright (C) 1991, 1994 Wolfgang Solfrank.
566ffc9f8Sws  * Copyright (C) 1991, 1994 TooLs GmbH.
666ffc9f8Sws  * All rights reserved.
766ffc9f8Sws  *
866ffc9f8Sws  * Redistribution and use in source and binary forms, with or without
966ffc9f8Sws  * modification, are permitted provided that the following conditions
1066ffc9f8Sws  * are met:
1166ffc9f8Sws  * 1. Redistributions of source code must retain the above copyright
1266ffc9f8Sws  *    notice, this list of conditions and the following disclaimer.
1366ffc9f8Sws  * 2. Redistributions in binary form must reproduce the above copyright
1466ffc9f8Sws  *    notice, this list of conditions and the following disclaimer in the
1566ffc9f8Sws  *    documentation and/or other materials provided with the distribution.
1666ffc9f8Sws  * 3. All advertising materials mentioning features or use of this software
1766ffc9f8Sws  *    must display the following acknowledgement:
1866ffc9f8Sws  *	This product includes software developed by TooLs GmbH.
1966ffc9f8Sws  * 4. The name of TooLs GmbH may not be used to endorse or promote products
2066ffc9f8Sws  *    derived from this software without specific prior written permission.
2166ffc9f8Sws  *
2266ffc9f8Sws  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
2366ffc9f8Sws  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2466ffc9f8Sws  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2566ffc9f8Sws  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2666ffc9f8Sws  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2766ffc9f8Sws  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2866ffc9f8Sws  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2966ffc9f8Sws  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3066ffc9f8Sws  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3166ffc9f8Sws  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3266ffc9f8Sws  */
335b9674ecScgd 
34d07d0944Slukem #include <sys/cdefs.h>
355b9674ecScgd #ifndef lint
36*87ba0e2aSchs __RCSID("$NetBSD: quot.c,v 1.35 2022/11/17 06:40:41 chs Exp $");
375b9674ecScgd #endif /* not lint */
385b9674ecScgd 
394417e586Scgd #include <sys/param.h>
404417e586Scgd #include <sys/mount.h>
414417e586Scgd #include <sys/time.h>
4242614ed3Sfvdl #include <ufs/ufs/dinode.h>
43e5bc90f4Sfvdl #include <ufs/ffs/fs.h>
444417e586Scgd 
45de441420Slukem #include <err.h>
46d07d0944Slukem #include <errno.h>
47d07d0944Slukem #include <fcntl.h>
48d07d0944Slukem #include <pwd.h>
4966ffc9f8Sws #include <stdio.h>
5066ffc9f8Sws #include <stdlib.h>
5166ffc9f8Sws #include <string.h>
5230cdcc3bSmrg #include <unistd.h>
5366ffc9f8Sws 
5466ffc9f8Sws /* some flags of what to do: */
5566ffc9f8Sws static char estimate;
5666ffc9f8Sws static char count;
5766ffc9f8Sws static char unused;
58e73fcb71Schristos static void (*func)(int, struct fs *, const char *);
5966ffc9f8Sws static long blocksize;
6066ffc9f8Sws static char *header;
6166ffc9f8Sws 
625b9674ecScgd /*
635b9674ecScgd  * Original BSD quot doesn't round to number of frags/blocks,
645b9674ecScgd  * doesn't account for indirection blocks and gets it totally
655b9674ecScgd  * wrong if the	size is a multiple of the blocksize.
6602132669Sws  * The new code always counts the number of DEV_BSIZE byte blocks
675b9674ecScgd  * instead of the number of kilobytes and converts them	to
685b9674ecScgd  * kByte when done (on request).
695b9674ecScgd  */
7066ffc9f8Sws #ifdef	COMPAT
71c960d1daSmycroft #define	SIZE(n)	((long long)(n))
7266ffc9f8Sws #else
73c960d1daSmycroft #define	SIZE(n)	howmany((long long)(n) * DEV_BSIZE, (long long)blocksize)
7466ffc9f8Sws #endif
7566ffc9f8Sws 
7666ffc9f8Sws #define	INOCNT(fs)	((fs)->fs_ipg)
7742614ed3Sfvdl #define INOSZ(fs) \
7842614ed3Sfvdl 	(((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \
7942614ed3Sfvdl 	sizeof(struct ufs2_dinode)) * INOCNT(fs))
8042614ed3Sfvdl 
8142614ed3Sfvdl union dinode {
8242614ed3Sfvdl 	struct ufs1_dinode dp1;
8342614ed3Sfvdl 	struct ufs2_dinode dp2;
8442614ed3Sfvdl };
8542614ed3Sfvdl #define       DIP(fs, dp, field) \
8642614ed3Sfvdl 	(((fs)->fs_magic == FS_UFS1_MAGIC) ? \
8742614ed3Sfvdl 	(dp)->dp1.di_##field : (dp)->dp2.di_##field)
8842614ed3Sfvdl 
8966ffc9f8Sws 
90e73fcb71Schristos static	int		cmpusers(const void *, const void *);
91e73fcb71Schristos static	void		dofsizes(int, struct fs *, const char *);
92e73fcb71Schristos static	void		donames(int, struct fs *, const char *);
93e73fcb71Schristos static	void		douser(int, struct fs *, const char *);
94e73fcb71Schristos static	union dinode  *get_inode(int, struct fs *, ino_t);
95e73fcb71Schristos static	void		ffs_oldfscompat(struct fs *);
96e73fcb71Schristos static	void		initfsizes(void);
97e73fcb71Schristos static	void		inituser(void);
98e73fcb71Schristos static	int		isfree(struct fs *, union dinode *);
99e73fcb71Schristos static	void		quot(const char *, const char *);
100e73fcb71Schristos static	void		usage(void) __attribute__((__noreturn__));
101e73fcb71Schristos static	struct user    *user(uid_t);
102e73fcb71Schristos static	void		uses(uid_t, daddr_t, time_t);
103e73fcb71Schristos static	void		usrrehash(void);
104e73fcb71Schristos static	int		virtualblocks(struct fs *, union dinode *);
105d07d0944Slukem 
106d07d0944Slukem 
10742614ed3Sfvdl static union dinode *
get_inode(int fd,struct fs * super,ino_t ino)108e73fcb71Schristos get_inode(int fd, struct fs *super, ino_t ino)
10966ffc9f8Sws {
11042614ed3Sfvdl 	static char *ipbuf;
11166ffc9f8Sws 	static ino_t last;
11266ffc9f8Sws 
11366ffc9f8Sws 	if (fd < 0) {		/* flush cache */
11442614ed3Sfvdl 		if (ipbuf) {
11542614ed3Sfvdl 			free(ipbuf);
11642614ed3Sfvdl 			ipbuf = NULL;
11766ffc9f8Sws 		}
11866ffc9f8Sws 		return 0;
11966ffc9f8Sws 	}
12066ffc9f8Sws 
12142614ed3Sfvdl 	if (!ipbuf || ino < last || ino >= last + INOCNT(super)) {
12242614ed3Sfvdl 		if (!ipbuf
12342614ed3Sfvdl 		    && !(ipbuf = malloc(INOSZ(super))))
124de441420Slukem 			errx(1, "allocate inodes");
12566ffc9f8Sws 		last = (ino / INOCNT(super)) * INOCNT(super);
12602132669Sws 		if (lseek(fd,
12702132669Sws 		    (off_t)ino_to_fsba(super, last) << super->fs_fshift,
12811f8dfe5Smycroft 		    0) < 0 ||
129e73fcb71Schristos 		    read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super))
130de441420Slukem 			errx(1, "read inodes");
13166ffc9f8Sws 	}
13266ffc9f8Sws 
13342614ed3Sfvdl 	if (super->fs_magic == FS_UFS1_MAGIC)
13442614ed3Sfvdl 		return ((union dinode *)
13542614ed3Sfvdl 		    &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]);
13642614ed3Sfvdl 	return ((union dinode *)
13742614ed3Sfvdl 	    &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]);
13866ffc9f8Sws }
13966ffc9f8Sws 
14066ffc9f8Sws #ifdef	COMPAT
141e73fcb71Schristos #define	actualblocks(fs, dp)	(int)(DIP(fs, dp, blocks) / 2)
14266ffc9f8Sws #else
143e73fcb71Schristos #define	actualblocks(fs, dp)	(int)(DIP(fs, dp, blocks))
14466ffc9f8Sws #endif
14566ffc9f8Sws 
14602132669Sws static int
virtualblocks(struct fs * super,union dinode * dp)147e73fcb71Schristos virtualblocks(struct fs *super, union dinode *dp)
14866ffc9f8Sws {
149de441420Slukem 	off_t nblk, sz;
15066ffc9f8Sws 
15142614ed3Sfvdl 	sz = DIP(super, dp, size);
15266ffc9f8Sws #ifdef	COMPAT
153e1610ba4Sdholland 	if (ffs_lblkno(super, sz) >= UFS_NDADDR) {
154e1610ba4Sdholland 		nblk = ffs_blkroundup(super, sz);
15566ffc9f8Sws 		if (sz == nblk)
15666ffc9f8Sws 			nblk += super->fs_bsize;
15766ffc9f8Sws 	}
15866ffc9f8Sws 
15966ffc9f8Sws 	return sz / 1024;
16066ffc9f8Sws #else	/* COMPAT */
16166ffc9f8Sws 
162e1610ba4Sdholland 	if (ffs_lblkno(super, sz) >= UFS_NDADDR) {
163e1610ba4Sdholland 		nblk = ffs_blkroundup(super, sz);
164e1610ba4Sdholland 		sz = ffs_lblkno(super, nblk);
165f1333577Sdholland 		sz = howmany(sz - UFS_NDADDR, FFS_NINDIR(super));
16666ffc9f8Sws 		while (sz > 0) {
16766ffc9f8Sws 			nblk += sz * super->fs_bsize;
16802132669Sws 			/* One block on this level is in the inode itself */
169f1333577Sdholland 			sz = howmany(sz - 1, FFS_NINDIR(super));
17066ffc9f8Sws 		}
17166ffc9f8Sws 	} else
172e1610ba4Sdholland 		nblk = ffs_fragroundup(super, sz);
17366ffc9f8Sws 
17402132669Sws 	return nblk / DEV_BSIZE;
17566ffc9f8Sws #endif	/* COMPAT */
17666ffc9f8Sws }
17766ffc9f8Sws 
17802132669Sws static int
isfree(fs,dp)17942614ed3Sfvdl isfree(fs, dp)
18042614ed3Sfvdl 	struct fs *fs;
18142614ed3Sfvdl 	union dinode *dp;
18266ffc9f8Sws {
18366ffc9f8Sws #ifdef	COMPAT
18442614ed3Sfvdl 	return (DIP(fs, dp, mode) & IFMT) == 0;
18566ffc9f8Sws #else	/* COMPAT */
18642614ed3Sfvdl 	switch (DIP(fs, dp, mode) & IFMT) {
18766ffc9f8Sws 	case IFIFO:
18866ffc9f8Sws 	case IFLNK:		/* should check FASTSYMLINK? */
18966ffc9f8Sws 	case IFDIR:
19066ffc9f8Sws 	case IFREG:
19166ffc9f8Sws 		return 0;
19266ffc9f8Sws 	default:
19366ffc9f8Sws 		return 1;
19466ffc9f8Sws 	}
19566ffc9f8Sws #endif
19666ffc9f8Sws }
19766ffc9f8Sws 
19866ffc9f8Sws static struct user {
19966ffc9f8Sws 	uid_t uid;
20066ffc9f8Sws 	char *name;
20166ffc9f8Sws 	daddr_t space;
20266ffc9f8Sws 	long count;
20366ffc9f8Sws 	daddr_t spc30;
20466ffc9f8Sws 	daddr_t spc60;
20566ffc9f8Sws 	daddr_t spc90;
20666ffc9f8Sws } *users;
20766ffc9f8Sws static int nusers;
20866ffc9f8Sws 
20902132669Sws static void
inituser(void)210e73fcb71Schristos inituser(void)
21166ffc9f8Sws {
212de441420Slukem 	int i;
213de441420Slukem 	struct user *usr;
21466ffc9f8Sws 
21566ffc9f8Sws 	if (!nusers) {
21666ffc9f8Sws 		nusers = 8;
217e73fcb71Schristos 		if (!(users = calloc(nusers, sizeof(*users))))
218e73fcb71Schristos 			err(1, "allocate users");
21966ffc9f8Sws 	} else {
22066ffc9f8Sws 		for (usr = users, i = nusers; --i >= 0; usr++) {
22166ffc9f8Sws 			usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0;
22266ffc9f8Sws 			usr->count = 0;
22366ffc9f8Sws 		}
22466ffc9f8Sws 	}
22566ffc9f8Sws }
22666ffc9f8Sws 
22702132669Sws static void
usrrehash(void)228e73fcb71Schristos usrrehash(void)
22966ffc9f8Sws {
230de441420Slukem 	int i;
231de441420Slukem 	struct user *usr, *usrn;
23266ffc9f8Sws 	struct user *svusr;
23366ffc9f8Sws 
23466ffc9f8Sws 	svusr = users;
23566ffc9f8Sws 	nusers <<= 1;
236e73fcb71Schristos 	if (!(users = calloc(nusers, sizeof(*users))))
237e73fcb71Schristos 		err(1, "allocate users");
23866ffc9f8Sws 	for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) {
23902132669Sws 		for (usrn = users + (usr->uid&(nusers - 1));
24002132669Sws 		     usrn->name;
2415b9674ecScgd 		     usrn--) {
24266ffc9f8Sws 			if (usrn <= users)
24366ffc9f8Sws 				usrn = users + nusers;
24466ffc9f8Sws 		}
24566ffc9f8Sws 		*usrn = *usr;
24666ffc9f8Sws 	}
24766ffc9f8Sws }
24866ffc9f8Sws 
24902132669Sws static struct user *
user(uid_t uid)250e73fcb71Schristos user(uid_t uid)
25166ffc9f8Sws {
252de441420Slukem 	struct user *usr;
253de441420Slukem 	int i;
25466ffc9f8Sws 	struct passwd *pwd;
25566ffc9f8Sws 
256e73fcb71Schristos 	for (;;) {
25702132669Sws 		for (usr = users + (uid & (nusers - 1)), i = nusers;
25802132669Sws 		     --i >= 0;
2595b9674ecScgd 		     usr--) {
26066ffc9f8Sws 			if (!usr->name) {
26166ffc9f8Sws 				usr->uid = uid;
26266ffc9f8Sws 
263e73fcb71Schristos 				if (!(pwd = getpwuid(uid)))
264e73fcb71Schristos 					asprintf(&usr->name, "#%u", uid);
265e73fcb71Schristos 				else
266e73fcb71Schristos 					asprintf(&usr->name, "%s",
267e73fcb71Schristos 					    pwd->pw_name);
268de441420Slukem 				if (!usr->name)
269de441420Slukem 					errx(1, "allocate users");
27066ffc9f8Sws 				return usr;
27166ffc9f8Sws 			} else if (usr->uid == uid)
27266ffc9f8Sws 				return usr;
27366ffc9f8Sws 
27466ffc9f8Sws 			if (usr <= users)
27566ffc9f8Sws 				usr = users + nusers;
27666ffc9f8Sws 		}
27766ffc9f8Sws 		usrrehash();
27866ffc9f8Sws 	}
27966ffc9f8Sws }
28066ffc9f8Sws 
28102132669Sws static int
cmpusers(u1,u2)28202132669Sws cmpusers(u1, u2)
283d07d0944Slukem 	const void *u1, *u2;
28466ffc9f8Sws {
2854d2da769Slukem 	return ((const struct user *)u2)->space - ((const struct user *)u1)->space;
28666ffc9f8Sws }
2875b9674ecScgd 
2885b9674ecScgd #define	sortusers(users)	(qsort((users), nusers, sizeof(struct user), \
2895b9674ecScgd 				       cmpusers))
29066ffc9f8Sws 
29102132669Sws static void
uses(uid,blks,act)29202132669Sws uses(uid, blks, act)
29366ffc9f8Sws 	uid_t uid;
29466ffc9f8Sws 	daddr_t blks;
29566ffc9f8Sws 	time_t act;
29666ffc9f8Sws {
29766ffc9f8Sws 	static time_t today;
298de441420Slukem 	struct user *usr;
29966ffc9f8Sws 
30066ffc9f8Sws 	if (!today)
30166ffc9f8Sws 		time(&today);
30266ffc9f8Sws 
30366ffc9f8Sws 	usr = user(uid);
30466ffc9f8Sws 	usr->count++;
30566ffc9f8Sws 	usr->space += blks;
30666ffc9f8Sws 
30766ffc9f8Sws 	if (today - act > 90L * 24L * 60L * 60L)
30866ffc9f8Sws 		usr->spc90 += blks;
30966ffc9f8Sws 	if (today - act > 60L * 24L * 60L * 60L)
31066ffc9f8Sws 		usr->spc60 += blks;
31166ffc9f8Sws 	if (today - act > 30L * 24L * 60L * 60L)
31266ffc9f8Sws 		usr->spc30 += blks;
31366ffc9f8Sws }
31466ffc9f8Sws 
31566ffc9f8Sws #ifdef	COMPAT
31666ffc9f8Sws #define	FSZCNT	500
31766ffc9f8Sws #else
31866ffc9f8Sws #define	FSZCNT	512
31966ffc9f8Sws #endif
32066ffc9f8Sws struct fsizes {
32166ffc9f8Sws 	struct fsizes *fsz_next;
32266ffc9f8Sws 	daddr_t fsz_first, fsz_last;
32366ffc9f8Sws 	ino_t fsz_count[FSZCNT];
32466ffc9f8Sws 	daddr_t fsz_sz[FSZCNT];
32566ffc9f8Sws } *fsizes;
32666ffc9f8Sws 
32702132669Sws static void
initfsizes()32802132669Sws initfsizes()
32966ffc9f8Sws {
330de441420Slukem 	struct fsizes *fp;
331de441420Slukem 	int i;
33266ffc9f8Sws 
33366ffc9f8Sws 	for (fp = fsizes; fp; fp = fp->fsz_next) {
33466ffc9f8Sws 		for (i = FSZCNT; --i >= 0;) {
33566ffc9f8Sws 			fp->fsz_count[i] = 0;
33666ffc9f8Sws 			fp->fsz_sz[i] = 0;
33766ffc9f8Sws 		}
33866ffc9f8Sws 	}
33966ffc9f8Sws }
34066ffc9f8Sws 
34102132669Sws static void
dofsizes(int fd,struct fs * super,const char * name)342e73fcb71Schristos dofsizes(int fd, struct fs *super, const char *name)
34366ffc9f8Sws {
34466ffc9f8Sws 	ino_t inode, maxino;
34542614ed3Sfvdl 	union dinode *dp;
34666ffc9f8Sws 	daddr_t sz, ksz;
34766ffc9f8Sws 	struct fsizes *fp, **fsp;
348de441420Slukem 	int i;
34966ffc9f8Sws 
35066ffc9f8Sws 	maxino = super->fs_ncg * super->fs_ipg - 1;
35166ffc9f8Sws #ifdef	COMPAT
352e73fcb71Schristos 	if (!(fsizes = malloc(sizeof(*fsizes))))
353e73fcb71Schristos 		err(1, "alloc fsize structure");
35466ffc9f8Sws #endif	/* COMPAT */
35566ffc9f8Sws 	for (inode = 0; inode < maxino; inode++) {
35666ffc9f8Sws 		errno = 0;
35742614ed3Sfvdl 		if ((dp = get_inode(fd, super, inode))
35866ffc9f8Sws #ifdef	COMPAT
35942614ed3Sfvdl 		    && ((DIP(super, dp, mode) & IFMT) == IFREG
36042614ed3Sfvdl 			|| (DIP(dp, mode) & IFMT) == IFDIR)
36166ffc9f8Sws #else	/* COMPAT */
36242614ed3Sfvdl 		    && !isfree(super, dp)
36366ffc9f8Sws #endif	/* COMPAT */
36466ffc9f8Sws 		    ) {
36542614ed3Sfvdl 			sz = estimate ? virtualblocks(super, dp) :
36642614ed3Sfvdl 			    actualblocks(super, dp);
36766ffc9f8Sws #ifdef	COMPAT
36866ffc9f8Sws 			if (sz >= FSZCNT) {
36966ffc9f8Sws 				fsizes->fsz_count[FSZCNT-1]++;
37066ffc9f8Sws 				fsizes->fsz_sz[FSZCNT-1] += sz;
37166ffc9f8Sws 			} else {
37266ffc9f8Sws 				fsizes->fsz_count[sz]++;
37366ffc9f8Sws 				fsizes->fsz_sz[sz] += sz;
37466ffc9f8Sws 			}
37566ffc9f8Sws #else	/* COMPAT */
37666ffc9f8Sws 			ksz = SIZE(sz);
377d07d0944Slukem 			for (fsp = &fsizes; (fp = *fsp) != NULL;
378d07d0944Slukem 			    fsp = &fp->fsz_next) {
37966ffc9f8Sws 				if (ksz < fp->fsz_last)
38066ffc9f8Sws 					break;
38166ffc9f8Sws 			}
38266ffc9f8Sws 			if (!fp || ksz < fp->fsz_first) {
383e73fcb71Schristos 				if (!(fp = malloc(sizeof(*fp))))
384e73fcb71Schristos 					err(1, "alloc fsize structure");
38566ffc9f8Sws 				fp->fsz_next = *fsp;
38666ffc9f8Sws 				*fsp = fp;
38766ffc9f8Sws 				fp->fsz_first = (ksz / FSZCNT) * FSZCNT;
38866ffc9f8Sws 				fp->fsz_last = fp->fsz_first + FSZCNT;
38966ffc9f8Sws 				for (i = FSZCNT; --i >= 0;) {
39066ffc9f8Sws 					fp->fsz_count[i] = 0;
39166ffc9f8Sws 					fp->fsz_sz[i] = 0;
39266ffc9f8Sws 				}
39366ffc9f8Sws 			}
39466ffc9f8Sws 			fp->fsz_count[ksz % FSZCNT]++;
39566ffc9f8Sws 			fp->fsz_sz[ksz % FSZCNT] += sz;
39666ffc9f8Sws #endif	/* COMPAT */
397de441420Slukem 		} else if (errno)
398de441420Slukem 			errx(1, "%s", name);
39966ffc9f8Sws 	}
40066ffc9f8Sws 	sz = 0;
40166ffc9f8Sws 	for (fp = fsizes; fp; fp = fp->fsz_next) {
40266ffc9f8Sws 		for (i = 0; i < FSZCNT; i++) {
40366ffc9f8Sws 			if (fp->fsz_count[i])
404c960d1daSmycroft 				printf("%ld\t%ld\t%lld\n",
405d07d0944Slukem 				    (long)(fp->fsz_first + i),
406d07d0944Slukem 				    (long)fp->fsz_count[i],
407357cd619Smycroft 				    SIZE(sz += fp->fsz_sz[i]));
40866ffc9f8Sws 		}
40966ffc9f8Sws 	}
41066ffc9f8Sws }
41166ffc9f8Sws 
41202132669Sws static void
douser(int fd,struct fs * super,const char * name)413e73fcb71Schristos douser(int fd, struct fs *super, const char *name)
41466ffc9f8Sws {
41566ffc9f8Sws 	ino_t inode, maxino;
41666ffc9f8Sws 	struct user *usr, *usrs;
41742614ed3Sfvdl 	union dinode *dp;
418de441420Slukem 	int n;
41966ffc9f8Sws 
42066ffc9f8Sws 	maxino = super->fs_ncg * super->fs_ipg - 1;
42166ffc9f8Sws 	for (inode = 0; inode < maxino; inode++) {
42266ffc9f8Sws 		errno = 0;
42342614ed3Sfvdl 		if ((dp = get_inode(fd, super, inode))
42442614ed3Sfvdl 		    && !isfree(super, dp))
42542614ed3Sfvdl 			uses(DIP(super, dp, uid),
42642614ed3Sfvdl 			    estimate ? virtualblocks(super, dp) :
42742614ed3Sfvdl 			    actualblocks(super, dp), DIP(super, dp, atime));
428de441420Slukem 		else if (errno)
429de441420Slukem 			errx(1, "%s", name);
43066ffc9f8Sws 	}
431e73fcb71Schristos 	if (!(usrs = calloc(nusers, sizeof(*usrs))))
432de441420Slukem 		errx(1, "allocate users");
433e73fcb71Schristos 	memmove(usrs, users, nusers * sizeof(*usrs));
43466ffc9f8Sws 	sortusers(usrs);
43566ffc9f8Sws 	for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) {
436c960d1daSmycroft 		printf("%5lld", SIZE(usr->space));
43766ffc9f8Sws 		if (count)
438357cd619Smycroft 			printf("\t%5ld", usr->count);
43966ffc9f8Sws 		printf("\t%-8s", usr->name);
44066ffc9f8Sws 		if (unused)
441c960d1daSmycroft 			printf("\t%5lld\t%5lld\t%5lld",
44211f8dfe5Smycroft 			    SIZE(usr->spc30), SIZE(usr->spc60),
44366ffc9f8Sws 			    SIZE(usr->spc90));
44466ffc9f8Sws 		printf("\n");
44566ffc9f8Sws 	}
44666ffc9f8Sws 	free(usrs);
44766ffc9f8Sws }
44866ffc9f8Sws 
44902132669Sws static void
donames(int fd,struct fs * super,const char * name)450e73fcb71Schristos donames(int fd, struct fs *super, const char *name)
45166ffc9f8Sws {
45266ffc9f8Sws 	int c;
4530a77b69aSchristos 	ino_t inode;
4540a77b69aSchristos #ifdef COMPAT
4550a77b69aSchristos 	ino_t inode1 = -1;
4560a77b69aSchristos #endif
45766ffc9f8Sws 	ino_t maxino;
45842614ed3Sfvdl 	union dinode *dp;
45966ffc9f8Sws 
46066ffc9f8Sws 	maxino = super->fs_ncg * super->fs_ipg - 1;
46166ffc9f8Sws 	/* first skip the name of the filesystem */
46266ffc9f8Sws 	while ((c = getchar()) != EOF && (c < '0' || c > '9'))
46366ffc9f8Sws 		while ((c = getchar()) != EOF && c != '\n');
46466ffc9f8Sws 	ungetc(c, stdin);
465ed574610Skent 	while (scanf("%" SCNu64, &inode) == 1) {
4664d2da769Slukem 		if (inode > maxino) {
46702132669Sws #ifndef	COMPAT
468ed574610Skent 			warnx("invalid inode %" PRIu64, inode);
46902132669Sws #endif
47066ffc9f8Sws 			return;
47166ffc9f8Sws 		}
47202132669Sws #ifdef	COMPAT
47302132669Sws 		if (inode < inode1)
47402132669Sws 			continue;
47502132669Sws #endif
47666ffc9f8Sws 		errno = 0;
47742614ed3Sfvdl 		if ((dp = get_inode(fd, super, inode))
47842614ed3Sfvdl 		    && !isfree(super, dp)) {
47942614ed3Sfvdl 			printf("%s\t", user(DIP(super, dp, uid))->name);
48066ffc9f8Sws 			/* now skip whitespace */
48166ffc9f8Sws 			while ((c = getchar()) == ' ' || c == '\t');
48266ffc9f8Sws 			/* and print out the remainder of the input line */
48366ffc9f8Sws 			while (c != EOF && c != '\n') {
48466ffc9f8Sws 				putchar(c);
48566ffc9f8Sws 				c = getchar();
48666ffc9f8Sws 			}
48766ffc9f8Sws 			putchar('\n');
4880a77b69aSchristos #ifdef COMPAT
48966ffc9f8Sws 			inode1 = inode;
4900a77b69aSchristos #endif
49166ffc9f8Sws 		} else {
492de441420Slukem 			if (errno)
493de441420Slukem 				errx(1, "%s", name);
49466ffc9f8Sws 			/* skip this line */
495e73fcb71Schristos 			while ((c = getchar()) != EOF && c != '\n')
496e73fcb71Schristos 				continue;
49766ffc9f8Sws 		}
49866ffc9f8Sws 		if (c == EOF)
49966ffc9f8Sws 			break;
50066ffc9f8Sws 	}
50166ffc9f8Sws }
50266ffc9f8Sws 
50302132669Sws static void
usage(void)504e73fcb71Schristos usage(void)
50566ffc9f8Sws {
506e73fcb71Schristos 	const char *p = getprogname();
50766ffc9f8Sws #ifdef	COMPAT
508e73fcb71Schristos 	fprintf(stderr, "Usage: %s [-nfcvha] [<filesystem> ...]\n", p);
50966ffc9f8Sws #else	/* COMPAT */
510e73fcb71Schristos 	fprintf(stderr, "Usage: %s [ -acfhknv ] [<filesystem> ... ]\n", p);
51166ffc9f8Sws #endif	/* COMPAT */
51266ffc9f8Sws 	exit(1);
51366ffc9f8Sws }
51466ffc9f8Sws 
51502132669Sws /*
51602132669Sws  * Sanity checks for old file systems.
51702132669Sws  * Stolen from <sys/lib/libsa/ufs.c>
51802132669Sws  */
51902132669Sws static void
ffs_oldfscompat(struct fs * fs)520e73fcb71Schristos ffs_oldfscompat(struct fs *fs)
52102132669Sws {
52202132669Sws 	int i;
52302132669Sws 
524f0b8f9c1Smartin 	if (fs->fs_magic == FS_UFS1_MAGIC &&
525f0b8f9c1Smartin 	    fs->fs_old_inodefmt < FS_44INODEFMT) {
52642614ed3Sfvdl 		quad_t sizepb = fs->fs_bsize;
52742614ed3Sfvdl 
528dcd34a91Sdholland 		fs->fs_maxfilesize = fs->fs_bsize * UFS_NDADDR - 1;
529dcd34a91Sdholland 		for (i = 0; i < UFS_NIADDR; i++) {
530f1333577Sdholland 			sizepb *= FFS_NINDIR(fs);
53142614ed3Sfvdl 			fs->fs_maxfilesize += sizepb;
53202132669Sws 		}
53342614ed3Sfvdl 		fs->fs_qbmask = ~fs->fs_bmask;
53442614ed3Sfvdl 		fs->fs_qfmask = ~fs->fs_fmask;
53542614ed3Sfvdl 	}
53642614ed3Sfvdl }
53742614ed3Sfvdl 
53842614ed3Sfvdl /*
53942614ed3Sfvdl  * Possible superblock locations ordered from most to least likely.
54042614ed3Sfvdl  */
54142614ed3Sfvdl static int sblock_try[] = SBLOCKSEARCH;
54242614ed3Sfvdl static char superblock[SBLOCKSIZE];
54342614ed3Sfvdl 
54402132669Sws 
545e73fcb71Schristos static void
quot(const char * name,const char * mp)546e73fcb71Schristos quot(const char *name, const char *mp)
54766ffc9f8Sws {
54842614ed3Sfvdl 	int fd, i;
54942614ed3Sfvdl 	struct fs *fs;
550a78ca723Sdsl 	int sbloc;
55166ffc9f8Sws 
552d07d0944Slukem 	get_inode(-1, 0, 0);		/* flush cache */
55366ffc9f8Sws 	inituser();
55466ffc9f8Sws 	initfsizes();
55542614ed3Sfvdl 	if ((fd = open(name, 0)) < 0) {
556507e5c88Smrg 		warn("%s", name);
55766ffc9f8Sws 		return;
55866ffc9f8Sws 	}
55942614ed3Sfvdl 
560a78ca723Sdsl 	for (i = 0; ; i++) {
561a78ca723Sdsl 		sbloc = sblock_try[i];
562a78ca723Sdsl 		if (sbloc == -1) {
563de441420Slukem 			warnx("%s: not a BSD filesystem", name);
56466ffc9f8Sws 			close(fd);
56566ffc9f8Sws 			return;
56666ffc9f8Sws 		}
567a78ca723Sdsl 		if (pread(fd, superblock, SBLOCKSIZE, sbloc) != SBLOCKSIZE)
568a78ca723Sdsl 			continue;
569a78ca723Sdsl 		fs = (struct fs *)superblock;
570a78ca723Sdsl 
571a78ca723Sdsl 		if (fs->fs_magic != FS_UFS1_MAGIC &&
572*87ba0e2aSchs 		    fs->fs_magic != FS_UFS2_MAGIC &&
573*87ba0e2aSchs 		    fs->fs_magic != FS_UFS2EA_MAGIC)
574a78ca723Sdsl 			continue;
575a78ca723Sdsl 
576893680a0Sdsl 		if (fs->fs_magic == FS_UFS2_MAGIC
577*87ba0e2aSchs 		    || fs->fs_magic == FS_UFS2EA_MAGIC
578893680a0Sdsl 		    || fs->fs_old_flags & FS_FLAGS_UPDATED) {
579a78ca723Sdsl 			/* Not the main superblock */
580a78ca723Sdsl 			if (fs->fs_sblockloc != sbloc)
581a78ca723Sdsl 				continue;
582a78ca723Sdsl 		} else {
583a78ca723Sdsl 			/* might be a first alt. id blocksize 64k */
584a78ca723Sdsl 			if (sbloc == SBLOCK_UFS2)
585a78ca723Sdsl 				continue;
586a78ca723Sdsl 		}
587a78ca723Sdsl 
588a78ca723Sdsl 		if (fs->fs_bsize > MAXBSIZE ||
589e73fcb71Schristos 		    (size_t)fs->fs_bsize < sizeof(struct fs))
590a78ca723Sdsl 			continue;
591a78ca723Sdsl 		break;
592a78ca723Sdsl 	}
593a78ca723Sdsl 
594d07d0944Slukem 	ffs_oldfscompat((struct fs *)superblock);
59566ffc9f8Sws 	printf("%s:", name);
59666ffc9f8Sws 	if (mp)
59766ffc9f8Sws 		printf(" (%s)", mp);
59866ffc9f8Sws 	putchar('\n');
59942614ed3Sfvdl 	(*func)(fd, fs, name);
60066ffc9f8Sws 	close(fd);
60166ffc9f8Sws }
60266ffc9f8Sws 
60302132669Sws int
main(int argc,char ** argv)604e73fcb71Schristos main(int argc, char **argv)
60566ffc9f8Sws {
60666ffc9f8Sws 	char all = 0;
6076bd1d6d4Schristos 	struct statvfs *mp;
60866ffc9f8Sws 	char dev[MNAMELEN + 1];
60966ffc9f8Sws 	char *nm;
61066ffc9f8Sws 	int cnt;
61166ffc9f8Sws 
61266ffc9f8Sws 	func = douser;
61366ffc9f8Sws #ifndef	COMPAT
614ae46649fSsimonb 	header = getbsize(NULL, &blocksize);
61566ffc9f8Sws #endif
61666ffc9f8Sws 	while (--argc > 0 && **++argv == '-') {
61766ffc9f8Sws 		while (*++*argv) {
61866ffc9f8Sws 			switch (**argv) {
61966ffc9f8Sws 			case 'n':
62066ffc9f8Sws 				func = donames;
62166ffc9f8Sws 				break;
62266ffc9f8Sws 			case 'c':
62366ffc9f8Sws 				func = dofsizes;
62466ffc9f8Sws 				break;
62566ffc9f8Sws 			case 'a':
62666ffc9f8Sws 				all = 1;
62766ffc9f8Sws 				break;
62866ffc9f8Sws 			case 'f':
62966ffc9f8Sws 				count = 1;
63066ffc9f8Sws 				break;
63166ffc9f8Sws 			case 'h':
63266ffc9f8Sws 				estimate = 1;
63366ffc9f8Sws 				break;
63466ffc9f8Sws #ifndef	COMPAT
63566ffc9f8Sws 			case 'k':
63666ffc9f8Sws 				blocksize = 1024;
63766ffc9f8Sws 				break;
63866ffc9f8Sws #endif	/* COMPAT */
63966ffc9f8Sws 			case 'v':
64066ffc9f8Sws 				unused = 1;
64166ffc9f8Sws 				break;
64266ffc9f8Sws 			default:
64366ffc9f8Sws 				usage();
64466ffc9f8Sws 			}
64566ffc9f8Sws 		}
64666ffc9f8Sws 	}
64766ffc9f8Sws 	if (all) {
64866ffc9f8Sws 		cnt = getmntinfo(&mp, MNT_NOWAIT);
64966ffc9f8Sws 		for (; --cnt >= 0; mp++) {
65063bf90c6Schristos 			if (!strncmp(mp->f_fstypename, MOUNT_FFS,
65163bf90c6Schristos 			    sizeof(mp->f_fstypename))) {
652d07d0944Slukem 				if ((nm =
653d07d0944Slukem 				    strrchr(mp->f_mntfromname, '/')) != NULL) {
654e73fcb71Schristos 					snprintf(dev, sizeof(dev), "/dev/r%s",
655e73fcb71Schristos 					    nm + 1);
65666ffc9f8Sws 					nm = dev;
65766ffc9f8Sws 				} else
65866ffc9f8Sws 					nm = mp->f_mntfromname;
65966ffc9f8Sws 				quot(nm, mp->f_mntonname);
66066ffc9f8Sws 			}
66166ffc9f8Sws 		}
66266ffc9f8Sws 	}
66366ffc9f8Sws 	while (--argc >= 0)
66466ffc9f8Sws 		quot(*argv++, 0);
66566ffc9f8Sws 	return 0;
66666ffc9f8Sws }
667