Search code examples
c++libzip

Unzipping with libzip loses file metadata


I have a zip file created with another application (written in Java) that compresses files using deflate method into one zip file, I verified that information like "last modified" wasn't modified to the current date, and when unzipping with Ubuntu's default archive manager it stays intact.

However, using libzip to decompress loses that data. Is there any way to avoid that behavior, or another library that guarantees metadata persistence?

Decompression code:

void decompress_zip(const std::string& zip, const std::string& out_dir, std::function<void(const std::string&)> fileListener) {
    std::string finput = zip;
    std::string foutput = out_dir;

    if(!boost::filesystem::create_directories(foutput) && !fileExists(foutput))
        throw "Failed to create directory for unzipping";

    foutput += "/tmp.zip";
    if (rename(finput.c_str(), foutput.c_str()))
        throw "Failed to move zip to new dir";
    finput = foutput;

    struct zip *za;
    struct zip_file *zf;
    struct zip_stat sb;
    char buf[100];
    int err;
    int i, len;
    int fd;
    long long sum;

    if ((za = zip_open(finput.c_str(), 0, &err)) == NULL) {
        zip_error_to_str(buf, sizeof(buf), err, errno);
        throw "can't open zip! (" + finput + ")";
    }

    for (i = 0; i < zip_get_num_entries(za, 0); i++) {
        if (zip_stat_index(za, i, 0, &sb) == 0) {
            len = strlen(sb.name);

            if (sb.name[len - 1] == '/') {
                safe_create_dir(sb.name);
            } else {
                zf = zip_fopen_index(za, i, 0);
                if (!zf) {
                    throw "failed to open file in zip! Probably corrupted!!!";
                }

                std::string cFile = out_dir + "/" + std::string(sb.name);
                fd = open(cFile.c_str(), O_RDWR | O_TRUNC | O_CREAT, 0644);
                if (fd < 0) {
                    throw "failed to create output file!";
                }

                sum = 0;
                while (sum != sb.size) {
                    len = zip_fread(zf, buf, 100);
                    if (len < 0) {
                        throw "failed to read file in zip!";
                    }
                    write(fd, buf, len);
                    sum += len;
                }
                close(fd);
                zip_fclose(zf);

                fileListener(cFile);
            }
        }
    }   

    if (zip_close(za) == -1) {
        throw "Failed to close zip archive! " + finput;
    }

    if ( std::remove(foutput.c_str()) )
        throw "Failed to remove temporary zip file! " + foutput;
}

Solution

  • I think libzip only stores the data, not metadata. It's your responsibility to store metadata separately if you require it.

    In other words, it's a feature of the archive manager application, not libzip itself.