【问题标题】:corrupted size vs. prev_size Abortado (`core' generado) with function fclose使用函数 fclose 损坏的大小与 prev_size 中止(“核心”generado)
【发布时间】:2021-08-20 21:25:04
【问题描述】:

我正在尝试编写脚本文件来创建和提取 tarball。这是大学的代码,我是用 c 编程的新手。无论如何,我可以创建一个 tarball,顺便说一句,当我想提取时,由于 fclose 函数,问题就来了。一切似乎都正确,我找不到问题。 所有程序运行正常,但是当我尝试使用 fclose(tarFile) 关闭 tarFile 时,它​​给了我一条消息:

输出:

corrupted size vs. prev_size
Abortado (`core' generado)

通过调试,我发现问题出在哪里。当 header[i].size 在 readHeader 函数中的 for 循环中分配时,就会发生这种情况。

这个错误是我完成练习唯一需要解决的问题,因为来自 tarball 的数据已正确复制,但关闭 de tarFile 很重要,因此我感谢任何类型的帮助。

非常感谢,如果我能够更好地解释它,我很抱歉。这是我在 stackoverflow 上的第一篇文章,英语不是我的母语。

这里是代码

  • 提取函数
/** Extract files stored in a tarball archive
 *
 * tarName: tarball's pathname
 *
 * On success, it returns EXIT_SUCCESS; upon error it returns EXIT_FAILURE. 
 * (macros defined in stdlib.h).
 *
 * HINTS: First load the tarball's header into memory.
 * After reading the header, the file position indicator will be located at the 
 * tarball's data section. By using information from the 
 * header --number of files and (file name, file size) pairs--, extract files 
 * stored in the data section of the tarball.
 *
 */
int
extractTar(char tarName[])
{
    //1. Load Headers
    FILE* tar = NULL;
    tar = fopen(tarName, "r");
    if(tar == NULL){
        printf ("Error al abrir el tarball\n");
        return EXIT_FAILURE;
    }
    printf("ADDRES INIT: %p\n", &tar);
    int nFiles = 0;
    stHeaderEntry* header = NULL;
    //Carga el header y nFiles
    header = readHeader(tar, &nFiles);
    if(header == NULL || nFiles <= 0){
        printf("Error al crear header // NUM FILES: %d\n", nFiles);
        return EXIT_FAILURE;
    }
    printf("Copiando...\n");
    
    //3. Copia de datos
    //Para este punto el tar debería apuntar a la sección de datos
    //por lo que se podría pasar a copiar los datos en función
    //del header anteriormente cargado
    FILE* output = NULL;
    for(int i = 0; i < nFiles; i++){
        output = fopen(header[i].name, "w");
        if(output == NULL){
            printf("Error al abrir output\n");
            return EXIT_FAILURE;
        }
        int n = copynFile(tar, output, header[i].size);
        if(n == -1){
            printf("Error al copìar los datos en output\n");
            return EXIT_FAILURE;
        }

        if(fclose(output) != 0){
            printf("Error al cerra output\n");
            return EXIT_FAILURE;
        }
    }   


    printf("LLAMADA A FREE\n");
//-------------FREE_MEMORY-----------------------//
    printf("%s\n", header[0].name);
    for(int i = 0; i < nFiles; i++){
        printf("FREE HEADER NAME\n");
        free(header[i].name);
        header[i].name = NULL;
    }   
    
    printf("FREE HEADER\n");
    free(header);
    
    printf("CLOSE TAR\n");
    printf("ADDRES END: %p\n", &tar);
    if(fclose(tar) != 0){
        printf("Fallo al cerrar tarFile\n");
        return EXIT_FAILURE;
    }
    
    printf("Extracción realizada con éxito\n");
    return EXIT_SUCCESS;
}
  • readHeader 函数
/** Read tarball header and store it in memory.
 *
 * tarFile: pointer to the tarball's FILE descriptor 
 * nFiles: output parameter. Used to return the number 
 * of files stored in the tarball archive (first 4 bytes of the header).
 *
 * On success it returns the starting memory address of an array that stores 
 * the (name,size) pairs read from the tar file. Upon failure, the function returns NULL.
 */
stHeaderEntry*
readHeader(FILE * tarFile, int *nFiles)
{
    int nrFiles = 0;
    //Lectura de nFiles
    if((fread(&nrFiles, sizeof(int), 1, tarFile)) != 1){
        printf("Error al leer nFiles\n");
        return NULL;
    }
    
    //Reserva de memoria en función de nFiles
    stHeaderEntry* header = malloc(sizeof(stHeaderEntry) * (*nFiles));

    //Lectura de los datos del header
    char* str = NULL;
    
    int size = 0;
    for(int i = 0; i < nrFiles; i++){
        //Primero el nombre
        str = loadstr(tarFile);
        if(str == NULL){
            return NULL;
        }
        
        header[i].name = malloc(sizeof(str) + 1);
        header[i].name = strcpy(header[i].name, str);
        header[i].name = strcat(header[i].name, "\0");
    
        
        //Segundo los bytes del archivo
        int n = fread(&size, sizeof(unsigned int), 1, tarFile);
        if(n != 1){
            printf("Error al leer header size\n");
            return NULL;
        }

        header[i].size = size;
    }       

    //Carga completa
    printf("READHEADER SUCCESSFUL\n");
    (*nFiles) = nrFiles;
    return header;
}

【问题讨论】:

  • 我认为你应该添加loadstr函数的源代码;需要确保发布的答案是正确的(例如,我们需要知道它返回的字符串是否以空值结尾)。
  • @idz 不知道你为什么删除你的答案 - 'malloc(sizeof(str) + 1);'在我看来也很可疑(str 是一个指针)。
  • 看来readHeader 可能混淆了nFilesnrFiles?您从文件中读取了nrFiles,但随后您根据*nFilesheader 分配空间,该空间尚未修改且仍为零。无论如何,拥有两个具有如此相似名称的变量可能是个坏主意。
  • @MartinJames 因为我认为可能还有其他事情正在发生,如果没有loadstr 来源,很难确定。 OP 附加一个 null 的事实可能意味着 str 我们没有以 null 结尾,然后 strlen 不是解决方案; loadstr 需要修改。我的意思是sizeof(str) 肯定是错误的,但这只是部分答案。
  • 好的,伙计们。我发布了loadstr函数,谢谢大家的回复

标签: c abort fclose


【解决方案1】:
  • loadstr 包含这个
/** Loads a string from a file.
 *
 * file: pointer to the FILE descriptor 
 * 
 * The loadstr() function must allocate memory from the heap to store 
 * the contents of the string read from the FILE. 
 * Once the string has been properly built in memory, the function returns
 * the starting address of the string (pointer returned by malloc()) 
 * 
 * Returns: !=NULL if success, NULL if error
 */
char*
loadstr(FILE * file)
{
    char* str = NULL;
    char buf[1];
    int length = 0;
    int n = 0;
    //Asignación del tamaño del buffer
    while((n = fread(buf, 1, 1, file)) != 0 && strcmp(buf, "\0") != 0){
        length++;
    }
    
    str = malloc(length + 1);
    
    //Debido al while anterior, ahora file apunta a otro lado
    //Hay que hacer un rewind
    fseek(file, -(length + 1), SEEK_CUR);
    
    //Se guarda el nombre en str
    if(fread(str, length + 1, 1, file) != 1){
        printf("Error en loadstr\n");
        return NULL;
    }
    return  str;
}

我之前没有发帖,因为我认为没有必要。

【讨论】:

  • strcmp(buf, "\0") 中,1 字节数组char buf[1]; 中的任何值(0 除外)在传递给strcmp() 时都会导致未定义的行为。您计算的length 可能不正确,如果fseek(file, -(length + 1), SEEK_CUR); 旨在倒带文件,那么rewind(file) 会更可靠。 (这段代码真的应该添加到问题中,而不是作为答案)
  • 感谢@WeatherVane,但我不想使用倒带,因为它将文件倒带到开头,我不想要那个。另一方面,我将在 te futur 中编辑问题,这是一个非常愚蠢的错误:P 我真的是这个论坛的新帖子,我没有看到编辑按钮。
【解决方案2】:

在您的readHeader 函数中,您为字符串分配空间,如下所示:

header[i].name = malloc(sizeof(str) + 1);

这将使用str 的大小,一个字符串指针,它是一个常量值,取决于系统指针的大小(通常为 4 或 8);这不是你想要的。

您应该使用strlen 来计算str 指向的C 字符串的长度。

header[i].name = malloc(strlen(str) + 1);

loadstr 也有问题,因为你只将一个字符读入缓冲区,你应该改变:

    while((n = fread(buf, 1, 1, file)) != 0 && strcmp(buf, "\0") != 0){
        length++;
    }

    while((n = fread(buf, 1, 1, file)) != 0 && buf[0] != '\0'){
        length++;
    }

否则strcmp 可能正在从未初始化的内存中读取。

其他一些观察:

  • 您实际上不需要复制字符串,因为 loadstr 正在分配它,但如果这样做,您还需要从 loadstr 释放返回。
  • 您不需要在strcpy 之后终止字符串,它会为您完成。
  • 阅读strncpy,它比strcpy 更安全。

【讨论】:

  • 好的,感谢您的提示,我已经按照您告诉我的那样更改了代码,但这并没有解决问题,因为它比看起来更愚蠢。在问题的 cmets 中,@NateEldredge 有密钥,因为名称 nrFilenFile 在 readHeader 函数中被混淆了;将两个变量命名为非常相似的名称是个坏主意。
猜你喜欢
  • 2019-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-12
  • 2018-01-23
  • 1970-01-01
  • 2021-10-23
相关资源
最近更新 更多