~cfuhrman/+junk/netbsd-othersrc-trunk

« back to all changes in this revision

Viewing changes to usr.bin/du/du.c

  • Committer: stacktic
  • Date: 2009-03-23 21:04:00 UTC
  • Revision ID: svn-v4:288d5a72-fed7-e111-8680-000c29dcf8fe:trunk:1946
ImportedĀ fs-utilsĀ binaries

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*      $NetBSD$        */
 
2
/* from */
 
3
/*      NetBSD: du.c,v 1.33 2008/07/30 22:03:40 dsl Exp */
 
4
 
 
5
/*
 
6
 * Copyright (c) 1989, 1993, 1994
 
7
 *      The Regents of the University of California.  All rights reserved.
 
8
 *
 
9
 * This code is derived from software contributed to Berkeley by
 
10
 * Chris Newcomb.
 
11
 *
 
12
 * Redistribution and use in source and binary forms, with or without
 
13
 * modification, are permitted provided that the following conditions
 
14
 * are met:
 
15
 * 1. Redistributions of source code must retain the above copyright
 
16
 *    notice, this list of conditions and the following disclaimer.
 
17
 * 2. Redistributions in binary form must reproduce the above copyright
 
18
 *    notice, this list of conditions and the following disclaimer in the
 
19
 *    documentation and/or other materials provided with the distribution.
 
20
 * 3. 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
#include <sys/cdefs.h>
 
38
#ifndef lint
 
39
__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\
 
40
 The Regents of the University of California.  All rights reserved.");
 
41
#endif /* not lint */
 
42
 
 
43
#ifndef lint
 
44
#if 0
 
45
static char sccsid[] = "@(#)du.c        8.5 (Berkeley) 5/4/95";
 
46
#else
 
47
__RCSID("$NetBSD: du.c,v 1.33 2008/07/30 22:03:40 dsl Exp $");
 
48
#endif
 
49
#endif /* not lint */
 
50
 
 
51
#include <sys/types.h>
 
52
#include <sys/stat.h>
 
53
 
 
54
#include <dirent.h>
 
55
#include <err.h>
 
56
#include <errno.h>
 
57
#ifndef USE_UKFS
 
58
#include <fts.h>
 
59
#endif
 
60
#include <util.h>
 
61
#include <stdio.h>
 
62
#include <stdlib.h>
 
63
#include <string.h>
 
64
#include <unistd.h>
 
65
#include <limits.h>
 
66
 
 
67
#ifdef USE_UKFS
 
68
#include <rump/ukfs.h>
 
69
 
 
70
#include <fts2fsufts.h>
 
71
#include <fsu_mount.h>
 
72
 
 
73
DECLARE_UKFS(ukfs)
 
74
#endif
 
75
 
 
76
int     linkchk(dev_t, ino_t);
 
77
void    prstat(const char *, int64_t);
 
78
void    usage(void);
 
79
 
 
80
int hflag;
 
81
long blocksize;
 
82
 
 
83
int
 
84
main(int argc, char *argv[])
 
