代码之家  ›  专栏  ›  技术社区  ›  JJ Stamp

使用C删除非空目录时出现SEGFULT

  •  0
  • JJ Stamp  · 技术社区  · 7 年前

    我正在尝试删除一个非空目录,无需系统调用,也无需使用大量库。到目前为止,我的代码是。。。

    int rmrf(char *path) {
        char* path_copy = (char *) malloc(1024 * sizeof(char));
        strcpy(path_copy, path);
        DIR *directory = opendir(path_copy);
        struct dirent *entry = readdir(directory);
        while (entry != NULL) {
            if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { //skip /. and /..
            } else if (entry->d_type == DT_DIR) { //directory recurse
                strcat(path_copy, "/");
                strcat(path_copy, entry->d_name);
                rmrf(path_copy);
                remove(path);
            } else { //file delete
                strcat(path_copy, "/");
                strcat(path_copy, entry->d_name);
                remove(path_copy);
            }
            entry = readdir(directory);
        }
        closedir(directory);
        return 0;
    }
    

    我当前的文件结构如下所示。。。

    Who
    |---Region 1
        |---County 1
            |---SubCounty 1
        |---County 2
    |---Region 2
        |---County 1
    |---Region 3
    

    目前,我遇到了seg故障,但随着时间的推移,出现在不同的地方。今天早些时候,我将得到大约两个级别的递归深度,然后将故障分离出来,但到目前为止,我甚至无法通过一个完整的级别。我无法找出问题所在,当我使用gdb来调查问题时,我得到。。。

    malloc.c: No such file or directory.
    

    任何帮助都将不胜感激!

    更新时间:

    我采纳了paxdiablo的建议,并提出了结果函数。。。

    int rmrf(char *path) {
        char* path_copy = malloc(1024);
        DIR *directory = opendir(path);
        struct dirent *entry = readdir(directory);
        while (entry != NULL) {
            if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { //skip /. and /..
            } else if (entry->d_type == DT_DIR) { //directory recurse
                strcpy(path_copy, path);
                strcat(path_copy, "/");
                strcat(path_copy, entry->d_name);
                rmrf(path_copy);
                remove(path);
            } else { //file delete
                strcpy(path_copy, path);
                strcat(path_copy, "/");
                strcat(path_copy, entry->d_name);
                remove(path_copy);
            }
            entry = readdir(directory);
        }
        closedir(directory);
        free(path_copy);
        return 0;
    }
    

    然而,我仍然得到一个seg错误,尽管它在递归中越来越深入。seg故障的gdb输出如下。。。

    Program received signal SIGSEGV, Segmentation fault.
    _int_malloc (av=av@entry=0x7ffff7dd1b20 <main_arena>, bytes=bytes@entry=32816) at malloc.c:3802
    3802    malloc.c: No such file or directory.
    (gdb) where
    #0  _int_malloc (av=av@entry=0x7ffff7dd1b20 <main_arena>, bytes=bytes@entry=32816) at malloc.c:3802
    #1  0x00007ffff7a91184 in __GI___libc_malloc (bytes=32816) at malloc.c:2913
    #2  0x00007ffff7ad51ba in __alloc_dir (statp=0x7fffffffe190, flags=0, close_fd=true, fd=6) at ../sysdeps/posix/opendir.c:247
    #3  opendir_tail (fd=6) at ../sysdeps/posix/opendir.c:145
    #4  __opendir (name=<optimized out>) at ../sysdeps/posix/opendir.c:200
    #5  0x0000000000401bca in rmrf ()
    #6  0x0000000000401c8d in rmrf ()
    #7  0x0000000000401c8d in rmrf ()
    #8  0x0000000000402380 in main ()
    

    有什么想法?

    1 回复  |  直到 7 年前
        1
  •  4
  •   paxdiablo    7 年前

    为您的 最初的 代码,你这样做 一旦 进入功能时:

    strcpy(path_copy, path);
    

    那么你这样做是为了 每个 当前目录中的文件或目录:

    strcat(path_copy, "/");
    strcat(path_copy, entry->d_name);
    

    也就是说,如果你有文件 a ,则, b c 在当前目录中 /xx 这个 path_copy 变量将循环:

    /xx/a   /xx/a/b   /xx/a/b/c
    

    而不是正确的:

    /xx/a   /xx/b     /xx/c
    

    有了足够多的文件,您将很容易地耗尽为路径分配的1024个字节。

    如果要解决此问题,则每次都应从头开始变量:

    if ((strcmp(entry->d_name, ".") != 0) && (strcmp(entry->d_name, "..") != 0)) {
        if (entry->d_type == DT_DIR) {
            strcpy(path_copy, path);
            strcat(path_copy, "/");
            strcat(path_copy, entry->d_name);
            rmrf(path_copy);
            remove(path);
        } else {
            sprintf(path_copy, "%s/%s", path, entry->d_name);
            remove(path_copy);
        }
    }
    

    您会注意到,我对您的初始条件做了一些修改,使其更有意义(仅当文件既不是 . 也没有 .. )。

    我还展示了 else 子句,使用 sprintf 而不是一套 strcpy/strcat 呼叫。您可以在 if 子句,如果您愿意的话,我已经使用旧方法保留了它,这样您就可以看到所需要做的只是添加初始路径。

    还有一些额外的要点,适用于您的第一个和/或第二个代码段:

    • 你还应该确保 自由的 在从函数返回之前,您在每个级别分配的内存 closedir() return

    • 从不 需要强制转换的返回值 malloc 自a void * 可以隐式转换为任何其他类型的指针。事实上,这样做是危险的,因为它可以隐藏某些细微的错误。

    • 同样,你 从不 需要乘以 sizeof(char) -根据定义,这始终是一个问题。

    • 您可以移动 path\u复制 之前 文件/目录检查,因为它对这两个部分都是通用的。

    • 最后,如果您正在处理的目录实际上不存在,那么您将遇到麻烦 opendir 将返回NULL,您将立即尝试将其传递给 readdir


    考虑到所有这些,我将从以下程序开始 步行 并打印出它找到的所有文件。一旦您对此感到满意,就可以在删除的位中重新添加:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <dirent.h>
    
    int rmrf(char *path) {
        char *path_copy = malloc(1024);
        DIR *directory = opendir(path);
        if (directory != NULL) {
            struct dirent *entry = readdir(directory);
            while (entry != NULL) {
                if ((strcmp(entry->d_name, ".") != 0) && (strcmp(entry->d_name, "..") != 0)) {
                    sprintf(path_copy, "%s/%s", path, entry->d_name);
                    if (entry->d_type == DT_DIR) {
                        rmrf(path_copy);
                        puts(path);
                    } else {
                        puts(path_copy);
                    }
                }
                entry = readdir(directory);
            }
            closedir(directory);
        }
        free(path_copy);
        return 0;
    }
    

    主代码只是一个驱动程序,用于确保正确设置思考。在运行之前,请确保(在当前目录中)没有 paxtest paxtest2 要保留的文件或目录。

    int main(void) {
        system("rm -rf paxjunk");
        system("mkdir paxjunk");
        system("touch paxjunk/0.txt");
        system("mkdir paxjunk/1");
        system("touch paxjunk/1/1.txt");
        system("mkdir paxjunk/2");
        system("touch paxjunk/2/2.txt");
    
        rmrf("paxjunk");
        puts("===");
    
        system("rm -rf paxjunk2");
    
        rmrf("paxjunk2");
        puts("===");
    
        system("rm -rf paxjunk");
    
        return 0;
    }
    

    运行此操作时,您应该看到它正常工作:

    paxjunk/0.txt
    paxjunk/1/1.txt
    paxjunk
    paxjunk/2/2.txt
    paxjunk
    ===
    ===