Compare commits

..

No commits in common. "d8068a6f5f8999dd274e25603a15df2f987af3b3" and "d939939723fbc653f593d707d68448c9f162b300" have entirely different histories.

5 changed files with 91 additions and 261 deletions

View file

@ -55,8 +55,6 @@ Following the file-count bytes, the following bytes are added for each file:
2. The second byte. 2. The second byte.
1. The first bit is "other write permission". 1. The first bit is "other write permission".
2. The second bit is "other execute permission". 2. The second bit is "other execute permission".
3. The third bit is UNSET if relative links are preferred, and is SET
if absolute links are preferred.
3. The third byte. 3. The third byte.
1. Currently unused. 1. Currently unused.
4. The fourth byte. 4. The fourth byte.

View file

@ -37,6 +37,7 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#include "data_structures/hash_map.h"
#include "helpers.h" #include "helpers.h"
#define TEMP_FILENAME_CMP "simple_archiver_compressed_%u.tmp" #define TEMP_FILENAME_CMP "simple_archiver_compressed_%u.tmp"
@ -103,43 +104,6 @@ void cleanup_temp_filename_delete(void ***ptrs_array) {
#endif #endif
} }
char *filename_to_absolute_path(const char *filename) {
__attribute__((cleanup(free_malloced_memory))) void *path =
malloc(strlen(filename) + 1);
strncpy(path, filename, strlen(filename) + 1);
char *path_dir = dirname(path);
if (!path_dir) {
return NULL;
}
__attribute__((cleanup(free_malloced_memory))) void *dir_realpath =
realpath(path_dir, NULL);
if (!dir_realpath) {
return NULL;
}
// Recreate "path" since it may have been modified by dirname().
free_malloced_memory(&path);
path = malloc(strlen(filename) + 1);
strncpy(path, filename, strlen(filename) + 1);
char *filename_basename = basename(path);
if (!filename_basename) {
return NULL;
}
// Get combined full path to file.
char *fullpath =
malloc(strlen(dir_realpath) + 1 + strlen(filename_basename) + 1);
strncpy(fullpath, dir_realpath, strlen(dir_realpath) + 1);
fullpath[strlen(dir_realpath)] = '/';
strncpy(fullpath + strlen(dir_realpath) + 1, filename_basename,
strlen(filename_basename) + 1);
return fullpath;
}
int write_files_fn(void *data, void *ud) { int write_files_fn(void *data, void *ud) {
const SDArchiverFileInfo *file_info = data; const SDArchiverFileInfo *file_info = data;
SDArchiverState *state = ud; SDArchiverState *state = ud;
@ -704,90 +668,89 @@ int write_files_fn(void *data, void *ud) {
// Unsupported platform. Just set the permission bits for user. // Unsupported platform. Just set the permission bits for user.
((unsigned char *)temp_to_write->buf)[0] |= 0xE; ((unsigned char *)temp_to_write->buf)[0] |= 0xE;
#endif #endif
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
// Need to get abs_path for checking/setting a flag before storing flags.
// Get absolute path. // Get absolute path.
__attribute__((cleanup(free_malloced_memory))) void *abs_path = __attribute__((cleanup(free_malloced_memory))) void *abs_path =
realpath(file_info->filename, NULL); realpath(file_info->filename, NULL);
__attribute__((cleanup(free_malloced_memory))) void *rel_path = NULL;
if (abs_path) {
// Get relative path.
// First get absolute path of link.
__attribute__((cleanup(free_malloced_memory))) void *link_abs_path =
filename_to_absolute_path(file_info->filename);
if (!link_abs_path) {
fprintf(stderr, "WARNING: Failed to get absolute path of link!\n");
} else {
// fprintf(stderr, "DEBUG: abs_path: %s\nDEBUG: link_abs_path: %s\n",
// (char*)abs_path, (char*)link_abs_path);
// Compare paths to get relative path.
// Get first non-common char.
unsigned int idx;
unsigned int last_slash;
for (idx = 0, last_slash = 0;
idx < strlen(abs_path) && idx < strlen(link_abs_path); ++idx) {
if (((const char *)abs_path)[idx] !=
((const char *)link_abs_path)[idx]) {
break;
} else if (((const char *)abs_path)[idx] == '/') {
last_slash = idx + 1;
}
}
// Get substrings of both paths.
char *link_substr = (char *)link_abs_path + last_slash;
char *dest_substr = (char *)abs_path + last_slash;
rel_path = malloc(strlen(dest_substr) + 1);
strncpy(rel_path, dest_substr, strlen(dest_substr) + 1);
// fprintf(stderr, "DEBUG: link_substr: %s\nDEBUG: dest_substr: %s\n",
// link_substr, dest_substr);
// Generate the relative path.
int has_slash = 0;
idx = 0;
do {
for (; link_substr[idx] != '/' && link_substr[idx] != 0; ++idx);
if (link_substr[idx] == 0) {
has_slash = 0;
} else {
has_slash = 1;
char *new_rel_path = malloc(strlen(rel_path) + 1 + 3);
new_rel_path[0] = '.';
new_rel_path[1] = '.';
new_rel_path[2] = '/';
strncpy(new_rel_path + 3, rel_path, strlen(rel_path) + 1);
free(rel_path);
rel_path = new_rel_path;
++idx;
}
} while (has_slash);
}
}
// Check if absolute path refers to one of the filenames.
if (abs_path && (state->parsed->flags & 0x20) == 0 &&
!simple_archiver_hash_map_get(state->map, abs_path,
strlen(abs_path) + 1)) {
// Is not a filename being archived, set preference to absolute path.
fprintf(stderr,
"NOTICE: abs_path exists, \"--no-abs-symlink\" not specified, "
"and link refers to file NOT in archive; preferring abs_path.\n");
((unsigned char *)temp_to_write->buf)[1] |= 0x4;
}
// Store the 4 byte bit-flags for file.
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
// Store the absolute and relative paths.
if (!abs_path) { if (!abs_path) {
fprintf(stderr, fprintf(stderr,
"WARNING: Failed to get absolute path of link destination!\n"); "WARNING: Failed to get absolute path of link destination!\n");
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite)); }
temp_to_write->buf = malloc(2);
temp_to_write->size = 2; // Get relative path.
memset(temp_to_write->buf, 0, 2); __attribute__((cleanup(free_malloced_memory))) void *rel_path = NULL;
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write); // First get absolute path of link.
} else if ((state->parsed->flags & 0x20) == 0) { // Get abs path to dirname of link.
unsigned int link_dir_path_len = strlen(file_info->filename) + 1;
__attribute__((cleanup(free_malloced_memory))) void *link_dir_path =
malloc(link_dir_path_len);
strncpy(link_dir_path, file_info->filename, link_dir_path_len);
char *link_dirname = dirname(link_dir_path);
__attribute__((cleanup(free_malloced_memory))) void *link_dir_abs_path =
realpath(link_dirname, NULL);
if (!link_dir_abs_path) {
fprintf(stderr,
"WARNING: Failed to get absolute path of link directory!\n");
} else {
// Get basename of link to append.
__attribute__((cleanup(free_malloced_memory))) void *link_filename =
malloc(strlen(file_info->filename) + 1);
strncpy(link_filename, file_info->filename,
strlen(file_info->filename) + 1);
char *link_basename = basename(link_filename);
// Set up full path to link.
unsigned int link_path_len = strlen(link_dir_abs_path);
__attribute__((cleanup(free_malloced_memory))) void *combined_path =
malloc(link_path_len + 1 + strlen(link_basename) + 1);
strncpy(combined_path, link_dir_abs_path, link_path_len + 1);
((char *)combined_path)[link_path_len] = '/';
strncpy((char *)combined_path + link_path_len + 1, link_basename,
strlen(link_basename) + 1);
// fprintf(stderr, "DEBUG: abs_path: %s\nDEBUG: combined_path: %s\n",
// (char*)abs_path, (char*)combined_path);
// Compare paths to get relative path.
// Get first non-common char.
unsigned int idx;
unsigned int last_slash;
for (idx = 0, last_slash = 0;
idx < strlen(abs_path) && idx < strlen(combined_path); ++idx) {
if (((const char *)abs_path)[idx] !=
((const char *)combined_path)[idx]) {
break;
} else if (((const char *)abs_path)[idx] == '/') {
last_slash = idx + 1;
}
}
// Get substrings of both paths.
char *link_substr = (char *)combined_path + last_slash;
char *dest_substr = (char *)abs_path + last_slash;
rel_path = malloc(strlen(dest_substr) + 1);
strncpy(rel_path, dest_substr, strlen(dest_substr) + 1);
// fprintf(stderr, "DEBUG: link_substr: %s\nDEBUG: dest_substr: %s\n",
// link_substr, dest_substr);
// Generate the relative path.
int has_slash = 0;
idx = 0;
do {
for (; link_substr[idx] != '/' && link_substr[idx] != 0; ++idx);
if (link_substr[idx] == 0) {
has_slash = 0;
} else {
has_slash = 1;
char *new_rel_path = malloc(strlen(rel_path) + 1 + 3);
new_rel_path[0] = '.';
new_rel_path[1] = '.';
new_rel_path[2] = '/';
strncpy(new_rel_path + 3, rel_path, strlen(rel_path) + 1);
free(rel_path);
rel_path = new_rel_path;
++idx;
}
} while (has_slash);
}
if (abs_path) {
// Write absolute path length. // Write absolute path length.
u16 = strlen(abs_path); u16 = strlen(abs_path);
simple_archiver_helper_16_bit_be(&u16); simple_archiver_helper_16_bit_be(&u16);
@ -806,9 +769,6 @@ int write_files_fn(void *data, void *ud) {
strncpy(temp_to_write->buf, abs_path, u16 + 1); strncpy(temp_to_write->buf, abs_path, u16 + 1);
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write); simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
} else { } else {
fprintf(stderr,
"NOTICE: Not saving absolute path since \"--no-abs-symlink\" "
"was specified.\n");
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite)); temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
temp_to_write->buf = malloc(2); temp_to_write->buf = malloc(2);
temp_to_write->size = 2; temp_to_write->size = 2;
@ -843,9 +803,7 @@ int write_files_fn(void *data, void *ud) {
// Write all previously set data. // Write all previously set data.
fprintf(stderr, "Writing symlink info: %s\n", file_info->filename); fprintf(stderr, "Writing symlink info: %s\n", file_info->filename);
if ((state->parsed->flags & 0x20) == 0) { fprintf(stderr, " abs path: %s\n", (char *)abs_path);
fprintf(stderr, " abs path: %s\n", (char *)abs_path);
}
fprintf(stderr, " rel path: %s\n", (char *)rel_path); fprintf(stderr, " rel path: %s\n", (char *)rel_path);
simple_archiver_list_get(to_write, write_list_datas_fn, state->out_f); simple_archiver_list_get(to_write, write_list_datas_fn, state->out_f);
simple_archiver_list_free(&to_write); simple_archiver_list_free(&to_write);
@ -858,54 +816,6 @@ int write_files_fn(void *data, void *ud) {
void cleanup_nop_fn(__attribute__((unused)) void *unused) {} void cleanup_nop_fn(__attribute__((unused)) void *unused) {}
void cleanup_free_fn(void *data) { free(data); } void cleanup_free_fn(void *data) { free(data); }
int filenames_to_abs_map_fn(void *data, void *ud) {
SDArchiverFileInfo *file_info = data;
SDArchiverHashMap **abs_filenames = ud;
// Get combined full path to file.
char *fullpath = filename_to_absolute_path(file_info->filename);
if (!fullpath) {
return 1;
}
simple_archiver_hash_map_insert(abs_filenames, fullpath, fullpath,
strlen(fullpath) + 1, cleanup_nop_fn, NULL);
// Try putting all parent dirs up to current working directory.
// First get absolute path to current working directory.
__attribute__((cleanup(free_malloced_memory))) void *cwd_dirname =
realpath(".", NULL);
if (!cwd_dirname) {
return 1;
}
// Use copy of fullpath to avoid clobbering it.
__attribute__((cleanup(free_malloced_memory))) void *fullpath_copy =
malloc(strlen(fullpath) + 1);
strncpy(fullpath_copy, fullpath, strlen(fullpath) + 1);
// Get dirnames.
char *prev = fullpath_copy;
char *fullpath_dirname;
while (1) {
fullpath_dirname = dirname(prev);
if (!fullpath_dirname || strlen(fullpath_dirname) <= strlen(cwd_dirname)) {
break;
} else {
// Make and store copy of fullpath_dirname.
char *fullpath_dirname_copy = malloc(strlen(fullpath_dirname) + 1);
strncpy(fullpath_dirname_copy, fullpath_dirname,
strlen(fullpath_dirname) + 1);
simple_archiver_hash_map_insert(
abs_filenames, fullpath_dirname_copy, fullpath_dirname_copy,
strlen(fullpath_dirname_copy) + 1, cleanup_nop_fn, NULL);
}
prev = fullpath_dirname;
}
return 0;
}
char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) { char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) {
switch (error) { switch (error) {
case SDAS_SUCCESS: case SDAS_SUCCESS:
@ -924,10 +834,6 @@ char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) {
return "Invalid file"; return "Invalid file";
case SDAS_INTERNAL_ERROR: case SDAS_INTERNAL_ERROR:
return "Internal error"; return "Internal error";
case SDAS_FAILED_TO_CREATE_MAP:
return "Failed to create set of filenames (internal error)";
case SDAS_FAILED_TO_EXTRACT_SYMLINK:
return "Failed to extract symlink (internal error)";
default: default:
return "Unknown error"; return "Unknown error";
} }
@ -942,7 +848,6 @@ SDArchiverState *simple_archiver_init_state(const SDArchiverParsed *parsed) {
state->flags = 0; state->flags = 0;
state->parsed = parsed; state->parsed = parsed;
state->out_f = NULL; state->out_f = NULL;
state->map = NULL;
return state; return state;
} }
@ -956,14 +861,6 @@ void simple_archiver_free_state(SDArchiverState **state) {
int simple_archiver_write_all(FILE *out_f, SDArchiverState *state, int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
const SDArchiverLinkedList *filenames) { const SDArchiverLinkedList *filenames) {
// First create a "set" of absolute paths to given filenames.
__attribute__((cleanup(simple_archiver_hash_map_free)))
SDArchiverHashMap *abs_filenames = simple_archiver_hash_map_init();
if (simple_archiver_list_get(filenames, filenames_to_abs_map_fn,
&abs_filenames)) {
return SDAS_FAILED_TO_CREATE_MAP;
}
if (fwrite("SIMPLE_ARCHIVE_VER", 1, 18, out_f) != 18) { if (fwrite("SIMPLE_ARCHIVE_VER", 1, 18, out_f) != 18) {
return SDAS_FAILED_TO_WRITE; return SDAS_FAILED_TO_WRITE;
} }
@ -1047,7 +944,6 @@ int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
state->count = 0; state->count = 0;
state->max = filenames->count; state->max = filenames->count;
state->out_f = out_f; state->out_f = out_f;
state->map = abs_filenames;
fprintf(stderr, "Begin archiving...\n"); fprintf(stderr, "Begin archiving...\n");
fprintf(stderr, "[%10u/%10u]\n", state->count, state->max); fprintf(stderr, "[%10u/%10u]\n", state->count, state->max);
if (simple_archiver_list_get(filenames, write_files_fn, state)) { if (simple_archiver_list_get(filenames, write_files_fn, state)) {
@ -1615,14 +1511,6 @@ int simple_archiver_parse_archive_info(FILE *in_f, int do_extract,
} }
} else { } else {
// Is a symbolic link. // Is a symbolic link.
int abs_preferred = (buf[1] & 0x4) != 0 ? 1 : 0;
fprintf(stderr, " Absolute path is %s\n",
(abs_preferred ? "preferred" : "NOT preferred"));
__attribute__((cleanup(free_malloced_memory))) void *abs_path = NULL;
__attribute__((cleanup(free_malloced_memory))) void *rel_path = NULL;
if (fread(&u16, 2, 1, in_f) != 1) { if (fread(&u16, 2, 1, in_f) != 1) {
return SDAS_INVALID_FILE; return SDAS_INVALID_FILE;
} }
@ -1635,15 +1523,15 @@ int simple_archiver_parse_archive_info(FILE *in_f, int do_extract,
} }
buf[1023] = 0; buf[1023] = 0;
fprintf(stderr, " Link absolute path: %s\n", buf); fprintf(stderr, " Link absolute path: %s\n", buf);
abs_path = malloc((size_t)u16 + 1);
strncpy(abs_path, (char *)buf, (size_t)u16 + 1);
} else { } else {
abs_path = malloc(u16 + 1); __attribute__((cleanup(free_malloced_memory))) void *heap_buf =
if (fread(abs_path, 1, u16 + 1, in_f) != (size_t)u16 + 1) { malloc(u16 + 1);
unsigned char *uc_heap_buf = heap_buf;
if (fread(uc_heap_buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
return SDAS_INVALID_FILE; return SDAS_INVALID_FILE;
} }
((char *)abs_path)[u16 - 1] = 0; uc_heap_buf[u16 - 1] = 0;
fprintf(stderr, " Link absolute path: %s\n", (char *)abs_path); fprintf(stderr, " Link absolute path: %s\n", uc_heap_buf);
} }
if (fread(&u16, 2, 1, in_f) != 1) { if (fread(&u16, 2, 1, in_f) != 1) {
@ -1658,62 +1546,15 @@ int simple_archiver_parse_archive_info(FILE *in_f, int do_extract,
} }
buf[1023] = 0; buf[1023] = 0;
fprintf(stderr, " Link relative path: %s\n", buf); fprintf(stderr, " Link relative path: %s\n", buf);
rel_path = malloc((size_t)u16 + 1);
strncpy(rel_path, (char *)buf, (size_t)u16 + 1);
} else { } else {
rel_path = malloc(u16 + 1); __attribute__((cleanup(free_malloced_memory))) void *heap_buf =
if (fread(rel_path, 1, u16 + 1, in_f) != (size_t)u16 + 1) { malloc(u16 + 1);
unsigned char *uc_heap_buf = heap_buf;
if (fread(uc_heap_buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
return SDAS_INVALID_FILE; return SDAS_INVALID_FILE;
} }
((char *)rel_path)[u16 - 1] = 0; uc_heap_buf[u16 - 1] = 0;
fprintf(stderr, " Link relative path: %s\n", (char *)rel_path); fprintf(stderr, " Link relative path: %s\n", uc_heap_buf);
}
if (do_extract) {
simple_archiver_helper_make_dirs((const char *)out_f_name);
if (abs_path && rel_path) {
if (abs_preferred) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
int ret = symlink(abs_path, out_f_name);
if (ret == -1) {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
#endif
} else {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
int ret = symlink(rel_path, out_f_name);
if (ret == -1) {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
#endif
}
} else if (abs_path) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
int ret = symlink(abs_path, out_f_name);
if (ret == -1) {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
#endif
} else if (rel_path) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
int ret = symlink(rel_path, out_f_name);
if (ret == -1) {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
#endif
} else {
fprintf(
stderr,
"WARNING: Symlink entry in archive has no paths to link to!\n");
}
} }
} }
} }

