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 ino_t ino; 93 dev_t dev; 94 } *new; 95 96 for (new = *list; new; new = new->next) 97 if(new->ino == st->st_ino && new->dev == st->st_dev) 98 return 1; 99 100 new = xzalloc(sizeof(*new)); 101 new->ino = st->st_ino; 102 new->dev = st->st_dev; 103 new->next = *list; 104 *list = new; 105 } 106 107 return 0; 108} 109 110// dirtree callback, compute/display size of node 111static int do_du(struct dirtree *node) 112{ 113 unsigned long blocks; 114 115 if (!node->parent) TT.st_dev = node->st.st_dev; 116 else if (!dirtree_notdotdot(node)) return 0; 117 118 // detect swiching filesystems 119 if (FLAG(x) && (TT.st_dev != node->st.st_dev)) 120 return 0; 121 122 // Don't loop endlessly on recursive directory symlink 123 if (FLAG(L)) { 124 struct dirtree *try = node; 125 126 while ((try = try->parent)) 127 if (node->st.st_dev==try->st.st_dev && node->st.st_ino==try->st.st_ino) 128 return 0; 129 } 130 131 // Don't count hard links twice 132 if (!FLAG(l) && !node->again) 133 if (seen_inode(&TT.inodes, &node->st)) return 0; 134 135 // Collect child info before printing directory size 136 if (S_ISDIR(node->st.st_mode)) { 137 if (!node->again) { 138 TT.depth++; 139 return DIRTREE_COMEAGAIN|(DIRTREE_SYMFOLLOW*!!FLAG(L)); 140 } else TT.depth--; 141 } 142 143 // Modern compilers' optimizers are insane and think signed overflow 144 // behaves differently than unsigned overflow. Sigh. Big hammer. 145 blocks = FLAG(b) ? node->st.st_size : node->st.st_blocks; 146 blocks += (unsigned long)node->extra; 147 node->extra = blocks; 148 if (node->parent) 149 node->parent->extra = (unsigned long)node->parent->extra+blocks; 150 else TT.total += node->extra; 151 152 if (FLAG(a) || !node->parent || (S_ISDIR(node->st.st_mode) && !FLAG(s))) { 153 blocks = node->extra; 154 print(FLAG(b) ? blocks : blocks*512LL, node); 155 } 156 157 return 0; 158} 159 160void du_main(void) 161{ 162 char *noargs[] = {".", 0}, **args; 163 164 // Loop over command line arguments, recursing through children 165 for (args = toys.optc ? toys.optargs : noargs; *args; args++) 166 dirtree_flagread(*args, DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)), 167 do_du); 168 if (FLAG(c)) print(FLAG(b) ? TT.total : TT.total*512, 0); 169 170 if (CFG_TOYBOX_FREE) seen_inode(TT.inodes, 0); 171} 172