PL3026: This is the upgraded version of vdu that maintains an internal
[util-vserver.git] / src / vdu.c
1 // $Id: vdu-new.c,v 1.2 2004/08/17 14:44:14 mef-pl_kernel Exp $
2
3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 // based on vdu.cc by Jacques Gelinas
5 //  
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2, or (at your option)
9 // any later version.
10 //  
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //  
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <sys/ioctl.h>
30
31 #include <assert.h>
32
33 #include "vdu.h"
34
35 HashTable tbl;
36
37 static int // boolean
38 INOPut(PHashTable tbl, ino_t* key, struct stat **val){
39     return Put(tbl, key, val);
40 }
41
42 __extension__ typedef long long         longlong;
43 //__extension__ typedef long            longlong;
44
45 static longlong inodes;
46 static longlong blocks;
47 static longlong size;
48
49 static short verbose = 0;
50
51 static inline void warning(char *s) {
52     fprintf(stderr,"%s (%s)\n",s,strerror(errno));    
53 }
54
55 void panic(char *s) {
56     warning(s);
57     exit(2);
58 }
59
60 static void vdu_onedir (char const *path)
61 {
62     char const *foo = path;
63     struct stat dirst, st;
64     struct dirent *ent;
65     char *name;
66     DIR *dir;
67     int dirfd;
68     longlong dirsize, dirinodes, dirblocks;
69
70     dirsize = dirinodes = dirblocks = 0;
71
72     // A handle to speed up chdir
73     if ((dirfd = open (path,O_RDONLY)) == -1) {
74         fprintf (stderr,"Can't open directory %s\n",path);
75         panic("open failed");
76     }
77
78     if (fchdir (dirfd) == -1) {
79         fprintf (stderr,"Can't fchdir directory %s\n",path);
80         panic("fchdir failed");
81     }
82
83     if (fstat (dirfd,&dirst) != 0) {
84         fprintf (stderr,"Can't lstat directory %s\n",path);
85         panic("lstat failed");
86     }
87
88     if ((dir = opendir (".")) == NULL) {
89         fprintf (stderr,"Can't open (opendir) directory %s\n",path);
90         panic("opendir failed");
91     }
92
93
94     /* Walk the directory entries and compute the sum of inodes,
95      * blocks, and disk space used. This code will recursively descend
96      * down the directory structure. 
97      */
98
99     while ((ent=readdir(dir))!=NULL){
100         if (lstat(ent->d_name,&st)==-1){
101             fprintf (stderr,"Can't stat %s/%s\n",path,ent->d_name);
102             warning("lstat failed");
103             continue;
104         }
105         
106         dirinodes ++;
107
108         if (S_ISREG(st.st_mode)){
109             if (st.st_nlink > 1){
110                 struct stat *val;
111                 int nlink;
112
113                 /* Check hash table if we've seen this inode
114                  * before. Note that the hash maintains a
115                  * (inode,struct stat) key value pair.
116                  */
117
118                 val = &st;
119
120                 (void) INOPut(&tbl,&st.st_ino,&val);
121
122                 /* Note that after the INOPut call "val" refers to the
123                  * value entry in the hash table --- not &st.  This
124                  * means that if the inode has been put into the hash
125                  * table before, val will refer to the first st that
126                  * was put into the hashtable.  Otherwise, if it is
127                  * the first time it is put into the hash table, then
128                  * val will be equal to this &st.
129                  */
130                 nlink = val->st_nlink;
131                 nlink --;
132
133                 /* val refers to value in hash tbale */
134                 if (nlink == 0) {
135
136                     /* We saw all hard links to this particular inode
137                      * as part of this sweep of vdu. So account for
138                      * the size and blocks required by the file.
139                      */
140
141                     dirsize += val->st_size;
142                     dirblocks += val->st_blocks;
143
144                     /* Do not delete the (ino,val) tuple from the tbl,
145                      * as we need to handle the case when we are
146                      * double counting a file due to a bind mount.
147                      */
148                     val->st_nlink = 0;
149
150                 } else if (nlink > 0) {
151                     val->st_nlink = nlink;
152                 } else /* if(nlink < 0) */ {
153                     /* We get here when we are double counting nlinks
154                        due a bind mount. */
155
156                     /* DO NOTHING */
157                 }
158             } else {
159                 dirsize += st.st_size;
160                 dirblocks += st.st_blocks;
161             }
162
163         } else if (S_ISDIR(st.st_mode)) {
164             if ((st.st_dev == dirst.st_dev) &&
165                 (strcmp(ent->d_name,".")!=0) &&
166                 (strcmp(ent->d_name,"..")!=0)) {
167
168                 dirsize += st.st_size;
169                 dirblocks += st.st_blocks;
170
171                 name = strdup(ent->d_name);
172                 if (name==0) {
173                     panic("Out of memory\n");
174                 }
175                 vdu_onedir(name);
176                 free(name);
177                 fchdir(dirfd);
178             }
179         } else {
180             // dirsize += st.st_size;
181             // dirblocks += st.st_blocks;
182         }
183     }
184     closedir (dir);
185     close (dirfd);
186     if (verbose)
187         printf("%16lld %16lld %16lld %s\n",dirinodes, dirblocks, dirsize,foo);
188     inodes += dirinodes;
189     blocks += dirblocks;
190     size   += dirsize;
191 }
192
193 static void
194 Count(ino_t* key, struct stat* val) {
195     if(val->st_nlink) {
196         blocks += val->st_blocks;
197         size += val->st_size;
198         printf("ino=%ld nlink=%d\n",val->st_ino, val->st_nlink);
199     }
200 }
201
202 int
203 main (int argc, char **argv)
204 {
205     int startdir, i;
206
207     if (argc < 2){
208         fprintf (stderr,"vdu version %s\n",VERSION);
209         fprintf (stderr,"vdu directory ...\n\n");
210         fprintf (stderr,"Compute the size of a directory tree.\n");
211     }else{
212         if ((startdir = open (".",O_RDONLY)) == -1) {
213             fprintf (stderr,"Can't open current working directory\n");
214             panic("open failed");
215         }
216
217         /* hash table support for hard link count */
218         (void) Init(&tbl,0,0);
219
220         for (i=1; i<argc; i++){
221             inodes = blocks = size = 0;
222             vdu_onedir (argv[i]);
223
224             printf("%16lld %16lld %16lld %s\n",
225                    inodes, 
226                    blocks>>1, 
227                    size,
228                    argv[i]);
229             if (fchdir (startdir) == -1) {
230                 panic("fchdir failed");
231             }
232         }
233
234         if(0) {
235             /* show size & blocks for files with nlinks from outside of dir */
236             inodes = blocks = size = 0;
237             Iterate(&tbl,Count);
238             printf("%16lld %16lld %16lld NOT COUNTED\n",
239                    inodes, 
240                    blocks, 
241                    size);
242         }
243
244         // Dispose(&tbl); this fails to delete all entries 
245         close(startdir);
246     }
247     return 0;
248 }
249
250 /*
251  * Local variables:
252  *  c-basic-offset: 4
253  * End:
254  */