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(¬used, &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