diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1ecbcee..4b53330 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,6 +28,49 @@ endif()
add_executable(simplearchiver ${SimpleArchiver_SOURCES})
+target_compile_options(simplearchiver PUBLIC
+ -Wall -Wformat -Wformat=2 -Wconversion -Wimplicit-fallthrough
+ -Werror=format-security
+ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3
+ -D_GLIBCXX_ASSERTIONS
+ -fstrict-flex-arrays=3
+ -fstack-clash-protection -fstack-protector-strong
+ -Wl,-z,nodlopen -Wl,-z,noexecstack
+ -Wl,-z,relro -Wl,-z,now
+ -Wl,--as-needed -Wl,--no-copy-dt-needed-entries
+ -fPIE -pie
+)
+
+target_link_options(simplearchiver PUBLIC
+ -Wall -Wformat -Wformat=2 -Wconversion -Wimplicit-fallthrough
+ -Werror=format-security
+ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3
+ -D_GLIBCXX_ASSERTIONS
+ -fstrict-flex-arrays=3
+ -fstack-clash-protection -fstack-protector-strong
+ -Wl,-z,nodlopen -Wl,-z,noexecstack
+ -Wl,-z,relro -Wl,-z,now
+ -Wl,--as-needed -Wl,--no-copy-dt-needed-entries
+ -fPIE -pie
+)
+
+# Inhibit format-string-related warning in src/archiver.c .
+set_source_files_properties(src/archiver.c
+ PROPERTIES
+ COMPILE_FLAGS -Wno-format-nonliteral
+)
+
+if(CMAKE_BUILD_TYPE STREQUAL "Release")
+ target_compile_options(simplearchiver PUBLIC
+ -fno-delete-null-pointer-checks -fno-strict-overflow
+ -fno-strict-aliasing -ftrivial-auto-var-init=zero
+ )
+ target_link_options(simplearchiver PUBLIC
+ -fno-delete-null-pointer-checks -fno-strict-overflow
+ -fno-strict-aliasing -ftrivial-auto-var-init=zero
+ )
+endif()
+
add_executable(test_datastructures
src/data_structures/test.c
src/data_structures/linked_list.c
@@ -40,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
diff --git a/README.md b/README.md
index d1aaaba..ec456c3 100644
--- a/README.md
+++ b/README.md
@@ -20,13 +20,15 @@ API calls.
Use "-f -" to work on stdout when creating archive or stdin when reading archive
NOTICE: "-f" is not affected by "-C"!
-C
: Change current working directory before archiving/extracting
- --compressor : requires --decompressor
- --decompressor : requires --compressor
+ --compressor : requires --decompressor and cmd must use stdin/stdout
+ --decompressor : requires --compressor and cmd must use stdin/stdout
Specifying "--decompressor" when extracting overrides archive file's stored decompressor cmd
--overwrite-create : allows overwriting an archive file
--overwrite-extract : allows overwriting when extracting
--no-abs-symlink : do not store absolute paths for symlinks
--temp-files-dir : where to store temporary files created when compressing (defaults to current working directory)
+ --write-version : Force write version file format (default 1)
+ --chunk-min-size : v1 file format minimum chunk size (default 4194304 or 4MiB)
-- : specifies remaining arguments are files to archive/extract
If creating archive file, remaining args specify files to archive.
If extracting archive file, remaining args specify files to extract.
diff --git a/file_format.md b/file_format.md
index fb9c210..4e39544 100644
--- a/file_format.md
+++ b/file_format.md
@@ -76,3 +76,117 @@ Following the file-count bytes, the following bytes are added for each file:
1. 8 bytes 64-bit unsigned integer "size of filename in this archive file"
in big-endian.
2. X bytes file data (length defined by previous value).
+
+## Format Version 1
+
+File extension is "*.simplearchive" but this isn't really checked.
+
+First 18 bytes of file will be (in ascii):
+
+ SIMPLE_ARCHIVE_VER
+
+Next 2 bytes is a 16-bit unsigned integer "version" in big-endian. It will be:
+
+ 0x00 0x01
+
+Next 4 bytes are bit-flags.
+
+1. The first byte
+ 1. The first bit is set if de/compressor is set for this archive.
+
+The remaining unused flags in the previous bit-flags bytes are reserved for
+future revisions and are currently ignored.
+
+If the previous "de/compressor is set" flag is enabled, then the next section is
+added:
+
+1. 2 bytes is 16-bit unsigned integer "compressor cmd+args" in big-endian. This
+ does not include the NULL at the end of the string.
+2. X bytes of "compressor cmd+args" (length defined by previous value). Is a
+ NULL-terminated string.
+3. 2 bytes is 16-bit unsigned integer "decompressor cmd+args" in big-endian.
+ This does not include the NULL at the end of the string.
+4. X bytes of "decompressor cmd+args" (length defined by previous value). Is a
+ NULL-terminated string.
+
+The next 4 bytes is a 32-bit unsigned integer "link count" in big-endian which
+will indicate the number of symbolic links in this archive.
+
+Following the link-count bytes, the following bytes are added for each symlink:
+
+1. 2 bytes bit-flags:
+ 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. 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
+ string.
+4. 2 bytes is 16-bit unsigned integer "link target absolute path" in
+ big-endian. This does not include the NULL at the end of the string.
+5. X bytes of link-target-absolute-path (length defined by previous value).
+ Is a NULL-terminated string. If the previous "size" value is 0, then
+ this entry does not exist and should be skipped.
+6. 2 bytes is 16-bit unsigned integer "link target relative path" in
+ big-endian. This does not include the NULL at the end of the string.
+7. X bytes of link-target-relative-path (length defined by previous value).
+ Is a NULL-terminated string. If the previous "size" value is 0, then
+ this entry does not exist and should be skipped.
+
+After the symlink related data, the next 4 bytes is a 32-bit unsigned integer
+"chunk count" in big-endian which will indicate the number of chunks in this
+archive.
+
+Following the chunk-count bytes, the following bytes are added for each chunk:
+
+1. 4 bytes that are a 32-bit unsigned integer "file count" in big-endian.
+
+The following bytes are added for each file within the current chunk:
+
+1. 2 bytes that are a 16-bit unsigned integer "filename length" in big-endian.
+ This does not include the NULL at the end of the string.
+2. X bytes of filename (length defined by previous value). Is a NULL-terminated
+ string.
+3. 4 bytes bit-flags.
+ 1. The first byte.
+ 1. The first bit is "user read permission".
+ 2. The second bit is "user write permission".
+ 3. The third bit is "user execute permission".
+ 4. The fourth bit is "group read permission".
+ 5. The fifth bit is "group write permission".
+ 6. The sixth bit is "group execute permission".
+ 7. The seventh bit is "other read permission".
+ 8. The eighth bit is "other write permission".
+ 2. The second byte.
+ 1. The first bit is "other execute permission".
+ 3. The third byte.
+ 1. Currently unused.
+ 4. The fourth byte.
+ 1. Currently unused.
+4. Two 4-byte unsigned integers in big-endian for UID and GID.
+ 1. A 32-bit unsigned integer in big endian that specifies the UID of the
+ file. Note that during extraction, if the user is not root, then this
+ value will be ignored.
+ 2. A 32-bit unsigned integer in big endian that specifies the GID of the
+ file. Note that during extraction, if the user is not root, then this
+ value will be ignored.
+5. A 64-bit unsigned integer in big endian for the "size of file".
+
+After the files' metadata are the current chunk's data:
+
+1. A 64-bit unsigned integer in big endian for the "size of chunk".
+2. X bytes of data for the current chunk of the previously specified size. If
+ not using de/compressor, this section is the previously mentioned files
+ concatenated with each other. If using de/compressor, this section is the
+ previously mentioned files concatenated and compressed into a single blob of
+ data.
diff --git a/file_format_1_example_0 b/file_format_1_example_0
new file mode 100644
index 0000000..a39ecf6
Binary files /dev/null and b/file_format_1_example_0 differ
diff --git a/file_format_1_example_1 b/file_format_1_example_1
new file mode 100644
index 0000000..696a49f
Binary files /dev/null and b/file_format_1_example_1 differ
diff --git a/invalid_file_format_0_example_0 b/invalid_file_format_0_example_0
new file mode 100644
index 0000000..9755999
Binary files /dev/null and b/invalid_file_format_0_example_0 differ
diff --git a/invalid_file_format_0_example_1 b/invalid_file_format_0_example_1
new file mode 100644
index 0000000..77f4424
Binary files /dev/null and b/invalid_file_format_0_example_1 differ
diff --git a/invalid_file_format_1_example_0 b/invalid_file_format_1_example_0
new file mode 100644
index 0000000..29d9159
Binary files /dev/null and b/invalid_file_format_1_example_0 differ
diff --git a/invalid_file_format_1_example_1 b/invalid_file_format_1_example_1
new file mode 100644
index 0000000..87a8c65
Binary files /dev/null and b/invalid_file_format_1_example_1 differ
diff --git a/invalid_file_format_1_example_2 b/invalid_file_format_1_example_2
new file mode 100644
index 0000000..9b003b0
Binary files /dev/null and b/invalid_file_format_1_example_2 differ
diff --git a/src/archiver.c b/src/archiver.c
index a993b18..e1ca153 100644
--- a/src/archiver.c
+++ b/src/archiver.c
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
#include
#endif
@@ -43,10 +44,12 @@
#define FILE_COUNTS_OUTPUT_FORMAT_STR_0 "\nFile %%%lulu of %%%lulu.\n"
#define FILE_COUNTS_OUTPUT_FORMAT_STR_1 "[%%%lulu/%%%lulu]\n"
+#define SIMPLE_ARCHIVER_BUFFER_SIZE (1024 * 32)
+
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
-int is_sig_pipe_occurred = 0;
+volatile int is_sig_pipe_occurred = 0;
void handle_sig_pipe(int sig) {
if (sig == SIGPIPE) {
@@ -60,6 +63,16 @@ typedef struct SDArchiverInternalToWrite {
uint64_t size;
} SDArchiverInternalToWrite;
+typedef struct SDArchiverInternalFileInfo {
+ char *filename;
+ uint8_t bit_flags[4];
+ uint32_t uid;
+ uint32_t gid;
+ uint64_t file_size;
+ /// xxxx xxx1 - is invalid.
+ int_fast8_t other_flags;
+} SDArchiverInternalFileInfo;
+
void free_internal_to_write(void *data) {
SDArchiverInternalToWrite *to_write = data;
free(to_write->buf);
@@ -91,49 +104,6 @@ void cleanup_temp_filename_delete(void ***ptrs_array) {
#endif
}
-char *filename_to_absolute_path(const char *filename) {
-#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
- SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
- SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
- __attribute__((cleanup(simple_archiver_helper_cleanup_malloced))) void *path =
- malloc(strlen(filename) + 1);
- strncpy(path, filename, strlen(filename) + 1);
-
- char *path_dir = dirname(path);
- if (!path_dir) {
- return NULL;
- }
-
- __attribute__((
- cleanup(simple_archiver_helper_cleanup_malloced))) void *dir_realpath =
- realpath(path_dir, NULL);
- if (!dir_realpath) {
- return NULL;
- }
-
- // Recreate "path" since it may have been modified by dirname().
- simple_archiver_helper_cleanup_malloced(&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;
-#endif
- return NULL;
-}
-
int write_files_fn(void *data, void *ud) {
const SDArchiverFileInfo *file_info = data;
SDArchiverState *state = ud;
@@ -278,8 +248,8 @@ int write_files_fn(void *data, void *ud) {
}
// Write file to pipe, and read from other pipe.
- char write_buf[1024];
- char read_buf[1024];
+ char write_buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
+ char read_buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
int_fast8_t write_again = 0;
int_fast8_t write_done = 0;
int_fast8_t read_done = 0;
@@ -297,7 +267,8 @@ int write_files_fn(void *data, void *ud) {
// Read from file.
if (!write_done) {
if (!write_again) {
- write_count = fread(write_buf, 1, 1024, file_fd);
+ write_count =
+ fread(write_buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, file_fd);
}
if (write_count > 0) {
ret = write(pipe_into_cmd[1], write_buf, write_count);
@@ -339,7 +310,7 @@ int write_files_fn(void *data, void *ud) {
// Read from compressor.
if (!read_done) {
- ret = read(pipe_outof_cmd[0], read_buf, 1024);
+ ret = read(pipe_outof_cmd[0], read_buf, SIMPLE_ARCHIVER_BUFFER_SIZE);
if (ret > 0) {
read_count = fwrite(read_buf, 1, (size_t)ret, tmp_fd);
if (read_count != (size_t)ret) {
@@ -495,9 +466,9 @@ int write_files_fn(void *data, void *ud) {
// Write file.
fprintf(stderr, "Writing compressed file: %s\n", file_info->filename);
do {
- write_count = fread(write_buf, 1, 1024, tmp_fd);
- if (write_count == 1024) {
- fwrite(write_buf, 1, 1024, state->out_f);
+ write_count = fread(write_buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, tmp_fd);
+ if (write_count == SIMPLE_ARCHIVER_BUFFER_SIZE) {
+ fwrite(write_buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, state->out_f);
} else if (write_count > 0) {
fwrite(write_buf, 1, write_count, state->out_f);
}
@@ -636,12 +607,12 @@ int write_files_fn(void *data, void *ud) {
// Write file.
fprintf(stderr, "Writing file: %s\n", file_info->filename);
- char buf[1024];
+ char buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
size_t ret;
do {
- ret = fread(buf, 1, 1024, fd);
- if (ret == 1024) {
- fwrite(buf, 1, 1024, state->out_f);
+ ret = fread(buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, fd);
+ if (ret == SIMPLE_ARCHIVER_BUFFER_SIZE) {
+ fwrite(buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, state->out_f);
} else if (ret > 0) {
fwrite(buf, 1, ret, state->out_f);
}
@@ -752,53 +723,15 @@ int write_files_fn(void *data, void *ud) {
// First get absolute path of link.
__attribute__((cleanup(
simple_archiver_helper_cleanup_malloced))) void *link_abs_path =
- filename_to_absolute_path(file_info->filename);
+ simple_archiver_file_abs_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.
- 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);
}
}
@@ -925,7 +858,7 @@ int filenames_to_abs_map_fn(void *data, void *ud) {
}
// Get combined full path to file.
- char *fullpath = filename_to_absolute_path(file_info->filename);
+ char *fullpath = simple_archiver_file_abs_path(file_info->filename);
if (!fullpath) {
return 1;
}
@@ -977,6 +910,684 @@ int filenames_to_abs_map_fn(void *data, void *ud) {
return 0;
}
+int read_buf_full_from_fd(FILE *fd, char *read_buf, const size_t read_buf_size,
+ const size_t amount_total, char *dst_buf) {
+ size_t amount = amount_total;
+ while (amount != 0) {
+ if (amount >= read_buf_size) {
+ if (fread(read_buf, 1, read_buf_size, fd) != read_buf_size) {
+ return SDAS_INVALID_FILE;
+ }
+ if (dst_buf) {
+ memcpy(dst_buf + (amount_total - amount), read_buf, read_buf_size);
+ }
+ amount -= read_buf_size;
+ } else {
+ if (fread(read_buf, 1, amount, fd) != amount) {
+ return SDAS_INVALID_FILE;
+ }
+ if (dst_buf) {
+ memcpy(dst_buf + (amount_total - amount), read_buf, amount);
+ }
+ amount = 0;
+ }
+ }
+
+ return SDAS_SUCCESS;
+}
+
+int read_fd_to_out_fd(FILE *in_fd, FILE *out_fd, char *read_buf,
+ const size_t read_buf_size, const size_t amount_total) {
+ size_t amount = amount_total;
+ while (amount != 0) {
+ if (amount >= read_buf_size) {
+ if (fread(read_buf, 1, read_buf_size, in_fd) != read_buf_size) {
+ return SDAS_INVALID_FILE;
+ } else if (fwrite(read_buf, 1, read_buf_size, out_fd) != read_buf_size) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ amount -= read_buf_size;
+ } else {
+ if (fread(read_buf, 1, amount, in_fd) != amount) {
+ return SDAS_INVALID_FILE;
+ } else if (fwrite(read_buf, 1, amount, out_fd) != amount) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ amount = 0;
+ }
+ }
+ return SDAS_SUCCESS;
+}
+
+int try_write_to_decomp(int *to_dec_pipe, uint64_t *chunk_remaining, FILE *in_f,
+ char *buf, const size_t buf_size, char *hold_buf,
+ ssize_t *has_hold) {
+ if (*to_dec_pipe >= 0) {
+ if (*chunk_remaining > 0) {
+ if (*chunk_remaining > buf_size) {
+ if (*has_hold < 0) {
+ size_t fread_ret = fread(buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, in_f);
+ if (fread_ret == 0) {
+ goto TRY_WRITE_TO_DECOMP_END;
+ } else {
+ ssize_t write_ret = write(*to_dec_pipe, buf, fread_ret);
+ if (write_ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ *has_hold = (ssize_t)fread_ret;
+ memcpy(hold_buf, buf, fread_ret);
+ return SDAS_SUCCESS;
+ } else {
+ return SDAS_INTERNAL_ERROR;
+ }
+ } else if (write_ret == 0) {
+ return SDAS_INTERNAL_ERROR;
+ } else if ((size_t)write_ret < fread_ret) {
+ *chunk_remaining -= (size_t)write_ret;
+ *has_hold = (ssize_t)fread_ret - write_ret;
+ memcpy(hold_buf, buf + write_ret, (size_t)*has_hold);
+ return SDAS_SUCCESS;
+ } else {
+ *chunk_remaining -= (size_t)write_ret;
+ }
+ }
+ } else {
+ ssize_t write_ret = write(*to_dec_pipe, hold_buf, (size_t)*has_hold);
+ if (write_ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return SDAS_SUCCESS;
+ } else {
+ return SDAS_INTERNAL_ERROR;
+ }
+ } else if (write_ret == 0) {
+ return SDAS_INTERNAL_ERROR;
+ } else if (write_ret < *has_hold) {
+ *chunk_remaining -= (size_t)write_ret;
+ memcpy(buf, hold_buf + write_ret, (size_t)(*has_hold - write_ret));
+ memcpy(hold_buf, buf, (size_t)(*has_hold - write_ret));
+ *has_hold = *has_hold - write_ret;
+ return SDAS_SUCCESS;
+ } else {
+ *chunk_remaining -= (size_t)*has_hold;
+ *has_hold = -1;
+ }
+ }
+ } else {
+ if (*has_hold < 0) {
+ size_t fread_ret = fread(buf, 1, *chunk_remaining, in_f);
+ if (fread_ret == 0) {
+ goto TRY_WRITE_TO_DECOMP_END;
+ } else {
+ ssize_t write_ret = write(*to_dec_pipe, buf, fread_ret);
+ if (write_ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ *has_hold = (int)fread_ret;
+ memcpy(hold_buf, buf, fread_ret);
+ return SDAS_SUCCESS;
+ } else {
+ return SDAS_INTERNAL_ERROR;
+ }
+ } else if (write_ret == 0) {
+ return SDAS_INTERNAL_ERROR;
+ } else if ((size_t)write_ret < fread_ret) {
+ *chunk_remaining -= (size_t)write_ret;
+ *has_hold = (ssize_t)fread_ret - write_ret;
+ memcpy(hold_buf, buf + write_ret, (size_t)*has_hold);
+ return SDAS_SUCCESS;
+ } else if ((size_t)write_ret <= *chunk_remaining) {
+ *chunk_remaining -= (size_t)write_ret;
+ } else {
+ return SDAS_INTERNAL_ERROR;
+ }
+ }
+ } else {
+ ssize_t write_ret = write(*to_dec_pipe, hold_buf, (size_t)*has_hold);
+ if (write_ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return SDAS_SUCCESS;
+ } else {
+ return SDAS_INTERNAL_ERROR;
+ }
+ } else if (write_ret == 0) {
+ return SDAS_INTERNAL_ERROR;
+ } else if (write_ret < *has_hold) {
+ *chunk_remaining -= (size_t)write_ret;
+ memcpy(buf, hold_buf + write_ret, (size_t)(*has_hold - write_ret));
+ memcpy(hold_buf, buf, (size_t)(*has_hold - write_ret));
+ *has_hold = *has_hold - write_ret;
+ return SDAS_SUCCESS;
+ } else {
+ *chunk_remaining -= (size_t)*has_hold;
+ *has_hold = -1;
+ }
+ }
+ }
+ }
+ }
+
+TRY_WRITE_TO_DECOMP_END:
+ if (*to_dec_pipe >= 0 && *chunk_remaining == 0 && *has_hold < 0) {
+ close(*to_dec_pipe);
+ *to_dec_pipe = -1;
+ }
+
+ return SDAS_SUCCESS;
+}
+
+/// Returns SDAS_SUCCESS on success.
+int read_decomp_to_out_file(const char *out_filename, int in_pipe,
+ char *read_buf, const size_t read_buf_size,
+ const uint64_t file_size, int *to_dec_pipe,
+ uint64_t *chunk_remaining, FILE *in_f,
+ char *hold_buf, ssize_t *has_hold) {
+ __attribute__((cleanup(simple_archiver_helper_cleanup_FILE))) FILE *out_fd =
+ NULL;
+ if (out_filename) {
+ out_fd = fopen(out_filename, "wb");
+ if (!out_fd) {
+ fprintf(stderr, "ERROR Failed to open \"%s\" for writing!\n",
+ out_filename);
+ return SDAS_INTERNAL_ERROR;
+ }
+ }
+
+ uint64_t written_amt = 0;
+ ssize_t read_ret;
+ size_t fwrite_ret;
+ while (written_amt < file_size) {
+ int ret = try_write_to_decomp(to_dec_pipe, chunk_remaining, in_f, read_buf,
+ read_buf_size, hold_buf, has_hold);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+ if (file_size - written_amt >= read_buf_size) {
+ read_ret = read(in_pipe, read_buf, read_buf_size);
+ if (read_ret > 0) {
+ if (out_fd) {
+ fwrite_ret = fwrite(read_buf, 1, (size_t)read_ret, out_fd);
+ if (fwrite_ret == (size_t)read_ret) {
+ written_amt += fwrite_ret;
+ } else if (ferror(out_fd)) {
+ fprintf(stderr, "ERROR Failed to write decompressed data!\n");
+ return SDAS_INTERNAL_ERROR;
+ } else {
+ fprintf(
+ stderr,
+ "ERROR Failed to write decompressed data (invalid state)!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ } else {
+ written_amt += (size_t)read_ret;
+ }
+ } else if (read_ret == 0) {
+ // EOF.
+ if (written_amt < file_size) {
+ fprintf(stderr,
+ "ERROR Decompressed EOF while file needs more bytes!\n");
+ return SDAS_INTERNAL_ERROR;
+ } else {
+ break;
+ }
+ } else {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ // Non-blocking read from pipe.
+ continue;
+ } else {
+ // Error.
+ fprintf(stderr, "ERROR Failed to read from decompressor! (%lu)\n",
+ read_ret);
+ return SDAS_INTERNAL_ERROR;
+ }
+ }
+ } else {
+ read_ret = read(in_pipe, read_buf, file_size - written_amt);
+ if (read_ret > 0) {
+ if (out_fd) {
+ fwrite_ret = fwrite(read_buf, 1, (size_t)read_ret, out_fd);
+ if (fwrite_ret == (size_t)read_ret) {
+ written_amt += fwrite_ret;
+ } else if (ferror(out_fd)) {
+ fprintf(stderr, "ERROR Failed to write decompressed data!\n");
+ return SDAS_INTERNAL_ERROR;
+ } else {
+ fprintf(
+ stderr,
+ "ERROR Failed to write decompressed data (invalid state)!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ } else {
+ written_amt += (size_t)read_ret;
+ }
+ } else if (read_ret == 0) {
+ // EOF.
+ if (written_amt < file_size) {
+ fprintf(stderr,
+ "ERROR Decompressed EOF while file needs more bytes!\n");
+ return SDAS_INTERNAL_ERROR;
+ } else {
+ break;
+ }
+ } else {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ // Non-blocking read from pipe.
+ continue;
+ } else {
+ // Error.
+ fprintf(stderr, "ERROR Failed to read from decompressor! (%d)\n",
+ errno);
+ return SDAS_INTERNAL_ERROR;
+ }
+ }
+ }
+ }
+
+ return written_amt == file_size ? SDAS_SUCCESS : SDAS_INTERNAL_ERROR;
+}
+
+void free_internal_file_info(void *data) {
+ SDArchiverInternalFileInfo *file_info = data;
+ if (file_info) {
+ if (file_info->filename) {
+ free(file_info->filename);
+ }
+ free(file_info);
+ }
+}
+
+void cleanup_internal_file_info(SDArchiverInternalFileInfo **file_info) {
+ if (file_info && *file_info) {
+ if ((*file_info)->filename) {
+ free((*file_info)->filename);
+ }
+ free(*file_info);
+ *file_info = NULL;
+ }
+}
+
+#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;
+
+ if ((flags[0] & 1) != 0) {
+ permissions |= S_IRUSR;
+ if (print) {
+ fprintf(stderr, "r");
+ }
+ } else if (print) {
+ fprintf(stderr, "-");
+ }
+ if ((flags[0] & 2) != 0) {
+ permissions |= S_IWUSR;
+ if (print) {
+ fprintf(stderr, "w");
+ }
+ } else if (print) {
+ fprintf(stderr, "-");
+ }
+ if ((flags[0] & 4) != 0) {
+ permissions |= S_IXUSR;
+ if (print) {
+ fprintf(stderr, "x");
+ }
+ } else if (print) {
+ fprintf(stderr, "-");
+ }
+ if ((flags[0] & 8) != 0) {
+ permissions |= S_IRGRP;
+ if (print) {
+ fprintf(stderr, "r");
+ }
+ } else if (print) {
+ fprintf(stderr, "-");
+ }
+ if ((flags[0] & 0x10) != 0) {
+ permissions |= S_IWGRP;
+ if (print) {
+ fprintf(stderr, "w");
+ }
+ } else if (print) {
+ fprintf(stderr, "-");
+ }
+ if ((flags[0] & 0x20) != 0) {
+ permissions |= S_IXGRP;
+ if (print) {
+ fprintf(stderr, "x");
+ }
+ } else if (print) {
+ fprintf(stderr, "-");
+ }
+ if ((flags[0] & 0x40) != 0) {
+ permissions |= S_IROTH;
+ if (print) {
+ fprintf(stderr, "r");
+ }
+ } else if (print) {
+ fprintf(stderr, "-");
+ }
+ if ((flags[0] & 0x80) != 0) {
+ permissions |= S_IWOTH;
+ if (print) {
+ fprintf(stderr, "w");
+ }
+ } else if (print) {
+ fprintf(stderr, "-");
+ }
+ if ((flags[1] & 1) != 0) {
+ permissions |= S_IXOTH;
+ if (print) {
+ fprintf(stderr, "x");
+ }
+ } else if (print) {
+ fprintf(stderr, "-");
+ }
+
+ 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) {
+ if (fd && *fd >= 0) {
+ close(*fd);
+ *fd = -1;
+ }
+}
+
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+void simple_archiver_internal_cleanup_decomp_pid(pid_t *decomp_pid) {
+ if (decomp_pid && *decomp_pid >= 0) {
+ int decompressor_status;
+ int decompressor_return_val;
+ int retries = 0;
+ int decompressor_ret;
+ CHECK_DECOMPRESSER:
+ decompressor_ret = waitpid(*decomp_pid, &decompressor_status, 0);
+ if (decompressor_ret == *decomp_pid) {
+ // Status is available.
+ decompressor_return_val = WIFEXITED(decompressor_status);
+ if (decompressor_return_val && WEXITSTATUS(decompressor_status)) {
+ fprintf(stderr,
+ "WARNING: Exec failed (exec exit code %d)! Invalid "
+ "decompressor cmd?\n",
+ decompressor_return_val);
+ }
+ } else if (decompressor_ret == 0) {
+ // Probably still running.
+ ++retries;
+ if (retries > 5) {
+ fprintf(stderr, "WARNING Decompressor process not stopped!\n");
+ return;
+ }
+ sleep(5);
+ goto CHECK_DECOMPRESSER;
+ } else {
+ // Error.
+ fprintf(stderr,
+ "WARNING: Exec failed (exec exit code unknown)! Invalid "
+ "decompressor cmd?\n");
+ }
+ *decomp_pid = -1;
+ }
+}
+#endif
+
+int symlinks_and_files_from_files(void *data, void *ud) {
+ SDArchiverFileInfo *file_info = data;
+ void **ptr_array = ud;
+ SDArchiverLinkedList *symlinks_list = ptr_array[0];
+ SDArchiverLinkedList *files_list = ptr_array[1];
+ const char *user_cwd = ptr_array[2];
+
+ if (file_info->filename) {
+ if (file_info->link_dest) {
+ simple_archiver_list_add(
+ symlinks_list, file_info->filename,
+ simple_archiver_helper_datastructure_cleanup_nop);
+ } else {
+ SDArchiverInternalFileInfo *file_info_struct =
+ malloc(sizeof(SDArchiverInternalFileInfo));
+ file_info_struct->filename = strdup(file_info->filename);
+ file_info_struct->bit_flags[0] = 0xFF;
+ file_info_struct->bit_flags[1] = 1;
+ file_info_struct->bit_flags[2] = 0;
+ file_info_struct->bit_flags[3] = 0;
+ file_info_struct->uid = 0;
+ file_info_struct->gid = 0;
+ file_info_struct->file_size = 0;
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+ __attribute__((cleanup(
+ simple_archiver_helper_cleanup_chdir_back))) char *original_cwd =
+ NULL;
+ if (user_cwd) {
+ original_cwd = realpath(".", NULL);
+ if (chdir(user_cwd)) {
+ free(file_info_struct);
+ return 1;
+ }
+ }
+ struct stat stat_buf;
+ memset(&stat_buf, 0, sizeof(struct stat));
+ int stat_status = fstatat(AT_FDCWD, file_info_struct->filename, &stat_buf,
+ AT_SYMLINK_NOFOLLOW);
+ if (stat_status != 0) {
+ free(file_info_struct);
+ return 1;
+ }
+ file_info_struct->bit_flags[0] = 0;
+ file_info_struct->bit_flags[1] &= 0xFE;
+ if ((stat_buf.st_mode & S_IRUSR) != 0) {
+ file_info_struct->bit_flags[0] |= 1;
+ }
+ if ((stat_buf.st_mode & S_IWUSR) != 0) {
+ file_info_struct->bit_flags[0] |= 2;
+ }
+ if ((stat_buf.st_mode & S_IXUSR) != 0) {
+ file_info_struct->bit_flags[0] |= 4;
+ }
+ if ((stat_buf.st_mode & S_IRGRP) != 0) {
+ file_info_struct->bit_flags[0] |= 8;
+ }
+ if ((stat_buf.st_mode & S_IWGRP) != 0) {
+ file_info_struct->bit_flags[0] |= 0x10;
+ }
+ if ((stat_buf.st_mode & S_IXGRP) != 0) {
+ file_info_struct->bit_flags[0] |= 0x20;
+ }
+ if ((stat_buf.st_mode & S_IROTH) != 0) {
+ file_info_struct->bit_flags[0] |= 0x40;
+ }
+ if ((stat_buf.st_mode & S_IWOTH) != 0) {
+ file_info_struct->bit_flags[0] |= 0x80;
+ }
+ if ((stat_buf.st_mode & S_IXOTH) != 0) {
+ file_info_struct->bit_flags[1] |= 1;
+ }
+ file_info_struct->uid = stat_buf.st_uid;
+ file_info_struct->gid = stat_buf.st_gid;
+#endif
+ __attribute__((cleanup(simple_archiver_helper_cleanup_FILE))) FILE *fd =
+ fopen(file_info_struct->filename, "rb");
+ if (!fd) {
+ free(file_info_struct);
+ return 1;
+ }
+ if (fseek(fd, 0, SEEK_END) < 0) {
+ free(file_info_struct);
+ return 1;
+ }
+ long ftell_ret = ftell(fd);
+ if (ftell_ret < 0) {
+ free(file_info_struct);
+ return 1;
+ }
+ file_info_struct->file_size = (uint64_t)ftell_ret;
+ simple_archiver_list_add(files_list, file_info_struct,
+ free_internal_file_info);
+ }
+ }
+
+ return 0;
+}
+
+int files_to_chunk_count(void *data, void *ud) {
+ SDArchiverInternalFileInfo *file_info_struct = data;
+ void **ptrs = ud;
+ const uint64_t *chunk_size = ptrs[0];
+ uint64_t *current_size = ptrs[1];
+ uint64_t *current_count = ptrs[2];
+ SDArchiverLinkedList *chunk_counts = ptrs[3];
+
+ ++(*current_count);
+
+ // Get file size.
+ *current_size += file_info_struct->file_size;
+
+ // Check size.
+ if (*current_size >= *chunk_size) {
+ uint64_t *count = malloc(sizeof(uint64_t));
+ *count = *current_count;
+ simple_archiver_list_add(chunk_counts, count, NULL);
+ *current_count = 0;
+ *current_size = 0;
+ }
+
+ return 0;
+}
+
char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) {
switch (error) {
case SDAS_SUCCESS:
@@ -1001,6 +1612,8 @@ char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) {
return "Failed to extract symlink (internal error)";
case SDAS_FAILED_TO_CHANGE_CWD:
return "Failed to change current working directory";
+ case SDAS_INVALID_WRITE_VERSION:
+ return "Unsupported write version file format";
default:
return "Unknown error";
}
@@ -1032,6 +1645,20 @@ void simple_archiver_free_state(SDArchiverState **state) {
int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
const SDArchiverLinkedList *filenames) {
+ switch (state->parsed->write_version) {
+ case 0:
+ return simple_archiver_write_v0(out_f, state, filenames);
+ case 1:
+ return simple_archiver_write_v1(out_f, state, filenames);
+ default:
+ fprintf(stderr, "ERROR: Unsupported write version %u!\n",
+ state->parsed->write_version);
+ return SDAS_INVALID_WRITE_VERSION;
+ }
+}
+
+int simple_archiver_write_v0(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();
@@ -1171,14 +1798,732 @@ int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
return SDAS_SUCCESS;
}
+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);
+
+ // Get a list of symlinks and a list of files.
+ __attribute__((cleanup(simple_archiver_list_free)))
+ SDArchiverLinkedList *symlinks_list = simple_archiver_list_init();
+ __attribute__((cleanup(simple_archiver_list_free)))
+ SDArchiverLinkedList *files_list = simple_archiver_list_init();
+
+ ptr_array = malloc(sizeof(void *) * 3);
+ ptr_array[0] = symlinks_list;
+ ptr_array[1] = files_list;
+ ptr_array[2] = (void *)state->parsed->user_cwd;
+
+ if (simple_archiver_list_get(filenames, symlinks_and_files_from_files,
+ ptr_array)) {
+ free(ptr_array);
+ return SDAS_INTERNAL_ERROR;
+ }
+ free(ptr_array);
+
+ if (fwrite("SIMPLE_ARCHIVE_VER", 1, 18, out_f) != 18) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+
+ char buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
+ uint16_t u16 = 1;
+
+ simple_archiver_helper_16_bit_be(&u16);
+
+ if (fwrite(&u16, 2, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+
+ if (state->parsed->compressor && !state->parsed->decompressor) {
+ return SDAS_NO_DECOMPRESSOR;
+ } else if (!state->parsed->compressor && state->parsed->decompressor) {
+ return SDAS_NO_COMPRESSOR;
+ } else if (state->parsed->compressor && state->parsed->decompressor) {
+ // 4 bytes flags, using de/compressor.
+ memset(buf, 0, 4);
+ buf[0] |= 1;
+ if (fwrite(buf, 1, 4, out_f) != 4) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+
+ size_t len = strlen(state->parsed->compressor);
+ if (len >= 0xFFFF) {
+ fprintf(stderr, "ERROR: Compressor cmd is too long!\n");
+ return SDAS_INVALID_PARSED_STATE;
+ }
+
+ u16 = (uint16_t)len;
+ simple_archiver_helper_16_bit_be(&u16);
+ if (fwrite(&u16, 1, 2, out_f) != 2) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ simple_archiver_helper_16_bit_be(&u16);
+
+ if (fwrite(state->parsed->compressor, 1, u16 + 1, out_f) !=
+ (size_t)u16 + 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+
+ len = strlen(state->parsed->decompressor);
+ if (len >= 0xFFFF) {
+ fprintf(stderr, "ERROR: Decompressor cmd is too long!\n");
+ return SDAS_INVALID_PARSED_STATE;
+ }
+
+ u16 = (uint16_t)len;
+ simple_archiver_helper_16_bit_be(&u16);
+ if (fwrite(&u16, 1, 2, out_f) != 2) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ simple_archiver_helper_16_bit_be(&u16);
+
+ if (fwrite(state->parsed->decompressor, 1, u16 + 1, out_f) !=
+ (size_t)u16 + 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ } else {
+ // 4 bytes flags, not using de/compressor.
+ memset(buf, 0, 4);
+ if (fwrite(buf, 1, 4, out_f) != 4) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ }
+
+ if (symlinks_list->count > 0xFFFFFFFF) {
+ fprintf(stderr, "ERROR: Too many symlinks!\n");
+ return SDAS_INVALID_PARSED_STATE;
+ }
+
+ uint32_t u32 = (uint32_t)symlinks_list->count;
+ simple_archiver_helper_32_bit_be(&u32);
+ if (fwrite(&u32, 4, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ simple_archiver_helper_32_bit_be(&u32);
+
+ // Change cwd if user specified.
+ __attribute__((cleanup(
+ simple_archiver_helper_cleanup_chdir_back))) char *original_cwd = NULL;
+ if (state->parsed->user_cwd) {
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+ original_cwd = realpath(".", NULL);
+ if (chdir(state->parsed->user_cwd)) {
+ return SDAS_INTERNAL_ERROR;
+ }
+#endif
+ }
+
+ {
+ const SDArchiverLLNode *node = symlinks_list->head;
+ for (u32 = 0;
+ u32 < (uint32_t)symlinks_list->count && node != symlinks_list->tail;) {
+ node = node->next;
+ ++u32;
+ memset(buf, 0, 2);
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+ // Check if symlink points to thing to be stored into archive.
+ __attribute__((
+ cleanup(simple_archiver_helper_cleanup_malloced))) void *abs_path =
+ realpath(node->data, NULL);
+ __attribute__((cleanup(
+ simple_archiver_helper_cleanup_malloced))) void *rel_path = NULL;
+ if (abs_path) {
+ __attribute__((cleanup(
+ simple_archiver_helper_cleanup_malloced))) void *link_abs_path =
+ simple_archiver_file_abs_path(node->data);
+ if (!link_abs_path) {
+ fprintf(stderr, "WARNING: Failed to get absolute path to link!\n");
+ } else {
+ rel_path = simple_archiver_filenames_to_relative_path(link_abs_path,
+ abs_path);
+ }
+ }
+ if (abs_path && (state->parsed->flags & 0x20) == 0 &&
+ !simple_archiver_hash_map_get(abs_filenames, abs_path,
+ strlen(abs_path) + 1)) {
+ // Is not a filename being archived, set preference to absolute path.
+ buf[0] |= 1;
+ }
+
+ // Get symlink stats for permissions.
+ struct stat stat_buf;
+ memset(&stat_buf, 0, sizeof(struct stat));
+ int stat_status =
+ fstatat(AT_FDCWD, node->data, &stat_buf, AT_SYMLINK_NOFOLLOW);
+ if (stat_status != 0) {
+ return SDAS_INTERNAL_ERROR;
+ }
+
+ if ((stat_buf.st_mode & S_IRUSR) != 0) {
+ buf[0] |= 2;
+ }
+ if ((stat_buf.st_mode & S_IWUSR) != 0) {
+ buf[0] |= 4;
+ }
+ if ((stat_buf.st_mode & S_IXUSR) != 0) {
+ buf[0] |= 8;
+ }
+ if ((stat_buf.st_mode & S_IRGRP) != 0) {
+ buf[0] |= 0x10;
+ }
+ if ((stat_buf.st_mode & S_IWGRP) != 0) {
+ buf[0] |= 0x20;
+ }
+ if ((stat_buf.st_mode & S_IXGRP) != 0) {
+ buf[0] |= 0x40;
+ }
+ if ((stat_buf.st_mode & S_IROTH) != 0) {
+ buf[0] |= (char)0x80;
+ }
+ if ((stat_buf.st_mode & S_IWOTH) != 0) {
+ buf[1] |= 1;
+ }
+ if ((stat_buf.st_mode & S_IXOTH) != 0) {
+ buf[1] |= 2;
+ }
+#else
+ buf[0] = 0xFE;
+ buf[1] = 3;
+#endif
+ if (fwrite(buf, 1, 2, out_f) != 2) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+
+ size_t len = strlen(node->data);
+ if (len >= 0xFFFF) {
+ fprintf(stderr, "ERROR: Link name is too long!\n");
+ return SDAS_INVALID_PARSED_STATE;
+ }
+
+ u16 = (uint16_t)len;
+ simple_archiver_helper_16_bit_be(&u16);
+ if (fwrite(&u16, 2, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ simple_archiver_helper_16_bit_be(&u16);
+ if (fwrite(node->data, 1, u16 + 1, out_f) != (size_t)u16 + 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+
+ if (abs_path && (state->parsed->flags & 0x20) == 0) {
+ len = strlen(abs_path);
+ if (len >= 0xFFFF) {
+ fprintf(stderr,
+ "ERROR: Symlink destination absolute path is too long!\n");
+ return SDAS_INVALID_PARSED_STATE;
+ }
+
+ u16 = (uint16_t)len;
+ simple_archiver_helper_16_bit_be(&u16);
+ if (fwrite(&u16, 2, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ simple_archiver_helper_16_bit_be(&u16);
+ if (fwrite(abs_path, 1, u16 + 1, out_f) != (size_t)u16 + 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ } else {
+ u16 = 0;
+ if (fwrite(&u16, 2, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ }
+
+ if (rel_path) {
+ len = strlen(rel_path);
+ if (len >= 0xFFFF) {
+ fprintf(stderr,
+ "ERROR: Symlink destination relative path is too long!\n");
+ return SDAS_INVALID_PARSED_STATE;
+ }
+
+ u16 = (uint16_t)len;
+ simple_archiver_helper_16_bit_be(&u16);
+ if (fwrite(&u16, 2, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ simple_archiver_helper_16_bit_be(&u16);
+ if (fwrite(rel_path, 1, u16 + 1, out_f) != (size_t)u16 + 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ } else {
+ u16 = 0;
+ if (fwrite(&u16, 2, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ }
+ }
+ if (u32 != (uint32_t)symlinks_list->count) {
+ fprintf(stderr, "ERROR: Iterated through %u symlinks out of %u total!\n",
+ u32, (uint32_t)symlinks_list->count);
+ return SDAS_INTERNAL_ERROR;
+ }
+ }
+
+ __attribute__((cleanup(simple_archiver_list_free)))
+ SDArchiverLinkedList *chunk_counts = simple_archiver_list_init();
+
+ {
+ uint64_t current_size = 0;
+ uint64_t current_count = 0;
+ void **ptrs = malloc(sizeof(void *) * 4);
+ ptrs[0] = (void *)&state->parsed->minimum_chunk_size;
+ ptrs[1] = ¤t_size;
+ ptrs[2] = ¤t_count;
+ ptrs[3] = chunk_counts;
+ if (simple_archiver_list_get(files_list, files_to_chunk_count, ptrs)) {
+ free(ptrs);
+ fprintf(stderr, "ERROR: Internal error calculating chunk counts!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ free(ptrs);
+ if (current_size > 0 && current_count > 0) {
+ uint64_t *count = malloc(sizeof(uint64_t));
+ *count = current_count;
+ simple_archiver_list_add(chunk_counts, count, NULL);
+ }
+ }
+
+ // Verify chunk counts.
+ {
+ uint64_t count = 0;
+ for (SDArchiverLLNode *node = chunk_counts->head->next;
+ node != chunk_counts->tail; node = node->next) {
+ if (*((uint64_t *)node->data) > 0xFFFFFFFF) {
+ fprintf(stderr, "ERROR: file count in chunk is too large!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ count += *((uint64_t *)node->data);
+ // fprintf(stderr, "DEBUG: chunk count %4llu\n",
+ // *((uint64_t*)node->data));
+ }
+ if (count != files_list->count) {
+ fprintf(stderr,
+ "ERROR: Internal error calculating chunk counts (invalid number "
+ "of files)!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ }
+
+ // Write number of chunks.
+ if (chunk_counts->count > 0xFFFFFFFF) {
+ fprintf(stderr, "ERROR: Too many chunks!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ u32 = (uint32_t)chunk_counts->count;
+ simple_archiver_helper_32_bit_be(&u32);
+ if (fwrite(&u32, 4, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+
+ __attribute__((cleanup(simple_archiver_helper_cleanup_malloced))) void
+ *non_compressing_chunk_size = NULL;
+ if (!state->parsed->compressor || !state->parsed->decompressor) {
+ non_compressing_chunk_size = malloc(sizeof(uint64_t));
+ }
+ uint64_t *non_c_chunk_size = non_compressing_chunk_size;
+
+ SDArchiverLLNode *file_node = files_list->head;
+ uint64_t chunk_count = 0;
+ for (SDArchiverLLNode *chunk_c_node = chunk_counts->head->next;
+ chunk_c_node != chunk_counts->tail; chunk_c_node = chunk_c_node->next) {
+ fprintf(stderr, "CHUNK %3lu of %3lu\n", ++chunk_count, chunk_counts->count);
+ // Write file count before iterating through files.
+ if (non_c_chunk_size) {
+ *non_c_chunk_size = 0;
+ }
+
+ u32 = (uint32_t)(*((uint64_t *)chunk_c_node->data));
+ simple_archiver_helper_32_bit_be(&u32);
+ if (fwrite(&u32, 4, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ SDArchiverLLNode *saved_node = file_node;
+ for (uint64_t file_idx = 0; file_idx < *((uint64_t *)chunk_c_node->data);
+ ++file_idx) {
+ file_node = file_node->next;
+ if (file_node == files_list->tail) {
+ return SDAS_INTERNAL_ERROR;
+ }
+ const SDArchiverInternalFileInfo *file_info_struct = file_node->data;
+ if (non_c_chunk_size) {
+ *non_c_chunk_size += file_info_struct->file_size;
+ }
+ size_t len = strlen(file_info_struct->filename);
+ if (len >= 0xFFFF) {
+ fprintf(stderr, "ERROR: Filename is too large!\n");
+ return SDAS_INVALID_FILE;
+ }
+ u16 = (uint16_t)len;
+ simple_archiver_helper_16_bit_be(&u16);
+ if (fwrite(&u16, 2, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ simple_archiver_helper_16_bit_be(&u16);
+ if (fwrite(file_info_struct->filename, 1, u16 + 1, out_f) !=
+ (size_t)u16 + 1) {
+ return SDAS_FAILED_TO_WRITE;
+ } else if (fwrite(file_info_struct->bit_flags, 1, 4, out_f) != 4) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ // UID and GID.
+ u32 = file_info_struct->uid;
+ simple_archiver_helper_32_bit_be(&u32);
+ if (fwrite(&u32, 4, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ u32 = file_info_struct->gid;
+ simple_archiver_helper_32_bit_be(&u32);
+ if (fwrite(&u32, 4, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+
+ uint64_t u64 = file_info_struct->file_size;
+ simple_archiver_helper_64_bit_be(&u64);
+ if (fwrite(&u64, 8, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+ }
+
+ file_node = saved_node;
+
+ if (state->parsed->compressor && state->parsed->decompressor) {
+ // Is compressing.
+
+ size_t temp_filename_size = strlen(state->parsed->temp_dir) + 1 + 64;
+ __attribute__((cleanup(
+ simple_archiver_helper_cleanup_c_string))) char *temp_filename =
+ malloc(temp_filename_size);
+
+ __attribute__((cleanup(cleanup_temp_filename_delete))) void **ptrs_array =
+ malloc(sizeof(void *) * 2);
+ ptrs_array[0] = NULL;
+ ptrs_array[1] = NULL;
+
+ __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
+ FILE *temp_fd = NULL;
+
+ if (state->parsed->temp_dir) {
+ size_t idx = 0;
+ size_t temp_dir_len = strlen(state->parsed->temp_dir);
+ snprintf(temp_filename, temp_filename_size, TEMP_FILENAME_CMP,
+ state->parsed->temp_dir,
+ state->parsed->temp_dir[temp_dir_len - 1] == '/' ? "" : "/",
+ idx);
+ do {
+ FILE *test_fd = fopen(temp_filename, "rb");
+ if (test_fd) {
+ // File exists.
+ fclose(test_fd);
+ snprintf(
+ temp_filename, temp_filename_size, TEMP_FILENAME_CMP,
+ state->parsed->temp_dir,
+ state->parsed->temp_dir[temp_dir_len - 1] == '/' ? "" : "/",
+ ++idx);
+ } else if (idx > 0xFFFF) {
+ return SDAS_INTERNAL_ERROR;
+ } else {
+ break;
+ }
+ } while (1);
+ temp_fd = fopen(temp_filename, "w+b");
+ ptrs_array[0] = temp_filename;
+ } else {
+ temp_fd = tmpfile();
+ }
+
+ if (!temp_fd) {
+ return SDAS_INTERNAL_ERROR;
+ }
+
+ // Handle SIGPIPE.
+ is_sig_pipe_occurred = 0;
+ signal(SIGPIPE, handle_sig_pipe);
+
+ int pipe_into_cmd[2];
+ int pipe_outof_cmd[2];
+ __attribute__((cleanup(
+ simple_archiver_internal_cleanup_decomp_pid))) pid_t compressor_pid =
+ -1;
+
+ if (pipe(pipe_into_cmd) != 0) {
+ // Unable to create pipes.
+ return SDAS_INTERNAL_ERROR;
+ } else if (pipe(pipe_outof_cmd) != 0) {
+ // Unable to create second set of pipes.
+ close(pipe_into_cmd[0]);
+ close(pipe_into_cmd[1]);
+ return SDAS_INTERNAL_ERROR;
+ } else if (fcntl(pipe_into_cmd[1], F_SETFL, O_NONBLOCK) != 0) {
+ // Unable to set non-blocking on into-write-pipe.
+ close(pipe_into_cmd[0]);
+ close(pipe_into_cmd[1]);
+ close(pipe_outof_cmd[0]);
+ close(pipe_outof_cmd[1]);
+ return SDAS_INTERNAL_ERROR;
+ } else if (fcntl(pipe_outof_cmd[0], F_SETFL, O_NONBLOCK) != 0) {
+ // Unable to set non-blocking on outof-read-pipe.
+ close(pipe_into_cmd[0]);
+ close(pipe_into_cmd[1]);
+ close(pipe_outof_cmd[0]);
+ close(pipe_outof_cmd[1]);
+ return SDAS_INTERNAL_ERROR;
+ } else if (simple_archiver_de_compress(pipe_into_cmd, pipe_outof_cmd,
+ state->parsed->compressor,
+ &compressor_pid) != 0) {
+ // Failed to spawn compressor.
+ close(pipe_into_cmd[1]);
+ close(pipe_outof_cmd[0]);
+ fprintf(stderr,
+ "WARNING: Failed to start compressor cmd! Invalid cmd?\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+
+ // Close unnecessary pipe fds on this end of the transfer.
+ close(pipe_into_cmd[0]);
+ close(pipe_outof_cmd[1]);
+
+ // Set up cleanup so that remaining open pipes in this side is cleaned up.
+ __attribute__((cleanup(
+ simple_archiver_internal_cleanup_int_fd))) int pipe_outof_read =
+ pipe_outof_cmd[0];
+ __attribute__((cleanup(
+ simple_archiver_internal_cleanup_int_fd))) int pipe_into_write =
+ pipe_into_cmd[1];
+
+ int_fast8_t to_temp_finished = 0;
+ for (uint64_t file_idx = 0; file_idx < *((uint64_t *)chunk_c_node->data);
+ ++file_idx) {
+ file_node = file_node->next;
+ if (file_node == files_list->tail) {
+ return SDAS_INTERNAL_ERROR;
+ }
+ const SDArchiverInternalFileInfo *file_info_struct = file_node->data;
+ fprintf(stderr, " FILE %3lu of %3lu: %s\n", file_idx + 1,
+ *(uint64_t *)chunk_c_node->data, file_info_struct->filename);
+ __attribute__((cleanup(simple_archiver_helper_cleanup_FILE))) FILE *fd =
+ fopen(file_info_struct->filename, "rb");
+
+ int_fast8_t to_comp_finished = 0;
+ char hold_buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
+ ssize_t has_hold = -1;
+ while (!to_comp_finished) {
+ if (!to_comp_finished) {
+ // Write to compressor.
+ if (ferror(fd)) {
+ fprintf(stderr, "ERROR: Writing to chunk, file read error!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ if (has_hold < 0) {
+ size_t fread_ret = fread(buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, fd);
+ if (fread_ret > 0) {
+ ssize_t write_ret = write(pipe_into_write, buf, fread_ret);
+ if (write_ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ // Non-blocking write.
+ has_hold = (int)fread_ret;
+ memcpy(hold_buf, buf, fread_ret);
+ } else {
+ fprintf(
+ stderr,
+ "ERROR: Writing to compressor, pipe write error!\n");
+ return SDAS_FAILED_TO_WRITE;
+ }
+ } else if (write_ret == 0) {
+ fprintf(
+ stderr,
+ "ERROR: Writing to compressor, unable to write bytes!\n");
+ return SDAS_FAILED_TO_WRITE;
+ } else if ((size_t)write_ret < fread_ret) {
+ has_hold = (ssize_t)fread_ret - write_ret;
+ memcpy(hold_buf, buf + write_ret, (size_t)has_hold);
+ }
+ }
+
+ if (feof(fd) && has_hold < 0) {
+ to_comp_finished = 1;
+ }
+ } else {
+ ssize_t write_ret =
+ write(pipe_into_write, hold_buf, (size_t)has_hold);
+ if (write_ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ // Non-blocking write.
+ } else {
+ return SDAS_INTERNAL_ERROR;
+ }
+ } else if (write_ret < has_hold) {
+ memcpy(buf, hold_buf + write_ret,
+ (size_t)(has_hold - write_ret));
+ memcpy(hold_buf, buf, (size_t)(has_hold - write_ret));
+ has_hold = has_hold - write_ret;
+ } else if (write_ret != has_hold) {
+ return SDAS_INTERNAL_ERROR;
+ } else {
+ has_hold = -1;
+ }
+ }
+ }
+
+ // Write compressed data to temp file.
+ ssize_t read_ret =
+ read(pipe_outof_read, buf, SIMPLE_ARCHIVER_BUFFER_SIZE);
+ if (read_ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ // Non-blocking read.
+ } else {
+ fprintf(stderr,
+ "ERROR: Reading from compressor, pipe read error!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ } else if (read_ret == 0) {
+ // EOF.
+ to_temp_finished = 1;
+ } else {
+ size_t fwrite_ret = fwrite(buf, 1, (size_t)read_ret, temp_fd);
+ if (fwrite_ret != (size_t)read_ret) {
+ fprintf(stderr,
+ "ERROR: Reading from compressor, failed to write to "
+ "temporary file!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ }
+ }
+ }
+
+ simple_archiver_internal_cleanup_int_fd(&pipe_into_write);
+
+ // Finish writing.
+ if (!to_temp_finished) {
+ while (1) {
+ ssize_t read_ret =
+ read(pipe_outof_read, buf, SIMPLE_ARCHIVER_BUFFER_SIZE);
+ if (read_ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ // Non-blocking read.
+ } else {
+ fprintf(stderr,
+ "ERROR: Reading from compressor, pipe read error!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ } else if (read_ret == 0) {
+ // EOF.
+ break;
+ } else {
+ size_t fwrite_ret = fwrite(buf, 1, (size_t)read_ret, temp_fd);
+ if (fwrite_ret != (size_t)read_ret) {
+ fprintf(stderr,
+ "ERROR: Reading from compressor, failed to write to "
+ "temporary file!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ }
+ }
+ }
+
+ long comp_chunk_size = ftell(temp_fd);
+ if (comp_chunk_size < 0) {
+ fprintf(stderr,
+ "ERROR: Temp file reported negative size after compression!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+
+ // Write compressed chunk size.
+ uint64_t u64 = (uint64_t)comp_chunk_size;
+ simple_archiver_helper_64_bit_be(&u64);
+ if (fwrite(&u64, 8, 1, out_f) != 1) {
+ return SDAS_FAILED_TO_WRITE;
+ }
+
+ if (fseek(temp_fd, 0, SEEK_SET) != 0) {
+ return SDAS_INTERNAL_ERROR;
+ }
+
+ size_t written_size = 0;
+
+ // Write compressed chunk.
+ while (!feof(temp_fd)) {
+ if (ferror(temp_fd)) {
+ return SDAS_INTERNAL_ERROR;
+ }
+ size_t fread_ret = fread(buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, temp_fd);
+ if (fread_ret > 0) {
+ size_t fwrite_ret = fwrite(buf, 1, fread_ret, out_f);
+ written_size += fread_ret;
+ if (fwrite_ret != fread_ret) {
+ fprintf(stderr,
+ "ERROR: Partial write of read bytes from temp file to "
+ "output file!\n");
+ return SDAS_FAILED_TO_WRITE;
+ }
+ }
+ }
+
+ if (written_size != (size_t)comp_chunk_size) {
+ fprintf(stderr,
+ "ERROR: Written chunk size is not actual chunk size!\n");
+ return SDAS_FAILED_TO_WRITE;
+ }
+
+ // Cleanup and remove temp_fd.
+ simple_archiver_helper_cleanup_FILE(&temp_fd);
+ } else {
+ // Is NOT compressing.
+ if (!non_c_chunk_size) {
+ return SDAS_INTERNAL_ERROR;
+ }
+ simple_archiver_helper_64_bit_be(non_c_chunk_size);
+ fwrite(non_c_chunk_size, 8, 1, out_f);
+ for (uint64_t file_idx = 0; file_idx < *((uint64_t *)chunk_c_node->data);
+ ++file_idx) {
+ file_node = file_node->next;
+ if (file_node == files_list->tail) {
+ return SDAS_INTERNAL_ERROR;
+ }
+ const SDArchiverInternalFileInfo *file_info_struct = file_node->data;
+ fprintf(stderr, " FILE %3lu of %3lu: %s\n", file_idx + 1,
+ *(uint64_t *)chunk_c_node->data, file_info_struct->filename);
+ __attribute__((cleanup(simple_archiver_helper_cleanup_FILE))) FILE *fd =
+ fopen(file_info_struct->filename, "rb");
+ while (!feof(fd)) {
+ if (ferror(fd)) {
+ fprintf(stderr, "ERROR: Writing to chunk, file read error!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ size_t fread_ret = fread(buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, fd);
+ if (fread_ret > 0) {
+ size_t fwrite_ret = fwrite(buf, 1, fread_ret, out_f);
+ if (fwrite_ret != fread_ret) {
+ fprintf(stderr, "ERROR: Writing to chunk, file write error!\n");
+ return SDAS_FAILED_TO_WRITE;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return SDAS_SUCCESS;
+}
+
int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
const SDArchiverState *state) {
- uint8_t buf[1024];
- memset(buf, 0, 1024);
+ uint8_t buf[32];
+ memset(buf, 0, 32);
uint16_t u16;
- uint32_t u32;
- uint64_t u64;
- int_fast8_t is_compressed = 0;
if (fread(buf, 1, 18, in_f) != 18) {
return SDAS_INVALID_FILE;
@@ -1186,10 +2531,30 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
return SDAS_INVALID_FILE;
} else if (fread(buf, 1, 2, in_f) != 2) {
return SDAS_INVALID_FILE;
- } else if (buf[0] != 0 || buf[1] != 0) {
- // Version is not zero.
+ }
+
+ memcpy(&u16, buf, 2);
+ simple_archiver_helper_16_bit_be(&u16);
+
+ if (u16 == 0) {
+ return simple_archiver_parse_archive_version_0(in_f, do_extract, state);
+ } else if (u16 == 1) {
+ return simple_archiver_parse_archive_version_1(in_f, do_extract, state);
+ } else {
+ fprintf(stderr, "ERROR Unsupported archive version %u!\n", u16);
return SDAS_INVALID_FILE;
- } else if (fread(buf, 1, 4, in_f) != 4) {
+ }
+}
+
+int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
+ const SDArchiverState *state) {
+ uint8_t buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ int_fast8_t is_compressed = 0;
+
+ if (fread(buf, 1, 4, in_f) != 4) {
return SDAS_INVALID_FILE;
}
@@ -1216,11 +2581,11 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
}
simple_archiver_helper_16_bit_be(&u16);
fprintf(stderr, "Compressor size is %u\n", u16);
- if (u16 < 1024) {
+ if (u16 < SIMPLE_ARCHIVER_BUFFER_SIZE) {
if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
return SDAS_INVALID_FILE;
}
- buf[1023] = 0;
+ buf[SIMPLE_ARCHIVER_BUFFER_SIZE - 1] = 0;
fprintf(stderr, "Compressor cmd: %s\n", buf);
} else {
__attribute__((
@@ -1240,11 +2605,11 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
}
simple_archiver_helper_16_bit_be(&u16);
fprintf(stderr, "Decompressor size is %u\n", u16);
- if (u16 < 1024) {
+ if (u16 < SIMPLE_ARCHIVER_BUFFER_SIZE) {
if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
return SDAS_INVALID_FILE;
}
- buf[1023] = 0;
+ buf[SIMPLE_ARCHIVER_BUFFER_SIZE - 1] = 0;
fprintf(stderr, "Decompressor cmd: %s\n", buf);
decompressor_cmd = malloc(u16 + 1);
memcpy((char *)decompressor_cmd, buf, u16 + 1);
@@ -1259,6 +2624,8 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
}
uc_heap_buf[u16 - 1] = 0;
fprintf(stderr, "Decompressor cmd: %s\n", uc_heap_buf);
+ decompressor_cmd = heap_buf;
+ heap_buf = NULL;
}
} else {
fprintf(stderr, "De/compressor flag is NOT set.\n");
@@ -1274,7 +2641,7 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
const size_t digits = simple_archiver_helper_num_digits(size);
char format_str[128];
snprintf(format_str, 128, FILE_COUNTS_OUTPUT_FORMAT_STR_0, digits, digits);
- int_fast8_t skip = 0;
+ int_fast8_t skip;
__attribute__((cleanup(simple_archiver_hash_map_free)))
SDArchiverHashMap *hash_map = NULL;
if (state && state->parsed->working_files &&
@@ -1292,6 +2659,7 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
}
}
for (uint32_t idx = 0; idx < size; ++idx) {
+ skip = 0;
fprintf(stderr, format_str, idx + 1, size);
if (feof(in_f) || ferror(in_f)) {
return SDAS_INVALID_FILE;
@@ -1303,13 +2671,18 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
simple_archiver_helper_cleanup_malloced))) void *out_f_name = NULL;
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE))) FILE *out_f =
NULL;
- if (u16 < 1024) {
+ if (u16 < SIMPLE_ARCHIVER_BUFFER_SIZE) {
if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
return SDAS_INVALID_FILE;
}
- buf[1023] = 0;
+ buf[SIMPLE_ARCHIVER_BUFFER_SIZE - 1] = 0;
fprintf(stderr, " Filename: %s\n", buf);
- if (do_extract) {
+ if (simple_archiver_validate_file_path((char *)buf)) {
+ fprintf(stderr, " ERROR: Invalid filename!\n");
+ skip = 1;
+ }
+
+ if (do_extract && !skip) {
if ((state->parsed->flags & 0x8) == 0) {
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
FILE *test_fd = fopen((const char *)buf, "rb");
@@ -1323,6 +2696,17 @@ int simple_archiver_parse_archive_info(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);
@@ -1332,17 +2716,23 @@ int simple_archiver_parse_archive_info(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 (simple_archiver_validate_file_path((char *)uc_heap_buf)) {
+ fprintf(stderr, " ERROR: Invalid filename!\n");
+ skip = 1;
+ }
+
+ if (do_extract && !skip) {
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,
@@ -1353,10 +2743,22 @@ int simple_archiver_parse_archive_info(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);
}
}
}
@@ -1365,10 +2767,10 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
return SDAS_INVALID_FILE;
}
- mode_t permissions = 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 = 0;
if (do_extract == 0) {
fprintf(stderr, " Permissions: ");
@@ -1479,6 +2881,12 @@ int simple_archiver_parse_archive_info(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);
@@ -1487,7 +2895,7 @@ int simple_archiver_parse_archive_info(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);
@@ -1576,8 +2984,8 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
int_fast8_t write_again = 0;
int_fast8_t write_pipe_done = 0;
int_fast8_t read_pipe_done = 0;
- size_t fread_ret;
- char recv_buf[1024];
+ size_t fread_ret = 0;
+ char recv_buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
size_t amount_to_read;
while (!write_pipe_done || !read_pipe_done) {
if (is_sig_pipe_occurred) {
@@ -1590,8 +2998,8 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
// Read from file.
if (!write_pipe_done) {
if (!write_again && compressed_file_size != 0) {
- if (compressed_file_size > 1024) {
- amount_to_read = 1024;
+ if (compressed_file_size > SIMPLE_ARCHIVER_BUFFER_SIZE) {
+ amount_to_read = SIMPLE_ARCHIVER_BUFFER_SIZE;
} else {
amount_to_read = compressed_file_size;
}
@@ -1633,7 +3041,8 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
// Read output from decompressor and write to file.
if (!read_pipe_done) {
- ssize_t read_ret = read(pipe_outof_cmd[0], recv_buf, 1024);
+ ssize_t read_ret = read(pipe_outof_cmd[0], recv_buf,
+ SIMPLE_ARCHIVER_BUFFER_SIZE);
if (read_ret > 0) {
size_t fwrite_ret =
fwrite(recv_buf, 1, (size_t)read_ret, out_f);
@@ -1689,16 +3098,18 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
uint64_t compressed_file_size = u64;
size_t fread_ret;
while (compressed_file_size != 0) {
- if (compressed_file_size > 1024) {
- fread_ret = fread(buf, 1, 1024, in_f);
+ if (compressed_file_size > SIMPLE_ARCHIVER_BUFFER_SIZE) {
+ fread_ret = fread(buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, in_f);
if (ferror(in_f)) {
// 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 {
@@ -1707,10 +3118,12 @@ int simple_archiver_parse_archive_info(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;
}
@@ -1723,12 +3136,14 @@ int simple_archiver_parse_archive_info(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) {
- if (u64 > 1024) {
- size_t read_ret = fread(buf, 1, 1024, in_f);
+ if (u64 > SIMPLE_ARCHIVER_BUFFER_SIZE) {
+ size_t read_ret = fread(buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, in_f);
if (read_ret > 0) {
u64 -= read_ret;
} else if (ferror(in_f)) {
@@ -1762,11 +3177,11 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
simple_archiver_helper_16_bit_be(&u16);
if (u16 == 0) {
fprintf(stderr, " Link does not have absolute path.\n");
- } else if (u16 < 1024) {
+ } else if (u16 < SIMPLE_ARCHIVER_BUFFER_SIZE) {
if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
return SDAS_INVALID_FILE;
}
- buf[1023] = 0;
+ buf[SIMPLE_ARCHIVER_BUFFER_SIZE - 1] = 0;
fprintf(stderr, " Link absolute path: %s\n", buf);
abs_path = malloc((size_t)u16 + 1);
strncpy(abs_path, (char *)buf, (size_t)u16 + 1);
@@ -1785,11 +3200,11 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
simple_archiver_helper_16_bit_be(&u16);
if (u16 == 0) {
fprintf(stderr, " Link does not have relative path.\n");
- } else if (u16 < 1024) {
+ } else if (u16 < SIMPLE_ARCHIVER_BUFFER_SIZE) {
if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
return SDAS_INVALID_FILE;
}
- buf[1023] = 0;
+ buf[SIMPLE_ARCHIVER_BUFFER_SIZE - 1] = 0;
fprintf(stderr, " Link relative path: %s\n", buf);
rel_path = malloc((size_t)u16 + 1);
strncpy(rel_path, (char *)buf, (size_t)u16 + 1);
@@ -1802,26 +3217,108 @@ int simple_archiver_parse_archive_info(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) {
@@ -1832,6 +3329,19 @@ int simple_archiver_parse_archive_info(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 || \
@@ -1841,6 +3351,19 @@ int simple_archiver_parse_archive_info(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(
@@ -1854,6 +3377,723 @@ int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
return SDAS_SUCCESS;
}
+int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
+ const SDArchiverState *state) {
+ uint8_t buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+
+ __attribute__((cleanup(simple_archiver_hash_map_free)))
+ SDArchiverHashMap *working_files_map = NULL;
+ if (state && state->parsed->working_files &&
+ state->parsed->working_files[0] != NULL) {
+ working_files_map = simple_archiver_hash_map_init();
+ for (char **iter = state->parsed->working_files; *iter != NULL; ++iter) {
+ size_t len = strlen(*iter) + 1;
+ char *key = malloc(len);
+ memcpy(key, *iter, len);
+ key[len - 1] = 0;
+ simple_archiver_hash_map_insert(
+ working_files_map, key, key, len,
+ simple_archiver_helper_datastructure_cleanup_nop, NULL);
+ }
+ }
+
+ if (fread(buf, 1, 4, in_f) != 4) {
+ return SDAS_INVALID_FILE;
+ }
+
+ if (do_extract && state->parsed->user_cwd) {
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+ if (chdir(state->parsed->user_cwd)) {
+ return SDAS_FAILED_TO_CHANGE_CWD;
+ }
+#endif
+ }
+
+ const int_fast8_t is_compressed = (buf[0] & 1) ? 1 : 0;
+
+ __attribute__((cleanup(
+ simple_archiver_helper_cleanup_c_string))) char *compressor_cmd = NULL;
+ __attribute__((cleanup(
+ simple_archiver_helper_cleanup_c_string))) char *decompressor_cmd = NULL;
+
+ if (is_compressed) {
+ if (fread(buf, 1, 2, in_f) != 2) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u16, buf, 2);
+ simple_archiver_helper_16_bit_be(&u16);
+ compressor_cmd = malloc(u16 + 1);
+ int ret =
+ read_buf_full_from_fd(in_f, (char *)buf, SIMPLE_ARCHIVER_BUFFER_SIZE,
+ u16 + 1, compressor_cmd);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+ compressor_cmd[u16] = 0;
+
+ fprintf(stderr, "Compressor command: %s\n", compressor_cmd);
+
+ if (fread(buf, 1, 2, in_f) != 2) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u16, buf, 2);
+ simple_archiver_helper_16_bit_be(&u16);
+ decompressor_cmd = malloc(u16 + 1);
+ ret = read_buf_full_from_fd(in_f, (char *)buf, SIMPLE_ARCHIVER_BUFFER_SIZE,
+ u16 + 1, decompressor_cmd);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+ decompressor_cmd[u16] = 0;
+
+ fprintf(stderr, "Decompressor command: %s\n", decompressor_cmd);
+ if (state && state->parsed && state->parsed->decompressor) {
+ fprintf(stderr, "Overriding decompressor with: %s\n",
+ state->parsed->decompressor);
+ }
+ }
+
+ // Link count.
+ if (fread(buf, 1, 4, in_f) != 4) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u32, buf, 4);
+ simple_archiver_helper_32_bit_be(&u32);
+
+ for (uint32_t idx = 0; idx < u32; ++idx) {
+ fprintf(stderr, "SYMLINK %3u of %3u\n", idx + 1, u32);
+ if (fread(buf, 1, 2, in_f) != 2) {
+ 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;
+ uint_fast8_t skip_due_to_invalid = 0;
+
+ if (fread(buf, 1, 2, in_f) != 2) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u16, buf, 2);
+ simple_archiver_helper_16_bit_be(&u16);
+
+ __attribute__((
+ cleanup(simple_archiver_helper_cleanup_c_string))) char *link_name =
+ malloc(u16 + 1);
+
+ int ret = read_buf_full_from_fd(
+ in_f, (char *)buf, SIMPLE_ARCHIVER_BUFFER_SIZE, u16 + 1, link_name);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+
+ if (!do_extract) {
+ fprintf(stderr, " Link name: %s\n", link_name);
+ if (absolute_preferred) {
+ fprintf(stderr, " Absolute path preferred.\n");
+ } else {
+ fprintf(stderr, " Relative path preferred.\n");
+ }
+#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 (simple_archiver_validate_file_path(link_name)) {
+ fprintf(stderr, " WARNING: Invalid link name \"%s\"!\n", link_name);
+ skip_due_to_invalid = 1;
+ }
+
+ if (working_files_map &&
+ simple_archiver_hash_map_get(working_files_map, link_name, u16 + 1) ==
+ NULL) {
+ skip_due_to_map = 1;
+ fprintf(stderr, " Skipping not specified in args...\n");
+ }
+
+ if (fread(buf, 1, 2, in_f) != 2) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u16, buf, 2);
+ simple_archiver_helper_16_bit_be(&u16);
+ if (u16 != 0) {
+ __attribute__((
+ cleanup(simple_archiver_helper_cleanup_c_string))) char *path =
+ malloc(u16 + 1);
+ ret = read_buf_full_from_fd(in_f, (char *)buf,
+ SIMPLE_ARCHIVER_BUFFER_SIZE, u16 + 1, path);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+ path[u16] = 0;
+ if (do_extract && !skip_due_to_map && !skip_due_to_invalid &&
+ absolute_preferred) {
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+ 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);
+ }
+ } else if (!do_extract) {
+ fprintf(stderr, " No Absolute path.\n");
+ }
+
+ if (fread(buf, 1, 2, in_f) != 2) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u16, buf, 2);
+ simple_archiver_helper_16_bit_be(&u16);
+ if (u16 != 0) {
+ __attribute__((
+ cleanup(simple_archiver_helper_cleanup_c_string))) char *path =
+ malloc(u16 + 1);
+ ret = read_buf_full_from_fd(in_f, (char *)buf,
+ SIMPLE_ARCHIVER_BUFFER_SIZE, u16 + 1, path);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+ path[u16] = 0;
+ if (do_extract && !skip_due_to_map && !skip_due_to_invalid &&
+ !absolute_preferred) {
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+ 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);
+ }
+ } else if (!do_extract) {
+ fprintf(stderr, " No Relative path.\n");
+ }
+
+ if (do_extract && !link_extracted && !skip_due_to_map &&
+ !skip_due_to_invalid) {
+ fprintf(stderr, " WARNING: Symlink \"%s\" was not created!\n",
+ link_name);
+ }
+ }
+
+ if (fread(buf, 1, 4, in_f) != 4) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u32, buf, 4);
+ simple_archiver_helper_32_bit_be(&u32);
+
+ const uint32_t chunk_count = u32;
+ for (uint32_t chunk_idx = 0; chunk_idx < chunk_count; ++chunk_idx) {
+ fprintf(stderr, "CHUNK %3u of %3u\n", chunk_idx + 1, chunk_count);
+
+ if (fread(buf, 1, 4, in_f) != 4) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u32, buf, 4);
+ simple_archiver_helper_32_bit_be(&u32);
+
+ const uint32_t file_count = u32;
+
+ __attribute__((cleanup(simple_archiver_list_free)))
+ SDArchiverLinkedList *file_info_list = simple_archiver_list_init();
+
+ __attribute__((cleanup(cleanup_internal_file_info)))
+ SDArchiverInternalFileInfo *file_info = NULL;
+
+ for (uint32_t file_idx = 0; file_idx < file_count; ++file_idx) {
+ file_info = malloc(sizeof(SDArchiverInternalFileInfo));
+ memset(file_info, 0, sizeof(SDArchiverInternalFileInfo));
+
+ if (fread(buf, 1, 2, in_f) != 2) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u16, buf, 2);
+ simple_archiver_helper_16_bit_be(&u16);
+
+ file_info->filename = malloc(u16 + 1);
+ int ret =
+ read_buf_full_from_fd(in_f, (char *)buf, SIMPLE_ARCHIVER_BUFFER_SIZE,
+ u16 + 1, file_info->filename);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+ file_info->filename[u16] = 0;
+
+ if (simple_archiver_validate_file_path(file_info->filename)) {
+ fprintf(stderr, "ERROR: File idx %u: Invalid filename!\n", file_idx);
+ file_info->other_flags |= 1;
+ }
+
+ 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;
+ }
+
+ if (fread(buf, 1, 4, in_f) != 4) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u32, buf, 4);
+ simple_archiver_helper_32_bit_be(&u32);
+ file_info->uid = u32;
+
+ if (fread(buf, 1, 4, in_f) != 4) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u32, buf, 4);
+ simple_archiver_helper_32_bit_be(&u32);
+ file_info->gid = u32;
+
+ if (fread(buf, 1, 8, in_f) != 8) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u64, buf, 8);
+ simple_archiver_helper_64_bit_be(&u64);
+ file_info->file_size = u64;
+
+ simple_archiver_list_add(file_info_list, file_info,
+ free_internal_file_info);
+ file_info = NULL;
+ }
+
+ if (fread(buf, 1, 8, in_f) != 8) {
+ return SDAS_INVALID_FILE;
+ }
+ memcpy(&u64, buf, 8);
+ simple_archiver_helper_64_bit_be(&u64);
+
+ const uint64_t chunk_size = u64;
+ uint64_t chunk_remaining = chunk_size;
+ uint64_t chunk_idx = 0;
+
+ SDArchiverLLNode *node = file_info_list->head;
+ uint16_t file_idx = 0;
+
+#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) {
+ // Start the decompressing process and read into files.
+
+ // Handle SIGPIPE.
+ is_sig_pipe_occurred = 0;
+ signal(SIGPIPE, handle_sig_pipe);
+
+ int pipe_into_cmd[2];
+ int pipe_outof_cmd[2];
+ __attribute__((cleanup(
+ simple_archiver_internal_cleanup_decomp_pid))) pid_t decompressor_pid;
+ if (pipe(pipe_into_cmd) != 0) {
+ // Unable to create pipes.
+ break;
+ } else if (pipe(pipe_outof_cmd) != 0) {
+ // Unable to create second set of pipes.
+ close(pipe_into_cmd[0]);
+ close(pipe_into_cmd[1]);
+ return SDAS_INTERNAL_ERROR;
+ } else if (fcntl(pipe_into_cmd[1], F_SETFL, O_NONBLOCK) != 0) {
+ // Unable to set non-blocking on into-write-pipe.
+ close(pipe_into_cmd[0]);
+ close(pipe_into_cmd[1]);
+ close(pipe_outof_cmd[0]);
+ close(pipe_outof_cmd[1]);
+ return SDAS_INTERNAL_ERROR;
+ } else if (fcntl(pipe_outof_cmd[0], F_SETFL, O_NONBLOCK) != 0) {
+ // Unable to set non-blocking on outof-read-pipe.
+ close(pipe_into_cmd[0]);
+ close(pipe_into_cmd[1]);
+ close(pipe_outof_cmd[0]);
+ close(pipe_outof_cmd[1]);
+ return SDAS_INTERNAL_ERROR;
+ }
+
+ if (state && state->parsed && state->parsed->decompressor) {
+ if (simple_archiver_de_compress(pipe_into_cmd, pipe_outof_cmd,
+ state->parsed->decompressor,
+ &decompressor_pid) != 0) {
+ // Failed to spawn compressor.
+ close(pipe_into_cmd[1]);
+ close(pipe_outof_cmd[0]);
+ fprintf(stderr,
+ "WARNING: Failed to start decompressor cmd! Invalid cmd?\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ } else {
+ if (simple_archiver_de_compress(pipe_into_cmd, pipe_outof_cmd,
+ decompressor_cmd,
+ &decompressor_pid) != 0) {
+ // Failed to spawn compressor.
+ close(pipe_into_cmd[1]);
+ close(pipe_outof_cmd[0]);
+ fprintf(stderr,
+ "WARNING: Failed to start decompressor cmd! Invalid cmd?\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+ }
+
+ // Close unnecessary pipe fds on this end of the transfer.
+ close(pipe_into_cmd[0]);
+ close(pipe_outof_cmd[1]);
+
+ __attribute__((cleanup(
+ simple_archiver_internal_cleanup_int_fd))) int pipe_outof_read =
+ pipe_outof_cmd[0];
+ __attribute__((cleanup(
+ simple_archiver_internal_cleanup_int_fd))) int pipe_into_write =
+ pipe_into_cmd[1];
+
+ int decompressor_status;
+ int decompressor_return_val;
+ int decompressor_ret =
+ waitpid(decompressor_pid, &decompressor_status, WNOHANG);
+ if (decompressor_ret == decompressor_pid) {
+ // Status is available.
+ if (WIFEXITED(decompressor_status)) {
+ decompressor_return_val = WEXITSTATUS(decompressor_status);
+ fprintf(stderr,
+ "WARNING: Exec failed (exec exit code %d)! Invalid "
+ "decompressor cmd?\n",
+ decompressor_return_val);
+ return SDAS_INTERNAL_ERROR;
+ }
+ } else if (decompressor_ret == 0) {
+ // Probably still running, continue on.
+ } else {
+ // Error.
+ fprintf(stderr,
+ "WARNING: Exec failed (exec exit code unknown)! Invalid "
+ "decompressor cmd?\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+
+ char hold_buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
+ ssize_t has_hold = -1;
+
+ while (node->next != file_info_list->tail) {
+ node = node->next;
+ const SDArchiverInternalFileInfo *file_info = node->data;
+ fprintf(stderr, " FILE %3u of %3u: %s\n", ++file_idx, file_count,
+ file_info->filename);
+
+ uint_fast8_t skip_due_to_map = 0;
+ if (working_files_map && simple_archiver_hash_map_get(
+ working_files_map, file_info->filename,
+ strlen(file_info->filename) + 1) == NULL) {
+ skip_due_to_map = 1;
+ fprintf(stderr, " Skipping not specified in args...\n");
+ } else if ((file_info->other_flags & 1) != 0) {
+ fprintf(stderr, " Skipping invalid filename...\n");
+ }
+
+ if (do_extract && !skip_due_to_map &&
+ (file_info->other_flags & 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_version_1(file_info->bit_flags, 0);
+#endif
+ if ((state->parsed->flags & 8) == 0) {
+ // Check if file already exists.
+ __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
+ FILE *temp_fd = fopen(file_info->filename, "r");
+ if (temp_fd) {
+ fprintf(stderr,
+ " WARNING: File already exists and "
+ "\"--overwrite-extract\" is not specified, skipping!\n");
+ read_decomp_to_out_file(NULL, pipe_outof_read, (char *)buf,
+ SIMPLE_ARCHIVER_BUFFER_SIZE,
+ file_info->file_size, &pipe_into_write,
+ &chunk_remaining, in_f, hold_buf,
+ &has_hold);
+ continue;
+ }
+ }
+
+ simple_archiver_helper_make_dirs(file_info->filename);
+ int ret = read_decomp_to_out_file(
+ file_info->filename, pipe_outof_read, (char *)buf,
+ SIMPLE_ARCHIVER_BUFFER_SIZE, file_info->file_size,
+ &pipe_into_write, &chunk_remaining, in_f, hold_buf, &has_hold);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+ if (chmod(file_info->filename, permissions) == -1) {
+ return SDAS_INTERNAL_ERROR;
+ } else if (geteuid() == 0 &&
+ chown(file_info->filename, file_info->uid,
+ file_info->gid) != 0) {
+ fprintf(stderr,
+ "ERROR Failed to set UID/GID as EUID 0 of file \"%s\"!\n",
+ file_info->filename);
+ return SDAS_INTERNAL_ERROR;
+ }
+#endif
+ } else if (!skip_due_to_map && (file_info->other_flags & 1) == 0) {
+ fprintf(stderr, " Permissions: ");
+ permissions_from_bits_version_1(file_info->bit_flags, 1);
+ fprintf(stderr, "\n UID: %u\n GID: %u\n", file_info->uid,
+ file_info->gid);
+ if (is_compressed) {
+ fprintf(stderr, " File size (uncompressed): %lu\n",
+ file_info->file_size);
+ } else {
+ fprintf(stderr, " File size: %lu\n", file_info->file_size);
+ }
+ int ret = read_decomp_to_out_file(
+ NULL, pipe_outof_read, (char *)buf, SIMPLE_ARCHIVER_BUFFER_SIZE,
+ file_info->file_size, &pipe_into_write, &chunk_remaining, in_f,
+ hold_buf, &has_hold);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+ } else {
+ int ret = read_decomp_to_out_file(
+ NULL, pipe_outof_read, (char *)buf, SIMPLE_ARCHIVER_BUFFER_SIZE,
+ file_info->file_size, &pipe_into_write, &chunk_remaining, in_f,
+ hold_buf, &has_hold);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ // Ensure EOF is left from pipe.
+ ssize_t read_ret =
+ read(pipe_outof_read, buf, SIMPLE_ARCHIVER_BUFFER_SIZE);
+ if (read_ret > 0) {
+ fprintf(stderr, "WARNING decompressor didn't reach EOF!\n");
+ }
+ } else {
+#else
+ // } (This comment exists so that vim can correctly match curly-braces.
+ if (!is_compressed) {
+#endif
+ while (node->next != file_info_list->tail) {
+ node = node->next;
+ const SDArchiverInternalFileInfo *file_info = node->data;
+ fprintf(stderr, " FILE %3u of %3u: %s\n", ++file_idx, file_count,
+ file_info->filename);
+ chunk_idx += file_info->file_size;
+ if (chunk_idx > chunk_size) {
+ fprintf(stderr, "ERROR Files in chunk is larger than chunk!\n");
+ return SDAS_INTERNAL_ERROR;
+ }
+
+ uint_fast8_t skip_due_to_map = 0;
+ if (working_files_map && simple_archiver_hash_map_get(
+ working_files_map, file_info->filename,
+ strlen(file_info->filename) + 1) == NULL) {
+ skip_due_to_map = 1;
+ fprintf(stderr, " Skipping not specified in args...\n");
+ } else if (file_info->other_flags & 1) {
+ fprintf(stderr, " Skipping invalid filename...\n");
+ }
+
+ if (do_extract && !skip_due_to_map &&
+ (file_info->other_flags & 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_version_1(file_info->bit_flags, 0);
+#endif
+ if ((state->parsed->flags & 8) == 0) {
+ // Check if file already exists.
+ __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
+ FILE *temp_fd = fopen(file_info->filename, "r");
+ if (temp_fd) {
+ fprintf(stderr,
+ " WARNING: File already exists and "
+ "\"--overwrite-extract\" is not specified, skipping!\n");
+ int ret = read_buf_full_from_fd(in_f, (char *)buf,
+ SIMPLE_ARCHIVER_BUFFER_SIZE,
+ file_info->file_size, NULL);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+ continue;
+ }
+ }
+ simple_archiver_helper_make_dirs(file_info->filename);
+ __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
+ FILE *out_fd = fopen(file_info->filename, "wb");
+ int ret = read_fd_to_out_fd(in_f, out_fd, (char *)buf,
+ SIMPLE_ARCHIVER_BUFFER_SIZE,
+ file_info->file_size);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+ simple_archiver_helper_cleanup_FILE(&out_fd);
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+ if (chmod(file_info->filename, permissions) == -1) {
+ return SDAS_INTERNAL_ERROR;
+ } else if (geteuid() == 0 &&
+ chown(file_info->filename, file_info->uid,
+ file_info->gid) != 0) {
+ fprintf(stderr,
+ "ERROR Failed to set UID/GID as EUID 0 of file \"%s\"!\n",
+ file_info->filename);
+ return SDAS_INTERNAL_ERROR;
+ }
+#endif
+ } else if (!skip_due_to_map && (file_info->other_flags & 1) == 0) {
+ fprintf(stderr, " Permissions: ");
+ permissions_from_bits_version_1(file_info->bit_flags, 1);
+ fprintf(stderr, "\n UID: %u\n GID: %u\n", file_info->uid,
+ file_info->gid);
+ if (is_compressed) {
+ fprintf(stderr, " File size (compressed): %lu\n",
+ file_info->file_size);
+ } else {
+ fprintf(stderr, " File size: %lu\n", file_info->file_size);
+ }
+ int ret = read_buf_full_from_fd(in_f, (char *)buf,
+ SIMPLE_ARCHIVER_BUFFER_SIZE,
+ file_info->file_size, NULL);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+ } else {
+ int ret = read_buf_full_from_fd(in_f, (char *)buf,
+ SIMPLE_ARCHIVER_BUFFER_SIZE,
+ file_info->file_size, NULL);
+ if (ret != SDAS_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+ }
+ }
+
+ return SDAS_SUCCESS;
+}
+
int simple_archiver_de_compress(int pipe_fd_in[2], int pipe_fd_out[2],
const char *cmd, void *pid_out) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
@@ -1911,3 +4151,126 @@ 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;
+ size_t new_rel_path_size = strlen(rel_path) + 1 + 3;
+ char *new_rel_path = malloc(new_rel_path_size);
+ new_rel_path[0] = '.';
+ new_rel_path[1] = '.';
+ new_rel_path[2] = '/';
+ strncpy(new_rel_path + 3, rel_path, new_rel_path_size - 3);
+ free(rel_path);
+ rel_path = new_rel_path;
+ ++idx;
+ }
+ } while (has_slash);
+
+ return rel_path;
+}
+
+char *simple_archiver_file_abs_path(const char *filename) {
+#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
+ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
+ __attribute__((cleanup(simple_archiver_helper_cleanup_malloced))) void *path =
+ malloc(strlen(filename) + 1);
+ strncpy(path, filename, strlen(filename) + 1);
+
+ char *path_dir = dirname(path);
+ if (!path_dir) {
+ return NULL;
+ }
+
+ __attribute__((
+ cleanup(simple_archiver_helper_cleanup_malloced))) void *dir_realpath =
+ realpath(path_dir, NULL);
+ if (!dir_realpath) {
+ return NULL;
+ }
+
+ // Recreate "path" since it may have been modified by dirname().
+ simple_archiver_helper_cleanup_malloced(&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.
+ const size_t realpath_size = strlen(dir_realpath) + 1;
+ const size_t basename_size = strlen(filename_basename) + 1;
+ const size_t fullpath_size = realpath_size + basename_size;
+ char *fullpath = malloc(fullpath_size);
+ strncpy(fullpath, dir_realpath, realpath_size);
+ fullpath[realpath_size - 1] = '/';
+ strcpy(fullpath + realpath_size, filename_basename);
+
+ return fullpath;
+#endif
+ return NULL;
+}
+
+int simple_archiver_validate_file_path(const char *filepath) {
+ if (!filepath) {
+ return 5;
+ }
+
+ const size_t len = strlen(filepath);
+
+ if (len >= 1 && filepath[0] == '/') {
+ return 1;
+ } else if (len >= 3 && filepath[0] == '.' && filepath[1] == '.' &&
+ filepath[2] == '/') {
+ return 2;
+ } else if (len >= 3 && filepath[len - 1] == '.' && filepath[len - 2] == '.' &&
+ filepath[len - 3] == '/') {
+ return 4;
+ }
+
+ for (size_t idx = 0; idx < len; ++idx) {
+ if (len - idx < 4) {
+ break;
+ } else if (strncmp(filepath + idx, "/../", 4) == 0) {
+ return 3;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/archiver.h b/src/archiver.h
index e651b87..012fd39 100644
--- a/src/archiver.h
+++ b/src/archiver.h
@@ -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,
@@ -51,8 +51,9 @@ enum SDArchiverStateReturns {
SDAS_INTERNAL_ERROR,
SDAS_FAILED_TO_CREATE_MAP,
SDAS_FAILED_TO_EXTRACT_SYMLINK,
- SDAS_FAILED_TO_CHANGE_CWD
-};
+ 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);
@@ -65,12 +66,46 @@ void simple_archiver_free_state(SDArchiverState **state);
int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
const SDArchiverLinkedList *filenames);
+int simple_archiver_write_v0(FILE *out_f, SDArchiverState *state,
+ const SDArchiverLinkedList *filenames);
+
+int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
+ const SDArchiverLinkedList *filenames);
+
/// Returns zero on success.
int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
const SDArchiverState *state);
+/// Returns zero on success.
+int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
+ const SDArchiverState *state);
+
+/// Returns zero on success.
+int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
+ const SDArchiverState *state);
+
/// Returns zero on success.
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);
+
+/// Gets the absolute path to a file given a path to a file.
+/// Should also work on symlinks such that the returned string is the path to
+/// the link itself, not what it points to.
+/// Non-NULL on success, and must be free'd if non-NULL.
+char *simple_archiver_file_abs_path(const char *filename);
+
+/// Used to validate a file in a ".simplearchive" file to avoid writing outside
+/// of current working directory.
+/// Returns zero if file is OK.
+/// Returns 1 if file starts with '/'.
+/// Returns 2 if file contains '../' at the start.
+/// Returns 3 if file contains '/../' in the middle.
+/// Returns 4 if file contains '/..' at the end.
+/// Returns 5 if "filepath" is NULL.
+int simple_archiver_validate_file_path(const char *filepath);
+
#endif
diff --git a/src/main.c b/src/main.c
index fef432f..937b5c2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -67,12 +67,13 @@ int main(int argc, const char **argv) {
}
}
+ SDArchiverParsedStatus parsed_status;
__attribute__((cleanup(simple_archiver_list_free)))
SDArchiverLinkedList *filenames =
- simple_archiver_parsed_to_filenames(&parsed);
- if (!filenames) {
- fprintf(stderr,
- "ERROR: Failed to resolve filenames from positional arguments!\n");
+ simple_archiver_parsed_to_filenames(&parsed, &parsed_status);
+ if (!filenames || parsed_status != SDAPS_SUCCESS) {
+ fprintf(stderr, "ERROR: %s!\n",
+ simple_archiver_parsed_status_to_str(parsed_status));
return 8;
}
@@ -96,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);
@@ -112,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);
}
}
@@ -129,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);
@@ -137,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);
}
}
@@ -156,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);
@@ -164,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);
}
}
diff --git a/src/parser.c b/src/parser.c
index a69dfbf..31a9ea8 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -131,6 +131,17 @@ int list_remove_same_str_fn(void *data, void *ud) {
return 0;
}
+char *simple_archiver_parsed_status_to_str(SDArchiverParsedStatus status) {
+ switch (status) {
+ case SDAPS_SUCCESS:
+ return "Success";
+ case SDAPS_NO_USER_CWD:
+ return "No user current working directory (-C )";
+ default:
+ return "Unknown error";
+ }
+}
+
void simple_archiver_print_usage(void) {
fprintf(stderr, "Usage flags:\n");
fprintf(stderr, "-c : create archive file\n");
@@ -145,9 +156,11 @@ void simple_archiver_print_usage(void) {
"-C : Change current working directory before "
"archiving/extracting\n");
fprintf(stderr,
- "--compressor : requires --decompressor\n");
+ "--compressor : requires --decompressor and cmd "
+ "must use stdin/stdout\n");
fprintf(stderr,
- "--decompressor : requires --compressor\n");
+ "--decompressor : requires --compressor and "
+ "cmd must use stdin/stdout\n");
fprintf(stderr,
" Specifying \"--decompressor\" when extracting overrides archive "
"file's stored decompressor cmd\n");
@@ -158,6 +171,12 @@ void simple_archiver_print_usage(void) {
fprintf(stderr,
"--temp-files-dir : where to store temporary files created "
"when compressing (defaults to current working directory)\n");
+ fprintf(stderr,
+ "--write-version : Force write version file format "
+ "(default 1)\n");
+ fprintf(stderr,
+ "--chunk-min-size : v1 file format minimum chunk size "
+ "(default 4194304 or 4MiB)\n");
fprintf(stderr,
"-- : specifies remaining arguments are files to archive/extract\n");
fprintf(
@@ -178,6 +197,8 @@ SDArchiverParsed simple_archiver_create_parsed(void) {
parsed.working_files = NULL;
parsed.temp_dir = NULL;
parsed.user_cwd = NULL;
+ parsed.write_version = 1;
+ parsed.minimum_chunk_size = 4194304;
return parsed;
}
@@ -288,11 +309,50 @@ int simple_archiver_parse_args(int argc, const char **argv,
out->temp_dir = argv[1];
--argc;
++argv;
+ } else if (strcmp(argv[0], "--write-version") == 0) {
+ if (argc < 2) {
+ fprintf(stderr,
+ "ERROR: --write-version expects an integer argument!\n");
+ simple_archiver_print_usage();
+ return 1;
+ }
+ int version = atoi(argv[1]);
+ if (version < 0) {
+ fprintf(stderr, "ERROR: --write-version cannot be negative!\n");
+ simple_archiver_print_usage();
+ return 1;
+ } else if (version > 1) {
+ fprintf(stderr, "ERROR: --write-version must be 0 or 1!\n");
+ simple_archiver_print_usage();
+ return 1;
+ }
+ out->write_version = (uint32_t)version;
+ --argc;
+ ++argv;
+ } else if (strcmp(argv[0], "--chunk-min-size") == 0) {
+ if (argc < 2) {
+ fprintf(stderr,
+ "ERROR: --chunk-min-size expects an integer argument!\n");
+ simple_archiver_print_usage();
+ return 1;
+ }
+ out->minimum_chunk_size = strtoull(argv[1], NULL, 10);
+ if (out->minimum_chunk_size == 0) {
+ fprintf(stderr, "ERROR: --chunk-min-size cannot be zero!\n");
+ simple_archiver_print_usage();
+ return 1;
+ }
+ --argc;
+ ++argv;
} else if (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) {
is_remaining_args = 1;
} else if (argv[0][0] != '-') {
is_remaining_args = 1;
continue;
+ } else {
+ fprintf(stderr, "ERROR: Got invalid arg \"%s\"!\n", argv[0]);
+ simple_archiver_print_usage();
+ return 1;
}
} else {
if (out->working_files == NULL) {
@@ -367,7 +427,7 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed) {
}
SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
- const SDArchiverParsed *parsed) {
+ const SDArchiverParsed *parsed, SDArchiverParsedStatus *status_out) {
SDArchiverLinkedList *files_list = simple_archiver_list_init();
__attribute__((cleanup(simple_archiver_hash_map_free)))
SDArchiverHashMap *hash_map = simple_archiver_hash_map_init();
@@ -381,6 +441,9 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
original_cwd = realpath(".", NULL);
if (chdir(parsed->user_cwd)) {
simple_archiver_list_free(&files_list);
+ if (status_out) {
+ *status_out = SDAPS_NO_USER_CWD;
+ }
return NULL;
}
}
@@ -607,5 +670,8 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
}
}
+ if (status_out) {
+ *status_out = SDAPS_SUCCESS;
+ }
return files_list;
}
diff --git a/src/parser.h b/src/parser.h
index 008e800..c3734d0 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -51,6 +51,10 @@ typedef struct SDArchiverParsed {
const char *temp_dir;
/// Dir specified by "-C".
const char *user_cwd;
+ /// Currently only 0 and 1 is supported.
+ uint32_t write_version;
+ /// The minimum size of a chunk in bytes (the last chunk may be less).
+ uint64_t minimum_chunk_size;
} SDArchiverParsed;
typedef struct SDArchiverFileInfo {
@@ -59,6 +63,14 @@ typedef struct SDArchiverFileInfo {
char *link_dest;
} SDArchiverFileInfo;
+typedef enum SDArchiverParsedStatus {
+ SDAPS_SUCCESS,
+ SDAPS_NO_USER_CWD,
+} SDArchiverParsedStatus;
+
+/// Returned c-string does not need to be free'd.
+char *simple_archiver_parsed_status_to_str(SDArchiverParsedStatus status);
+
void simple_archiver_print_usage(void);
SDArchiverParsed simple_archiver_create_parsed(void);
@@ -74,6 +86,6 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed);
/// Each entry in the linked list is an SDArchiverFileInfo object.
SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
- const SDArchiverParsed *parsed);
+ const SDArchiverParsed *parsed, SDArchiverParsedStatus *status_out);
#endif
diff --git a/src/test.c b/src/test.c
index 869816f..420fc30 100644
--- a/src/test.c
+++ b/src/test.c
@@ -23,6 +23,7 @@
#include
// Local includes.
+#include "archiver.h"
#include "helpers.h"
#include "parser_internal.h"
@@ -241,6 +242,27 @@ 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);
+
+ CHECK_FALSE(simple_archiver_validate_file_path("Local/Path"));
+ CHECK_TRUE(simple_archiver_validate_file_path("/Abs/Path"));
+ CHECK_TRUE(simple_archiver_validate_file_path("Local/../../not/really"));
+ CHECK_TRUE(simple_archiver_validate_file_path("./../almost"));
+ CHECK_TRUE(simple_archiver_validate_file_path("strange/.."));
+ }
+
printf("Checks checked: %u\n", checks_checked);
printf("Checks passed: %u\n", checks_passed);
return checks_passed == checks_checked ? 0 : 1;