du.c revision 87216
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1989, 1993, 1994
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * This code is derived from software contributed to Berkeley by
61590Srgrimes * Chris Newcomb.
71590Srgrimes *
81590Srgrimes * Redistribution and use in source and binary forms, with or without
91590Srgrimes * modification, are permitted provided that the following conditions
101590Srgrimes * are met:
111590Srgrimes * 1. Redistributions of source code must retain the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer.
131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer in the
151590Srgrimes *    documentation and/or other materials provided with the distribution.
161590Srgrimes * 3. All advertising materials mentioning features or use of this software
171590Srgrimes *    must display the following acknowledgement:
181590Srgrimes *	This product includes software developed by the University of
191590Srgrimes *	California, Berkeley and its contributors.
201590Srgrimes * 4. Neither the name of the University nor the names of its contributors
211590Srgrimes *    may be used to endorse or promote products derived from this software
221590Srgrimes *    without specific prior written permission.
231590Srgrimes *
241590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341590Srgrimes * SUCH DAMAGE.
351590Srgrimes */
361590Srgrimes
371590Srgrimes#ifndef lint
3841568Sarchiestatic const char copyright[] =
391590Srgrimes"@(#) Copyright (c) 1989, 1993, 1994\n\
401590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
411590Srgrimes#endif /* not lint */
421590Srgrimes
431590Srgrimes#ifndef lint
4456597Smharo#if 0
4541568Sarchiestatic const char sccsid[] = "@(#)du.c	8.5 (Berkeley) 5/4/95";
4656597Smharo#endif
4758601Scharnierstatic const char rcsid[] =
4858601Scharnier  "$FreeBSD: head/usr.bin/du/du.c 87216 2001-12-02 13:48:40Z markm $";
491590Srgrimes#endif /* not lint */
501590Srgrimes
5132097Sjkh
521590Srgrimes#include <sys/param.h>
5378158Sroam#include <sys/queue.h>
541590Srgrimes#include <sys/stat.h>
551590Srgrimes
561590Srgrimes#include <err.h>
571590Srgrimes#include <errno.h>
5878158Sroam#include <fnmatch.h>
591590Srgrimes#include <fts.h>
6056597Smharo#include <math.h>
611590Srgrimes#include <stdio.h>
621590Srgrimes#include <stdlib.h>
631590Srgrimes#include <string.h>
6456597Smharo#include <sysexits.h>
6523693Speter#include <unistd.h>
661590Srgrimes
6756597Smharo#define	KILO_SZ(n) (n)
6856597Smharo#define	MEGA_SZ(n) ((n) * (n))
6956597Smharo#define	GIGA_SZ(n) ((n) * (n) * (n))
7056597Smharo#define	TERA_SZ(n) ((n) * (n) * (n) * (n))
7156597Smharo#define	PETA_SZ(n) ((n) * (n) * (n) * (n) * (n))
7256597Smharo
7356597Smharo#define	KILO_2_SZ (KILO_SZ(1024ULL))
7456597Smharo#define	MEGA_2_SZ (MEGA_SZ(1024ULL))
7556597Smharo#define	GIGA_2_SZ (GIGA_SZ(1024ULL))
7656597Smharo#define	TERA_2_SZ (TERA_SZ(1024ULL))
7756597Smharo#define	PETA_2_SZ (PETA_SZ(1024ULL))
7856597Smharo
7956597Smharo#define	KILO_SI_SZ (KILO_SZ(1000ULL))
8056597Smharo#define	MEGA_SI_SZ (MEGA_SZ(1000ULL))
8156597Smharo#define	GIGA_SI_SZ (GIGA_SZ(1000ULL))
8256597Smharo#define	TERA_SI_SZ (TERA_SZ(1000ULL))
8356597Smharo#define	PETA_SI_SZ (PETA_SZ(1000ULL))
8456597Smharo
8556597Smharounsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ};
8656597Smharounsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
8756597Smharounsigned long long *valp;
8856597Smharo
8956597Smharotypedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t;
9056597Smharo
9156597Smharoint unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA };
9256597Smharo
9378158SroamSLIST_HEAD(ignhead, ignentry) ignores;
9478158Sroamstruct ignentry {
9578158Sroam	char			*mask;
9678158Sroam	SLIST_ENTRY(ignentry)	next;
9778158Sroam};
9878158Sroam
9932097Sjkhint		linkchk __P((FTSENT *));
10032097Sjkhstatic void	usage __P((void));
10156597Smharovoid		prthumanval __P((double));
10256597Smharounit_t		unit_adjust __P((double *));
10378158Sroamvoid		ignoreadd __P((const char *));
10478158Sroamvoid		ignoreclean __P((void));
10578158Sroamint		ignorep __P((FTSENT *));
1061590Srgrimes
1071590Srgrimesint
1081590Srgrimesmain(argc, argv)
1091590Srgrimes	int argc;
1101590Srgrimes	char *argv[];
1111590Srgrimes{
11232097Sjkh	FTS		*fts;
11332097Sjkh	FTSENT		*p;
11437952Sdes	long		blocksize, savednumber = 0;
11532097Sjkh	int		ftsoptions;
11632097Sjkh	int		listall;
11732097Sjkh	int		depth;
11856597Smharo	int		Hflag, Lflag, Pflag, aflag, sflag, dflag, cflag, hflag, ch, notused, rval;
11932097Sjkh	char 		**save;
12087216Smarkm	static char	dot[] = ".";
1211590Srgrimes
12256597Smharo	Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0;
12332097Sjkh
1241590Srgrimes	save = argv;
12532097Sjkh	ftsoptions = 0;
12619120Sscrappy	depth = INT_MAX;
12778158Sroam	SLIST_INIT(&ignores);
12832097Sjkh
12978158Sroam	while ((ch = getopt(argc, argv, "HI:LPasd:chkrx")) != -1)
1301590Srgrimes		switch (ch) {
13132097Sjkh			case 'H':
13232097Sjkh				Hflag = 1;
13332097Sjkh				break;
13478158Sroam			case 'I':
13578158Sroam				ignoreadd(optarg);
13678158Sroam				break;
13732097Sjkh			case 'L':
13832097Sjkh				if (Pflag)
13932097Sjkh					usage();
14032097Sjkh				Lflag = 1;
14132097Sjkh				break;
14232097Sjkh			case 'P':
14332097Sjkh				if (Lflag)
14432097Sjkh					usage();
14532097Sjkh				Pflag = 1;
14632097Sjkh				break;
14732097Sjkh			case 'a':
14832097Sjkh				aflag = 1;
14932097Sjkh				break;
15032097Sjkh			case 's':
15132097Sjkh				sflag = 1;
15232097Sjkh				break;
15332097Sjkh			case 'd':
15432097Sjkh				dflag = 1;
15532097Sjkh				errno = 0;
15632097Sjkh				depth = atoi(optarg);
15732097Sjkh				if (errno == ERANGE || depth < 0) {
15858601Scharnier					warnx("invalid argument to option d: %s", optarg);
15932097Sjkh					usage();
16032097Sjkh				}
16132097Sjkh				break;
16232097Sjkh			case 'c':
16332097Sjkh				cflag = 1;
16432097Sjkh				break;
16556597Smharo			case 'h':
16656597Smharo				putenv("BLOCKSIZE=512");
16756597Smharo				hflag = 1;
16856597Smharo				valp = vals_base2;
16956597Smharo				break;
17056597Smharo			case 'k':
17182947Srobert				if (!hflag)
17282947Srobert					putenv("BLOCKSIZE=1024");
17356597Smharo				break;
17456597Smharo			case 'r':		 /* Compatibility. */
17556597Smharo				break;
17656597Smharo			case 'x':
17756597Smharo				ftsoptions |= FTS_XDEV;
17856597Smharo				break;
17932097Sjkh			case '?':
18032097Sjkh			default:
18119120Sscrappy				usage();
1821590Srgrimes		}
18332097Sjkh
1841590Srgrimes	argc -= optind;
1851590Srgrimes	argv += optind;
1861590Srgrimes
1871590Srgrimes	/*
1881590Srgrimes	 * XXX
1891590Srgrimes	 * Because of the way that fts(3) works, logical walks will not count
1901590Srgrimes	 * the blocks actually used by symbolic links.  We rationalize this by
1911590Srgrimes	 * noting that users computing logical sizes are likely to do logical
1921590Srgrimes	 * copies, so not counting the links is correct.  The real reason is
1931590Srgrimes	 * that we'd have to re-implement the kernel's symbolic link traversing
1941590Srgrimes	 * algorithm to get this right.  If, for example, you have relative
1951590Srgrimes	 * symbolic links referencing other relative symbolic links, it gets
1961590Srgrimes	 * very nasty, very fast.  The bottom line is that it's documented in
1971590Srgrimes	 * the man page, so it's a feature.
1981590Srgrimes	 */
19932097Sjkh
20032097Sjkh	if (Hflag + Lflag + Pflag > 1)
20132097Sjkh		usage();
20232097Sjkh
20332097Sjkh	if (Hflag + Lflag + Pflag == 0)
20432097Sjkh		Pflag = 1;			/* -P (physical) is default */
20532097Sjkh
2061590Srgrimes	if (Hflag)
2071590Srgrimes		ftsoptions |= FTS_COMFOLLOW;
20832097Sjkh
20932097Sjkh	if (Lflag)
2101590Srgrimes		ftsoptions |= FTS_LOGICAL;
2111590Srgrimes
21232097Sjkh	if (Pflag)
21332097Sjkh		ftsoptions |= FTS_PHYSICAL;
21432097Sjkh
21532097Sjkh	listall = 0;
21632097Sjkh
2171590Srgrimes	if (aflag) {
21819120Sscrappy		if (sflag || dflag)
2191590Srgrimes			usage();
22032097Sjkh		listall = 1;
22119120Sscrappy	} else if (sflag) {
22219120Sscrappy		if (dflag)
22319120Sscrappy			usage();
22432097Sjkh		depth = 0;
2251590Srgrimes	}
2261590Srgrimes
2271590Srgrimes	if (!*argv) {
2281590Srgrimes		argv = save;
22987216Smarkm		argv[0] = dot;
2301590Srgrimes		argv[1] = NULL;
2311590Srgrimes	}
2321590Srgrimes
23332097Sjkh	(void) getbsize(&notused, &blocksize);
2341590Srgrimes	blocksize /= 512;
2351590Srgrimes
23632097Sjkh	rval = 0;
23732097Sjkh
2381590Srgrimes	if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
23932097Sjkh		err(1, "fts_open");
2401590Srgrimes
24132097Sjkh	while ((p = fts_read(fts)) != NULL) {
2421590Srgrimes		switch (p->fts_info) {
24332097Sjkh			case FTS_D:			/* Ignore. */
24478158Sroam				if (ignorep(p))
24578158Sroam					fts_set(fts, p, FTS_SKIP);
2461590Srgrimes				break;
24732097Sjkh			case FTS_DP:
24878158Sroam				if (ignorep(p))
24978158Sroam					break;
25078158Sroam
25132097Sjkh				p->fts_parent->fts_number +=
25232097Sjkh				    p->fts_number += p->fts_statp->st_blocks;
25332097Sjkh
25458601Scharnier				if (p->fts_level <= depth) {
25556597Smharo					if (hflag) {
25658522Smharo						(void) prthumanval(howmany(p->fts_number, blocksize));
25756597Smharo						(void) printf("\t%s\n", p->fts_path);
25856597Smharo					} else {
25932097Sjkh					(void) printf("%ld\t%s\n",
26032097Sjkh					    howmany(p->fts_number, blocksize),
26132097Sjkh					    p->fts_path);
26256597Smharo					}
26358601Scharnier				}
26432097Sjkh				break;
26532097Sjkh			case FTS_DC:			/* Ignore. */
26632097Sjkh				break;
26732097Sjkh			case FTS_DNR:			/* Warn, continue. */
26832097Sjkh			case FTS_ERR:
26932097Sjkh			case FTS_NS:
27032097Sjkh				warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
27132097Sjkh				rval = 1;
27232097Sjkh				break;
27332097Sjkh			default:
27478158Sroam				if (ignorep(p))
27578158Sroam					break;
27678158Sroam
27732097Sjkh				if (p->fts_statp->st_nlink > 1 && linkchk(p))
27832097Sjkh					break;
27932097Sjkh
28058601Scharnier				if (listall || p->fts_level == 0) {
28156597Smharo					if (hflag) {
28256597Smharo						(void) prthumanval(howmany(p->fts_statp->st_blocks,
28356597Smharo							blocksize));
28456597Smharo						(void) printf("\t%s\n", p->fts_path);
28556597Smharo					} else {
28656597Smharo						(void) printf("%qd\t%s\n",
28756597Smharo							howmany(p->fts_statp->st_blocks, blocksize),
28856597Smharo							p->fts_path);
28956597Smharo					}
29058601Scharnier				}
29132097Sjkh
29232097Sjkh				p->fts_parent->fts_number += p->fts_statp->st_blocks;
2931590Srgrimes		}
29439076Sdes		savednumber = p->fts_parent->fts_number;
29532097Sjkh	}
29632097Sjkh
2971590Srgrimes	if (errno)
2981590Srgrimes		err(1, "fts_read");
29932097Sjkh
30058601Scharnier	if (cflag) {
30156597Smharo		if (hflag) {
30256597Smharo			(void) prthumanval(howmany(savednumber, blocksize));
30356597Smharo			(void) printf("\ttotal\n");
30456597Smharo		} else {
30556597Smharo			(void) printf("%ld\ttotal\n", howmany(savednumber, blocksize));
30656597Smharo		}
30758601Scharnier	}
30832097Sjkh
30978158Sroam	ignoreclean();
31028891Swosch	exit(rval);
3111590Srgrimes}
3121590Srgrimes
31332097Sjkh
3141590Srgrimestypedef struct _ID {
3151590Srgrimes	dev_t	dev;
3161590Srgrimes	ino_t	inode;
3171590Srgrimes} ID;
3181590Srgrimes
31932097Sjkh
3201590Srgrimesint
3211590Srgrimeslinkchk(p)
3221590Srgrimes	FTSENT *p;
3231590Srgrimes{
3241590Srgrimes	static ID *files;
3251590Srgrimes	static int maxfiles, nfiles;
3261590Srgrimes	ID *fp, *start;
3271590Srgrimes	ino_t ino;
3281590Srgrimes	dev_t dev;
3291590Srgrimes
3301590Srgrimes	ino = p->fts_statp->st_ino;
3311590Srgrimes	dev = p->fts_statp->st_dev;
3321590Srgrimes	if ((start = files) != NULL)
3331590Srgrimes		for (fp = start + nfiles - 1; fp >= start; --fp)
3341590Srgrimes			if (ino == fp->inode && dev == fp->dev)
3351590Srgrimes				return (1);
3361590Srgrimes
3371590Srgrimes	if (nfiles == maxfiles && (files = realloc((char *)files,
3381590Srgrimes	    (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL)
33958601Scharnier		errx(1, "can't allocate memory");
3401590Srgrimes	files[nfiles].inode = ino;
3411590Srgrimes	files[nfiles].dev = dev;
3421590Srgrimes	++nfiles;
3431590Srgrimes	return (0);
3441590Srgrimes}
3451590Srgrimes
34656597Smharo/*
34756597Smharo * Output in "human-readable" format.  Uses 3 digits max and puts
34856597Smharo * unit suffixes at the end.  Makes output compact and easy to read,
34956597Smharo * especially on huge disks.
35056597Smharo *
35156597Smharo */
35256597Smharounit_t
35356597Smharounit_adjust(val)
35456597Smharo	double *val;
35556597Smharo{
35656597Smharo	double abval;
35756597Smharo	unit_t unit;
35856597Smharo	unsigned int unit_sz;
35956597Smharo
36056597Smharo	abval = fabs(*val);
36156597Smharo
36256597Smharo	unit_sz = abval ? ilogb(abval) / 10 : 0;
36356597Smharo
36456597Smharo	if (unit_sz >= UNIT_MAX) {
36556597Smharo		unit = NONE;
36656597Smharo	} else {
36756597Smharo		unit = unitp[unit_sz];
36856597Smharo		*val /= (double)valp[unit_sz];
36956597Smharo	}
37056597Smharo
37156597Smharo	return (unit);
37256597Smharo}
37356597Smharo
37456597Smharovoid
37556597Smharoprthumanval(bytes)
37656597Smharo	double bytes;
37756597Smharo{
37856597Smharo	unit_t unit;
37956597Smharo
38056597Smharo	bytes *= 512;
38156597Smharo	unit = unit_adjust(&bytes);
38256597Smharo
38356597Smharo	if (bytes == 0)
38456597Smharo		(void)printf("  0B");
38556597Smharo	else if (bytes > 10)
38656597Smharo		(void)printf("%3.0f%c", bytes, "BKMGTPE"[unit]);
38756597Smharo	else
38856597Smharo		(void)printf("%3.1f%c", bytes, "BKMGTPE"[unit]);
38956597Smharo}
39056597Smharo
39127099Scharnierstatic void
3921590Srgrimesusage()
3931590Srgrimes{
3941590Srgrimes	(void)fprintf(stderr,
39578158Sroam		"usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k] [-x] [-I mask] [file ...]\n");
39656597Smharo	exit(EX_USAGE);
3971590Srgrimes}
39878158Sroam
39978158Sroamvoid
40078158Sroamignoreadd(mask)
40178158Sroam	const char *mask;
40278158Sroam{
40378158Sroam	struct ignentry *ign;
40478158Sroam
40578158Sroam	ign = calloc(1, sizeof(*ign));
40678158Sroam	if (ign == NULL)
40778158Sroam		errx(1, "cannot allocate memory");
40878158Sroam	ign->mask = strdup(mask);
40978158Sroam	if (ign->mask == NULL)
41078158Sroam		errx(1, "cannot allocate memory");
41178158Sroam	SLIST_INSERT_HEAD(&ignores, ign, next);
41278158Sroam}
41378158Sroam
41478158Sroamvoid
41578158Sroamignoreclean()
41678158Sroam{
41778158Sroam	struct ignentry *ign;
41878158Sroam
41978158Sroam	while (!SLIST_EMPTY(&ignores)) {
42078158Sroam		ign = SLIST_FIRST(&ignores);
42178158Sroam		SLIST_REMOVE_HEAD(&ignores, next);
42278158Sroam		free(ign->mask);
42378158Sroam		free(ign);
42478158Sroam	}
42578158Sroam}
42678158Sroam
42778158Sroamint
42878158Sroamignorep(ent)
42978158Sroam	FTSENT *ent;
43078158Sroam{
43178158Sroam	struct ignentry *ign;
43278158Sroam
43378158Sroam	SLIST_FOREACH(ign, &ignores, next)
43478158Sroam		if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH)
43578158Sroam			return 1;
43678158Sroam	return 0;
43778158Sroam}
438