View file

@ -21,7 +21,6 @@
#include <stdio.h> #include <stdio.h>
#include "data_structures/hash_map.h"
#include "data_structures/linked_list.h" #include "data_structures/linked_list.h"
#include "parser.h" #include "parser.h"
@ -31,7 +30,6 @@ typedef struct SDArchiverState {
unsigned int flags; unsigned int flags;
const SDArchiverParsed *parsed; const SDArchiverParsed *parsed;
FILE *out_f; FILE *out_f;
SDArchiverHashMap *map;
unsigned int count; unsigned int count;
unsigned int max; unsigned int max;
} SDArchiverState; } SDArchiverState;
@ -44,9 +42,7 @@ enum SDArchiverStateReturns {
SDAS_NO_DECOMPRESSOR, SDAS_NO_DECOMPRESSOR,
SDAS_INVALID_PARSED_STATE, SDAS_INVALID_PARSED_STATE,
SDAS_INVALID_FILE, SDAS_INVALID_FILE,
SDAS_INTERNAL_ERROR, SDAS_INTERNAL_ERROR
SDAS_FAILED_TO_CREATE_MAP,
SDAS_FAILED_TO_EXTRACT_SYMLINK
}; };
/// Returned pointer must not be freed. /// Returned pointer must not be freed.

