【问题标题】:Segfault when deleting non-empty directory using C使用 C 删除非空目录时的段错误
【发布时间】:2018-09-19 16:16:11
【问题描述】:

我正在尝试在不使用系统调用且不使用大量库的情况下删除非空目录。到目前为止,我的代码是...

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

目前我遇到了段错误,但随着时间的推移在不同的地方。今天早些时候,我会得到大约两个级别的递归深度,然后将故障排除,但到目前为止,我什至无法超过一个完整的级别。我不知道出了什么问题,当我使用 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;
}

但是我仍然遇到段错误,尽管它在递归中越来越远。段错误的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 ()

想法?

【问题讨论】:

  • 如果您在调试器中捕捉到崩溃(就像您似乎正在做的那样),那么请查看调用堆栈以找出它在 您的 代码中发生的位置。
  • 顺便问一下,在你的代码中free你为path_copy分配的内存在哪里?
  • 我目前没有为 path_copy 释放内存,因为正如我在 paxdiablos 回答中提到的那样,我不确定如何释放该内存,但在再次调用该函数时仍然通过路径。你能解释一下在离开函数之前释放吗?
  • 在函数返回之前调用free

标签: c directory segmentation-fault dirent.h


【解决方案1】:

对于您的初始代码,您在输入函数时执行此操作一次

strcpy(path_copy, path);

然后你对当前目录中的每个文件或目录执行此操作:

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

这意味着,如果您在当前目录/xx 中有文件abc,则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 子句中随意执行此操作,我使用旧方法保留了它,因此您可以看到您需要做的就是添加初始路径。

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

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

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

  • 同样,您从不需要乘以sizeof(char) - 也就是说,根据定义,总是一。

  • 您可以将path_copy 的创建移到文件/目录检查之前,因为这对两个部分都是通用的。

  • 最后,如果您正在处理的目录实际上并不存在,您将会遇到麻烦,因为 opendir 将返回 NULL,您将立即尝试将其传递给 readdir。 /p>


考虑到所有这些,我将从以下程序开始,该程序实际上遍历树并打印出它找到的所有文件。一旦您对此感到满意,您可以添加回删除的位:

#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;
}

主代码只是确保正确设置思想的驱动程序。只需确保在运行之前(在当前目录中)没有要保留的 paxtestpaxtest2 文件或目录。

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
===
===

【讨论】:

  • 你回复的最后一部分让我很困惑。如果我在离开函数之前释放在每个级别分配的内存(path_copy 因为这是唯一分配的东西),我如何将它传递给递归?
  • @JJStamp,在函数结束时,所有依赖于该字符串的递归调用都已经完全执行。因此删除是安全的。
  • 因此,离开前释放意味着在返回之前释放,而不是在第一个 else if 中调用自身之前释放,对吧?
  • @JJStamp:是的,我可以看到“离开函数”在这里可能被误解为包括“调用子函数”。将澄清以确保它只是“从功能返回”。希望能解决这个问题。
  • @paxdiablo 感受到失血的影响了吗?
猜你喜欢
  • 1970-01-01
  • 2013-08-05
  • 2012-05-31
  • 2016-10-16
  • 1970-01-01
  • 2010-12-11
  • 1970-01-01
  • 2010-12-06
  • 1970-01-01
相关资源
最近更新 更多