Annotation of src/usr.bin/du/du.c, Revision 1.24
1.24 ! deraadt 1: /* $OpenBSD: du.c,v 1.23 2011/04/27 07:52:11 sobrado 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: #include <sys/types.h>
37: #include <sys/stat.h>
38:
39: #include <dirent.h>
40: #include <err.h>
41: #include <errno.h>
42: #include <fts.h>
43: #include <stdio.h>
44: #include <stdlib.h>
45: #include <string.h>
1.17 otto 46: #include <sys/tree.h>
1.1 deraadt 47: #include <unistd.h>
1.16 otto 48: #include <util.h>
1.1 deraadt 49:
1.14 deraadt 50:
1.11 millert 51: int linkchk(FTSENT *);
52: void prtout(quad_t, char *, int);
53: void usage(void);
1.1 deraadt 54:
55: int
1.13 deraadt 56: main(int argc, char *argv[])
1.1 deraadt 57: {
58: FTS *fts;
59: FTSENT *p;
1.7 pjanzen 60: long blocksize;
61: quad_t totalblocks;
1.1 deraadt 62: int ftsoptions, listdirs, listfiles;
1.19 tedu 63: int Hflag, Lflag, aflag, cflag, hflag, kflag, sflag;
1.7 pjanzen 64: int ch, notused, rval;
1.1 deraadt 65: char **save;
66:
67: save = argv;
1.19 tedu 68: Hflag = Lflag = aflag = cflag = hflag = kflag = sflag = 0;
1.3 millert 69: totalblocks = 0;
1.1 deraadt 70: ftsoptions = FTS_PHYSICAL;
1.7 pjanzen 71: while ((ch = getopt(argc, argv, "HLPachksxr")) != -1)
1.1 deraadt 72: switch (ch) {
73: case 'H':
74: Hflag = 1;
1.19 tedu 75: Lflag = 0;
1.1 deraadt 76: break;
77: case 'L':
78: Lflag = 1;
1.19 tedu 79: Hflag = 0;
1.1 deraadt 80: break;
81: case 'P':
82: Hflag = Lflag = 0;
83: break;
84: case 'a':
85: aflag = 1;
86: break;
1.3 millert 87: case 'c':
88: cflag = 1;
89: break;
1.7 pjanzen 90: case 'h':
91: hflag = 1;
1.20 millert 92: kflag = 0;
1.7 pjanzen 93: break;
1.1 deraadt 94: case 'k':
95: kflag = 1;
1.20 millert 96: hflag = 0;
1.1 deraadt 97: break;
98: case 's':
99: sflag = 1;
100: break;
1.5 deraadt 101: case 'r':
102: break;
1.1 deraadt 103: case 'x':
104: ftsoptions |= FTS_XDEV;
105: break;
106: case '?':
107: default:
108: usage();
109: }
110: argc -= optind;
111: argv += optind;
112:
113: /*
114: * XXX
115: * Because of the way that fts(3) works, logical walks will not count
116: * the blocks actually used by symbolic links. We rationalize this by
117: * noting that users computing logical sizes are likely to do logical
118: * copies, so not counting the links is correct. The real reason is
119: * that we'd have to re-implement the kernel's symbolic link traversing
120: * algorithm to get this right. If, for example, you have relative
121: * symbolic links referencing other relative symbolic links, it gets
122: * very nasty, very fast. The bottom line is that it's documented in
123: * the man page, so it's a feature.
124: */
125: if (Hflag)
126: ftsoptions |= FTS_COMFOLLOW;
127: if (Lflag) {
128: ftsoptions &= ~FTS_PHYSICAL;
129: ftsoptions |= FTS_LOGICAL;
130: }
131:
132: if (aflag) {
133: if (sflag)
134: usage();
135: listdirs = listfiles = 1;
136: } else if (sflag)
137: listdirs = listfiles = 0;
138: else {
139: listfiles = 0;
140: listdirs = 1;
141: }
142:
143: if (!*argv) {
144: argv = save;
145: argv[0] = ".";
146: argv[1] = NULL;
147: }
148:
1.8 pjanzen 149: if (hflag)
150: blocksize = 512;
151: else if (kflag)
152: blocksize = 1024;
153: else
1.1 deraadt 154: (void)getbsize(¬used, &blocksize);
155: blocksize /= 512;
156:
157: if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
1.3 millert 158: err(1, "fts_open");
1.1 deraadt 159:
160: for (rval = 0; (p = fts_read(fts)) != NULL;)
161: switch (p->fts_info) {
162: case FTS_D: /* Ignore. */
163: break;
164: case FTS_DP:
165: p->fts_parent->fts_number +=
166: p->fts_number += p->fts_statp->st_blocks;
1.3 millert 167: if (cflag)
168: totalblocks += p->fts_statp->st_blocks;
1.1 deraadt 169: /*
170: * If listing each directory, or not listing files
171: * or directories and this is post-order of the
172: * root of a traversal, display the total.
173: */
1.21 millert 174: if (listdirs ||
175: (!listfiles && p->fts_level == FTS_ROOTLEVEL)) {
1.23 sobrado 176: prtout((quad_t)howmany(p->fts_number,
177: (unsigned long)blocksize), p->fts_path,
178: hflag);
1.21 millert 179: }
1.1 deraadt 180: break;
181: case FTS_DC: /* Ignore. */
182: break;
183: case FTS_DNR: /* Warn, continue. */
184: case FTS_ERR:
185: case FTS_NS:
186: warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
187: rval = 1;
188: break;
189: default:
190: if (p->fts_statp->st_nlink > 1 && linkchk(p))
191: break;
192: /*
193: * If listing each file, or a non-directory file was
194: * the root of a traversal, display the total.
195: */
1.21 millert 196: if (listfiles || p->fts_level == FTS_ROOTLEVEL)
1.23 sobrado 197: prtout(howmany(p->fts_statp->st_blocks,
198: blocksize), p->fts_path, hflag);
1.1 deraadt 199: p->fts_parent->fts_number += p->fts_statp->st_blocks;
1.3 millert 200: if (cflag)
201: totalblocks += p->fts_statp->st_blocks;
1.1 deraadt 202: }
203: if (errno)
204: err(1, "fts_read");
1.7 pjanzen 205: if (cflag) {
206: prtout((quad_t)howmany(totalblocks, blocksize), "total", hflag);
207: }
1.6 ericj 208: exit(rval);
1.1 deraadt 209: }
210:
1.17 otto 211:
212: struct links_entry {
213: RB_ENTRY(links_entry) entry;
214: struct links_entry *fnext;
215: int links;
216: dev_t dev;
217: ino_t ino;
218: };
219:
1.24 ! deraadt 220: static int
1.17 otto 221: links_cmp(struct links_entry *e1, struct links_entry *e2)
222: {
223: if (e1->dev == e2->dev) {
224: if (e1->ino == e2->ino)
225: return (0);
226: else
227: return (e1->ino < e2->ino ? -1 : 1);
228: }
229: else
230: return (e1->dev < e2->dev ? -1 : 1);
231: }
232:
233: RB_HEAD(ltree, links_entry) links = RB_INITIALIZER(&links);
234:
1.24 ! deraadt 235: RB_GENERATE_STATIC(ltree, links_entry, entry, links_cmp);
1.17 otto 236:
237:
1.1 deraadt 238: int
1.13 deraadt 239: linkchk(FTSENT *p)
1.1 deraadt 240: {
1.17 otto 241: static struct links_entry *free_list = NULL;
242: static int stop_allocating = 0;
243: struct links_entry ltmp, *le;
1.16 otto 244: struct stat *st;
245:
246: st = p->fts_statp;
247:
1.17 otto 248: ltmp.ino = st->st_ino;
249: ltmp.dev = st->st_dev;
1.16 otto 250:
1.17 otto 251: le = RB_FIND(ltree, &links, <mp);
252: if (le != NULL) {
253: /*
254: * Save memory by releasing an entry when we've seen
255: * all of it's links.
256: */
257: if (--le->links <= 0) {
258: RB_REMOVE(ltree, &links, le);
259: /* Recycle this node through the free list */
260: if (stop_allocating) {
1.16 otto 261: free(le);
1.17 otto 262: } else {
263: le->fnext = free_list;
264: free_list = le;
1.16 otto 265: }
266: }
1.17 otto 267: return (1);
1.16 otto 268: }
1.7 pjanzen 269:
1.16 otto 270: if (stop_allocating)
271: return (0);
1.7 pjanzen 272:
1.16 otto 273: /* Add this entry to the links cache. */
274: if (free_list != NULL) {
275: /* Pull a node from the free list if we can. */
276: le = free_list;
1.17 otto 277: free_list = le->fnext;
1.16 otto 278: } else
279: /* Malloc one if we have to. */
280: le = malloc(sizeof(struct links_entry));
1.17 otto 281:
1.16 otto 282: if (le == NULL) {
283: stop_allocating = 1;
284: warnx("No more memory for tracking hard links");
285: return (0);
1.7 pjanzen 286: }
1.17 otto 287:
1.16 otto 288: le->dev = st->st_dev;
289: le->ino = st->st_ino;
290: le->links = st->st_nlink - 1;
1.17 otto 291: le->fnext = NULL;
292:
293: RB_INSERT(ltree, &links, le);
294:
1.16 otto 295: return (0);
1.7 pjanzen 296: }
297:
298: void
1.13 deraadt 299: prtout(quad_t size, char *path, int hflag)
1.7 pjanzen 300: {
301: if (!hflag)
1.9 deraadt 302: (void)printf("%lld\t%s\n", (long long)size, path);
1.7 pjanzen 303: else {
1.16 otto 304: char buf[FMT_SCALED_STRSIZE];
1.7 pjanzen 305:
1.16 otto 306: if (fmt_scaled(size * 512, buf) == 0)
307: (void)printf("%s\t%s\n", buf, path);
1.7 pjanzen 308: else
1.16 otto 309: (void)printf("%lld\t%s\n", (long long)size, path);
1.7 pjanzen 310: }
311: }
312:
1.1 deraadt 313: void
1.13 deraadt 314: usage(void)
1.1 deraadt 315: {
316:
317: (void)fprintf(stderr,
1.18 jmc 318: "usage: du [-a | -s] [-chkrx] [-H | -L | -P] [file ...]\n");
1.1 deraadt 319: exit(1);
320: }