Accounts for hardlinks more accurately than the original version that came
authorMarc Fiuczynski <mef@cs.princeton.edu>
Fri, 17 Sep 2004 09:38:45 +0000 (09:38 +0000)
committerMarc Fiuczynski <mef@cs.princeton.edu>
Fri, 17 Sep 2004 09:38:45 +0000 (09:38 +0000)
with util-vserver. The original version ignored to account for all files
whose hard link count > 1. This version maintains a table of such files
and then accounts for their size if it visits all hard links.

src/vdu.c

index 55f6082..04c060c 100644 (file)
--- a/src/vdu.c
+++ b/src/vdu.c
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+#include "compat.h"
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <errno.h>
 #include <string.h>
 
-__extension__ typedef long long                longlong;
+#include <sys/ioctl.h>
+#include "ext2fs.h"
+
+// Patch to help compile this utility on unpatched kernel source
+#ifndef EXT2_IMMUTABLE_FILE_FL
+       #define EXT2_IMMUTABLE_FILE_FL  0x00000010
+       #define EXT2_IMMUTABLE_LINK_FL  0x00008000
+#endif
+
+
+//__extension__ typedef long long              longlong;
+__extension__ typedef long             longlong;
+
+static longlong inodes;
+static longlong blocks;
+static longlong size;
 
