* SUCH DAMAGE.
*/
+#include <sys/cdefs.h>
#ifndef lint
-static const char copyright[] =
+__used static const char copyright[] =
"@(#) Copyright (c) 1989, 1993, 1994\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/stat.h>
+#include <sys/attr.h>
#include <err.h>
#include <errno.h>
#ifdef __APPLE__
#include <get_compat.h>
+#include <sys/sysctl.h>
#else
#define COMPAT_MODE(func, mode) (1)
#endif
#define TERA_SI_SZ (TERA_SZ(1000ULL))
#define PETA_SI_SZ (PETA_SZ(1000ULL))
-#define TWO_TB (2LL * 1024LL * 1024LL * 1024LL * 1024LL)
-
unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ};
unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
unsigned long long *valp;
};
static int linkchk(FTSENT *);
+static int dirlinkchk(FTSENT *);
static void usage(void);
void prthumanval(double);
unit_t unit_adjust(double *);
Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0;
save = argv;
- ftsoptions = 0;
+ ftsoptions = FTS_NOCHDIR;
depth = INT_MAX;
SLIST_INIT(&ignores);
- while ((ch = getopt(argc, argv, "HI:LPasd:chkmrx")) != -1)
+ while ((ch = getopt(argc, argv, "HI:LPasd:cghkmrx")) != -1)
switch (ch) {
case 'H':
Lflag = Pflag = 0;
hflag = 0;
putenv("BLOCKSIZE=1048576");
break;
+ case 'g':
+ hflag = 0;
+ putenv("BLOCKSIZE=1g");
+ break;
case 'r': /* Compatibility. */
break;
case 'x':
usage();
}
- argc -= optind;
+// argc -= optind;
argv += optind;
/*
(void) getbsize(¬used, &blocksize);
blocksize /= 512;
+#ifdef __APPLE__
+ // "du" should not have any side effect on disk usage,
+ // so prevent materializing dataless directories upon traversal
+ rval = 1;
+ (void) sysctlbyname("vfs.nspace.prevent_materialization", NULL, NULL, &rval, sizeof(rval));
+#endif /* __APPLE__ */
+
rval = 0;
if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
while ((p = fts_read(fts)) != NULL) {
switch (p->fts_info) {
- case FTS_D: /* Ignore. */
- if (ignorep(p))
+ case FTS_D:
+ if (ignorep(p) || dirlinkchk(p))
fts_set(fts, p, FTS_SKIP);
break;
case FTS_DP:
ftsparnum = (off_t *)&p->fts_parent->fts_number;
ftsnum = (off_t *)&p->fts_number;
- if (p->fts_statp->st_size < TWO_TB) {
- ftsparnum[0] += ftsnum[0] += p->fts_statp->st_blocks;
- } else {
- ftsparnum[0] += ftsnum[0] += howmany(p->fts_statp->st_size, 512LL);
- }
+ ftsparnum[0] += ftsnum[0] += p->fts_statp->st_blocks;
if (p->fts_level <= depth) {
if (hflag) {
if (listall || p->fts_level == 0) {
if (hflag) {
- if (p->fts_statp->st_size < TWO_TB) {
(void) prthumanval(howmany(p->fts_statp->st_blocks,
blocksize));
- } else {
- (void) prthumanval(howmany(howmany(p->fts_statp->st_size, 512LL),
- blocksize));
- }
(void) printf("\t%s\n", p->fts_path);
} else {
- if (p->fts_statp->st_size < TWO_TB) {
(void) printf("%jd\t%s\n",
(intmax_t)howmany(p->fts_statp->st_blocks, blocksize),
p->fts_path);
- } else {
- (void) printf("%jd\t%s\n",
- (intmax_t)howmany(howmany(p->fts_statp->st_size, 512LL), blocksize),
- p->fts_path);
- }
}
}
ftsparnum = (off_t *)&p->fts_parent->fts_number;
- if (p->fts_statp->st_size < TWO_TB) {
- ftsparnum[0] += p->fts_statp->st_blocks;
- } else {
- ftsparnum[0] += p->fts_statp->st_size / 512LL;
- }
+ ftsparnum[0] += p->fts_statp->st_blocks;
}
savednumber = ((off_t *)&p->fts_parent->fts_number)[0];
}
return (0);
}
+static int
+dirlinkchk(FTSENT *p)
+{
+ struct links_entry {
+ struct links_entry *next;
+ struct links_entry *previous;
+ int links;
+ dev_t dev;
+ ino_t ino;
+ };
+ static const size_t links_hash_initial_size = 8192;
+ static struct links_entry **buckets;
+ static struct links_entry *free_list;
+ static size_t number_buckets;
+ static unsigned long number_entries;
+ static char stop_allocating;
+ struct links_entry *le, **new_buckets;
+ struct stat *st;
+ size_t i, new_size;
+ int hash;
+ struct attrbuf {
+ int size;
+ int linkcount;
+ } buf;
+ struct attrlist attrList;
+
+ memset(&attrList, 0, sizeof(attrList));
+ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
+ attrList.dirattr = ATTR_DIR_LINKCOUNT;
+ if (-1 == getattrlist(p->fts_path, &attrList, &buf, sizeof(buf), 0))
+ return 0;
+ if (buf.linkcount == 1)
+ return 0;
+ st = p->fts_statp;
+
+ /* If necessary, initialize the hash table. */
+ if (buckets == NULL) {
+ number_buckets = links_hash_initial_size;
+ buckets = malloc(number_buckets * sizeof(buckets[0]));
+ if (buckets == NULL)
+ errx(1, "No memory for directory hardlink detection");
+ for (i = 0; i < number_buckets; i++)
+ buckets[i] = NULL;
+ }
+
+ /* If the hash table is getting too full, enlarge it. */
+ if (number_entries > number_buckets * 10 && !stop_allocating) {
+ new_size = number_buckets * 2;
+ new_buckets = malloc(new_size * sizeof(struct links_entry *));
+
+ /* Try releasing the free list to see if that helps. */
+ if (new_buckets == NULL && free_list != NULL) {
+ while (free_list != NULL) {
+ le = free_list;
+ free_list = le->next;
+ free(le);
+ }
+ new_buckets = malloc(new_size * sizeof(new_buckets[0]));
+ }
+
+ if (new_buckets == NULL) {
+ stop_allocating = 1;
+ warnx("No more memory for tracking directory hard links");
+ } else {
+ memset(new_buckets, 0,
+ new_size * sizeof(struct links_entry *));
+ for (i = 0; i < number_buckets; i++) {
+ while (buckets[i] != NULL) {
+ /* Remove entry from old bucket. */
+ le = buckets[i];
+ buckets[i] = le->next;
+
+ /* Add entry to new bucket. */
+ hash = (le->dev ^ le->ino) % new_size;
+
+ if (new_buckets[hash] != NULL)
+ new_buckets[hash]->previous =
+ le;
+ le->next = new_buckets[hash];
+ le->previous = NULL;
+ new_buckets[hash] = le;
+ }
+ }
+ free(buckets);
+ buckets = new_buckets;
+ number_buckets = new_size;
+ }
+ }
+
+ /* Try to locate this entry in the hash table. */
+ hash = ( st->st_dev ^ st->st_ino ) % number_buckets;
+ for (le = buckets[hash]; le != NULL; le = le->next) {
+ if (le->dev == st->st_dev && le->ino == st->st_ino) {
+ /*
+ * Save memory by releasing an entry when we've seen
+ * all of it's links.
+ */
+ if (--le->links <= 0) {
+ if (le->previous != NULL)
+ le->previous->next = le->next;
+ if (le->next != NULL)
+ le->next->previous = le->previous;
+ if (buckets[hash] == le)
+ buckets[hash] = le->next;
+ number_entries--;
+ /* Recycle this node through the free list */
+ if (stop_allocating) {
+ free(le);
+ } else {
+ le->next = free_list;
+ free_list = le;
+ }
+ }
+ return (1);
+ }
+ }
+
+ if (stop_allocating)
+ return (0);
+ /* Add this entry to the links cache. */
+ if (free_list != NULL) {
+ /* Pull a node from the free list if we can. */
+ le = free_list;
+ free_list = le->next;
+ } else
+ /* Malloc one if we have to. */
+ le = malloc(sizeof(struct links_entry));
+ if (le == NULL) {
+ stop_allocating = 1;
+ warnx("No more memory for tracking hard links");
+ return (0);
+ }
+ le->dev = st->st_dev;
+ le->ino = st->st_ino;
+ le->links = buf.linkcount - 1;
+ number_entries++;
+ le->next = buckets[hash];
+ le->previous = NULL;
+ if (buckets[hash] != NULL)
+ buckets[hash]->previous = le;
+ buckets[hash] = le;
+ return (0);
+}
+
/*
* Output in "human-readable" format. Uses 3 digits max and puts
* unit suffixes at the end. Makes output compact and easy to read,
usage(void)
{
(void)fprintf(stderr,
- "usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k | -m] [-x] [-I mask] [file ...]\n");
+ "usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k | -m | -g] [-x] [-I mask] [file ...]\n");
exit(EX_USAGE);
}
if (S_ISDIR(ent->fts_statp->st_mode) && !strcmp("fd", ent->fts_name)) {
struct statfs sfsb;
int rc = statfs(ent->fts_accpath, &sfsb);
- if (rc >= 0 && !strcmp("fdesc", sfsb.f_fstypename)) {
+ if (rc >= 0 && !strcmp("devfs", sfsb.f_fstypename)) {
/* Don't cd into /dev/fd/N since one of those is likely to be
the cwd as of the start of du which causes all manner of
unpleasant surprises */