All formats

libarchive

LanguageC (usable from C++)
LicenseBSD 2-Clause
Repositorygithub.com/libarchive/libarchive
Documentationlibarchive.org, supported formats
Depends onoptional zlib, bzip2, liblzma, zstd, OpenSSL (for other formats); RAR needs none
Read / writeyes / no for RAR; RAR4 and RAR5, volumes, solid RAR5; no encrypted archives, no solid RAR4

libarchive is a C library that reads and writes many archive formats through one streaming API: TAR, ZIP, 7z, cpio, ISO 9660, and read-only RAR. It’s the engine behind bsdtar (the tar command on Windows, macOS and FreeBSD) and Windows 11’s built-in RAR and 7z support.

Its RAR readers are independent implementations under the BSD license, not based on the UnRAR source. They handle RAR4 and RAR5 compression, filters and volumes. Encrypted RAR entries can’t be decrypted: libarchive reports them as encrypted and fails to read their data. Solid RAR4 archives aren’t supported (solid RAR5 is). If you need those, use the UnRAR library.

Installation#

Shell
# Debian / Ubuntu
sudo apt install libarchive-dev
# Fedora
sudo dnf install libarchive-devel
# macOS
brew install libarchive
# Windows (vcpkg)
vcpkg install libarchive

With CMake:

cmake
find_package(LibArchive REQUIRED)
target_link_libraries(myapp PRIVATE LibArchive::LibArchive)

Basic usage: list and extract a RAR file#

C
#include <archive.h>
#include <archive_entry.h>
#include <stdio.h>

int main(int argc, char **argv) {
    if (argc < 2) return 2;
    struct archive *a = archive_read_new();
    archive_read_support_format_rar(a);    // RAR4
    archive_read_support_format_rar5(a);   // RAR5
    if (archive_read_open_filename(a, argv[1], 10240) != ARCHIVE_OK) {
        fprintf(stderr, "%s\n", archive_error_string(a));
        archive_read_free(a);
        return 1;
    }

    struct archive_entry *entry;
    int r;
    while ((r = archive_read_next_header(a, &entry)) == ARCHIVE_OK) {
        printf("%s (%lld bytes)\n", archive_entry_pathname(entry),
               (long long)archive_entry_size(entry));
        // write the entry to disk, relative to the current directory
        if (archive_read_extract(a, entry, ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_SECURE_NODOTDOT) != ARCHIVE_OK)
            fprintf(stderr, "%s\n", archive_error_string(a));
    }
    if (r != ARCHIVE_EOF)
        fprintf(stderr, "%s\n", archive_error_string(a));
    archive_read_free(a);
    return 0;
}

Use archive_read_support_format_all(a) instead to accept every format libarchive knows; the format is detected from the data, not the file name.

How to list files in a RAR without extracting#

Call archive_read_data_skip for each entry (libarchive also does it for you when you move to the next header):

C
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
    const char *type = archive_entry_filetype(entry) == AE_IFDIR ? "dir " : "file";
    printf("%s %10lld %s\n", type, (long long)archive_entry_size(entry),
           archive_entry_pathname_utf8(entry));
    archive_read_data_skip(a);
}

How to read a file from a RAR into memory#

C
#include <stdlib.h>
#include <string.h>

// returns a malloc'ed buffer with the contents of `name`, or NULL
unsigned char *read_entry(const char *path, const char *name, size_t *out_size) {
    struct archive *a = archive_read_new();
    archive_read_support_format_rar(a);
    archive_read_support_format_rar5(a);
    unsigned char *buf = NULL;
    if (archive_read_open_filename(a, path, 10240) == ARCHIVE_OK) {
        struct archive_entry *entry;
        while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
            if (strcmp(archive_entry_pathname(entry), name) != 0) continue;
            size_t size = (size_t)archive_entry_size(entry);
            buf = malloc(size ? size : 1);
            la_ssize_t n = archive_read_data(a, buf, size);
            if (n < 0 || (size_t)n != size) {  // error, e.g. encrypted entry
                fprintf(stderr, "%s\n", archive_error_string(a));
                free(buf);
                buf = NULL;
            } else {
                *out_size = size;
            }
            break;
        }
    }
    archive_read_free(a);
    return buf;
}

How to open a multi-volume RAR#

Pass all volumes, in order, to archive_read_open_filenames:

C
const char *volumes[] = { "movie.part1.rar", "movie.part2.rar", "movie.part3.rar", NULL };
struct archive *a = archive_read_new();
archive_read_support_format_rar(a);
archive_read_support_format_rar5(a);
if (archive_read_open_filenames(a, volumes, 10240) != ARCHIVE_OK) {
    fprintf(stderr, "%s\n", archive_error_string(a));
}
// read entries as usual

How to detect encrypted RAR entries#

C
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
    if (archive_entry_is_encrypted(entry)) {
        printf("%s is encrypted, skipping\n", archive_entry_pathname(entry));
        continue;
    }
    // ...
}

archive_read_has_encrypted_entries(a) answers the same for the whole archive once the first header is read. An archive with encrypted headers fails in archive_read_next_header.

Tips#