f784a35e8dd633623e8de7fea6e24c4a23250f76
[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 #define _LARGEFILE64_SOURCE
21
22 #ifdef HAVE_CONFIG_H
23 #  include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <dirent.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <sys/ioctl.h>
36
37 #include <assert.h>
38
39 #include "vdu.h"
40
41 HashTable tbl;
42
43 static int // boolean
44 INOPut(PHashTable tbl, ino64_t* key, struct stat64 **val){
45     return Put(tbl, key, val);
46 }
47
48 __extension__ typedef long long         longlong;
49 //__extension__ typedef long            longlong;
50
51 static longlong inodes;
52 static longlong blocks;
53 static longlong size;
54
55 static short verbose = 0;
56
57 static inline void warning(char *s) {
58     fprintf(stderr,"%s (%s)\n",s,strerror(errno));    
59 }
60
61 void panic(char *s) {
62     warning(s);
63     exit(2);
64 }
65
66 static void vdu_onedir (char const *path)
67 {
68     char const *foo = path;
69     struct stat64 dirst, st;
70     struct dirent *ent;
71     char *name;
72     DIR *dir;
73     int dirfd;
74     longlong dirsize, dirinodes, dirblocks;
75
76     dirsize = dirinodes = dirblocks = 0;
77
78     // A handle to speed up chdir
79     if ((dirfd = open (path,O_RDONLY)) == -1) {
80         fprintf (stderr,"Can't open directory %s\n",path);
81         panic("open failed");
82     }
83
84     if (fchdir (dirfd) == -1) {
85         fprintf (stderr,"Can't fchdir directory %s\n",path);
86         panic("fchdir failed");
87     }
88
89     if (fstat64 (dirfd,&dirst) != 0) {
90         fprintf (stderr,"Can't lstat directory %s\n",path);
91         panic("lstat failed");
92     }
93
94     if ((dir = opendir (".")) == NULL) {
95         fprintf (stderr,"Can't open (opendir) directory %s\n",path);
96         panic("opendir failed");
97     }
98
99
100     /* Walk the directory entries and compute the sum of inodes,
101      * blocks, and disk space used. This code will recursively descend
102      * down the directory structure. 
103      */
104
105     while ((ent=readdir(dir))!=NULL){
106         if (lstat64(ent->d_name,&st)==-1){
107             fprintf (stderr,"Can't stat %s/%s\n",path,ent->d_name);
108             warning("lstat failed");
109             continue;
110         }
111         
112         dirinodes ++;
113
114         if (S_ISREG(st.st_mode)){
115             if (st.st_nlink > 1){
116                 struct stat64 *val;
117                 int nlink;
118
119                 /* Check hash table if we've seen this inode
120                  * before. Note that the hash maintains a
121                  * (inode,struct stat) key value pair.
122                  */
123
124                 val = &st;
125
126                 (void) INOPut(&tbl,&st.st_ino,&val);
127
128                 /* Note that after the INOPut call "val" refers to the
129                  * value entry in the hash table --- not &st.  This
130                  * means that if the inode has been put into the hash
131                  * table before, val will refer to the first st that
132                  * was put into the hashtable.  Otherwise, if it is
133                  * the first time it is put into the hash table, then
134                  * val will be equal to this &st.
135                  */
136                 nlink = val->st_nlink;
137                 nlink --;
138
139                 /* val refers to value in hash tbale */
140                 if (nlink == 0) {
141
142                     /* We saw all hard links to this particular inode
143                      * as part of this sweep of vdu. So account for
144                      * the size and blocks required by the file.
145                      */
146
147                     dirsize += val->st_size;
148                     dirblocks += val->st_blocks;
149
150                     /* Do not delete the (ino,val) tuple from the tbl,
151                      * as we need to handle the case when we are
152                      * double counting a file due to a bind mount.
153                      */
154                     val->st_nlink = 0;
155
156                 } else if (nlink > 0) {
157                     val->st_nlink = nlink;
158                 } else /* if(nlink < 0) */ {
159                     /* We get here when we are double counting nlinks
160                        due a bind mount. */
161
162                     /* DO NOTHING */
163                 }
164             } else {
165                 dirsize += st.st_size;
166                 dirblocks += st.st_blocks;
167             }
168
169         } else if (S_ISDIR(st.st_mode)) {
170             if ((st.st_dev == dirst.st_dev) &&
171                 (strcmp(ent->d_name,".")!=0) &&
172                 (strcmp(ent->d_name,"..")!=0)) {
173
174                 dirsize += st.st_size;
175                 dirblocks += st.st_blocks;
176
177                 name = strdup(ent->d_name);
178                 if (name==0) {
179                     panic("Out of memory\n");
180                 }
181                 vdu_onedir(name);
182                 free(name);
183                 fchdir(dirfd);
184             }
185         } else {
186             // dirsize += st.st_size;
187             // dirblocks += st.st_blocks;
188         }
189     }
190     closedir (dir);
191     close (dirfd);
192     if (verbose)
193         printf("%16lld %16lld %16lld %s\n",dirinodes, dirblocks, dirsize,foo);
194     inodes += dirinodes;
195     blocks += dirblocks;
196     size   += dirsize;
197 }
198
199 static void
200 Count(ino64_t* key, struct stat64* val) {
201     if(val->st_nlink) {
202         blocks += val->st_blocks;
203         size += val->st_size;
204         printf("ino=%16lld nlink=%d\n",val->st_ino, val->st_nlink);
205     }
206 }
207
208 int
209 main (int argc, char **argv)
210 {
211     int startdir, i;
212
213     if (argc < 2){
214         fprintf (stderr,"vdu version %s\n",VERSION);
215         fprintf (stderr,"vdu directory ...\n\n");
216         fprintf (stderr,"Compute the size of a directory tree.\n");
217     }else{
218         if ((startdir = open (".",O_RDONLY)) == -1) {
219             fprintf (stderr,"Can't open current working directory\n");
220             panic("open failed");
221         }
222
223         /* hash table support for hard link count */
224         (void) Init(&tbl,0,0);
225
226         for (i=1; i<argc; i++){
227             inodes = blocks = size = 0;
228             vdu_onedir (argv[i]);
229
230             printf("%16lld %16lld %16lld %s\n",
231                    inodes, 
232                    blocks>>1, 
233                    size,
234                    argv[i]);
235             if (fchdir (startdir) == -1) {
236                 panic("fchdir failed");
237             }
238         }
239
240         if(0) {
241             /* show size & blocks for files with nlinks from outside of dir */
242             inodes = blocks = size = 0;
243             Iterate(&tbl,Count);
244             printf("%16lld %16lld %16lld NOT COUNTED\n",
245                    inodes, 
246                    blocks, 
247                    size);
248         }
249
250         // Dispose(&tbl); this fails to delete all entries 
251         close(startdir);
252     }
253     return 0;
254 }
255
256 /*
257  * Local variables:
258  *  c-basic-offset: 4
259  * End:
260  */