即使我告诉 libzip 不要

问题描述

我遇到了 libzip 问题,我不知道是不是我做错了什么,或者是库中的错误。

首先,我从磁盘上的 zip 文件创建一个带有 zip_source_buffer_create() 的存档(不要问我为什么不直接打开文件)。

我将 freep 标志设置为 0,这意味着我将自己处理缓冲区释放,这不是 lipzip 的工作。

如果我不将文件添加到存档中,它会起作用。

如果我将一个文件添加到存档中,当我删除缓冲区时,libzip 已经释放了它并且我的应用程序崩溃了。这是库中的错误吗?

我写了这个不言自明的 mcve。使用 AUTO_FREE_BUF=0ADD_FILE=1,应用程序崩溃。每隔 (3) 个案例都有效。

libzip 版本:1.7.3

#include <zip.h>
#include <string>
#include <filesystem>
#include <fstream>
#include <iostream>

#define AUTO_FREE_BUF 0 //1-0
#define ADD_FILE 1 //1-0

int main(int argc,char** argv) {

    const std::string add_file = "file.txt";
    const std::string zip_file = "zipfile.zip";

    size_t sz = std::filesystem::file_size(zip_file);
    char* buf = new char[sz];
    {
        std::ifstream ifs{ zip_file,std::ios::binary };
        ifs.read(buf,sz);
        ifs.close();

        zip_error_t ze;
        zip_error_init(&ze);
        zip_source_t* zs = zip_source_buffer_create(buf,sz,AUTO_FREE_BUF,&ze);
        if (zs == nullptr) {
            zip_error_fini(&ze);
            return -1;
        }

        zip_error_init(&ze);
        zip_t* z = zip_open_from_source(zs,NULL,&ze);
        if (z == nullptr) {
            zip_error_fini(&ze);
            return -1;
        }
#if ADD_FILE == 1
        //add file
        {
            zip_source_t* file = zip_source_file(z,add_file.c_str(),-1);
            if (file == nullptr)
                return -1;

            zip_int64_t add = zip_file_add(z,file,ZIP_FL_ENC_GUESS);
            if (add == -1)
                return -1;
        }
#endif
        zip_error_fini(&ze);
        zip_source_keep(zs);
        int close = zip_close(z);
        if (close == -1)
            return -1;

        //write back archive to disk
        //..

#if AUTO_FREE_BUF == 1
        zip_source_free(zs);
#else
        zip_source_free(zs); //<-is supposed to NOT free the buffer (with freep=0) but sometimes does
        delete[] buf;
#endif
    }

    return 0;
}

解决方法

我已经从源代码(如 install.md 中所述)安装了 libzip 并重新设计了您的源代码。我添加了一个文本文件(带有“Hello world”)和一个包含一个文本文件的 zip 文件。

我没有收到错误消息。 (但是,zip 文件中没有添加任何内容,所以这也是不对的)

(忽略注释掉的异常参数 :P )

#include <zip.h>

#include <exception>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>

static constexpr auto add_file{"file.txt"};
static constexpr auto zip_file{"zipfile.zip"};
static constexpr auto AUTO_FREE_BUF{false};  //1-0
static constexpr auto ADD_FILE{true};        //1-0

class MsgException : public std::exception {
   public:
    MsgException(std::string message) : _message(message) {}
    char const *what() const noexcept override { return _message.c_str(); }

   private:
    std::string const _message;
};

