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