0

I tried to search for this question but couldn't find satisfying answer. So here's my question:

I am traversing through directories with following variations of code:

I.

#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>
#include<string.h>
#include<sys/stat.h>

void traverseDirectory(char name[100]){
        DIR* dir;
        struct dirent *ent;
        struct stat states;

        dir = opendir(name);

        while((ent=readdir(dir)) != NULL){
                stat(ent->d_name,&states);
                if(!strcmp(".", ent->d_name) || !strcmp("..", ent->d_name)){
                        continue;
                }
                else{
                        printf("%s/%s\n",name,ent->d_name);
                        if(S_ISDIR(states.st_mode)){
                                strcat(name,"/");
                                strcat(name,ent->d_name);
                                traverseDirectory(name);
                        }
                }
        }

        closedir(dir);
}

int main(){
        char path[100];
        printf("Enter the path:\n");
        scanf("%s",&path);
        traverseDirectory(path);
}

This one traverses through sub-directories but gives segmentation fault after traversing through first sub-directory & printing its files.

Output is:

Enter the path:
/home/harshad/dump

/home/harshad/dump/TraverseDirectory.c

/home/harshad/dump/temp

/home/harshad/dump/temp/temptest.txt
Segmentation fault

II.

#include<stdio.h>
#include<sys/types.h>
#include<dirent.h>
#include<string.h>
#include<sys/stat.h>

void traverseDirectory(char name[100]){
        DIR* dir;
        struct dirent *ent;
        struct stat states;

        dir = opendir(name);

        while((ent=readdir(dir)) != NULL){
                stat(ent->d_name,&states);
                if(!strcmp(".", ent->d_name) || !strcmp("..", ent->d_name)){
                        continue;
                }
                else{
                        printf("%s/%s\n",name,ent->d_name);
                        if(S_ISDIR(ent->d_type & DT_DIR)){
                                strcat(name,"/");
                                strcat(name,ent->d_name);
                                traverseDirectory(name);
                        }
                }
        }

        closedir(dir);
}

int main(){
        char path[100];
        printf("Enter the path:\n");
        scanf("%s",&path);
        traverseDirectory(path);
}

This one prints all the files & sub-directories in given directory but doesn't traverse in sub-directories. It's output is:

Enter the path:
/home/harshad/dump

/home/harshad/dump/TraverseDirectory.c

/home/harshad/dump/temp

/home/harshad/dump/TraverseDirectory1.out

/home/harshad/dump/dump

/home/harshad/dump/test.txt

/home/harshad/dump/SortMarks.c

/home/harshad/dump/TraverseDirectory.out

/home/harshad/dump/TraverseDirectoryTemp.out

/home/harshad/dump/TraverseDirectory1.c

/home/harshad/dump/TraverseDirectoryTemp.c

/home/harshad/dump/FindEven.c

Here dump & temp are sub-directories each of which contains some files. First I thought because of user permissions it might not be able to traverse in sub-directories (as they are created & owned by root) but as you can see in 1st program's output that is not the case. So I am not able to figure out problems with both the programs. P.S.: In outputs after first two lines program prints directories & files.

harshad
  • 380
  • 3
  • 16

2 Answers2

5

You should really check for error in opendir(). If it fails it will return NULL, and then the next readdir() will seg-fault.

This may be enough:

dir = opendir(name);
if (!dir)
{
    perror(name);
    return;
}
rodrigo
  • 79,651
  • 7
  • 121
  • 162
  • Thanks @rodrigo. It worked like a charm. But I am new to C and couldn't really understand the importance of condition. I thought it might be terminating condition for recursive call. But it did avoid segmentation fault. Can you please explain how & why. – harshad Jun 30 '15 at 08:12
  • 1
    @harshad: your `IS_DIR` test seems to fail sometimes. That's why (1) you get `/home/harshad/dump/temp/temptest.txt` (which, I assume, is *not* a directory) and (2) the `opendir` in the next iteration fails (because you tell it to open a non-existing directory). `IS_DIR` may be failing because you are using it wrong. – Jongware Jun 30 '15 at 08:21
  • 2
    @harshad: Any system call may fail, that includes `opendir()`, `stat()`, `readdir()`. It is usally a programming error not checking for failure. Sometimes the consequences of ignoring error are mild, sometimes they are fatal. In case of `opendir()` it returns a `NULL` pointer on failure. And using a `NULL` pointer is undefined behavior, but it usually ends up with a segmentation fault. More suble is your not checking the failure of `stat()`, because the `states` variable will be left uninitialized... – rodrigo Jun 30 '15 at 08:26
  • @Jongware "temp/" is surely a sub-directory in "dump/" directory containing "temptext.txt" file. And I think all the suggestions are pointing to similar solution i.e. to check the return values of the functions. So I think now I have a clear idea of what's going on in the code & what's the proper solution. Thanks for your help. – harshad Jun 30 '15 at 08:44
2

You are concatenating each file name onto the name passed with this:

strcat(name,ent->d_name);

but not undoing it ready for the next file. So the string gets longer and longer until it breaks its allocated size.

You should be building another string locally, so as to preserve the name passed.

char localname[100];
strcpy(localname, name);
strcat(localname,"/");
strcat(localname,ent->d_name);
traverseDirectory(localname);

but with better error protection than I have used.

Weather Vane
  • 31,226
  • 6
  • 28
  • 47
  • Yeah that logical error I found but didn't update in the question. My bad :)! Thanks for the help. – harshad Jun 30 '15 at 08:38