toybox/toys/posix/du.c
<<
>>
Prefs
   1/* du.c - disk usage program.
   2 *
   3 * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
   4 *
   5 * See http://opengroup.org/onlinepubs/9699919799/utilities/du.html
   6 *
   7 * TODO: cleanup (should seen_inode be lib?)
   8 * 32 bit du -b maxes out at 4 gigs (instead of 2 terabytes via *512 trick)
   9 * because dirtree->extra is a long.
  10
  11USE_DU(NEWTOY(du, "d#<0=-1hmlcaHkKLsxb[-HL][-kKmh]", TOYFLAG_USR|TOYFLAG_BIN))
  12
  13config DU
  14  bool "du"
  15  default y
  16  help
  17    usage: du [-d N] [-askxHLlmc] [FILE...]
  18
  19    Show disk usage, space consumed by files and directories.
  20
  21    Size in:
  22    -b  Apparent bytes (directory listing size, not space used)
  23    -k  1024 byte blocks (default)
  24    -K  512 byte blocks (posix)
  25    -m  Megabytes
  26    -h  Human readable (e.g., 1K 243M 2G)
  27
  28    What to show:
  29    -a  All files, not just directories
  30    -H  Follow symlinks on cmdline
  31    -L  Follow all symlinks
  32    -s  Only total size of each argument
  33    -x  Don't leave this filesystem
  34    -c  Cumulative total
  35    -d N        Only depth < N
  36    -l  Disable hardlink filter
  37*/
  38
  39#define FOR_du
  40#include "toys.h"
  41
  42GLOBALS(
  43  long d;
  44
  45  unsigned long depth, total;
  46  dev_t st_dev;
  47  void *inodes;
  48)
  49
  50typedef struct node_size {
  51  struct dirtree *node;
  52  long size;
  53} node_size;
  54
  55// Print the size and name, given size in bytes
  56static void print(long long size, struct dirtree *node)
  57{
  58  char *name = "total";
  59
  60  if (TT.depth > TT.d) return;
  61
  62  if (FLAG(h)) {
  63    human_readable(toybuf, size, 0);
  64    printf("%s", toybuf);
  65  } else {
  66    int bits = 10;
  67
  68    if (FLAG(K)) bits = 9;
  69    else if (FLAG(m)) bits = 20;
  70
  71    if (FLAG(b) && bits == 10 && !FLAG(k)) printf("%llu", size);
  72    else printf("%llu", (size>>bits)+!!(size&((1<<bits)-1)));
  73  }
  74  if (node) name = dirtree_path(node, NULL);
  75  xprintf("\t%s\n", name);
  76  if (node) free(name);
  77}
  78
  79// Return whether or not we've seen this inode+dev, adding it to the list if
  80// we haven't.
  81static int seen_inode(void **list, struct stat *st)
  82{
  83  if (!st) llist_traverse(st, free);
  84
  85  // Skipping dir nodes isn't _quite_ right. They're not hardlinked, but could
  86  // be bind mounted. Still, it's more efficient and the archivers can't use
  87  // hardlinked directory info anyway. (Note that we don't catch bind mounted
  88  // _files_ because it doesn't change st_nlink.)
  89  else if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
  90    struct inode_list {
  91      struct inode_list *next;
  92      struct dev_ino di;
  93    } *new;
  94
  95    for (new = *list; new; new = new->next)
  96      if(same_dev_ino(st, &new->di)) return 1;
  97
  98    new = xzalloc(sizeof(*new));
  99    new->di.ino = st->st_ino;
 100    new->di.dev = st->st_dev;
 101    new->next = *list;
 102    *list = new;
 103  }
 104
 105  return 0;
 106}
 107
 108// dirtree callback, compute/display size of node
 109static int do_du(struct dirtree *node)
 110{
 111  unsigned long blocks;
 112
 113  if (!node->parent) TT.st_dev = node->st.st_dev;
 114  else if (!dirtree_notdotdot(node)) return 0;
 115
 116  // detect swiching filesystems
 117  if (FLAG(x) && (TT.st_dev != node->st.st_dev))
 118    return 0;
 119
 120  // Don't loop endlessly on recursive directory symlink
 121  if (FLAG(L)) {
 122    struct dirtree *try = node;
 123
 124    while ((try = try->parent)) if (same_file(&node->st, &try->st)) return 0;
 125  }
 126
 127  // Don't count hard links twice
 128  if (!FLAG(l) && !node->again)
 129    if (seen_inode(&TT.inodes, &node->st)) return 0;
 130
 131  // Collect child info before printing directory size
 132  if (S_ISDIR(node->st.st_mode)) {
 133    if (!node->again) {
 134      TT.depth++;
 135      return DIRTREE_COMEAGAIN|(DIRTREE_SYMFOLLOW*!!FLAG(L));
 136    } else TT.depth--;
 137  }
 138
 139  // Modern compilers' optimizers are insane and think signed overflow
 140  // behaves differently than unsigned overflow. Sigh. Big hammer.
 141  blocks = FLAG(b) ? node->st.st_size : node->st.st_blocks;
 142  blocks += (unsigned long)node->extra;
 143  node->extra = blocks;
 144  if (node->parent)
 145    node->parent->extra = (unsigned long)node->parent->extra+blocks;
 146  else TT.total += node->extra;
 147
 148  if (FLAG(a) || !node->parent || (S_ISDIR(node->st.st_mode) && !FLAG(s))) {
 149    blocks = node->extra;
 150    print(FLAG(b) ? blocks : blocks*512LL, node);
 151  }
 152
 153  return 0;
 154}
 155
 156void du_main(void)
 157{
 158  char *noargs[] = {".", 0}, **args;
 159
 160  // Loop over command line arguments, recursing through children
 161  for (args = toys.optc ? toys.optargs : noargs; *args; args++)
 162    dirtree_flagread(*args, DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)),
 163      do_du);
 164  if (FLAG(c)) print(FLAG(b) ? TT.total : TT.total*512, 0);
 165
 166  if (CFG_TOYBOX_FREE) seen_inode(TT.inodes, 0);
 167}
 168