658
|
1 /* vi: set sw=4 ts=4:
|
|
2 *
|
|
3 * du.c - disk usage program.
|
|
4 *
|
|
5 * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
|
|
6 *
|
|
7 * See http://opengroup.org/onlinepubs/9699919799/utilities/du.html
|
|
8
|
|
9 USE_DU(NEWTOY(du, "d#<0hmlcaHkLsx", 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 Estimate file space usage (default in unit of 512 blocks).
|
|
18 -a Show all file sizes
|
|
19 -H Follow symlinks on cmdline
|
|
20 -L Follow all symlinks
|
|
21 -k Show size in units of 1024.
|
|
22 -s Show only the total Size for each file specified
|
|
23 -x Estimate size only on the same device
|
|
24 -c Print total size of all arguments
|
|
25 -d N Limit output to directories (and files with -a) of depth < N
|
|
26 -l Count sizes many times if hard linked
|
|
27 -h Sizes in human readable format (e.g., 1K 243M 2G )
|
|
28 -m Sizes in megabytes
|
|
29 */
|
|
30
|
|
31 #include "toys.h"
|
|
32
|
|
33 DEFINE_GLOBALS(
|
|
34 long maxdepth;
|
|
35 long depth;
|
|
36 long *dirsum;
|
|
37 long total;
|
|
38 dev_t st_dev;
|
|
39 struct arg_list *inodes;
|
|
40 )
|
|
41
|
|
42 #define TT this.du
|
|
43
|
|
44 #define FLAG_x 1
|
|
45 #define FLAG_s 2
|
|
46 #define FLAG_L 4
|
|
47 #define FLAG_k 8
|
|
48 #define FLAG_H 16
|
|
49 #define FLAG_a 32
|
|
50 #define FLAG_c 64
|
|
51 #define FLAG_l 128
|
|
52 #define FLAG_m 256
|
|
53 #define FLAG_h 512
|
|
54 #define FLAG_d 1024
|
|
55
|
|
56 typedef struct node_size {
|
|
57 struct dirtree *node;
|
|
58 long size;
|
|
59 }node_size;
|
|
60
|
|
61 typedef struct inode_ent {
|
|
62 ino_t ino;
|
|
63 dev_t dev;
|
|
64 }inode_ent_t;
|
|
65
|
|
66 /*
|
|
67 * Adding '/' to the name if name is '.' or '..'
|
|
68 */
|
|
69
|
|
70 char *make_pathproper(char *str)
|
|
71 {
|
|
72 char *path = str;
|
|
73 switch(strlen(str)) {
|
|
74 case 1:
|
|
75 if(str[0] == '.') path = xstrdup("./");
|
|
76 break;
|
|
77 case 2:
|
|
78 if(str[0] == '.' && str[1] == '.') path = xstrdup("../");
|
|
79 break;
|
|
80 default:
|
|
81 break;
|
|
82 }
|
|
83 return path;
|
|
84 }
|
|
85
|
|
86 /*
|
|
87 * Print the size of the given entry in specified format, default in blocks of 512 bytes
|
|
88 */
|
|
89 void print(long size, char* name)
|
|
90 {
|
|
91 unsigned long long tempsize = (unsigned long long)size * 512;
|
|
92 unsigned long unit = 512;
|
|
93 char *sizestr = NULL;
|
|
94 if(TT.depth > TT.maxdepth) return;
|
|
95 if(toys.optflags & FLAG_h) unit = 0;
|
|
96 if(toys.optflags & FLAG_k) unit = 1024;
|
|
97 if(toys.optflags & FLAG_m) unit = 1024*1024;
|
|
98 sizestr = make_human_readable(tempsize, unit); //make human readable string, depending upon unit size.
|
|
99 xprintf("%s\t%s\n",sizestr, name);
|
|
100 free(sizestr);
|
|
101 }
|
|
102
|
|
103 /*
|
|
104 * free the inodes which are stored for hard link reference
|
|
105 */
|
|
106 void free_inodes(void *data)
|
|
107 {
|
|
108 void *arg = ((struct arg_list*)data)->arg;
|
|
109 if(arg) free(arg);
|
|
110 free(data);
|
|
111 }
|
|
112
|
|
113 /*
|
|
114 * allocate and add a node to the list
|
|
115 */
|
|
116 static void llist_add_inode(struct arg_list **old, void *data)
|
|
117 {
|
|
118 struct arg_list *new = xmalloc(sizeof(struct arg_list));
|
|
119
|
|
120 new->arg = (char*)data;
|
|
121 new->next = *old;
|
|
122 *old = new;
|
|
123 }
|
|
124
|
|
125 /*
|
|
126 * check if the given stat entry is already there in list or not
|
|
127 */
|
|
128 int is_inode_present(struct stat *st)
|
|
129 {
|
|
130 struct arg_list *temparg = NULL;
|
|
131 inode_ent_t *ent = NULL;
|
|
132 if(!TT.inodes) return 0;
|
|
133 for(temparg = TT.inodes; temparg; temparg = (struct arg_list *)temparg->next) {
|
|
134 ent = (inode_ent_t*)temparg->arg;
|
|
135 if(ent && ent->ino == st->st_ino && ent->dev == st->st_dev) return 1;
|
|
136 }
|
|
137 return 0;
|
|
138 }
|
|
139
|
|
140 /*
|
|
141 * Compute the size of the node
|
|
142 */
|
|
143 int do_du(struct dirtree *node)
|
|
144 {
|
|
145 inode_ent_t *ino_details = NULL;
|
|
146 node_size *nd = NULL;
|
|
147 if(!dirtree_notdotdot(node)) return 0;
|
|
148 if((toys.optflags & FLAG_x) && (TT.st_dev != node->st.st_dev)) //if file not on same device, don't count size
|
|
149 return DIRTREE_RECURSE;
|
|
150
|
|
151 if(!(toys.optflags & FLAG_l) && node->st.st_nlink > 1 && !node->extra) { //keeping reference for hard links
|
|
152 if(is_inode_present(&node->st)) return DIRTREE_RECURSE;
|
|
153 ino_details = xzalloc(sizeof(inode_ent_t));
|
|
154 ino_details->ino = node->st.st_ino;
|
|
155 ino_details->dev = node->st.st_dev;
|
|
156 llist_add_inode(&TT.inodes, (void*)ino_details);
|
|
157 }
|
|
158
|
|
159 if(S_ISDIR(node->st.st_mode)) {
|
|
160 if(!(node->extra && (long)((node_size*)(node->extra))->node == (long)node)) {
|
|
161 nd = xzalloc(sizeof(node_size));
|
|
162 nd->node = node;
|
|
163 nd->size = 0;
|
|
164 TT.dirsum = (long*)&(nd->size);
|
|
165 node->extra = (long)nd;
|
|
166 *TT.dirsum = 0;
|
|
167 TT.depth++;
|
|
168 return (DIRTREE_RECURSE|DIRTREE_COMEAGAIN | ((toys.optflags & FLAG_L) ? DIRTREE_SYMFOLLOW : 0)); //DIRTREE_COMEAGAIN to comeback and print the entry.
|
|
169 }
|
|
170 else if(node->extra) { //extra is set for a returning DIR entry.
|
|
171 long offset = 0;
|
|
172 nd = (node_size*)node->extra;
|
|
173 offset = nd->size;
|
|
174 nd->size += node->st.st_blocks;
|
|
175 TT.depth--;
|
|
176 if(!(toys.optflags & FLAG_s))
|
|
177 print(*TT.dirsum, dirtree_path(node, NULL));
|
|
178 if((node->parent) && (node->parent->extra)) {
|
|
179 /* when returning from internal directory, get the saved size of the parent and continue from there */
|
|
180 nd = (node_size*)node->parent->extra;
|
|
181 TT.dirsum = (long*)&(nd->size);
|
|
182 *TT.dirsum += offset;
|
|
183 *TT.dirsum += node->st.st_blocks;
|
|
184 return DIRTREE_RECURSE;
|
|
185 }
|
|
186 else if(!node->parent) {
|
|
187 /*if node has no parent, it means it is the top in the tree, stop recursing here */
|
|
188 TT.total += *TT.dirsum;
|
|
189 if((toys.optflags & FLAG_s))
|
|
190 print(*TT.dirsum, dirtree_path(node, NULL));
|
|
191 return 0;
|
|
192 }
|
|
193 }
|
|
194 }
|
|
195 else if(!(node->parent)) {
|
|
196 /* this is the file specified on cmdline */
|
|
197 TT.total += node->st.st_blocks;
|
|
198 print(node->st.st_blocks, dirtree_path(node, NULL));
|
|
199 return 0;
|
|
200 }
|
|
201 if(TT.dirsum) *TT.dirsum += node->st.st_blocks;
|
|
202 if(toys.optflags & FLAG_a && !(toys.optflags & FLAG_s))
|
|
203 print(node->st.st_blocks, dirtree_path(node, NULL));
|
|
204 return DIRTREE_RECURSE;
|
|
205 }
|
|
206
|
|
207 /*
|
|
208 * DU utility main function
|
|
209 */
|
|
210 void du_main(void)
|
|
211 {
|
|
212 int symfollow = toys.optflags & (FLAG_H | FLAG_L);
|
|
213 TT.total = 0;
|
|
214 TT.inodes = NULL;
|
|
215
|
|
216 if(!(toys.optflags & FLAG_d)) TT.maxdepth = INT_MAX;
|
|
217 if(toys.optc == 0) toys.optargs[0] = "./";
|
|
218 while(*toys.optargs) {
|
|
219 TT.depth = 0;
|
|
220 char *path = make_pathproper(*toys.optargs);
|
|
221 struct dirtree *root = dirtree_add_node(AT_FDCWD, path, symfollow); //create a node
|
|
222 if(root) {
|
|
223 TT.st_dev = root->st.st_dev;
|
|
224 handle_callback(root, do_du); //this will recurse thru the DIR children.
|
|
225 }
|
|
226 toys.optargs++;
|
|
227 }
|
|
228 if(TT.inodes) llist_traverse(TT.inodes, free_inodes); //free the stored nodes
|
|
229 if(toys.optflags & FLAG_c) print(TT.total, "total");
|
|
230 }
|