Search code examples
crecursionstat

C - recursively copying files


I dont know why but I am not finding where the problem is. I run a test to see if it is a directory or a file and even the files get considered as folders.

The problem is that while running the loop even for the files it goes into the directory process and postrs " file1 for directories"

Here is the code:

void copie_dossier(char *doss1, char *doss2) {
    struct dirent *dptr;
    DIR *dp;
    struct stat stbuf;

    char newDest[strlen(doss2) + 1];
    char tempSrc[strlen(doss1) + 1];
    strcat(doss2, "/");
    strcat(doss1, "/");
    strcpy(newDest, doss2);
    strcpy(tempSrc, doss1);

    stat(doss1, &stbuf);

    dp = opendir(doss1);

    /*
    if (!dp) {
       perror("open_dir");
       exit(1);
    }
    */

    if (!(dp))
        return;
    if (!(dptr = readdir(dp)))
        return;

    int mk = mkdir(doss2, 0777);
    if (mk == -1) {
        perror("create_dir");
        exit(1);
    }

    while ((dptr = readdir(dp)) != NULL) {
        struct stat stbuf2;
        //stat(dptr->d_name, &stbuf2);
        char *new = dptr->d_name;

        if (!strcmp(dptr->d_name, ".") || !strcmp(dptr->d_name, "..")) {
            continue;
        }

        if (!stat(tempSrc, &stbuf2)) {
            if (S_ISREG(stbuf.st_mode)) {
                printf("%s for files\n", dptr->d_name);
                strcat(newDest, dptr->d_name);
                strcat(tempSrc, dptr->d_name);
                copy_files(tempSrc, newDest);
                strcpy(newDest, doss2);
                strcpy(tempSrc, doss1);
            }

            if (S_ISDIR(stbuf.st_mode)) {
                printf("%s for directory \n", dptr->d_name);
                strcat(newDest, dptr->d_name);
                strcat(tempSrc, dptr->d_name);
                copie_dossier(tempSrc, newDest);
                strcpy(newDest, doss2);
                strcpy(tempSrc, doss1);
            } 
        }
    }
    closedir(dp);
}

Solution

  • The problem is in the allocation of the local buffers:

    char newDest[strlen(doss2) + 1];
    char tempSrc[strlen(doss1) + 1];
    strcat(doss2, "/");
    strcat(doss1, "/");
    strcpy(newDest, doss2);
    strcpy(tempSrc, doss1);
    

    Even the very first use of newDest and tempSrc invoke undefined behavior as you copy strings that do not fit in the destination. newDest and tempSrc are allocated for exactly the number of characters in doss1 and doss2 respectively (ad the final '\0'), but you append an extra / before copying.

    Later in the code, you concatenate directory entry names, causing further undefined behavior.

    You must change the logic for pathname composition. You can use memory allocation or a global buffer in which you concatenate the entries while remembering where the pathname used to end to strip it back to the previous state.

    There is another big issue in your test for directory / filename: You do not stat() the proper pathname and you use a separate stbuf2 instead of the stbuf. You should first create the pathname to the current entry, pass it to stat() and check for directory and filename on the resulting stbuf.

    Here is an improved version:

    char *make_path(const char *path, const char *name) {
        size_t len = strlen(path);
        char *p = malloc(len + 1 + strlen(name) + 1);
        if (p) {
            strcpy(p, path);
            p[len] = '/';
            strcpy(p + len + 1, name);
        }
        return p;
    }
    
    void copie_dossier(const char *doss1, const char *doss2) {
        DIR *dp;
        struct dirent *dptr;
    
        dp = opendir(doss1);
        if (dp == NULL)
            return;
    
        int mk = mkdir(doss2, 0777);
        if (mk == -1) {
            perror("create_dir");
            exit(1);
        }
    
        while ((dptr = readdir(dp)) != NULL) {
            struct stat stbuf;
            char *source, *dest;
    
            if (!strcmp(dptr->d_name, ".") || !strcmp(dptr->d_name, "..")) {
                continue;
            }
    
            source = makepath(doss1, dptr->d_name);
            dest = makepath(doss2, dptr->d_name);
            if (!source || !dest) {
                perror("makepath");
                exit(1);
            }                
            if (!stat(source, &stbuf)) {
                if (S_ISREG(stbuf.st_mode)) {
                    printf("%s for files\n", dptr->d_name);
                    copy_files(source, dest);
                } else
                if (S_ISDIR(stbuf.st_mode)) {
                    printf("%s for directory \n", dptr->d_name);
                    copie_dossier(source, dest);
                } 
            } else {
                // should issue a diagnostic message
            }
            free(source);
            free(dest);
        }
        closedir(dp);
    }
    

    You did not post the code for function copy_files. I assume it copies one file, despite the name.