file_cmds-321.100.10.0.1.tar.gz
[apple/file_cmds.git] / du / du.c
diff --git a/du/du.c b/du/du.c
index 4a426eef05075b0cc9590b00f93dac6d2b88e64d..9aea790c1ea6fa59eab2a9bc766dff5d820eba68 100644 (file)
--- a/du/du.c
+++ b/du/du.c
@@ -34,8 +34,9 @@
  * 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 */
@@ -52,6 +53,7 @@ __FBSDID("$FreeBSD: src/usr.bin/du/du.c,v 1.38 2005/04/09 14:31:40 stefanf Exp $
 #include <sys/param.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
+#include <sys/attr.h>
 
 #include <err.h>
 #include <errno.h>
@@ -68,6 +70,7 @@ __FBSDID("$FreeBSD: src/usr.bin/du/du.c,v 1.38 2005/04/09 14:31:40 stefanf Exp $
 
 #ifdef __APPLE__
 #include <get_compat.h>
+#include <sys/sysctl.h>
 #else
 #define COMPAT_MODE(func, mode) (1)
 #endif
@@ -90,8 +93,6 @@ __FBSDID("$FreeBSD: src/usr.bin/du/du.c,v 1.38 2005/04/09 14:31:40 stefanf Exp $
 #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;
@@ -107,6 +108,7 @@ struct ignentry {
 };
 
 static int     linkchk(FTSENT *);
+static int     dirlinkchk(FTSENT *);
 static void    usage(void);
 void           prthumanval(double);
 unit_t         unit_adjust(double *);
@@ -134,11 +136,11 @@ main(int argc, char *argv[])
        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;
@@ -186,6 +188,10 @@ main(int argc, char *argv[])
                                hflag = 0;
                                putenv("BLOCKSIZE=1048576");
                                break;
+                       case 'g':
+                               hflag = 0;
+                               putenv("BLOCKSIZE=1g");
+                               break;
                        case 'r':                /* Compatibility. */
                                break;
                        case 'x':
@@ -196,7 +202,7 @@ main(int argc, char *argv[])
                                usage();
                }
 
-       argc -= optind;
+//     argc -= optind;
        argv += optind;
 
        /*
@@ -248,6 +254,13 @@ main(int argc, char *argv[])
        (void) getbsize(&notused, &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)
@@ -255,8 +268,8 @@ main(int argc, char *argv[])
 
        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:
@@ -265,11 +278,7 @@ main(int argc, char *argv[])
 
                                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) {
@@ -310,33 +319,18 @@ main(int argc, char *argv[])
 
                                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];
        }
@@ -490,6 +484,150 @@ linkchk(FTSENT *p)
        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,
@@ -537,7 +675,7 @@ static void
 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);
 }
 
@@ -577,7 +715,7 @@ ignorep(FTSENT *ent)
        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 */