Compare commits

..

16 commits

Author SHA1 Message Date
c7cd445139 Refactorings for v1 extract, other refactorings
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-10-02 15:11:23 +09:00
b09948d245 Split "rel-path-from-abs-paths" into function 2024-10-02 15:11:23 +09:00
6376be2840 Add support for writing multiple file formats 2024-10-02 15:11:23 +09:00
da18464d5d Test/fix symlink test/extract in v1 file format 2024-10-02 15:11:23 +09:00
8fa430f842 Fix v1 archive decompression
Previous implementation sent too many bytes to decompressor if size was
less than 1024.
2024-10-02 15:11:23 +09:00
53fefb7ae8 Remove unnecessary printf used for testing 2024-10-02 15:11:23 +09:00
45fdffdc9c Impl. extract with decompressor file format v1 2024-10-02 15:11:23 +09:00
7407972450 v1 extract skip non-specified args if exists 2024-10-02 15:11:23 +09:00
3d58f466af Impl. setting stored UID/GID if EUID 0 2024-10-02 15:11:23 +09:00
b273d91896 "Fix" Linux/Mac/Unix usage 2024-10-02 15:11:23 +09:00
c71f4f45c7 Impl. simple test/extract new file format (WIP)
TODO:
    Extract symlinks in new format (implemented but untested).
    Extract compressed files in new format.
2024-10-02 15:11:23 +09:00
c1faae90e9 Split up handling of archive file based on version 2024-10-02 15:11:23 +09:00
d625c1b1cb Fix typo 2024-10-02 15:11:23 +09:00
a7aa31fc89 Fix typo in file format specification version 1 2024-10-02 15:11:23 +09:00
f76e383e78 Create file format for format version 1
This is in preparation of improving compression by concatenating files
together before compressing them to reduce the per-file overhead.
2024-10-02 15:11:23 +09:00
efde02b4ab backport: Fixes for v0-file-format extract fn
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-10-02 15:11:00 +09:00
7 changed files with 510 additions and 70 deletions

View file

