du.c revision 56597
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
4756597Smharostatic const char rcsid[] = "$FreeBSD: head/usr.bin/du/du.c 56597 2000-01-25 17:46:59Z mharo $";
481590Srgrimes#endif /* not lint */
491590Srgrimes
5032097Sjkh
511590Srgrimes#include <sys/param.h>
521590Srgrimes#include <sys/stat.h>
531590Srgrimes
541590Srgrimes#include <dirent.h>
551590Srgrimes#include <err.h>
561590Srgrimes#include <errno.h>
571590Srgrimes#include <fts.h>
5856597Smharo#include <math.h>
591590Srgrimes#include <stdio.h>
601590Srgrimes#include <stdlib.h>
611590Srgrimes#include <string.h>
6256597Smharo#include <sysexits.h>
6323693Speter#include <unistd.h>
641590Srgrimes
6556597Smharo#define	KILO_SZ(n) (n)
6656597Smharo#define	MEGA_SZ(n) ((n) * (n))
6756597Smharo#define	GIGA_SZ(n) ((n) * (n) * (n))
6856597Smharo#define	TERA_SZ(n) ((n) * (n) * (n) * (n))
6956597Smharo#define	PETA_SZ(n) ((n) * (n) * (n) * (n) * (n))
7056597Smharo
7156597Smharo#define	KILO_2_SZ (KILO_SZ(1024ULL))
7256597Smharo#define	MEGA_2_SZ (MEGA_SZ(1024ULL))
7356597Smharo#define	GIGA_2_SZ (GIGA_SZ(1024ULL))
7456597Smharo#define	TERA_2_SZ (TERA_SZ(1024ULL))
7556597Smharo#define	PETA_2_SZ (PETA_SZ(1024ULL))
7656597Smharo
7756597Smharo#define	KILO_SI_SZ (KILO_SZ(1000ULL))
7856597Smharo#define	MEGA_SI_SZ (MEGA_SZ(1000ULL))
7956597Smharo#define	GIGA_SI_SZ (GIGA_SZ(1000ULL))
8056597Smharo#define	TERA_SI_SZ (TERA_SZ(1000ULL))
8156597Smharo#define	PETA_SI_SZ (PETA_SZ(1000ULL))
8256597Smharo
8356597Smharounsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ};
8456597Smharounsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
8556597Smharounsigned long long *valp;
8656597Smharo
8756597Smharotypedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t;
8856597Smharo
8956597Smharoint unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA };
9056597Smharo
9132097Sjkhint		linkchk __P((FTSENT *));
9232097Sjkhstatic void	usage __P((void));
9356597Smharovoid		prthumanval __P((double));
9456597Smharounit_t		unit_adjust __P((double *));
951590Srgrimes
961590Srgrimesint
971590Srgrimesmain(argc, argv)
981590Srgrimes	int argc;
991590Srgrimes	char *argv[];
1001590Srgrimes{
10132097Sjkh	FTS		*fts;
10232097Sjkh	FTSENT		*p;
10337952Sdes	long		blocksize, savednumber = 0;
10432097Sjkh	int		ftsoptions;
10532097Sjkh	int		listall;
10632097Sjkh	int		depth;
10756597Smharo	int		Hflag, Lflag, Pflag, aflag, sflag, dflag, cflag, hflag, ch, notused, rval;
10832097Sjkh	char 		**save;
1091590Srgrimes
11056597Smharo	Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0;
11132097Sjkh
1121590Srgrimes	save = argv;
11332097Sjkh	ftsoptions = 0;
11419120Sscrappy	depth = INT_MAX;
11532097Sjkh
11656597Smharo	while ((ch = getopt(argc, argv, "HLPasd:chkrx")) != -1)
1171590Srgrimes		switch (ch) {
11832097Sjkh			case 'H':
11932097Sjkh				Hflag = 1;
12032097Sjkh				break;
12132097Sjkh			case 'L':
12232097Sjkh				if (Pflag)
12332097Sjkh					usage();
12432097Sjkh				Lflag = 1;
12532097Sjkh				break;
12632097Sjkh			case 'P':
12732097Sjkh				if (Lflag)
12832097Sjkh					usage();
12932097Sjkh				Pflag = 1;
13032097Sjkh				break;
13132097Sjkh			case 'a':
13232097Sjkh				aflag = 1;
13332097Sjkh				break;
13432097Sjkh			case 's':
13532097Sjkh				sflag = 1;
13632097Sjkh				break;
13732097Sjkh			case 'd':
13832097Sjkh				dflag = 1;
13932097Sjkh				errno = 0;
14032097Sjkh				depth = atoi(optarg);
14132097Sjkh				if (errno == ERANGE || depth < 0) {
14240926Srnordier					(void) fprintf(stderr, "Invalid argument to option d: %s\n", optarg);
14332097Sjkh					usage();
14432097Sjkh				}
14532097Sjkh				break;
14632097Sjkh			case 'c':
14732097Sjkh				cflag = 1;
14832097Sjkh				break;
14956597Smharo			case 'h':
15056597Smharo				putenv("BLOCKSIZE=512");
15156597Smharo				hflag = 1;
15256597Smharo				valp = vals_base2;
15356597Smharo				break;
15456597Smharo			case 'k':
15556597Smharo				putenv("BLOCKSIZE=1024");
15656597Smharo				break;
15756597Smharo			case 'r':		 /* Compatibility. */
15856597Smharo				break;
15956597Smharo			case 'x':
16056597Smharo				ftsoptions |= FTS_XDEV;
16156597Smharo				break;
16232097Sjkh			case '?':
16332097Sjkh			default:
16419120Sscrappy				usage();
1651590Srgrimes		}
16632097Sjkh
1671590Srgrimes	argc -= optind;
1681590Srgrimes	argv += optind;
1691590Srgrimes
1701590Srgrimes	/*
1711590Srgrimes	 * XXX
1721590Srgrimes	 * Because of the way that fts(3) works, logical walks will not count
1731590Srgrimes	 * the blocks actually used by symbolic links.  We rationalize this by
1741590Srgrimes	 * noting that users computing logical sizes are likely to do logical
1751590Srgrimes	 * copies, so not counting the links is correct.  The real reason is
1761590Srgrimes	 * that we'd have to re-implement the kernel's symbolic link traversing
1771590Srgrimes	 * algorithm to get this right.  If, for example, you have relative
1781590Srgrimes	 * symbolic links referencing other relative symbolic links, it gets
1791590Srgrimes	 * very nasty, very fast.  The bottom line is that it's documented in
1801590Srgrimes	 * the man page, so it's a feature.
1811590Srgrimes	 */
18232097Sjkh
18332097Sjkh	if (Hflag + Lflag + Pflag > 1)
18432097Sjkh		usage();
18532097Sjkh
18632097Sjkh	if (Hflag + Lflag + Pflag == 0)
18732097Sjkh		Pflag = 1;			/* -P (physical) is default */
18832097Sjkh
1891590Srgrimes	if (Hflag)
1901590Srgrimes		ftsoptions |= FTS_COMFOLLOW;
19132097Sjkh
19232097Sjkh	if (Lflag)
1931590Srgrimes		ftsoptions |= FTS_LOGICAL;
1941590Srgrimes
19532097Sjkh	if (Pflag)
19632097Sjkh		ftsoptions |= FTS_PHYSICAL;
19732097Sjkh
19832097Sjkh	listall = 0;
19932097Sjkh
2001590Srgrimes	if (aflag) {
20119120Sscrappy		if (sflag || dflag)
2021590Srgrimes			usage();
20332097Sjkh		listall = 1;
20419120Sscrappy	} else if (sflag) {
20519120Sscrappy		if (dflag)
20619120Sscrappy			usage();
20732097Sjkh		depth = 0;
2081590Srgrimes	}
2091590Srgrimes
2101590Srgrimes	if (!*argv) {
2111590Srgrimes		argv = save;
2121590Srgrimes		argv[0] = ".";
2131590Srgrimes		argv[1] = NULL;
2141590Srgrimes	}
2151590Srgrimes
21632097Sjkh	(void) getbsize(&notused, &blocksize);
2171590Srgrimes	blocksize /= 512;
2181590Srgrimes
21932097Sjkh	rval = 0;
22032097Sjkh
2211590Srgrimes	if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
22232097Sjkh		err(1, "fts_open");
2231590Srgrimes
22432097Sjkh	while ((p = fts_read(fts)) != NULL) {
2251590Srgrimes		switch (p->fts_info) {
22632097Sjkh			case FTS_D:			/* Ignore. */
2271590Srgrimes				break;
22832097Sjkh			case FTS_DP:
22932097Sjkh				p->fts_parent->fts_number +=
23032097Sjkh				    p->fts_number += p->fts_statp->st_blocks;
23132097Sjkh
23232097Sjkh				if (p->fts_level <= depth)
23356597Smharo					if (hflag) {
23456597Smharo						(void) prthumanval(howmany(savednumber, blocksize));
23556597Smharo						(void) printf("\t%s\n", p->fts_path);
23656597Smharo					} else {
23732097Sjkh					(void) printf("%ld\t%s\n",
23832097Sjkh					    howmany(p->fts_number, blocksize),
23932097Sjkh					    p->fts_path);
24056597Smharo					}
24132097Sjkh				break;
24232097Sjkh			case FTS_DC:			/* Ignore. */
24332097Sjkh				break;
24432097Sjkh			case FTS_DNR:			/* Warn, continue. */
24532097Sjkh			case FTS_ERR:
24632097Sjkh			case FTS_NS:
24732097Sjkh				warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
24832097Sjkh				rval = 1;
24932097Sjkh				break;
25032097Sjkh			default:
25132097Sjkh				if (p->fts_statp->st_nlink > 1 && linkchk(p))
25232097Sjkh					break;
25332097Sjkh
25432097Sjkh				if (listall || p->fts_level == 0)
25556597Smharo					if (hflag) {
25656597Smharo						(void) prthumanval(howmany(p->fts_statp->st_blocks,
25756597Smharo							blocksize));
25856597Smharo						(void) printf("\t%s\n", p->fts_path);
25956597Smharo					} else {
26056597Smharo						(void) printf("%qd\t%s\n",
26156597Smharo							howmany(p->fts_statp->st_blocks, blocksize),
26256597Smharo							p->fts_path);
26356597Smharo					}
26432097Sjkh
26532097Sjkh				p->fts_parent->fts_number += p->fts_statp->st_blocks;
2661590Srgrimes		}
26739076Sdes		savednumber = p->fts_parent->fts_number;
26832097Sjkh	}
26932097Sjkh
2701590Srgrimes	if (errno)
2711590Srgrimes		err(1, "fts_read");
27232097Sjkh
27337952Sdes	if (cflag)
27456597Smharo		if (hflag) {
27556597Smharo			(void) prthumanval(howmany(savednumber, blocksize));
27656597Smharo			(void) printf("\ttotal\n");
27756597Smharo		} else {
27856597Smharo			(void) printf("%ld\ttotal\n", howmany(savednumber, blocksize));
27956597Smharo		}
28032097Sjkh
28128891Swosch	exit(rval);
2821590Srgrimes}
2831590Srgrimes
28432097Sjkh
2851590Srgrimestypedef struct _ID {
2861590Srgrimes	dev_t	dev;
2871590Srgrimes	ino_t	inode;
2881590Srgrimes} ID;
2891590Srgrimes
29032097Sjkh
2911590Srgrimesint
2921590Srgrimeslinkchk(p)
2931590Srgrimes	FTSENT *p;
2941590Srgrimes{
2951590Srgrimes	static ID *files;
2961590Srgrimes	static int maxfiles, nfiles;
2971590Srgrimes	ID *fp, *start;
2981590Srgrimes	ino_t ino;
2991590Srgrimes	dev_t dev;
3001590Srgrimes
3011590Srgrimes	ino = p->fts_statp->st_ino;
3021590Srgrimes	dev = p->fts_statp->st_dev;
3031590Srgrimes	if ((start = files) != NULL)
3041590Srgrimes		for (fp = start + nfiles - 1; fp >= start; --fp)
3051590Srgrimes			if (ino == fp->inode && dev == fp->dev)
3061590Srgrimes				return (1);
3071590Srgrimes
3081590Srgrimes	if (nfiles == maxfiles && (files = realloc((char *)files,
3091590Srgrimes	    (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL)
31027099Scharnier		err(1, "can't allocate memory");
3111590Srgrimes	files[nfiles].inode = ino;
3121590Srgrimes	files[nfiles].dev = dev;
3131590Srgrimes	++nfiles;
3141590Srgrimes	return (0);
3151590Srgrimes}
3161590Srgrimes
31756597Smharo/*
31856597Smharo * Output in "human-readable" format.  Uses 3 digits max and puts
31956597Smharo * unit suffixes at the end.  Makes output compact and easy to read,
32056597Smharo * especially on huge disks.
32156597Smharo *
32256597Smharo */
32356597Smharounit_t
32456597Smharounit_adjust(val)
32556597Smharo	double *val;
32656597Smharo{
32756597Smharo	double abval;
32856597Smharo	unit_t unit;
32956597Smharo	unsigned int unit_sz;
33056597Smharo
33156597Smharo	abval = fabs(*val);
33256597Smharo
33356597Smharo	unit_sz = abval ? ilogb(abval) / 10 : 0;
33456597Smharo
33556597Smharo	if (unit_sz >= UNIT_MAX) {
33656597Smharo		unit = NONE;
33756597Smharo	} else {
33856597Smharo		unit = unitp[unit_sz];
33956597Smharo		*val /= (double)valp[unit_sz];
34056597Smharo	}
34156597Smharo
34256597Smharo	return (unit);
34356597Smharo}
34456597Smharo
34556597Smharovoid
34656597Smharoprthumanval(bytes)
34756597Smharo	double bytes;
34856597Smharo{
34956597Smharo	unit_t unit;
35056597Smharo
35156597Smharo	bytes *= 512;
35256597Smharo	unit = unit_adjust(&bytes);
35356597Smharo
35456597Smharo	if (bytes == 0)
35556597Smharo		(void)printf("  0B");
35656597Smharo	else if (bytes > 10)
35756597Smharo		(void)printf("%3.0f%c", bytes, "BKMGTPE"[unit]);
35856597Smharo	else
35956597Smharo		(void)printf("%3.1f%c", bytes, "BKMGTPE"[unit]);
36056597Smharo}
36156597Smharo
36227099Scharnierstatic void
3631590Srgrimesusage()
3641590Srgrimes{
3651590Srgrimes	(void)fprintf(stderr,
36656597Smharo		"usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k] [-x] [file ...]\n");
36756597Smharo	exit(EX_USAGE);
3681590Srgrimes}
369