85
{
 
86
        FTS *fts;
 
87
        FTSENT *p;
 
88
        int64_t totalblocks;
 
89
        int ftsoptions, listfiles;
 
90
        int depth;
 
91
        int Hflag, Lflag, aflag, ch, cflag, dflag, gkmflag, nflag, rval, sflag;
 
92
        const char *noargv[2];
 
93
 
 
94
        setprogname(argv[0]);
 
95
 
 
96
#ifdef USE_UKFS
 
97
        FSU_MOUNT(argc, argv, ukfs);
 
98
#endif
 
99
 
 
100
        Hflag = Lflag = aflag = cflag = dflag = gkmflag = nflag = sflag = 0;
 
101
        totalblocks = 0;
 
102
        ftsoptions = FTS_PHYSICAL;
 
103
        depth = INT_MAX;
 
104
        while ((ch = getopt(argc, argv, "HLPacd:ghkmnrsx")) != -1)
 
105
                switch (ch) {
 
106
                case 'H':
 
107
                        Hflag = 1;
 
108
                        Lflag = 0;
 
109
                        break;
 
110
                case 'L':
 
111
                        Lflag = 1;
 
112
                        Hflag = 0;
 
113
                        break;
 
114
                case 'P':
 
115
                        Hflag = Lflag = 0;
 
116
                        break;
 
117
                case 'a':
 
118
                        aflag = 1;
 
119
                        break;
 
120
                case 'c':
 
121
                        cflag = 1;
 
122
                        break;
 
123
                case 'd':
 
124
                        dflag = 1;
 
125
                        depth = atoi(optarg);
 
126
                        if (depth < 0 || depth > SHRT_MAX) {
 
127
                                warnx("invalid argument to option d: %s",
 
128
                                        optarg);
 
129
                                usage();
 
130
                        }
 
131
                        break;
 
132
                case 'g':
 
133
                        blocksize = 1024 * 1024 * 1024;
 
134
                        gkmflag = 1;
 
135
                        break;
 
136
                case 'h':
 
137
                        hflag = 1;
 
138
                        break;
 
139
                case 'k':
 
140
                        blocksize = 1024;
 
141
                        gkmflag = 1;
 
142
                        break;
 
143
                case 'm':
 
144
                        blocksize = 1024 * 1024;
 
145
                        gkmflag = 1;
 
146
                        break;
 
147
                case 'n':
 
148
                        nflag = 1;
 
149
                        break;
 
150
                case 'r':
 
151
                        break;
 
152
                case 's':
 
153
                        sflag = 1;
 
154
                        break;
 
155
                case 'x':
 
156
                        ftsoptions |= FTS_XDEV;
 
157
                        break;
 
158
                case '?':
 
159
                default:
 
160
                        usage();
 
161
                }
 
162
        argc -= optind;
 
163
        argv += optind;
 
164
 
 
165
        /*
 
166
         * XXX
 
167
         * Because of the way that fts(3) works, logical walks will not count
 
168
         * the blocks actually used by symbolic links.  We rationalize this by
 
169
         * noting that users computing logical sizes are likely to do logical
 
170
         * copies, so not counting the links is correct.  The real reason is
 
171
         * that we'd have to re-implement the kernel's symbolic link traversing
 
172
         * algorithm to get this right.  If, for example, you have relative
 
173
         * symbolic links referencing other relative symbolic links, it gets
 
174
         * very nasty, very fast.  The bottom line is that it's documented in
 
175
         * the man page, so it's a feature.
 
176
         */
 
177
        if (Hflag)
 
178
                ftsoptions |= FTS_COMFOLLOW;
 
179
        if (Lflag) {
 
180
                ftsoptions &= ~FTS_PHYSICAL;
 
181
                ftsoptions |= FTS_LOGICAL;
 
182
        }
 
183
 
 
184
        listfiles = 0;
 
185
        if (aflag) {
 
186
                if (sflag || dflag)
 
187
                        usage();
 
188
                listfiles = 1;
 
189
        } else if (sflag) {
 
190
                if (dflag)
 
191
                        usage();
 
192
                depth = 0;
 
193
        }
 
194
 
 
195
        if (!*argv) {
 
196
                noargv[0] = ".";
 
197
                noargv[1] = NULL;
 
198
                argv = __UNCONST(noargv);
 
199
        }
 
200
 
 
201
        if (!gkmflag)
 
202
                (void)getbsize(NULL, &blocksize);
 
203
        blocksize /= 512;
 
204
 
 
205
        if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
 
206
                err(1, "fts_open `%s'", *argv);
 
207
 
 
208
        for (rval = 0; (p = fts_read(fts)) != NULL;) {
 
209
                if (nflag) {
 
210
                        switch (p->fts_info) {
 
211
                        case FTS_NS:
 
212
                        case FTS_SLNONE:
 
213
                                /* nothing */
 
214
                                break;
 
215
                        default:
 
216
                                if (p->fts_statp->st_flags & UF_NODUMP) {
 
217
                                        fts_set(fts, p, FTS_SKIP);
 
218
                                        continue;
 
219
                                }
 
220
                        }
 
221
                }
 
222
                switch (p->fts_info) {
 
223
                case FTS_D:                     /* Ignore. */
 
224
                        break;
 
225
                case FTS_DP:
 
226
                        p->fts_parent->fts_number +=
 
227
                            p->fts_number += p->fts_statp->st_blocks;
 
228
                        if (cflag)
 
229
                                totalblocks += p->fts_statp->st_blocks;
 
230
                        /*
 
231
                         * If listing each directory, or not listing files
 
232
                         * or directories and this is post-order of the
 
233
                         * root of a traversal, display the total.
 
234
                         */
 
235
                        if (p->fts_level <= depth
 
236
                            || (!listfiles && !p->fts_level))
 
237
                                prstat(p->fts_path, p->fts_number);
 
238
                        break;
 
239
                case FTS_DC:                    /* Ignore. */
 
240
                        break;
 
241
                case FTS_DNR:                   /* Warn, continue. */
 
242
                case FTS_ERR:
 
243
                case FTS_NS:
 
244
                        warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
 
245
                        rval = 1;
 
246
                        break;
 
247
                default:
 
248
                        if (p->fts_statp->st_nlink > 1 &&
 
249
                            linkchk(p->fts_statp->st_dev, p->fts_statp->st_ino))
 
250
                                break;
 
251
                        /*
 
252
                         * If listing each file, or a non-directory file was
 
253
                         * the root of a traversal, display the total.
 
254
                         */
 
255
                        if (listfiles || !p->fts_level)
 
256
                                prstat(p->fts_path, p->fts_statp->st_blocks);
 
257
                        p->fts_parent->fts_number += p->fts_statp->st_blocks;
 
258
                        if (cflag)
 
259
                                totalblocks += p->fts_statp->st_blocks;
 
260
                }
 
261
        }
 
262
        if (errno)
 
263
                err(1, "fts_read");
 
264
        if (cflag)
 
265
                prstat("total", totalblocks);
 
266
        exit(rval);
 
267
}
 
