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