1 1.36 shattere /* $NetBSD: du.c,v 1.36 2012/03/11 11:23:20 shattered Exp $ */ 2 1.9 glass 3 1.1 cgd /* 4 1.9 glass * Copyright (c) 1989, 1993, 1994 5 1.9 glass * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * This code is derived from software contributed to Berkeley by 8 1.1 cgd * Chris Newcomb. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.23 agc * 3. Neither the name of the University nor the names of its contributors 19 1.1 cgd * may be used to endorse or promote products derived from this software 20 1.1 cgd * without specific prior written permission. 21 1.1 cgd * 22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 cgd * SUCH DAMAGE. 33 1.1 cgd */ 34 1.1 cgd 35 1.12 lukem #include <sys/cdefs.h> 36 1.1 cgd #ifndef lint 37 1.32 lukem __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ 38 1.32 lukem The Regents of the University of California. All rights reserved."); 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.1 cgd #ifndef lint 42 1.9 glass #if 0 43 1.10 perry static char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; 44 1.9 glass #else 45 1.36 shattere __RCSID("$NetBSD: du.c,v 1.36 2012/03/11 11:23:20 shattered Exp $"); 46 1.9 glass #endif 47 1.1 cgd #endif /* not lint */ 48 1.1 cgd 49 1.34 rmind #include <sys/param.h> 50 1.9 glass #include <sys/types.h> 51 1.9 glass #include <sys/stat.h> 52 1.9 glass 53 1.9 glass #include <dirent.h> 54 1.9 glass #include <err.h> 55 1.9 glass #include <errno.h> 56 1.9 glass #include <fts.h> 57 1.36 shattere #include <inttypes.h> 58 1.18 provos #include <util.h> 59 1.5 jtc #include <stdio.h> 60 1.5 jtc #include <stdlib.h> 61 1.5 jtc #include <string.h> 62 1.5 jtc #include <unistd.h> 63 1.25 dbj #include <limits.h> 64 1.1 cgd 65 1.36 shattere /* Count inodes or file size */ 66 1.36 shattere #define COUNT (iflag ? 1 : p->fts_statp->st_blocks) 67 1.36 shattere 68 1.35 joerg static int linkchk(dev_t, ino_t); 69 1.35 joerg static void prstat(const char *, int64_t); 70 1.35 joerg __dead static void usage(void); 71 1.4 mycroft 72 1.36 shattere static int hflag, iflag; 73 1.35 joerg static long blocksize; 74 1.18 provos 75 1.9 glass int 76 1.26 xtraeme main(int argc, char *argv[]) 77 1.1 cgd { 78 1.9 glass FTS *fts; 79 1.9 glass FTSENT *p; 80 1.19 provos int64_t totalblocks; 81 1.33 dsl int ftsoptions, listfiles; 82 1.30 elad int depth; 83 1.30 elad int Hflag, Lflag, aflag, ch, cflag, dflag, gkmflag, nflag, rval, sflag; 84 1.26 xtraeme const char *noargv[2]; 85 1.1 cgd 86 1.30 elad Hflag = Lflag = aflag = cflag = dflag = gkmflag = nflag = sflag = 0; 87 1.11 thorpej totalblocks = 0; 88 1.1 cgd ftsoptions = FTS_PHYSICAL; 89 1.30 elad depth = INT_MAX; 90 1.36 shattere while ((ch = getopt(argc, argv, "HLPacd:ghikmnrsx")) != -1) 91 1.9 glass switch (ch) { 92 1.9 glass case 'H': 93 1.9 glass Hflag = 1; 94 1.27 liamjfoy Lflag = 0; 95 1.9 glass break; 96 1.9 glass case 'L': 97 1.9 glass Lflag = 1; 98 1.27 liamjfoy Hflag = 0; 99 1.9 glass break; 100 1.9 glass case 'P': 101 1.9 glass Hflag = Lflag = 0; 102 1.9 glass break; 103 1.1 cgd case 'a': 104 1.1 cgd aflag = 1; 105 1.1 cgd break; 106 1.11 thorpej case 'c': 107 1.11 thorpej cflag = 1; 108 1.11 thorpej break; 109 1.30 elad case 'd': 110 1.30 elad dflag = 1; 111 1.30 elad depth = atoi(optarg); 112 1.30 elad if (depth < 0 || depth > SHRT_MAX) { 113 1.30 elad warnx("invalid argument to option d: %s", 114 1.30 elad optarg); 115 1.30 elad usage(); 116 1.30 elad } 117 1.30 elad break; 118 1.20 grant case 'g': 119 1.20 grant blocksize = 1024 * 1024 * 1024; 120 1.20 grant gkmflag = 1; 121 1.20 grant break; 122 1.18 provos case 'h': 123 1.18 provos hflag = 1; 124 1.18 provos break; 125 1.36 shattere case 'i': 126 1.36 shattere iflag = 1; 127 1.36 shattere break; 128 1.1 cgd case 'k': 129 1.4 mycroft blocksize = 1024; 130 1.20 grant gkmflag = 1; 131 1.14 kleink break; 132 1.16 hubertf case 'm': 133 1.16 hubertf blocksize = 1024 * 1024; 134 1.20 grant gkmflag = 1; 135 1.16 hubertf break; 136 1.24 simonb case 'n': 137 1.24 simonb nflag = 1; 138 1.24 simonb break; 139 1.14 kleink case 'r': 140 1.1 cgd break; 141 1.1 cgd case 's': 142 1.1 cgd sflag = 1; 143 1.1 cgd break; 144 1.1 cgd case 'x': 145 1.1 cgd ftsoptions |= FTS_XDEV; 146 1.1 cgd break; 147 1.1 cgd case '?': 148 1.1 cgd default: 149 1.1 cgd usage(); 150 1.1 cgd } 151 1.9 glass argc -= optind; 152 1.1 cgd argv += optind; 153 1.1 cgd 154 1.9 glass /* 155 1.9 glass * XXX 156 1.9 glass * Because of the way that fts(3) works, logical walks will not count 157 1.9 glass * the blocks actually used by symbolic links. We rationalize this by 158 1.9 glass * noting that users computing logical sizes are likely to do logical 159 1.9 glass * copies, so not counting the links is correct. The real reason is 160 1.9 glass * that we'd have to re-implement the kernel's symbolic link traversing 161 1.9 glass * algorithm to get this right. If, for example, you have relative 162 1.9 glass * symbolic links referencing other relative symbolic links, it gets 163 1.9 glass * very nasty, very fast. The bottom line is that it's documented in 164 1.9 glass * the man page, so it's a feature. 165 1.9 glass */ 166 1.9 glass if (Hflag) 167 1.9 glass ftsoptions |= FTS_COMFOLLOW; 168 1.9 glass if (Lflag) { 169 1.9 glass ftsoptions &= ~FTS_PHYSICAL; 170 1.9 glass ftsoptions |= FTS_LOGICAL; 171 1.9 glass } 172 1.9 glass 173 1.33 dsl listfiles = 0; 174 1.1 cgd if (aflag) { 175 1.30 elad if (sflag || dflag) 176 1.1 cgd usage(); 177 1.33 dsl listfiles = 1; 178 1.30 elad } else if (sflag) { 179 1.30 elad if (dflag) 180 1.30 elad usage(); 181 1.33 dsl depth = 0; 182 1.1 cgd } 183 1.1 cgd 184 1.1 cgd if (!*argv) { 185 1.21 simonb noargv[0] = "."; 186 1.21 simonb noargv[1] = NULL; 187 1.28 skrll argv = __UNCONST(noargv); 188 1.1 cgd } 189 1.1 cgd 190 1.20 grant if (!gkmflag) 191 1.22 simonb (void)getbsize(NULL, &blocksize); 192 1.4 mycroft blocksize /= 512; 193 1.4 mycroft 194 1.4 mycroft if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) 195 1.12 lukem err(1, "fts_open `%s'", *argv); 196 1.1 cgd 197 1.24 simonb for (rval = 0; (p = fts_read(fts)) != NULL;) { 198 1.24 simonb if (nflag) { 199 1.24 simonb switch (p->fts_info) { 200 1.24 simonb case FTS_NS: 201 1.24 simonb case FTS_SLNONE: 202 1.24 simonb /* nothing */ 203 1.24 simonb break; 204 1.24 simonb default: 205 1.24 simonb if (p->fts_statp->st_flags & UF_NODUMP) { 206 1.24 simonb fts_set(fts, p, FTS_SKIP); 207 1.24 simonb continue; 208 1.24 simonb } 209 1.24 simonb } 210 1.24 simonb } 211 1.9 glass switch (p->fts_info) { 212 1.9 glass case FTS_D: /* Ignore. */ 213 1.1 cgd break; 214 1.1 cgd case FTS_DP: 215 1.1 cgd p->fts_parent->fts_number += 216 1.36 shattere p->fts_number += COUNT; 217 1.11 thorpej if (cflag) 218 1.36 shattere totalblocks += COUNT; 219 1.1 cgd /* 220 1.1 cgd * If listing each directory, or not listing files 221 1.1 cgd * or directories and this is post-order of the 222 1.1 cgd * root of a traversal, display the total. 223 1.1 cgd */ 224 1.33 dsl if (p->fts_level <= depth 225 1.33 dsl || (!listfiles && !p->fts_level)) 226 1.18 provos prstat(p->fts_path, p->fts_number); 227 1.1 cgd break; 228 1.9 glass case FTS_DC: /* Ignore. */ 229 1.9 glass break; 230 1.9 glass case FTS_DNR: /* Warn, continue. */ 231 1.1 cgd case FTS_ERR: 232 1.1 cgd case FTS_NS: 233 1.10 perry warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 234 1.10 perry rval = 1; 235 1.1 cgd break; 236 1.1 cgd default: 237 1.25 dbj if (p->fts_statp->st_nlink > 1 && 238 1.25 dbj linkchk(p->fts_statp->st_dev, p->fts_statp->st_ino)) 239 1.1 cgd break; 240 1.1 cgd /* 241 1.1 cgd * If listing each file, or a non-directory file was 242 1.1 cgd * the root of a traversal, display the total. 243 1.1 cgd */ 244 1.1 cgd if (listfiles || !p->fts_level) 245 1.36 shattere prstat(p->fts_path, COUNT); 246 1.36 shattere p->fts_parent->fts_number += COUNT; 247 1.11 thorpej if (cflag) 248 1.36 shattere totalblocks += COUNT; 249 1.1 cgd } 250 1.24 simonb } 251 1.4 mycroft if (errno) 252 1.9 glass err(1, "fts_read"); 253 1.11 thorpej if (cflag) 254 1.18 provos prstat("total", totalblocks); 255 1.18 provos exit(rval); 256 1.1 cgd } 257 1.1 cgd 258 1.35 joerg static void 259 1.18 provos prstat(const char *fname, int64_t blocks) 260 1.18 provos { 261 1.36 shattere if (iflag) { 262 1.36 shattere (void)printf("%" PRId64 "\t%s\n", blocks, fname); 263 1.36 shattere return; 264 1.36 shattere } 265 1.36 shattere 266 1.18 provos if (hflag) { 267 1.18 provos char buf[5]; 268 1.18 provos int64_t sz = blocks * 512; 269 1.18 provos 270 1.18 provos humanize_number(buf, sizeof(buf), sz, "", HN_AUTOSCALE, 271 1.18 provos HN_B | HN_NOSPACE | HN_DECIMAL); 272 1.18 provos 273 1.18 provos (void)printf("%s\t%s\n", buf, fname); 274 1.18 provos } else 275 1.36 shattere (void)printf("%" PRId64 "\t%s\n", 276 1.36 shattere howmany(blocks, (int64_t)blocksize), 277 1.18 provos fname); 278 1.18 provos } 279 1.18 provos 280 1.35 joerg static int 281 1.25 dbj linkchk(dev_t dev, ino_t ino) 282 1.25 dbj { 283 1.25 dbj static struct entry { 284 1.25 dbj dev_t dev; 285 1.25 dbj ino_t ino; 286 1.25 dbj } *htable; 287 1.25 dbj static int htshift; /* log(allocated size) */ 288 1.25 dbj static int htmask; /* allocated size - 1 */ 289 1.25 dbj static int htused; /* 2*number of insertions */ 290 1.25 dbj static int sawzero; /* Whether zero is in table or not */ 291 1.25 dbj int h, h2; 292 1.25 dbj uint64_t tmp; 293 1.25 dbj /* this constant is (1<<64)/((1+sqrt(5))/2) 294 1.25 dbj * aka (word size)/(golden ratio) 295 1.25 dbj */ 296 1.25 dbj const uint64_t HTCONST = 11400714819323198485ULL; 297 1.25 dbj const int HTBITS = CHAR_BIT * sizeof(tmp); 298 1.25 dbj 299 1.25 dbj /* Never store zero in hashtable */ 300 1.25 dbj if (dev == 0 && ino == 0) { 301 1.25 dbj h = sawzero; 302 1.25 dbj sawzero = 1; 303 1.25 dbj return h; 304 1.25 dbj } 305 1.25 dbj 306 1.25 dbj /* Extend hash table if necessary, keep load under 0.5 */ 307 1.25 dbj if (htused<<1 >= htmask) { 308 1.25 dbj struct entry *ohtable; 309 1.25 dbj 310 1.25 dbj if (!htable) 311 1.25 dbj htshift = 10; /* starting hashtable size */ 312 1.25 dbj else 313 1.25 dbj htshift++; /* exponential hashtable growth */ 314 1.25 dbj 315 1.25 dbj htmask = (1 << htshift) - 1; 316 1.25 dbj htused = 0; 317 1.25 dbj 318 1.25 dbj ohtable = htable; 319 1.25 dbj htable = calloc(htmask+1, sizeof(*htable)); 320 1.25 dbj if (!htable) 321 1.25 dbj err(1, "calloc"); 322 1.25 dbj 323 1.25 dbj /* populate newly allocated hashtable */ 324 1.25 dbj if (ohtable) { 325 1.25 dbj int i; 326 1.25 dbj for (i = 0; i <= htmask>>1; i++) 327 1.25 dbj if (ohtable[i].ino || ohtable[i].dev) 328 1.25 dbj linkchk(ohtable[i].dev, ohtable[i].ino); 329 1.25 dbj free(ohtable); 330 1.25 dbj } 331 1.25 dbj } 332 1.18 provos 333 1.25 dbj /* multiplicative hashing */ 334 1.25 dbj tmp = dev; 335 1.25 dbj tmp <<= HTBITS>>1; 336 1.25 dbj tmp |= ino; 337 1.25 dbj tmp *= HTCONST; 338 1.25 dbj h = tmp >> (HTBITS - htshift); 339 1.25 dbj h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */ 340 1.25 dbj 341 1.25 dbj /* open address hashtable search with double hash probing */ 342 1.25 dbj while (htable[h].ino || htable[h].dev) { 343 1.25 dbj if ((htable[h].ino == ino) && (htable[h].dev == dev)) 344 1.25 dbj return 1; 345 1.25 dbj h = (h + h2) & htmask; 346 1.25 dbj } 347 1.1 cgd 348 1.25 dbj /* Insert the current entry into hashtable */ 349 1.25 dbj htable[h].dev = dev; 350 1.25 dbj htable[h].ino = ino; 351 1.25 dbj htused++; 352 1.25 dbj return 0; 353 1.1 cgd } 354 1.1 cgd 355 1.35 joerg static void 356 1.26 xtraeme usage(void) 357 1.1 cgd { 358 1.9 glass 359 1.9 glass (void)fprintf(stderr, 360 1.36 shattere "usage: du [-H | -L | -P] [-a | -d depth | -s] [-cghikmnrx] [file ...]\n"); 361 1.4 mycroft exit(1); 362 1.1 cgd } 363