268
 
 
269
void
 
270
prstat(const char *fname, int64_t blocks)
 
271
{
 
272
        if (hflag) {
 
273
                char buf[5];
 
274
                int64_t sz = blocks * 512;
 
275
 
 
276
                humanize_number(buf, sizeof(buf), sz, "", HN_AUTOSCALE,
 
277
                    HN_B | HN_NOSPACE | HN_DECIMAL);
 
278
 
 
279
                (void)printf("%s\t%s\n", buf, fname);
 
280
        } else
 
281
                (void)printf("%lld\t%s\n",
 
282
                    (long long)howmany(blocks, (int64_t)blocksize),
 
283
                    fname);
 
284
}
 
285
 
 
286
int
 
287
linkchk(dev_t dev, ino_t ino)
 
288
{
 
289
        static struct entry {
 
290
                dev_t   dev;
 
291
                ino_t   ino;
 
292
        } *htable;
 
293
        static int htshift;  /* log(allocated size) */
 
294
        static int htmask;   /* allocated size - 1 */
 
295
        static int htused;   /* 2*number of insertions */
 
296
        static int sawzero;  /* Whether zero is in table or not */
 
297
        int h, h2;
 
298
        uint64_t tmp;
 
299
        /* this constant is (1<<64)/((1+sqrt(5))/2)
 
300
         * aka (word size)/(golden ratio)
 
301
         */
 
302
        const uint64_t HTCONST = 11400714819323198485ULL;
 
303
        const int HTBITS = CHAR_BIT * sizeof(tmp);
 
304
 
 
305
        /* Never store zero in hashtable */
 
306
        if (dev == 0 && ino == 0) {
 
307
                h = sawzero;
 
308
                sawzero = 1;
 
309
                return h;
 
310
        }
 
311
 
 
312
        /* Extend hash table if necessary, keep load under 0.5 */
 
313
        if (htused<<1 >= htmask) {
 
314
                struct entry *ohtable;
 
315
 
 
316
                if (!htable)
 
317
                        htshift = 10;   /* starting hashtable size */
 
318
                else
 
319
                        htshift++;   /* exponential hashtable growth */
 
320
 
 
321
                htmask  = (1 << htshift) - 1;
 
322
                htused = 0;
 
323
 
 
324
                ohtable = htable;
 
325
                htable = calloc(htmask+1, sizeof(*htable));
 
326
                if (!htable)
 
327
                        err(1, "calloc");
 
328
 
 
329
                /* populate newly allocated hashtable */
 
330
                if (ohtable) {
 
331
                        int i;
 
332
                        for (i = 0; i <= htmask>>1; i++)
 
333
                                if (ohtable[i].ino || ohtable[i].dev)
 
334
                                        linkchk(ohtable[i].dev, ohtable[i].ino);
 
335
                        free(ohtable);
 
336
                }
 
337
        }
 
338
 
 
339
        /* multiplicative hashing */
 
340
        tmp = dev;
 
341
        tmp <<= HTBITS>>1;
 
342
        tmp |=  ino;
 
343
        tmp *= HTCONST;
 
344
        h  = tmp >> (HTBITS - htshift);
 
345
        h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */
 
346
 
 
347
        /* open address hashtable search with double hash probing */
 
348
        while (htable[h].ino || htable[h].dev) {
 
349
                if ((htable[h].ino == ino) && (htable[h].dev == dev))
 
350
                        return 1;
 
351
                h = (h + h2) & htmask;
 
352
        }
 
353
 
 
354
        /* Insert the current entry into hashtable */
 
355
        htable[h].dev = dev;
 
356
        htable[h].ino = ino;
 
357
        htused++;
 
358
        return 0;
 
359
}
 
360
 
 
361
void
 
362
usage(void)
 
363
{
 
364
#ifdef USE_UKFS
 
365
        (void)fprintf(stderr,
 
366
    "usage: %s %s [-H | -L | -P] [-a | -d depth | -s] [-cghkmnrx] [file ...]\n",
 
367
                      getprogname(), fsu_mount_usage());
 
368
#else
 
369
        (void)fprintf(stderr,
 
370
  "usage: %s [-H | -L | -P] [-a | -d depth | -s] [-cghkmnrx] [file ...]\n",
 
371
                      getprogname());
 
372
#endif
 
373
        exit(1);
 
374
}