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