@ -83,6 +83,7 @@ add_executable(test_simplearchiver
src/test.c
src/parser.c
src/helpers.c
src/archiver.c
src/algorithms/linear_congruential_gen.c
src/data_structures/linked_list.c
src/data_structures/hash_map.c

View file

@ -118,8 +118,16 @@ Following the link-count bytes, the following bytes are added for each symlink:
1. The first byte.
1. The first bit is UNSET if relative links are preferred, and is SET if
absolute links are preferred.
2. The second bit is "user read permission".
3. The third bit is "user write permission".
4. The fourth bit is "user execute permission".
5. The fifth bit is "group read permission".
6. The sixth bit is "group write permission".
7. The seventh bit is "group execute permission".
8. The eighth bit is "other read permission".
2. The second byte.
1. Currently unused.
1. The first bit is "other write permission".
2. The second bit is "other execute permission".
2. 2 bytes 16-bit unsigned integer "link name" in big-endian. This does not
include the NULL at the end of the string. Must not be zero.
3. X bytes of link-name (length defined by previous value). Is a NULL-terminated

Binary file not shown.

View file

@ -767,46 +767,8 @@ int write_files_fn(void *data, void *ud) {
// 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.
size_t idx;
size_t 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_fast8_t 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);
rel_path =
simple_archiver_filenames_to_relative_path(link_abs_path, abs_path);
}
}
@ -1150,6 +1112,86 @@ void cleanup_internal_file_info(SDArchiverInternalFileInfo **file_info) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
mode_t permissions_from_bits_v1_symlink(const uint8_t flags[2],
uint_fast8_t print) {
mode_t permissions = 0;
if ((flags[0] & 2) != 0) {
permissions |= S_IRUSR;
if (print) {
fprintf(stderr, "r");
}
} else if (print) {
fprintf(stderr, "-");
}
if ((flags[0] & 4) != 0) {
permissions |= S_IWUSR;
if (print) {
fprintf(stderr, "w");
}
} else if (print) {
fprintf(stderr, "-");
}
if ((flags[0] & 8) != 0) {
permissions |= S_IXUSR;
if (print) {
fprintf(stderr, "x");
}
} else if (print) {
fprintf(stderr, "-");
}
if ((flags[0] & 0x10) != 0) {
permissions |= S_IRGRP;
if (print) {
fprintf(stderr, "r");
}
} else if (print) {
fprintf(stderr, "-");
}
if ((flags[0] & 0x20) != 0) {
permissions |= S_IWGRP;
if (print) {
fprintf(stderr, "w");
}
} else if (print) {
fprintf(stderr, "-");
}
if ((flags[0] & 0x40) != 0) {
permissions |= S_IXGRP;
if (print) {
fprintf(stderr, "x");
}
} else if (print) {
fprintf(stderr, "-");
}
if ((flags[0] & 0x80) != 0) {
permissions |= S_IROTH;
if (print) {
fprintf(stderr, "r");
}
} else if (print) {
fprintf(stderr, "-");
}
if ((flags[1] & 1) != 0) {
permissions |= S_IWOTH;
if (print) {
fprintf(stderr, "w");
}
} else if (print) {
fprintf(stderr, "-");
}
if ((flags[1] & 2) != 0) {
permissions |= S_IXOTH;
if (print) {
fprintf(stderr, "x");
}
} else if (print) {
fprintf(stderr, "-");
}
return permissions;
}
mode_t permissions_from_bits_version_1(const uint8_t flags[4],
uint_fast8_t print) {
mode_t permissions = 0;
@ -1229,6 +1271,55 @@ mode_t permissions_from_bits_version_1(const uint8_t flags[4],
return permissions;
}
void print_permissions(mode_t permissions) {
if ((permissions & S_IRUSR)) {
fprintf(stderr, "r");
} else {
fprintf(stderr, "-");
}
if ((permissions & S_IWUSR)) {
fprintf(stderr, "w");
} else {
fprintf(stderr, "-");
}
if ((permissions & S_IXUSR)) {
fprintf(stderr, "x");
} else {
fprintf(stderr, "-");
}
if ((permissions & S_IRGRP)) {
fprintf(stderr, "r");
} else {
fprintf(stderr, "-");
}
if ((permissions & S_IWGRP)) {
fprintf(stderr, "w");
} else {
fprintf(stderr, "-");
}
if ((permissions & S_IXGRP)) {
fprintf(stderr, "x");
} else {
fprintf(stderr, "-");
}
if ((permissions & S_IROTH)) {
fprintf(stderr, "r");
} else {
fprintf(stderr, "-");
}
if ((permissions & S_IWOTH)) {
fprintf(stderr, "w");
} else {
fprintf(stderr, "-");
}
if ((permissions & S_IXOTH)) {
fprintf(stderr, "x");
} else {
fprintf(stderr, "-");
}
}
#endif
void simple_archiver_internal_cleanup_int_fd(int *fd) {
@ -1489,6 +1580,18 @@ int simple_archiver_write_v0(FILE *out_f, SDArchiverState *state,
int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
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();
void **ptr_array = malloc(sizeof(void *) * 2);
ptr_array[0] = abs_filenames;
ptr_array[1] = (void *)state->parsed->user_cwd;
if (simple_archiver_list_get(filenames, filenames_to_abs_map_fn, ptr_array)) {
free(ptr_array);
return SDAS_FAILED_TO_CREATE_MAP;
}
free(ptr_array);
// TODO Impl.
fprintf(stderr, "Writing v1 unimplemented\n");
return SDAS_INTERNAL_ERROR;
@ -1666,6 +1769,17 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
}
} else {
skip = 0;
int fd = open((const char *)buf, O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
if (errno == ELOOP) {
// Is an existing symbolic file.
unlink((const char *)buf);
}
} else {
close(fd);
// Is an existing file.
unlink((const char *)buf);
}
}
if (!skip) {
out_f_name = malloc(strlen((const char *)buf) + 1);
@ -1675,17 +1789,17 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
} else {
__attribute__((
cleanup(simple_archiver_helper_cleanup_malloced))) void *heap_buf =
malloc(u16 + 1);
malloc((uint32_t)u16 + 1);
uint8_t *uc_heap_buf = heap_buf;
if (fread(uc_heap_buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
if (fread(uc_heap_buf, 1, (uint32_t)u16 + 1, in_f) != (size_t)u16 + 1) {
return SDAS_INVALID_FILE;
}
uc_heap_buf[u16 - 1] = 0;
uc_heap_buf[u16] = 0;
fprintf(stderr, " Filename: %s\n", uc_heap_buf);
if (do_extract) {
if ((state->parsed->flags & 0x8) == 0) {
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
FILE *test_fd = fopen((const char *)buf, "rb");
FILE *test_fd = fopen((const char *)uc_heap_buf, "rb");
if (test_fd) {
skip = 1;
fprintf(stderr,
@ -1696,10 +1810,22 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
}
} else {
skip = 0;
int fd = open((const char *)uc_heap_buf, O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
if (errno == ELOOP) {
// Is an existing symbolic file.
unlink((const char *)uc_heap_buf);
}
} else {
close(fd);
// Is an existing file.
unlink((const char *)uc_heap_buf);
}
}
if (!skip) {
out_f_name = malloc(strlen((const char *)buf) + 1);
memcpy(out_f_name, buf, strlen((const char *)buf) + 1);
out_f_name = malloc(strlen((const char *)uc_heap_buf) + 1);
memcpy(out_f_name, uc_heap_buf,
strlen((const char *)uc_heap_buf) + 1);
}
}
}
@ -1822,6 +1948,12 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
simple_archiver_helper_make_dirs((const char *)out_f_name);
out_f = fopen(out_f_name, "wb");
if (!out_f) {
fprintf(stderr,
"WARNING: Failed to open \"%s\" for writing! (No write "
"permissions?)\n",
(char *)out_f_name);
}
__attribute__((
cleanup(cleanup_temp_filename_delete))) void **ptrs_array =
malloc(sizeof(void *) * 2);
@ -1830,7 +1962,7 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
if (is_compressed) {
if (is_compressed && out_f) {
// Handle SIGPIPE.
signal(SIGPIPE, handle_sig_pipe);
@ -2038,10 +2170,12 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
// Error.
return SDAS_INTERNAL_ERROR;
}
fwrite(buf, 1, fread_ret, out_f);
if (ferror(out_f)) {
// Error.
return SDAS_INTERNAL_ERROR;
if (out_f) {
fwrite(buf, 1, fread_ret, out_f);
if (ferror(out_f)) {
// Error.
return SDAS_INTERNAL_ERROR;
}
}
compressed_file_size -= fread_ret;
} else {
@ -2050,10 +2184,12 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
// Error.
return SDAS_INTERNAL_ERROR;
}
fwrite(buf, 1, fread_ret, out_f);
if (ferror(out_f)) {
// Error.
return SDAS_INTERNAL_ERROR;
if (out_f) {
fwrite(buf, 1, fread_ret, out_f);
if (ferror(out_f)) {
// Error.
return SDAS_INTERNAL_ERROR;
}
}
compressed_file_size -= fread_ret;
}
@ -2066,7 +2202,9 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
}
ptrs_array[0] = NULL;
fprintf(stderr, " Extracted.\n");
if (out_f) {
fprintf(stderr, " Extracted.\n");
}
#endif
} else {
while (u64 != 0) {
@ -2145,26 +2283,108 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
fprintf(stderr, " Link relative path: %s\n", (char *)rel_path);
}
if (do_extract) {
if (do_extract && !skip) {
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);
int_fast8_t retry_symlink = 0;
int ret;
V0_SYMLINK_CREATE_RETRY_0:
ret = symlink(abs_path, out_f_name);
if (ret == -1) {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
if (retry_symlink) {
fprintf(stderr,
"WARNING: Failed to create symlink after removing "
"existing symlink!\n");
goto V0_SYMLINK_CREATE_AFTER_0;
} else if (errno == EEXIST) {
if ((state->parsed->flags & 8) == 0) {
fprintf(
stderr,
"WARNING: Symlink already exists and "
"\"--overwrite-extract\" is not specified, skipping!\n");
goto V0_SYMLINK_CREATE_AFTER_0;
} else {
fprintf(stderr,
"NOTICE: Symlink already exists and "
"\"--overwrite-extract\" specified, attempting to "
"overwrite...\n");
unlink(out_f_name);
retry_symlink = 1;
goto V0_SYMLINK_CREATE_RETRY_0;
}
} else {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
}
ret = fchmodat(AT_FDCWD, out_f_name, permissions,
AT_SYMLINK_NOFOLLOW);
if (ret == -1) {
if (errno == EOPNOTSUPP) {
fprintf(stderr,
"NOTICE: Setting permissions of symlink is not "
"supported by FS/OS!\n");
} else {
fprintf(stderr,
"WARNING: Failed to set permissions of symlink (%d)!\n",
errno);
}
}
V0_SYMLINK_CREATE_AFTER_0:
retry_symlink = 1;
#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);
int_fast8_t retry_symlink = 0;
int ret;
V0_SYMLINK_CREATE_RETRY_1:
ret = symlink(rel_path, out_f_name);
if (ret == -1) {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
if (retry_symlink) {
fprintf(stderr,
"WARNING: Failed to create symlink after removing "
"existing symlink!\n");
goto V0_SYMLINK_CREATE_AFTER_1;
} else if (errno == EEXIST) {
if ((state->parsed->flags & 8) == 0) {
fprintf(
stderr,
"WARNING: Symlink already exists and "
"\"--overwrite-extract\" is not specified, skipping!\n");
goto V0_SYMLINK_CREATE_AFTER_1;
} else {
fprintf(stderr,
"NOTICE: Symlink already exists and "
"\"--overwrite-extract\" specified, attempting to "
"overwrite...\n");
unlink(out_f_name);
retry_symlink = 1;
goto V0_SYMLINK_CREATE_RETRY_1;
}
} else {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
}
ret = fchmodat(AT_FDCWD, out_f_name, permissions,
AT_SYMLINK_NOFOLLOW);
if (ret == -1) {
if (errno == EOPNOTSUPP) {
fprintf(stderr,
"NOTICE: Setting permissions of symlink is not "
"supported by FS/OS!\n");
} else {
fprintf(stderr,
"WARNING: Failed to set permissions of symlink (%d)!\n",
errno);
}
}
V0_SYMLINK_CREATE_AFTER_1:
retry_symlink = 1;
#endif
}
} else if (abs_path) {
@ -2175,6 +2395,19 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
if (ret == -1) {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
ret =
fchmodat(AT_FDCWD, out_f_name, permissions, AT_SYMLINK_NOFOLLOW);
if (ret == -1) {
if (errno == EOPNOTSUPP) {
fprintf(stderr,
"NOTICE: Setting permissions of symlink is not supported "
"by FS/OS!\n");
} else {
fprintf(stderr,
"WARNING: Failed to set permissions of symlink (%d)!\n",
errno);
}
}
#endif
} else if (rel_path) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
@ -2184,6 +2417,19 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
if (ret == -1) {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
ret =
fchmodat(AT_FDCWD, out_f_name, permissions, AT_SYMLINK_NOFOLLOW);
if (ret == -1) {
if (errno == EOPNOTSUPP) {
fprintf(stderr,
"NOTICE: Setting permissions of symlink is not supported "
"by FS/OS!\n");
} else {
fprintf(stderr,
"WARNING: Failed to set permissions of symlink (%d)!\n",
errno);
}
}
#endif
} else {
fprintf(
@ -2291,6 +2537,13 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
return SDAS_INVALID_FILE;
}
const uint_fast8_t absolute_preferred = (buf[0] & 1) ? 1 : 0;
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
mode_t permissions = permissions_from_bits_v1_symlink(buf, 0);
#endif
uint_fast8_t link_extracted = 0;
uint_fast8_t skip_due_to_map = 0;
@ -2312,6 +2565,13 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
if (!do_extract) {
fprintf(stderr, " Link name: %s\n", link_name);
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
fprintf(stderr, " Link Permissions: ");
print_permissions(permissions);
fprintf(stderr, "\n");
#endif
}
if (working_files_map &&
@ -2340,12 +2600,49 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
simple_archiver_helper_make_dirs(link_name);
int_fast8_t link_create_retry = 0;
V1_SYMLINK_CREATE_RETRY_0:
ret = symlink(path, link_name);
if (ret == -1) {
if (link_create_retry) {
fprintf(stderr,
"WARNING: Failed to create symlink after removing existing "
"symlink!\n");
goto V1_SYMLINK_CREATE_AFTER_0;
} else if (errno == EEXIST) {
if ((state->parsed->flags & 8) == 0) {
fprintf(stderr,
"WARNING: Symlink already exists and "
"\"--overwrite-extract\" is not specified, skipping!\n");
goto V1_SYMLINK_CREATE_AFTER_0;
} else {
fprintf(
stderr,
"NOTICE: Symlink already exists and \"--overwrite-extract\" "
"specified, attempting to overwrite...\n");
unlink(link_name);
link_create_retry = 1;
goto V1_SYMLINK_CREATE_RETRY_0;
}
}
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
ret = fchmodat(AT_FDCWD, link_name, permissions, AT_SYMLINK_NOFOLLOW);
if (ret == -1) {
if (errno == EOPNOTSUPP) {
fprintf(stderr,
"NOTICE: Setting permissions of symlink is not supported "
"by FS/OS!\n");
} else {
fprintf(stderr,
"WARNING: Failed to set permissions of symlink (%d)!\n",
errno);
}
}
link_extracted = 1;
fprintf(stderr, " %s -> %s\n", link_name, path);
V1_SYMLINK_CREATE_AFTER_0:
link_create_retry = 1;
#endif
} else {
fprintf(stderr, " Abs path: %s\n", path);
@ -2373,12 +2670,49 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
simple_archiver_helper_make_dirs(link_name);
int_fast8_t link_create_retry = 0;
V1_SYMLINK_CREATE_RETRY_1:
ret = symlink(path, link_name);
if (ret == -1) {
if (link_create_retry) {
fprintf(stderr,
"WARNING: Failed to create symlink after removing existing "
"symlink!\n");
goto V1_SYMLINK_CREATE_AFTER_1;
} else if (errno == EEXIST) {
if ((state->parsed->flags & 8) == 0) {
fprintf(stderr,
"WARNING: Symlink already exists and "
"\"--overwrite-extract\" is not specified, skipping!\n");
goto V1_SYMLINK_CREATE_AFTER_1;
} else {
fprintf(
stderr,
"NOTICE: Symlink already exists and \"--overwrite-extract\" "
"specified, attempting to overwrite...\n");
unlink(link_name);
link_create_retry = 1;
goto V1_SYMLINK_CREATE_RETRY_1;
}
}
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
ret = fchmodat(AT_FDCWD, link_name, permissions, AT_SYMLINK_NOFOLLOW);
if (ret == -1) {
if (errno == EOPNOTSUPP) {
fprintf(stderr,
"NOTICE: Setting permissions of symlink is not supported "
"by FS/OS!\n");
} else {
fprintf(stderr,
"WARNING: Failed to set permissions of symlink (%d)!\n",
errno);
}
}
link_extracted = 1;
fprintf(stderr, " %s -> %s\n", link_name, path);
V1_SYMLINK_CREATE_AFTER_1:
link_create_retry = 1;
#endif
} else {
fprintf(stderr, " Rel path: %s\n", path);
@ -2434,6 +2768,27 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
}
file_info->filename[u16] = 0;
if (state && state->parsed && (state->parsed->flags & 8) != 0) {
int fd = open((const char *)buf, O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
if (errno == ELOOP) {
// Exists as a symlink.
fprintf(stderr,
"WARNING: Filename \"%s\" already exists as symlink, "
"removing...\n",
(const char *)buf);
unlink((const char *)buf);
} else {
// File doesn't exist, do nothing.
}
} else {
close(fd);
fprintf(stderr, "WARNING: File \"%s\" already exists, removing...\n",
(const char *)buf);
unlink((const char *)buf);
}
}
if (fread(file_info->bit_flags, 1, 4, in_f) != 4) {
return SDAS_INVALID_FILE;
}
@ -2872,3 +3227,53 @@ int simple_archiver_de_compress(int pipe_fd_in[2], int pipe_fd_out[2],
return 1;
#endif
}
char *simple_archiver_filenames_to_relative_path(const char *from_abs,
const char *to_abs) {
if (!from_abs || !to_abs) {
return NULL;
}
// Get first non-common char and last slash before it.
uint_fast32_t idx;
uint_fast32_t last_slash;
for (idx = 0, last_slash = 0; idx < strlen(from_abs) && idx < strlen(to_abs);
++idx) {
if (((const char *)to_abs)[idx] != ((const char *)from_abs)[idx]) {
break;
} else if (((const char *)to_abs)[idx] == '/') {
last_slash = idx + 1;
}
}
// Get substrings of both paths.
char *link_substr = (char *)from_abs + last_slash;
char *dest_substr = (char *)to_abs + last_slash;
char *rel_path = malloc(strlen(dest_substr) + 1);
strncpy(rel_path, dest_substr, strlen(dest_substr) + 1);
// fprintf(stderr, "DEBUG: link_substr \"%s\", dest_substr \"%s\"\n",
// link_substr, dest_substr);
// Get the relative path finally.
int_fast8_t 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);
return rel_path;
}

View file

@ -40,7 +40,7 @@ typedef struct SDArchiverState {
size_t digits;
} SDArchiverState;
enum SDArchiverStateReturns {
typedef enum SDArchiverStateReturns {
SDAS_SUCCESS = 0,
SDAS_HEADER_ALREADY_WRITTEN = 1,
SDAS_FAILED_TO_WRITE,
@ -53,7 +53,7 @@ enum SDArchiverStateReturns {
SDAS_FAILED_TO_EXTRACT_SYMLINK,
SDAS_FAILED_TO_CHANGE_CWD,
SDAS_INVALID_WRITE_VERSION
};
} SDArchiverStateReturns;
/// Returned pointer must not be freed.
char *simple_archiver_error_to_string(enum SDArchiverStateReturns error);
@ -88,4 +88,8 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
int simple_archiver_de_compress(int pipe_fd_in[2], int pipe_fd_out[2],
const char *cmd, void *pid_out);
/// If returns non-NULL, must be free'd.
char *simple_archiver_filenames_to_relative_path(const char *from_abs,
const char *to_abs);
#endif

View file

@ -97,7 +97,8 @@ int main(int argc, const char **argv) {
int ret = simple_archiver_write_all(file, state, filenames);
if (ret != SDAS_SUCCESS) {
fprintf(stderr, "Error during writing.\n");
char *error_str = simple_archiver_error_to_string(ret);
char *error_str =
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
fprintf(stderr, " %s\n", error_str);
}
fclose(file);
@ -113,7 +114,8 @@ int main(int argc, const char **argv) {
int ret = simple_archiver_write_all(stdout, state, filenames);
if (ret != SDAS_SUCCESS) {
fprintf(stderr, "Error during writing.\n");
char *error_str = simple_archiver_error_to_string(ret);
char *error_str =
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
fprintf(stderr, " %s\n", error_str);
}
}
@ -130,7 +132,8 @@ int main(int argc, const char **argv) {
int ret = simple_archiver_parse_archive_info(file, 0, NULL);
if (ret != 0) {
fprintf(stderr, "Error during archive checking/examining.\n");
char *error_str = simple_archiver_error_to_string(ret);
char *error_str =
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
fprintf(stderr, " %s\n", error_str);
}
fclose(file);
@ -138,7 +141,8 @@ int main(int argc, const char **argv) {
int ret = simple_archiver_parse_archive_info(stdin, 0, NULL);
if (ret != 0) {
fprintf(stderr, "Error during archive checking/examining.\n");
char *error_str = simple_archiver_error_to_string(ret);
char *error_str =
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
fprintf(stderr, " %s\n", error_str);
}
}
@ -157,7 +161,8 @@ int main(int argc, const char **argv) {
int ret = simple_archiver_parse_archive_info(file, 1, state);
if (ret != SDAS_SUCCESS) {
fprintf(stderr, "Error during archive extracting.\n");
char *error_str = simple_archiver_error_to_string(ret);
char *error_str =
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
fprintf(stderr, " %s\n", error_str);
}
fclose(file);
@ -165,7 +170,8 @@ int main(int argc, const char **argv) {
int ret = simple_archiver_parse_archive_info(stdin, 1, state);
if (ret != SDAS_SUCCESS) {
fprintf(stderr, "Error during archive extracting.\n");
char *error_str = simple_archiver_error_to_string(ret);
char *error_str =
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
fprintf(stderr, " %s\n", error_str);
}
}

View file

@ -23,6 +23,7 @@
#include <string.h>
// Local includes.
#include "archiver.h"
#include "helpers.h"
#include "parser_internal.h"
@ -241,6 +242,21 @@ int main(void) {
free(out);
}
// Test archiver.
{
__attribute__((
cleanup(simple_archiver_helper_cleanup_c_string))) char *rel_path =
simple_archiver_filenames_to_relative_path(
"/one/two/three/four/five", "/one/two/branch/other/path");
CHECK_STREQ(rel_path, "../../branch/other/path");
simple_archiver_helper_cleanup_c_string(&rel_path);
rel_path = simple_archiver_filenames_to_relative_path(
"/one/two/three/four/five", "/one/two/three/other/dir/");
CHECK_STREQ(rel_path, "../other/dir/");
simple_archiver_helper_cleanup_c_string(&rel_path);
}
printf("Checks checked: %u\n", checks_checked);
printf("Checks passed: %u\n", checks_passed);
return checks_passed == checks_checked ? 0 : 1;