Add option to preserve symlinks exactly
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 16s
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 16s
The "--preserve-symlinks" option preserves the symlink target instead of deriving absolute/relative-paths from it. If archived symlinks are absolute-paths, then it is NOT recommended to use this option as the symlinks can be clobbered on extraction (unless if "--no-safe-links" is specified on extraction).
This commit is contained in:
parent
f81d007e7c
commit
a415ab22ad
4 changed files with 158 additions and 46 deletions
|
@ -27,6 +27,7 @@ API calls.
|
||||||
--overwrite-create : allows overwriting an archive file
|
--overwrite-create : allows overwriting an archive file
|
||||||
--overwrite-extract : allows overwriting when extracting
|
--overwrite-extract : allows overwriting when extracting
|
||||||
--no-abs-symlink : do not store absolute paths for symlinks
|
--no-abs-symlink : do not store absolute paths for symlinks
|
||||||
|
--preserve-symlinks : preserve the symlink's path on archive creation instead of deriving abs/relative paths, ignores "--no-abs-symlink" (It is not recommended to use this option, as absolute-path-symlinks may be clobbered on extraction)
|
||||||
--no-safe-links : keep symlinks that link to outside archive contents
|
--no-safe-links : keep symlinks that link to outside archive contents
|
||||||
--temp-files-dir <dir> : where to store temporary files created when compressing (defaults to current working directory)
|
--temp-files-dir <dir> : where to store temporary files created when compressing (defaults to current working directory)
|
||||||
--write-version <version> : Force write version file format (default 1)
|
--write-version <version> : Force write version file format (default 1)
|
||||||
|
|
128
src/archiver.c
128
src/archiver.c
|
@ -759,13 +759,28 @@ int write_files_fn(void *data, void *ud) {
|
||||||
// Get absolute path.
|
// Get absolute path.
|
||||||
__attribute__((cleanup(
|
__attribute__((cleanup(
|
||||||
simple_archiver_helper_cleanup_malloced))) void *abs_path = NULL;
|
simple_archiver_helper_cleanup_malloced))) void *abs_path = NULL;
|
||||||
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
|
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
|
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
|
|
||||||
abs_path = realpath(file_info->filename, NULL);
|
|
||||||
#endif
|
|
||||||
__attribute__((cleanup(
|
__attribute__((cleanup(
|
||||||
simple_archiver_helper_cleanup_malloced))) void *rel_path = NULL;
|
simple_archiver_helper_cleanup_malloced))) void *rel_path = NULL;
|
||||||
|
|
||||||
|
if ((state->parsed->flags & 0x100) != 0) {
|
||||||
|
// Preserve symlink target.
|
||||||
|
char *path_buf = malloc(1024);
|
||||||
|
ssize_t ret = readlink(file_info->filename, path_buf, 1023);
|
||||||
|
if (ret == -1) {
|
||||||
|
fprintf(stderr, "WARNING: Failed to get symlink's target!\n");
|
||||||
|
free(path_buf);
|
||||||
|
((uint8_t *)temp_to_write->buf)[1] |= 0x8;
|
||||||
|
} else {
|
||||||
|
path_buf[ret] = 0;
|
||||||
|
if (path_buf[0] == '/') {
|
||||||
|
abs_path = path_buf;
|
||||||
|
((uint8_t *)temp_to_write->buf)[1] |= 0x4;
|
||||||
|
} else {
|
||||||
|
rel_path = path_buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
abs_path = realpath(file_info->filename, NULL);
|
||||||
if (abs_path) {
|
if (abs_path) {
|
||||||
// Get relative path.
|
// Get relative path.
|
||||||
// First get absolute path of link.
|
// First get absolute path of link.
|
||||||
|
@ -778,13 +793,15 @@ int write_files_fn(void *data, void *ud) {
|
||||||
// fprintf(stderr, "DEBUG: abs_path: %s\nDEBUG: link_abs_path: %s\n",
|
// fprintf(stderr, "DEBUG: abs_path: %s\nDEBUG: link_abs_path: %s\n",
|
||||||
// (char*)abs_path, (char*)link_abs_path);
|
// (char*)abs_path, (char*)link_abs_path);
|
||||||
|
|
||||||
rel_path =
|
rel_path = simple_archiver_filenames_to_relative_path(link_abs_path,
|
||||||
simple_archiver_filenames_to_relative_path(link_abs_path, abs_path);
|
abs_path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if absolute path refers to one of the filenames.
|
// Check if absolute path refers to one of the filenames.
|
||||||
if (abs_path && (state->parsed->flags & 0x20) == 0 &&
|
if (abs_path && (state->parsed->flags & 0x20) == 0 &&
|
||||||
|
(state->parsed->flags & 0x100) == 0 &&
|
||||||
!simple_archiver_hash_map_get(state->map, abs_path,
|
!simple_archiver_hash_map_get(state->map, abs_path,
|
||||||
strlen(abs_path) + 1)) {
|
strlen(abs_path) + 1)) {
|
||||||
// Is not a filename being archived.
|
// Is not a filename being archived.
|
||||||
|
@ -804,6 +821,34 @@ int write_files_fn(void *data, void *ud) {
|
||||||
file_info->filename);
|
file_info->filename);
|
||||||
((uint8_t *)temp_to_write->buf)[1] |= 0x8;
|
((uint8_t *)temp_to_write->buf)[1] |= 0x8;
|
||||||
}
|
}
|
||||||
|
} else if ((state->parsed->flags & 0x100) != 0 &&
|
||||||
|
(state->parsed->flags & 0x80) == 0 &&
|
||||||
|
(((uint8_t *)temp_to_write->buf)[1] & 0x8) == 0) {
|
||||||
|
__attribute__((cleanup(
|
||||||
|
simple_archiver_helper_cleanup_c_string))) char *resolved_path = NULL;
|
||||||
|
if (abs_path || rel_path) {
|
||||||
|
resolved_path = realpath(file_info->filename, NULL);
|
||||||
|
if (!resolved_path) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"WARNING: Symlink \"%s\" is invalid, will not be stored! "
|
||||||
|
"(Use \"--no-safe-links\" to disable this behavior)\n",
|
||||||
|
file_info->filename);
|
||||||
|
((uint8_t *)temp_to_write->buf)[1] |= 0x8;
|
||||||
|
} else if (!simple_archiver_hash_map_get(state->map, resolved_path,
|
||||||
|
strlen(resolved_path) + 1)) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"WARNING: Symlink \"%s\" points to outside archive contents, "
|
||||||
|
"will not be stored! (Use \"--no-safe-links\" to disable "
|
||||||
|
"this behavior)\n",
|
||||||
|
file_info->filename);
|
||||||
|
((uint8_t *)temp_to_write->buf)[1] |= 0x8;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"WARNING: Unable to get target path from symlink \"%s\"!\n",
|
||||||
|
file_info->filename);
|
||||||
|
((uint8_t *)temp_to_write->buf)[1] |= 0x8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!abs_path && !rel_path) {
|
if (!abs_path && !rel_path) {
|
||||||
|
@ -831,8 +876,10 @@ int write_files_fn(void *data, void *ud) {
|
||||||
|
|
||||||
// Store the absolute and relative paths.
|
// Store the absolute and relative paths.
|
||||||
if (!abs_path) {
|
if (!abs_path) {
|
||||||
|
if ((state->parsed->flags & 0x100) == 0) {
|
||||||
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 = 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;
|
||||||
|
@ -905,9 +952,17 @@ 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) {
|
if ((state->parsed->flags & 0x20) == 0) {
|
||||||
|
if (abs_path) {
|
||||||
fprintf(stderr, " abs path: %s\n", (char *)abs_path);
|
fprintf(stderr, " abs path: %s\n", (char *)abs_path);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, " abs path is NOT set\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (rel_path) {
|
||||||
fprintf(stderr, " rel path: %s\n", (char *)rel_path);
|
fprintf(stderr, " rel path: %s\n", (char *)rel_path);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, " rel path is NOT set\n");
|
||||||
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -983,10 +1038,15 @@ int filenames_to_abs_map_fn(void *data, void *ud) {
|
||||||
char *fullpath_dirname_copy = malloc(strlen(fullpath_dirname) + 1);
|
char *fullpath_dirname_copy = malloc(strlen(fullpath_dirname) + 1);
|
||||||
strncpy(fullpath_dirname_copy, fullpath_dirname,
|
strncpy(fullpath_dirname_copy, fullpath_dirname,
|
||||||
strlen(fullpath_dirname) + 1);
|
strlen(fullpath_dirname) + 1);
|
||||||
|
if (!simple_archiver_hash_map_get(abs_filenames, fullpath_dirname_copy,
|
||||||
|
strlen(fullpath_dirname_copy) + 1)) {
|
||||||
simple_archiver_hash_map_insert(
|
simple_archiver_hash_map_insert(
|
||||||
abs_filenames, fullpath_dirname_copy, fullpath_dirname_copy,
|
abs_filenames, fullpath_dirname_copy, fullpath_dirname_copy,
|
||||||
strlen(fullpath_dirname_copy) + 1,
|
strlen(fullpath_dirname_copy) + 1,
|
||||||
simple_archiver_helper_datastructure_cleanup_nop, NULL);
|
simple_archiver_helper_datastructure_cleanup_nop, NULL);
|
||||||
|
} else {
|
||||||
|
free(fullpath_dirname_copy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
prev = fullpath_dirname;
|
prev = fullpath_dirname;
|
||||||
}
|
}
|
||||||
|
@ -2091,15 +2151,36 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
|
||||||
node = node->next;
|
node = node->next;
|
||||||
++u32;
|
++u32;
|
||||||
memset(buf, 0, 2);
|
memset(buf, 0, 2);
|
||||||
|
|
||||||
|
uint_fast8_t is_invalid = 0;
|
||||||
|
|
||||||
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
|
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
|
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
|
||||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
|
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
|
||||||
// Check if symlink points to thing to be stored into archive.
|
__attribute__((cleanup(
|
||||||
__attribute__((
|
simple_archiver_helper_cleanup_malloced))) void *abs_path = NULL;
|
||||||
cleanup(simple_archiver_helper_cleanup_malloced))) void *abs_path =
|
|
||||||
realpath(node->data, NULL);
|
|
||||||
__attribute__((cleanup(
|
__attribute__((cleanup(
|
||||||
simple_archiver_helper_cleanup_malloced))) void *rel_path = NULL;
|
simple_archiver_helper_cleanup_malloced))) void *rel_path = NULL;
|
||||||
|
if ((state->parsed->flags & 0x100) != 0) {
|
||||||
|
// Preserve symlink target.
|
||||||
|
char *path_buf = malloc(1024);
|
||||||
|
ssize_t ret = readlink(node->data, path_buf, 1023);
|
||||||
|
if (ret == -1) {
|
||||||
|
fprintf(stderr, "WARNING: Failed to get symlink's target!\n");
|
||||||
|
free(path_buf);
|
||||||
|
is_invalid = 1;
|
||||||
|
} else {
|
||||||
|
path_buf[ret] = 0;
|
||||||
|
if (path_buf[0] == '/') {
|
||||||
|
abs_path = path_buf;
|
||||||
|
buf[0] |= 1;
|
||||||
|
} else {
|
||||||
|
rel_path = path_buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
abs_path = realpath(node->data, NULL);
|
||||||
|
// Check if symlink points to thing to be stored into archive.
|
||||||
if (abs_path) {
|
if (abs_path) {
|
||||||
__attribute__((cleanup(
|
__attribute__((cleanup(
|
||||||
simple_archiver_helper_cleanup_malloced))) void *link_abs_path =
|
simple_archiver_helper_cleanup_malloced))) void *link_abs_path =
|
||||||
|
@ -2111,10 +2192,10 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
|
||||||
abs_path);
|
abs_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
uint_fast8_t is_invalid = 0;
|
|
||||||
|
|
||||||
if (abs_path && (state->parsed->flags & 0x20) == 0 &&
|
if (abs_path && (state->parsed->flags & 0x20) == 0 &&
|
||||||
|
(state->parsed->flags & 0x100) == 0 &&
|
||||||
!simple_archiver_hash_map_get(abs_filenames, abs_path,
|
!simple_archiver_hash_map_get(abs_filenames, abs_path,
|
||||||
strlen(abs_path) + 1)) {
|
strlen(abs_path) + 1)) {
|
||||||
// Is not a filename being archived.
|
// Is not a filename being archived.
|
||||||
|
@ -2130,6 +2211,27 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
|
||||||
// Safe links disabled, set preference to absolute path.
|
// Safe links disabled, set preference to absolute path.
|
||||||
buf[0] |= 1;
|
buf[0] |= 1;
|
||||||
}
|
}
|
||||||
|
} else if ((state->parsed->flags & 0x100) != 0 &&
|
||||||
|
(state->parsed->flags & 0x80) == 0 && !is_invalid) {
|
||||||
|
__attribute__((cleanup(
|
||||||
|
simple_archiver_helper_cleanup_c_string))) char *target_realpath =
|
||||||
|
realpath(node->data, NULL);
|
||||||
|
if (!target_realpath) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING: \"%s\" is an invalid symlink and \"--no-safe-links\" "
|
||||||
|
"not specified, will skip this symlink!\n",
|
||||||
|
(const char *)node->data);
|
||||||
|
is_invalid = 1;
|
||||||
|
} else if (!simple_archiver_hash_map_get(abs_filenames, target_realpath,
|
||||||
|
strlen(target_realpath) + 1)) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARNING: \"%s\" points to outside of archived files and "
|
||||||
|
"\"--no-safe-links\" not specified, will skip this symlink!\n",
|
||||||
|
(const char *)node->data);
|
||||||
|
is_invalid = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!abs_path && !rel_path) {
|
if (!abs_path && !rel_path) {
|
||||||
|
|
|
@ -168,6 +168,12 @@ void simple_archiver_print_usage(void) {
|
||||||
fprintf(stderr, "--overwrite-extract : allows overwriting when extracting\n");
|
fprintf(stderr, "--overwrite-extract : allows overwriting when extracting\n");
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"--no-abs-symlink : do not store absolute paths for symlinks\n");
|
"--no-abs-symlink : do not store absolute paths for symlinks\n");
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"--preserve-symlinks : preserve the symlink's path on archive creation "
|
||||||
|
"instead of deriving abs/relative paths, ignores \"--no-abs-symlink\" "
|
||||||
|
"(It is not recommended to use this option, as absolute-path-symlinks "
|
||||||
|
"may be clobbered on extraction)\n");
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"--no-safe-links : keep symlinks that link to outside archive "
|
"--no-safe-links : keep symlinks that link to outside archive "
|
||||||
"contents\n");
|
"contents\n");
|
||||||
|
@ -306,6 +312,8 @@ int simple_archiver_parse_args(int argc, const char **argv,
|
||||||
out->flags |= 0x8;
|
out->flags |= 0x8;
|
||||||
} else if (strcmp(argv[0], "--no-abs-symlink") == 0) {
|
} else if (strcmp(argv[0], "--no-abs-symlink") == 0) {
|
||||||
out->flags |= 0x20;
|
out->flags |= 0x20;
|
||||||
|
} else if (strcmp(argv[0], "--preserve-symlinks") == 0) {
|
||||||
|
out->flags |= 0x100;
|
||||||
} else if (strcmp(argv[0], "--no-safe-links") == 0) {
|
} else if (strcmp(argv[0], "--no-safe-links") == 0) {
|
||||||
out->flags |= 0x80;
|
out->flags |= 0x80;
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
|
|
@ -37,6 +37,7 @@ typedef struct SDArchiverParsed {
|
||||||
/// 0b xx1x xxxx - Do not save absolute paths for symlinks.
|
/// 0b xx1x xxxx - Do not save absolute paths for symlinks.
|
||||||
/// 0b x1xx xxxx - Sort files by size before archiving.
|
/// 0b x1xx xxxx - Sort files by size before archiving.
|
||||||
/// 0b 1xxx xxxx - No safe links.
|
/// 0b 1xxx xxxx - No safe links.
|
||||||
|
/// 0b xxxx xxx1 xxxx xxxx - Preserve symlink target.
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
/// Null-terminated string.
|
/// Null-terminated string.
|
||||||
char *filename;
|
char *filename;
|
||||||
|
|
Loading…
Reference in a new issue