int main() {
    std::cout << "Started main\n";
    auto const sz = std::filesystem::file_size(zip_file);
    auto *const buf = new char[sz];
    {
        std::ifstream ifs{zip_file,std::ios::binary};
        ifs.read(buf,sz);
    }
    std::cout << "Zipfile read\n";

    zip_error_t ze;
    try {
        zip_error_init(&ze);
        auto *zs = zip_source_buffer_create(buf,sz,AUTO_FREE_BUF ? 1 : 0,&ze);
        if (zs == nullptr) throw MsgException("Can't create source: %s." /*,zip_error_strerror(&ze)*/);

        //zip_error_init(&ze);
        auto *z = zip_open_from_source(zs,&ze);
        if (z == nullptr) throw MsgException("Can't open zip from source: %s." /*,zip_error_strerror(&ze)*/);

        if (ADD_FILE) {
            //add file
            auto *file = zip_source_file(z,add_file,-1);
            if (file == nullptr) throw MsgException("Can't create data source from file '%s': %s." /*,zip_strerror(z)*/);

            if (zip_file_add(z,file,ZIP_FL_ENC_GUESS) < 0) {
                //? zip_source_free(file);
                throw MsgException("Can't file to zip archive: %s." /*,zip_strerror(z)*/);
            }
        }

        zip_source_keep(zs);

        if (zip_close(z) < 0) throw MsgException("Can't close zip archive: %s." /*,zip_strerror(z)*/);

        //write back archive to disk
        //..

        zip_source_free(zs);  //<-is supposed to NOT free the buffer (with freep=0) but sometimes does
    } catch (std::exception &e) {
        std::cerr << "Program aborted with error: " << e.what() << '\n';
    }
    zip_error_fini(&ze);
    if (!AUTO_FREE_BUF) delete[] buf;

    std::cout << "Finished main\n";
    return 0;
}

使用构建

g++ -std=c++17 main.cpp -lzip -o main

编辑:添加了一些 RAII

#include <zip.h>

#include <exception>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

static constexpr auto add_file{ "file.txt" };
static constexpr auto zip_file{ "zipfile.zip" };
static constexpr auto AUTO_FREE_BUF{ false };  //1-0
static constexpr auto ADD_FILE{ true };        //1-0

class MsgException : public std::exception {
public:
    MsgException(std::string message) : _message(message) {}
    char const* what() const noexcept override { return _message.c_str(); }

private:
    std::string const _message;
};

//RAII
class ZipError {
public:
    ZipError() { zip_error_init(&ze); }
    ~ZipError() { zip_error_fini(&ze); }
    zip_error* operator&() { return &ze; }
    // actually probably need to delete copy/move constructor/assignment etc...
private:
    zip_error_t ze;
};

int main() {
    std::cout << "Started main\n";
    std::vector<char> buf(std::filesystem::file_size(zip_file)); // RAII
    {
        std::ifstream ifs{ zip_file,std::ios::binary };
        ifs.read(buf.data(),buf.size());
    }
    std::cout << "Zipfile read\n";

    
    try {
        ZipError ze;
        auto* zs = zip_source_buffer_create(buf.data(),buf.size(),zip_error_strerror(&ze)*/);

        //zip_error_init(&ze);
        auto* z = zip_open_from_source(zs,zip_error_strerror(&ze)*/);

        if (ADD_FILE) {
            //add file
            auto* file = zip_source_file(z,zip_strerror(z)*/);

        //write back archive to disk
        //..

        zip_source_free(zs);  //<-is supposed to NOT free the buffer (with freep=0) but sometimes does
    }
    catch (std::exception& e) {
        std::cerr << "Program aborted with error: " << e.what() << '\n';
    }
    
    std::cout << "Finished main\n";
    return 0;
}
,

不要使用 libzip 处理使用 WinRar 创建的 zip 存档......(使用 libzip 创建它们)

,

经过(现在几个小时)的调查,我想我明白这背后的基本原理,我认为 libzip 的行为不应该。

zip_source_free(src) 递减 src 的引用计数,如果它达到 0,则 src 对象被释放。此外,如果 src 是用 freep=1 创建的,则关联的数据缓冲区也会被释放。如果 freep=0 则不应释放数据缓冲区。

但在这种情况下以及在某些情况下,缓冲区被释放了,这可能是无意的。

在上面的 mcve 中,将文件添加到存档会使 libzip 释放数据缓冲区,即使源是使用 freep=0 创建的。如果没有文件添加到存档中,那么缓冲区不会按预期释放,我可以安全地delete[]

这不是我问题的解决方案。添加文件后,我在 mcve 中注意到 src.zip_err=18 [Invalid argument],因此文件的添加方式可能有问题 - 我还没有发现它有什么问题。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...