1 /* du.c - disk usage program. 2 * 3 * Copyright 2012 Ashwini Kumar <ak.ashwini (at) gmail.com> 4 * 5 * See http://opengroup.org/onlinepubs/9699919799/utilities/du.html 6 * 7 * TODO: cleanup 8 9 USE_DU(NEWTOY(du, "d#<0hmlcaHkKLsx[-HL][-kKmh]", TOYFLAG_USR|TOYFLAG_BIN)) 10 11 config 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 format (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 39 GLOBALS( 40 long maxdepth; 41 42 unsigned long depth, total; 43 dev_t st_dev; 44 void *inodes; 45 ) 46 47 typedef struct node_size { 48 struct dirtree *node; 49 long size; 50 } node_size; 51 52 // Print the size and name, given size in bytes 53 static void print(long long size, struct dirtree *node) 54 { 55 char *name = "total"; 56 57 if (TT.maxdepth && TT.depth > TT.maxdepth) 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. 77 static 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 107 static 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 157 void 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_handle_callback(dirtree_start(*args, 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