View file

@ -151,8 +151,6 @@ void simple_archiver_print_usage(void) {
"file's stored decompressor\n"); "file's stored decompressor\n");
fprintf(stderr, "--overwrite-create : allows overwriting an archive file\n"); fprintf(stderr, "--overwrite-create : allows overwriting an archive file\n");
fprintf(stderr, "--overwrite-extract : allows overwriting when extracting\n"); fprintf(stderr, "--overwrite-extract : allows overwriting when extracting\n");
fprintf(stderr,
"--no-abs-symlink : do not store absolute paths for symlinks\n");
fprintf(stderr, fprintf(stderr,
"-- : specifies remaining arguments are files to archive/extract\n"); "-- : specifies remaining arguments are files to archive/extract\n");
fprintf( fprintf(
@ -245,8 +243,6 @@ int simple_archiver_parse_args(int argc, const char **argv,
out->flags |= 0x4; out->flags |= 0x4;
} else if (strcmp(argv[0], "--overwrite-extract") == 0) { } else if (strcmp(argv[0], "--overwrite-extract") == 0) {
out->flags |= 0x8; out->flags |= 0x8;
} else if (strcmp(argv[0], "--no-abs-symlink") == 0) {
out->flags |= 0x20;
} else if (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) { } else if (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) {
is_remaining_args = 1; is_remaining_args = 1;
} else if (argv[0][0] != '-') { } else if (argv[0][0] != '-') {

View file

@ -30,7 +30,6 @@ typedef struct SDArchiverParsed {
/// 0b xxxx x1xx - Allow create archive overwrite. /// 0b xxxx x1xx - Allow create archive overwrite.
/// 0b xxxx 1xxx - Allow extract overwrite. /// 0b xxxx 1xxx - Allow extract overwrite.
/// 0b xxx1 xxxx - Create archive to stdout or read archive from stdin. /// 0b xxx1 xxxx - Create archive to stdout or read archive from stdin.
/// 0b xx1x xxxx - Do not save absolute paths for symlinks.
unsigned int flags; unsigned int flags;
/// Null-terminated string. /// Null-terminated string.
char *filename; char *filename;