-static int vdu_onedir (char const *path, longlong *size)
+static short verbose = 0;
+
+static inline void warning(char *s) {
+    fprintf(stderr,"%s (%s)\n",s,strerror(errno));    
+}
+
+void panic(char *s) {
+    warning(s);
+    exit(2);
+}
+
+static void vdu_onedir (char const *path)
 {
-       int ret = -1;
-       int dirfd = open (path,O_RDONLY);       // A handle to speed up
-                                                                                               // chdir
-       if (dirfd == -1){
-               fprintf (stderr,"Can't open directory %s (%s)\n",path
-                       ,strerror(errno));
-       }else{
-               DIR *dir;
-
-               fchdir (dirfd);
-               dir = opendir (".");
-               if (dir == NULL){
-                       fprintf (stderr,"Can't open (opendir) directory %s (%s)\n",path
-                               ,strerror(errno));
-               }else{
-                       struct stat dirst;
-                       struct dirent *ent;
-                       longlong dirsize = 0;
-
-                       ret = 0;
-                       lstat (".",&dirst);
-                       while ((ent=readdir(dir))!=NULL){
-                               struct stat st;
-                               if (lstat(ent->d_name,&st)==-1){
-                                       fprintf (stderr,"Can't stat %s/%s (%s)\n",path
-                                               ,ent->d_name,strerror(errno));
-                                       ret = -1;
-                                       break;
-                               }else if (S_ISREG(st.st_mode)){
-                                       if (st.st_nlink == 1){
-                                               dirsize += st.st_size;
-                                       }
-                               }else if (S_ISDIR(st.st_mode) && st.st_dev == dirst.st_dev){
-                                       if (strcmp(ent->d_name,".")!=0
-                                               && strcmp(ent->d_name,"..")!=0){
-                                               char    *tmp = malloc(strlen(path) + strlen(ent->d_name) + 2);
-                                               if (tmp==0) ret=-1;
-                                               else {
-                                                 strcpy(tmp, path);
-                                                 strcat(tmp, "/");
-                                                 strcat(tmp, ent->d_name);
-                                                 ret = vdu_onedir(tmp,&dirsize);
-                                                 free(tmp);
-                                                 fchdir (dirfd);
-                                               }
-                                       }
-                               }
-                       }
-                       closedir (dir);
-                       *size += dirsize;
+    struct stat dirst, st;
+    struct dirent *ent;
+    char *name;
+    DIR *dir;
+    int dirfd;
+    longlong dirsize, dirinodes, dirblocks;
+
+    dirsize = dirinodes = dirblocks = 0;
+
+    // A handle to speed up chdir
+    if ((dirfd = open (path,O_RDONLY)) == -1) {
+       fprintf (stderr,"Can't open directory %s\n",path);
+       panic("open failed");
+    }
+
+    if (fchdir (dirfd) == -1) {
+       fprintf (stderr,"Can't fchdir directory %s\n",path);
+       panic("fchdir failed");
+    }
+
+    if (fstat (dirfd,&dirst) != 0) {
+       fprintf (stderr,"Can't lstat directory %s\n",path);
+       panic("lstat failed");
+    }
+
+    if ((dir = opendir (".")) == NULL) {
+       fprintf (stderr,"Can't open (opendir) directory %s\n",path);
+       panic("opendir failed");
+    }
+
+
+    /* Walk the directory entries and compute the sum of inodes,
+       blocks, and disk space used. This code will recursively descend
+       down the directory structure. */
+
+    while ((ent=readdir(dir))!=NULL){
+       if (lstat(ent->d_name,&st)==-1){
+           fprintf (stderr,"Can't stat %s/%s\n",path,ent->d_name);
+           warning("lstat failed");
+           continue;
+       }
+       
+       dirinodes ++;
+
+       if (S_ISREG(st.st_mode)){
+           if (st.st_nlink > 1){
+               long flags;
+               int fd, res;
+
+               if ((fd = open(ent->d_name,O_RDONLY))==-1) {
+                   fprintf (stderr,"Can't open file %s/%s\n",path,ent->d_name);                    
+                   warning ("open failed");
+                   continue;
                }
-               close (dirfd);
+
+               flags = 0;
+               res = ioctl(fd, EXT2_IOC_GETFLAGS, &flags);
+               close(fd);
+
+               if ((res == 0) && (flags & EXT2_IMMUTABLE_LINK_FL)){
+                   if (verbose)
+                       printf ("Skipping %s\n",ent->d_name);               
+                   continue;
+               }
+           }
+           dirsize += st.st_size;
+           dirblocks += st.st_blocks;
+
+       } else if (S_ISDIR(st.st_mode)) {
+           if ((st.st_dev == dirst.st_dev) &&
+               (strcmp(ent->d_name,".")!=0) &&
+               (strcmp(ent->d_name,"..")!=0)) {
+
+               dirsize += st.st_size;
+               dirblocks += st.st_blocks;
+
+               name = strdup(ent->d_name);
+               if (name==0) {
+                   panic("Out of memory\n");
+               }
+               vdu_onedir(name);
+               free(name);
+               fchdir(dirfd);
+           }
+       } else {
+           dirsize += st.st_size;
+           dirblocks += st.st_blocks;
        }
-       return ret;
+    }
+    closedir (dir);
+    close (dirfd);
+    if (verbose)
+       printf("%8ld %8ld %8ld %s\n",dirinodes, dirblocks, dirsize>>10,path);
+    inodes += dirinodes;
+    blocks += dirblocks;
+    size   += dirsize;
 }
 
+
+
 int main (int argc, char *argv[])
 {
-       int ret = -1;
-       if (argc < 2){
-               fprintf (stderr,"vdu version %s\n",VERSION);
-               fprintf (stderr,"vdu directory ...\n\n");
-               fprintf (stderr
-                       ,"Compute the size of a directory tree, ignoring files\n"
-                        "with more than one link.\n");
-       }else{
-               int i;
-
-               ret = 0;
-               for (i=1; i<argc && ret != -1; i++){
-                       longlong size = 0;
-                       long ksize;
-                       
-                       ret = vdu_onedir (argv[i],&size);
-                       ksize = size >> 10;
-                       printf ("%s\t%ldK\n",argv[i],ksize);
-               }
+    int startdir, i;
+
+    if (argc < 2){
+       fprintf (stderr,"vdu version %s\n",VERSION);
+       fprintf (stderr,"vdu directory ...\n\n");
+       fprintf (stderr,"Compute the size of a directory tree.");
+    }else{
+       if ((startdir = open (".",O_RDONLY)) == -1) {
+           fprintf (stderr,"Can't open current working directory\n");
+           panic("open failed");
+       }
+
+       for (i=1; i<argc; i++){
+           inodes = blocks = size = 0;
+           vdu_onedir (argv[i]);
+           printf("%8ld %8ld %8ld %s TOTAL\n",inodes, blocks, size>>10,argv[i]);
+           if (fchdir (startdir) == -1) {
+               panic("fchdir failed");
+           }
        }
-       return ret;
+       close(startdir);
+    }
+    return 0;
 }
 
+/*
+ * Local variables:
+ *  c-basic-offset: 4
+ * End:
+ */