1 /* 2 * Copyright (c) 1989, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Chris Newcomb. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 static const char copyright[] = 39 "@(#) Copyright (c) 1989, 1993, 1994\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41 #endif /* not lint */ 42 43 #ifndef lint 44 #if 0 45 static const char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; 46 #endif 47 static const char rcsid[] = "$FreeBSD$"; 48 #endif /* not lint */ 49 50 51 #include <sys/param.h> 52 #include <sys/stat.h> 53 54 #include <dirent.h> 55 #include <err.h> 56 #include <errno.h> 57 #include <fts.h> 58 #include <math.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <sysexits.h> 63 #include <unistd.h> 64 65 #define KILO_SZ(n) (n) 66 #define MEGA_SZ(n) ((n) * (n)) 67 #define GIGA_SZ(n) ((n) * (n) * (n)) 68 #define TERA_SZ(n) ((n) * (n) * (n) * (n)) 69 #define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) 70 71 #define KILO_2_SZ (KILO_SZ(1024ULL)) 72 #define MEGA_2_SZ (MEGA_SZ(1024ULL)) 73 #define GIGA_2_SZ (GIGA_SZ(1024ULL)) 74 #define TERA_2_SZ (TERA_SZ(1024ULL)) 75 #define PETA_2_SZ (PETA_SZ(1024ULL)) 76 77 #define KILO_SI_SZ (KILO_SZ(1000ULL)) 78 #define MEGA_SI_SZ (MEGA_SZ(1000ULL)) 79 #define GIGA_SI_SZ (GIGA_SZ(1000ULL)) 80 #define TERA_SI_SZ (TERA_SZ(1000ULL)) 81 #define PETA_SI_SZ (PETA_SZ(1000ULL)) 82 83 unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ}; 84 unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; 85 unsigned long long *valp; 86 87 typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t; 88 89 int unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA }; 90 91 int linkchk __P((FTSENT *)); 92 static void usage __P((void)); 93 void prthumanval __P((double)); 94 unit_t unit_adjust __P((double *)); 95 96 int 97 main(argc, argv) 98 int argc; 99 char *argv[]; 100 { 101 FTS *fts; 102 FTSENT *p; 103 long blocksize, savednumber = 0; 104 int ftsoptions; 105 int listall; 106 int depth; 107 int Hflag, Lflag, Pflag, aflag, sflag, dflag, cflag, hflag, ch, notused, rval; 108 char **save; 109 110 Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0; 111 112 save = argv; 113 ftsoptions = 0; 114 depth = INT_MAX; 115 116 while ((ch = getopt(argc, argv, "HLPasd:chkrx")) != -1) 117 switch (ch) { 118 case 'H': 119 Hflag = 1; 120 break; 121 case 'L': 122 if (Pflag) 123 usage(); 124 Lflag = 1; 125 break; 126 case 'P': 127 if (Lflag) 128 usage(); 129 Pflag = 1; 130 break; 131 case 'a': 132 aflag = 1; 133 break; 134 case 's': 135 sflag = 1; 136 break; 137 case 'd': 138 dflag = 1; 139 errno = 0; 140 depth = atoi(optarg); 141 if (errno == ERANGE || depth < 0) { 142 (void) fprintf(stderr, "Invalid argument to option d: %s\n", optarg); 143 usage(); 144 } 145 break; 146 case 'c': 147 cflag = 1; 148 break; 149 case 'h': 150 putenv("BLOCKSIZE=512"); 151 hflag = 1; 152 valp = vals_base2; 153 break; 154 case 'k': 155 putenv("BLOCKSIZE=1024"); 156 break; 157 case 'r': /* Compatibility. */ 158 break; 159 case 'x': 160 ftsoptions |= FTS_XDEV; 161 break; 162 case '?': 163 default: 164 usage(); 165 } 166 167 argc -= optind; 168 argv += optind; 169 170 /* 171 * XXX 172 * Because of the way that fts(3) works, logical walks will not count 173 * the blocks actually used by symbolic links. We rationalize this by 174 * noting that users computing logical sizes are likely to do logical 175 * copies, so not counting the links is correct. The real reason is 176 * that we'd have to re-implement the kernel's symbolic link traversing 177 * algorithm to get this right. If, for example, you have relative 178 * symbolic links referencing other relative symbolic links, it gets 179 * very nasty, very fast. The bottom line is that it's documented in 180 * the man page, so it's a feature. 181 */ 182 183 if (Hflag + Lflag + Pflag > 1) 184 usage(); 185 186 if (Hflag + Lflag + Pflag == 0) 187 Pflag = 1; /* -P (physical) is default */ 188 189 if (Hflag) 190 ftsoptions |= FTS_COMFOLLOW; 191 192 if (Lflag) 193 ftsoptions |= FTS_LOGICAL; 194 195 if (Pflag) 196 ftsoptions |= FTS_PHYSICAL; 197 198 listall = 0; 199 200 if (aflag) { 201 if (sflag || dflag) 202 usage(); 203 listall = 1; 204 } else if (sflag) { 205 if (dflag) 206 usage(); 207 depth = 0; 208 } 209 210 if (!*argv) { 211 argv = save; 212 argv[0] = "."; 213 argv[1] = NULL; 214 } 215 216 (void) getbsize(¬used, &blocksize); 217 blocksize /= 512; 218 219 rval = 0; 220 221 if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) 222 err(1, "fts_open"); 223 224 while ((p = fts_read(fts)) != NULL) { 225 switch (p->fts_info) { 226 case FTS_D: /* Ignore. */ 227 break; 228 case FTS_DP: 229 p->fts_parent->fts_number += 230 p->fts_number += p->fts_statp->st_blocks; 231 232 if (p->fts_level <= depth) 233 if (hflag) { 234 (void) prthumanval(howmany(savednumber, blocksize)); 235 (void) printf("\t%s\n", p->fts_path); 236 } else { 237 (void) printf("%ld\t%s\n", 238 howmany(p->fts_number, blocksize), 239 p->fts_path); 240 } 241 break; 242 case FTS_DC: /* Ignore. */ 243 break; 244 case FTS_DNR: /* Warn, continue. */ 245 case FTS_ERR: 246 case FTS_NS: 247 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 248 rval = 1; 249 break; 250 default: 251 if (p->fts_statp->st_nlink > 1 && linkchk(p)) 252 break; 253 254 if (listall || p->fts_level == 0) 255 if (hflag) { 256 (void) prthumanval(howmany(p->fts_statp->st_blocks, 257 blocksize)); 258 (void) printf("\t%s\n", p->fts_path); 259 } else { 260 (void) printf("%qd\t%s\n", 261 howmany(p->fts_statp->st_blocks, blocksize), 262 p->fts_path); 263 } 264 265 p->fts_parent->fts_number += p->fts_statp->st_blocks; 266 } 267 savednumber = p->fts_parent->fts_number; 268 } 269 270 if (errno) 271 err(1, "fts_read"); 272 273 if (cflag) 274 if (hflag) { 275 (void) prthumanval(howmany(savednumber, blocksize)); 276 (void) printf("\ttotal\n"); 277 } else { 278 (void) printf("%ld\ttotal\n", howmany(savednumber, blocksize)); 279 } 280 281 exit(rval); 282 } 283 284 285 typedef struct _ID { 286 dev_t dev; 287 ino_t inode; 288 } ID; 289 290 291 int 292 linkchk(p) 293 FTSENT *p; 294 { 295 static ID *files; 296 static int maxfiles, nfiles; 297 ID *fp, *start; 298 ino_t ino; 299 dev_t dev; 300 301 ino = p->fts_statp->st_ino; 302 dev = p->fts_statp->st_dev; 303 if ((start = files) != NULL) 304 for (fp = start + nfiles - 1; fp >= start; --fp) 305 if (ino == fp->inode && dev == fp->dev) 306 return (1); 307 308 if (nfiles == maxfiles && (files = realloc((char *)files, 309 (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL) 310 err(1, "can't allocate memory"); 311 files[nfiles].inode = ino; 312 files[nfiles].dev = dev; 313 ++nfiles; 314 return (0); 315 } 316 317 /* 318 * Output in "human-readable" format. Uses 3 digits max and puts 319 * unit suffixes at the end. Makes output compact and easy to read, 320 * especially on huge disks. 321 * 322 */ 323 unit_t 324 unit_adjust(val) 325 double *val; 326 { 327 double abval; 328 unit_t unit; 329 unsigned int unit_sz; 330 331 abval = fabs(*val); 332 333 unit_sz = abval ? ilogb(abval) / 10 : 0; 334 335 if (unit_sz >= UNIT_MAX) { 336 unit = NONE; 337 } else { 338 unit = unitp[unit_sz]; 339 *val /= (double)valp[unit_sz]; 340 } 341 342 return (unit); 343 } 344 345 void 346 prthumanval(bytes) 347 double bytes; 348 { 349 unit_t unit; 350 351 bytes *= 512; 352 unit = unit_adjust(&bytes); 353 354 if (bytes == 0) 355 (void)printf(" 0B"); 356 else if (bytes > 10) 357 (void)printf("%3.0f%c", bytes, "BKMGTPE"[unit]); 358 else 359 (void)printf("%3.1f%c", bytes, "BKMGTPE"[unit]); 360 } 361 362 static void 363 usage() 364 { 365 (void)fprintf(stderr, 366 "usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k] [-x] [file ...]\n"); 367 exit(EX_USAGE); 368 } 369