Search code examples
cunixpathtraversaldirent.h

Traversing a Path in c


I am writing a program in c that takes a path name and traverses that path, and prints out all file paths it comes across and the size of that file in blocks and then if it is a dir it prints out the dir pathname and size in blocks.

The code is ending up in an infinate loop and keeps saying "Failed to get status:too many files open."

#include <dirent.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char **argv ){
    struct stat statbuf;
    struct dirent *direntp;
    DIR *dirp;
    if(stat(argv[1], &statbuf) == -1){
        perror("Failed to get file status");
        return 1;
    }
    else if(argc != 2){
        perror("Invalid amount of arguments, showtreesize requires 1 pathname");
        return 1;
    }
    else{
        if(S_ISDIR(statbuf.st_mode) || S_ISREG(statbuf.st_mode)){
            printf("%s      %d", argv[1], depthfirstapply(argv[1],     sizepathfun(argv[1])));
        }
        else{
            if(S_ISCHR(statbuf.st_mode)){
                printf("%s is a character special file.", argv[1]);
            }
            if(S_ISBLK(statbuf.st_mode)){
                printf("%s is a block special file", argv[1]);
            }
            if(S_ISFIFO(statbuf.st_mode)){
                printf("%s is a FIFO special file", argv[1]);
            }
            else{
                printf("%s is not a valid filetype", argv[1]);
            }
        }
        return 0;
    }
}


int sum = 0;
int levelcount = 0;
int isDirectory(char *path){
    struct stat statbuf;
    if(stat(path, &statbuf) == -1)
        return 0;
    else
        return S_ISDIR(statbuf.st_mode);
}

int depthfirstapply(char *path, int pathfun(char *path1)){
    struct dirent *direntp;
    DIR *dirp;
    if(isDirectory(path)){
        printf("%s\n", path);
        if((dirp = opendir(path)) == NULL){
            perror ("Failed to open directory");
            return -1;
        }
        else{
            while((direntp = readdir(dirp)) != NULL) {
                if(isDirectory(direntp->d_name)){
                    int result = depthfirstapply(direntp->d_name, pathfun);
                    if (result > 0){
                        sum += result;
                    }
                }
                else{
                    if(pathfun(direntp->d_name) >= 0){
                        sum += pathfun(direntp->d_name);
                    }
                }
            }
            while ((closedir(dirp) == -1) && (errno == EINTR)) ;
        }
    }
    else{
        sum += pathfun(path);
    }
    return sum;
}



int sizepathfun(char *path){
    struct stat statbuf;
    if(stat(path, &statbuf) == -1){
        perror("Failed to get file status");
        return -1;
    }
    if(S_ISREG(statbuf.st_mode) == 0){
        return -1;
    }
    else{
        printf("%s      %d", path,  statbuf.st_blocks);
        return statbuf.st_blocks;
    }
}

Solution

  • Several problems:

    1. You need to skip the . and .. entries. Otherwise, you'll keep looping on the same directory.

    2. Use lstat() rather than stat() in isDirectory. Otherwise, you'll recurse into symbolic links to directories, which could cause a loop.

    3. As you go down each directory level, you need to concatenate the directory components to the names.

    4. The 2nd argument to depthfirstapply is supposed to be a function. But in main() you call it with sizepathfun(argv[1)), which returns an integer. The argument should just be sizepathfun. You should be getting a compilation warning because of the type mismatch.

    POSIX provides a standard set of functions for this, fts_XXX().