Annotation of src/usr.bin/du/du.c, Revision 1.21
1.21 ! millert 1: /* $OpenBSD: du.c,v 1.20 2009/06/03 15:15:16 millert Exp $ */
1.3 millert 2: /* $NetBSD: du.c,v 1.11 1996/10/18 07:20:35 thorpej Exp $ */
1.1 deraadt 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.
1.12 millert 19: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 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
1.19 tedu 37: static const char copyright[] =
1.1 deraadt 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
1.21 ! millert 46: static const char rcsid[] = "$OpenBSD: du.c,v 1.20 2009/06/03 15:15:16 millert Exp $";
1.1 deraadt 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 <stdio.h>
58: #include <stdlib.h>
59: #include <string.h>
1.17 otto 60: #include <sys/tree.h>
1.1 deraadt 61: #include <unistd.h>
1.16 otto 62: #include <util.h>
1.1 deraadt 63:
1.14 deraadt 64:
1.11 millert 65: int linkchk(FTSENT *);
66: void prtout(quad_t, char *, int);
67: void usage(void);
1.1 deraadt 68:
69: int
1.13 deraadt 70: main(int argc, char *argv[])
1.1 deraadt 71: {
72: FTS *fts;
73: FTSENT *p;
1.7 pjanzen 74: long blocksize;
75: quad_t totalblocks;
1.1 deraadt 76: int ftsoptions, listdirs, listfiles;
1.19 tedu 77: int Hflag, Lflag, aflag, cflag, hflag, kflag, sflag;
1.7 pjanzen 78: int ch, notused, rval;
1.1 deraadt 79: char **save;
80:
81: save = argv;
1.19 tedu 82: Hflag = Lflag = aflag = cflag = hflag = kflag = sflag = 0;
1.3 millert 83: totalblocks = 0;
1.1 deraadt 84: ftsoptions = FTS_PHYSICAL;
1.7 pjanzen 85: while ((ch = getopt(argc, argv, "HLPachksxr")) != -1)
1.1 deraadt 86: switch (ch) {
87: case 'H':
88: Hflag = 1;
1.19 tedu 89: Lflag = 0;
1.1 deraadt 90: break;
91: case 'L':
92: Lflag = 1;
1.19 tedu 93: Hflag = 0;
1.1 deraadt 94: break;
95: case 'P':
96: Hflag = Lflag = 0;
97: break;
98: case 'a':
99: aflag = 1;
100: break;
1.3 millert 101: case 'c':
102: cflag = 1;
103: break;
1.7 pjanzen 104: case 'h':
105: hflag = 1;
1.20 millert 106: kflag = 0;
1.7 pjanzen 107: break;
1.1 deraadt 108: case 'k':
109: kflag = 1;
1.20 millert 110: hflag = 0;
1.1 deraadt 111: break;
112: case 's':
113: sflag = 1;
114: break;
1.5 deraadt 115: case 'r':
116: break;
1.1 deraadt 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:
1.8 pjanzen 163: if (hflag)
164: blocksize = 512;
165: else if (kflag)
166: blocksize = 1024;
167: else
1.1 deraadt 168: (void)getbsize(¬used, &blocksize);
169: blocksize /= 512;
170:
171: if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
1.3 millert 172: err(1, "fts_open");
1.1 deraadt 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;
1.3 millert 181: if (cflag)
182: totalblocks += p->fts_statp->st_blocks;
1.1 deraadt 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: */
1.21 ! millert 188: if (listdirs ||
! 189: (!listfiles && p->fts_level == FTS_ROOTLEVEL)) {
1.7 pjanzen 190: prtout((quad_t)howmany(p->fts_number, blocksize),
191: p->fts_path, hflag);
1.21 ! millert 192: }
1.1 deraadt 193: break;
194: case FTS_DC: /* Ignore. */
195: break;
196: case FTS_DNR: /* Warn, continue. */
197: case FTS_ERR:
198: case FTS_NS:
199: warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
200: rval = 1;
201: break;
202: default:
203: if (p->fts_statp->st_nlink > 1 && linkchk(p))
204: break;
205: /*
206: * If listing each file, or a non-directory file was
207: * the root of a traversal, display the total.
208: */
1.21 ! millert 209: if (listfiles || p->fts_level == FTS_ROOTLEVEL)
1.7 pjanzen 210: prtout(howmany(p->fts_statp->st_blocks, blocksize),
211: p->fts_path, hflag);
1.1 deraadt 212: p->fts_parent->fts_number += p->fts_statp->st_blocks;
1.3 millert 213: if (cflag)
214: totalblocks += p->fts_statp->st_blocks;
1.1 deraadt 215: }
216: if (errno)
217: err(1, "fts_read");
1.7 pjanzen 218: if (cflag) {
219: prtout((quad_t)howmany(totalblocks, blocksize), "total", hflag);
220: }
1.6 ericj 221: exit(rval);
1.1 deraadt 222: }
223:
1.17 otto 224:
225: struct links_entry {
226: RB_ENTRY(links_entry) entry;
227: struct links_entry *fnext;
228: int links;
229: dev_t dev;
230: ino_t ino;
231: };
232:
233: int
234: links_cmp(struct links_entry *e1, struct links_entry *e2)
235: {
236: if (e1->dev == e2->dev) {
237: if (e1->ino == e2->ino)
238: return (0);
239: else
240: return (e1->ino < e2->ino ? -1 : 1);
241: }
242: else
243: return (e1->dev < e2->dev ? -1 : 1);
244: }
245:
246: RB_HEAD(ltree, links_entry) links = RB_INITIALIZER(&links);
247:
248: RB_GENERATE(ltree, links_entry, entry, links_cmp);
249:
250:
1.1 deraadt 251: int
1.13 deraadt 252: linkchk(FTSENT *p)
1.1 deraadt 253: {
1.17 otto 254: static struct links_entry *free_list = NULL;
255: static int stop_allocating = 0;
256: struct links_entry ltmp, *le;
1.16 otto 257: struct stat *st;
258:
259: st = p->fts_statp;
260:
1.17 otto 261: ltmp.ino = st->st_ino;
262: ltmp.dev = st->st_dev;
1.16 otto 263:
1.17 otto 264: le = RB_FIND(ltree, &links, <mp);
265: if (le != NULL) {
266: /*
267: * Save memory by releasing an entry when we've seen
268: * all of it's links.
269: */
270: if (--le->links <= 0) {
271: RB_REMOVE(ltree, &links, le);
272: /* Recycle this node through the free list */
273: if (stop_allocating) {
1.16 otto 274: free(le);
1.17 otto 275: } else {
276: le->fnext = free_list;
277: free_list = le;
1.16 otto 278: }
279: }
1.17 otto 280: return (1);
1.16 otto 281: }
1.7 pjanzen 282:
1.16 otto 283: if (stop_allocating)
284: return (0);
1.7 pjanzen 285:
1.16 otto 286: /* Add this entry to the links cache. */
287: if (free_list != NULL) {
288: /* Pull a node from the free list if we can. */
289: le = free_list;
1.17 otto 290: free_list = le->fnext;
1.16 otto 291: } else
292: /* Malloc one if we have to. */
293: le = malloc(sizeof(struct links_entry));
1.17 otto 294:
1.16 otto 295: if (le == NULL) {
296: stop_allocating = 1;
297: warnx("No more memory for tracking hard links");
298: return (0);
1.7 pjanzen 299: }
1.17 otto 300:
1.16 otto 301: le->dev = st->st_dev;
302: le->ino = st->st_ino;
303: le->links = st->st_nlink - 1;
1.17 otto 304: le->fnext = NULL;
305:
306: RB_INSERT(ltree, &links, le);
307:
1.16 otto 308: return (0);
1.7 pjanzen 309: }
310:
311: void
1.13 deraadt 312: prtout(quad_t size, char *path, int hflag)
1.7 pjanzen 313: {
314: if (!hflag)
1.9 deraadt 315: (void)printf("%lld\t%s\n", (long long)size, path);
1.7 pjanzen 316: else {
1.16 otto 317: char buf[FMT_SCALED_STRSIZE];
1.7 pjanzen 318:
1.16 otto 319: if (fmt_scaled(size * 512, buf) == 0)
320: (void)printf("%s\t%s\n", buf, path);
1.7 pjanzen 321: else
1.16 otto 322: (void)printf("%lld\t%s\n", (long long)size, path);
1.7 pjanzen 323: }
324: }
325:
1.1 deraadt 326: void
1.13 deraadt 327: usage(void)
1.1 deraadt 328: {
329:
330: (void)fprintf(stderr,
1.18 jmc 331: "usage: du [-a | -s] [-chkrx] [-H | -L | -P] [file ...]\n");
1.1 deraadt 332: exit(1);
333: }