Compare commits

...

27 commits
1.0 ... main

Author SHA1 Message Date
9d31798da6 Version 1.5, update Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 14s
Build for Releases / ensure-release-exists (push) Successful in 0s
Build for Releases / push-build-x86_64 (push) Successful in 7s
Build for Releases / push-build-aarch64 (push) Successful in 57s
Build for Releases / push-build-x86_64_debian (push) Successful in 31s
Build for Releases / push-build-aarch64_debian (push) Successful in 2m2s
2024-10-21 17:38:53 +09:00
1c06462ca7 Fix where symlink marked invalid still created v1 2024-10-21 17:35:55 +09:00
50bc4a8a07 Version 1.4
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 15s
Build for Releases / ensure-release-exists (push) Successful in 0s
Build for Releases / push-build-x86_64 (push) Successful in 8s
Build for Releases / push-build-aarch64 (push) Successful in 58s
Build for Releases / push-build-x86_64_debian (push) Successful in 33s
Build for Releases / push-build-aarch64_debian (push) Successful in 5m48s
Resolves #19
2024-10-21 17:22:29 +09:00
df37f68bcf Update README.md 2024-10-21 17:22:08 +09:00
edcaee803c Update Changelog.md 2024-10-21 17:21:15 +09:00
50b1f4b274 Impl. safe-link checking for v1 file format
As mentioned in the previous commit, "safe links" is on by default,
meaning that any symlinks pointing to outside of archived files (or
invalid) will not be stored. To store such symlinks, "--no-safe-links"
must be specified.

This commit implements "safe links" for v1 of the file format.
2024-10-21 17:12:48 +09:00
cef3e4184a Impl. "safe links" by default for v0
By default, links that point to outside of archive (or possibly doesn't
point to anything) will be ignored. Use "--no-safe-links" to preserve
such symlinks.

Note this has only been implemented for v0 of the file format in this
commit, and not yet v1.
2024-10-21 17:12:28 +09:00
7ee54bddf2 Version 1.3, update Changelog.md
All checks were successful
Build for Releases / ensure-release-exists (push) Successful in 2s
Run Unit Tests / build-and-run-unit-tests (push) Successful in 6s
Build for Releases / push-build-x86_64 (push) Successful in 6s
Build for Releases / push-build-aarch64 (push) Successful in 57s
Build for Releases / push-build-x86_64_debian (push) Successful in 33s
Build for Releases / push-build-aarch64_debian (push) Successful in 5m40s
2024-10-16 13:56:37 +09:00
9d84b28efe Avoid busy-wait by sleep 1ms on non-blocking io 2024-10-16 13:54:29 +09:00
3dfc258fa4 Version 1.2
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 14s
Build for Releases / ensure-release-exists (push) Successful in 0s
Build for Releases / push-build-x86_64 (push) Successful in 7s
Build for Releases / push-build-aarch64 (push) Successful in 58s
Build for Releases / push-build-x86_64_debian (push) Successful in 34s
Build for Releases / push-build-aarch64_debian (push) Successful in 5m49s
2024-10-11 12:34:51 +09:00
1b7bfde458 Update Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 6s
2024-10-10 10:55:14 +09:00
04f4897dd3 Handle SIGINT (Ctrl+C) 2024-10-10 10:54:28 +09:00
fd5e9ab47d Update Changelog.md, version 1.1
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
Build for Releases / ensure-release-exists (push) Successful in 0s
Build for Releases / push-build-x86_64 (push) Successful in 7s
Build for Releases / push-build-aarch64 (push) Successful in 57s
Build for Releases / push-build-x86_64_debian (push) Successful in 31s
Build for Releases / push-build-aarch64_debian (push) Successful in 5m33s
2024-10-08 16:05:22 +09:00
925ea46877 Update Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-10-08 15:52:03 +09:00
d68c7caf41 Update Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 5s
2024-10-08 15:51:05 +09:00
2a63793bcd Update README.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 13s
2024-10-08 15:49:51 +09:00
adfa6836f5 Fix failing unit tests
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 6s
2024-10-08 15:47:57 +09:00
553cf2a6ec Pre-sort files by default, setting to disable
Some checks failed
Run Unit Tests / build-and-run-unit-tests (push) Failing after 6s
2024-10-08 15:45:25 +09:00
78ba5acd70 Add validation of files/symlinks count from files
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 6s
2024-10-08 15:31:40 +09:00
28c09b0232 Update README.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 6s
2024-10-08 15:20:21 +09:00
d54fc441a3 Impl. pre-sort files by size before put in chunks
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 6s
2024-10-08 15:12:44 +09:00
d2d202235c Update Changelog.md 2024-10-08 14:47:32 +09:00
52c78b2fb4 Handle SIGPIPE in v1 de/compress, minor fixes 2024-10-08 14:46:56 +09:00
9add2a650d Update README.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 1m5s
2024-10-08 14:05:20 +09:00
ccbb1acd95 Cleanup of example test files 2024-10-08 14:04:36 +09:00
c4e5fbf8ce Update README.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 6s
2024-10-08 13:31:26 +09:00
e7e4736aa5 Add Changelog.md
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
2024-10-08 13:26:50 +09:00
16 changed files with 372 additions and 35 deletions

View file

@ -87,4 +87,5 @@ add_executable(test_simplearchiver
src/algorithms/linear_congruential_gen.c src/algorithms/linear_congruential_gen.c
src/data_structures/linked_list.c src/data_structures/linked_list.c
src/data_structures/hash_map.c src/data_structures/hash_map.c
src/data_structures/priority_heap.c
) )

56
Changelog.md Normal file
View file

@ -0,0 +1,56 @@
# Changelog
## Upcoming Changes
## Version 1.5
Previous file-format-v1 implementation of "safe links" still created a symlink
if a relative or absolute link existed in the file. This version fixes this, and
prevents invalid symlinks from being created. (This check is only done if the
bit-flag is set in the file as mentioned in the file-format spec for v1 files.)
## Version 1.4
Do "safe links" behavior by default: symlinks pointing to outside of archived
files (or invalid symlinks) should not be included in the archive, unless if the
option "--no-safe-links" is specified. This is supported in both v0 and v1 file
formats.
## Version 1.3
Prevent `simplearchiver` from busy-waiting during non-blocking IO by sleeping
in "EWOULDBLOCK" conditions. This results in less consumed cpu time by the
process, especially during compression.
## Version 1.2
Proper handling of Ctrl+C (SIGINT). This prevents temporary files from
persisting by doing a proper cleanup before stopping the program.
## Version 1.1
More robust handling of de/compression process (handling SIGPIPE).
By default files are now pre-sorted by size before placed into chunks.
Add option to NOT pre-sort files by size.
## Version 1.0
First release.
Features:
- Can specify any command as de/compressor when archiving.
- The commands must accept file data in stdin and output processed data to
stdout.
- Can specify any command as decompressor when extracting to override the
simple-archive's stored decompressor.
- Archives/compresses into chunks to reduce overhead by compressing per chunk
instead of per file.
- Chunk size can be tweaked by a parameter setting.
- Can archive without de/compressor to be compressed separately.
- Supports pre-version-1 simple archiver file format (version 0).
- Archives regular files and symlinks.
- Keeps track of files and symlink permissions.
- Keeps track of file UID and GID (only set if extracting as root).
- Can be set to ignore absolute paths for symlinks by parameter setting.

View file

@ -1,7 +1,8 @@
# Simple Archiver # Simple Archiver
This program ~~is not yet~~ almost finished! Basic functionality is implemented This program ~~is not yet~~ ~~almost~~ basically finished! ~~Basic~~ Necessary
and only some advanced features are missing. You can track progress functionality is implemented and only ~~some advanced features are missing~~
some extra features are not yet implemented. You can track progress
[here](https://git.seodisparate.com/stephenseo/SimpleArchiver/projects/3). [here](https://git.seodisparate.com/stephenseo/SimpleArchiver/projects/3).
This program exists because I could not get `tar` or `ar` to compile with This program exists because I could not get `tar` or `ar` to compile with
@ -26,9 +27,11 @@ API calls.
--overwrite-create : allows overwriting an archive file --overwrite-create : allows overwriting an archive file
--overwrite-extract : allows overwriting when extracting --overwrite-extract : allows overwriting when extracting
--no-abs-symlink : do not store absolute paths for symlinks --no-abs-symlink : do not store absolute paths for symlinks
--no-safe-links : keep symlinks that link to outside archive contents
--temp-files-dir <dir> : where to store temporary files created when compressing (defaults to current working directory) --temp-files-dir <dir> : where to store temporary files created when compressing (defaults to current working directory)
--write-version <version> : Force write version file format (default 1) --write-version <version> : Force write version file format (default 1)
--chunk-min-size <bytes> : v1 file format minimum chunk size (default 4194304 or 4MiB) --chunk-min-size <bytes> : v1 file format minimum chunk size (default 4194304 or 4MiB)
--no-pre-sort-files : do NOT pre-sort files by size (by default enabled so that the first file is the largest)
-- : specifies remaining arguments are files to archive/extract -- : specifies remaining arguments are files to archive/extract
If creating archive file, remaining args specify files to archive. If creating archive file, remaining args specify files to archive.
If extracting archive file, remaining args specify files to extract. If extracting archive file, remaining args specify files to extract.
@ -36,6 +39,10 @@ API calls.
Note that `--compressor` and `--decompressor` cmds must accept data from stdin Note that `--compressor` and `--decompressor` cmds must accept data from stdin
and return processed data to stdout. and return processed data to stdout.
## Changes
See the [Changelog](https://git.seodisparate.com/stephenseo/SimpleArchiver/src/branch/main/Changelog.md).
## LICENSE Information ## LICENSE Information
Uses the [ISC License](https://choosealicense.com/licenses/isc/). Uses the [ISC License](https://choosealicense.com/licenses/isc/).

View file

@ -57,6 +57,11 @@ Following the file-count bytes, the following bytes are added for each file:
2. The second bit is "other execute permission". 2. The second bit is "other execute permission".
3. The third bit is UNSET if relative links are preferred, and is SET 3. The third bit is UNSET if relative links are preferred, and is SET
if absolute links are preferred. if absolute links are preferred.
4. The fourth bit is set if this file/symlink-entry is invalid and must
be skipped. Ignore following bytes after these 4 bytes bit-flags in
this specification and skip to the next entry; if marked invalid,
the following specification bytes for this file/symlink entry must
not exist.
3. The third byte. 3. The third byte.
1. Currently unused. 1. Currently unused.
4. The fourth byte. 4. The fourth byte.
@ -128,6 +133,9 @@ Following the link-count bytes, the following bytes are added for each symlink:
2. The second byte. 2. The second byte.
1. The first bit is "other write permission". 1. The first bit is "other write permission".
2. The second bit is "other execute permission". 2. The second bit is "other execute permission".
3. If this bit is set, then this entry is marked invalid. The link name
will be preserved in this entry, but the following link target paths
will be set to zero-length and will not be stored.
2. 2 bytes 16-bit unsigned integer "link name" in big-endian. This does not 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. 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 3. X bytes of link-name (length defined by previous value). Is a NULL-terminated

View file

@ -38,6 +38,7 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#include "data_structures/priority_heap.h"
#include "helpers.h" #include "helpers.h"
#define TEMP_FILENAME_CMP "%s%ssimple_archiver_compressed_%lu.tmp" #define TEMP_FILENAME_CMP "%s%ssimple_archiver_compressed_%lu.tmp"
@ -50,12 +51,21 @@
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
volatile int is_sig_pipe_occurred = 0; volatile int is_sig_pipe_occurred = 0;
volatile int is_sig_int_occurred = 0;
void handle_sig_pipe(int sig) { void handle_sig_pipe(int sig) {
if (sig == SIGPIPE) { if (sig == SIGPIPE) {
is_sig_pipe_occurred = 1; is_sig_pipe_occurred = 1;
} }
} }
void handle_sig_int(int sig) {
if (sig == SIGINT) {
is_sig_int_occurred = 1;
}
}
const struct timespec nonblock_sleep = {.tv_sec = 0, .tv_nsec = 1000000};
#endif #endif
typedef struct SDArchiverInternalToWrite { typedef struct SDArchiverInternalToWrite {
@ -104,7 +114,25 @@ void cleanup_temp_filename_delete(void ***ptrs_array) {
#endif #endif
} }
void cleanup_overwrite_filename_delete_simple(char **filename) {
if (filename && *filename) {
if ((*filename)[0] != 0) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
unlink(*filename);
#endif
}
free(*filename);
*filename = NULL;
}
}
int write_files_fn(void *data, void *ud) { int write_files_fn(void *data, void *ud) {
if (is_sig_int_occurred) {
return 1;
}
const SDArchiverFileInfo *file_info = data; const SDArchiverFileInfo *file_info = data;
SDArchiverState *state = ud; SDArchiverState *state = ud;
@ -257,6 +285,17 @@ int write_files_fn(void *data, void *ud) {
size_t read_count; size_t read_count;
ssize_t ret; ssize_t ret;
while (!write_done || !read_done) { while (!write_done || !read_done) {
if (is_sig_int_occurred) {
if (pipe_into_cmd[1] >= 0) {
close(pipe_into_cmd[1]);
pipe_into_cmd[1] = -1;
}
if (pipe_outof_cmd[0] >= 0) {
close(pipe_outof_cmd[0]);
pipe_outof_cmd[0] = -1;
}
return 1;
}
if (is_sig_pipe_occurred) { if (is_sig_pipe_occurred) {
fprintf(stderr, fprintf(stderr,
"WARNING: Failed to write to compressor (SIGPIPE)! Invalid " "WARNING: Failed to write to compressor (SIGPIPE)! Invalid "
@ -274,6 +313,7 @@ int write_files_fn(void *data, void *ud) {
ret = write(pipe_into_cmd[1], write_buf, write_count); ret = write(pipe_into_cmd[1], write_buf, write_count);
if (ret == -1) { if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
nanosleep(&nonblock_sleep, NULL);
write_again = 1; write_again = 1;
} else { } else {
// Error during write. // Error during write.
@ -297,6 +337,7 @@ int write_files_fn(void *data, void *ud) {
simple_archiver_helper_cleanup_FILE(&file_fd); simple_archiver_helper_cleanup_FILE(&file_fd);
write_done = 1; write_done = 1;
close(pipe_into_cmd[1]); close(pipe_into_cmd[1]);
pipe_into_cmd[1] = -1;
// fprintf(stderr, "write_done\n"); // fprintf(stderr, "write_done\n");
} else if (ferror(file_fd)) { } else if (ferror(file_fd)) {
// Error during read file. // Error during read file.
@ -326,10 +367,11 @@ int write_files_fn(void *data, void *ud) {
read_done = 1; read_done = 1;
simple_archiver_helper_cleanup_FILE(&tmp_fd); simple_archiver_helper_cleanup_FILE(&tmp_fd);
close(pipe_outof_cmd[0]); close(pipe_outof_cmd[0]);
pipe_outof_cmd[0] = -1;
// fprintf(stderr, "read_done\n"); // fprintf(stderr, "read_done\n");
} else if (ret == -1) { } else if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
// Nop. nanosleep(&nonblock_sleep, NULL);
} else { } else {
// Read error. // Read error.
fprintf(stderr, fprintf(stderr,
@ -348,6 +390,8 @@ int write_files_fn(void *data, void *ud) {
"WARNING: Failed to write to compressor (SIGPIPE)! Invalid " "WARNING: Failed to write to compressor (SIGPIPE)! Invalid "
"compressor cmd?\n"); "compressor cmd?\n");
return 1; return 1;
} else if (is_sig_int_occurred) {
return 1;
} }
waitpid(compressor_pid, NULL, 0); waitpid(compressor_pid, NULL, 0);
@ -605,6 +649,10 @@ int write_files_fn(void *data, void *ud) {
simple_archiver_list_free(&to_write); simple_archiver_list_free(&to_write);
if (is_sig_int_occurred) {
return 1;
}
// Write file. // Write file.
fprintf(stderr, "Writing file: %s\n", file_info->filename); fprintf(stderr, "Writing file: %s\n", file_info->filename);
char buf[SIMPLE_ARCHIVER_BUFFER_SIZE]; char buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
@ -739,16 +787,39 @@ int write_files_fn(void *data, void *ud) {
if (abs_path && (state->parsed->flags & 0x20) == 0 && if (abs_path && (state->parsed->flags & 0x20) == 0 &&
!simple_archiver_hash_map_get(state->map, abs_path, !simple_archiver_hash_map_get(state->map, abs_path,
strlen(abs_path) + 1)) { strlen(abs_path) + 1)) {
// Is not a filename being archived, set preference to absolute path. // Is not a filename being archived.
fprintf(stderr, if ((state->parsed->flags & 0x80) != 0) {
"NOTICE: abs_path exists, \"--no-abs-symlink\" not specified, " // No safe links, set preference to absolute path.
"and link refers to file NOT in archive; preferring abs_path.\n"); fprintf(
((uint8_t *)temp_to_write->buf)[1] |= 0x4; stderr,
"NOTICE: abs_path exists, \"--no-abs-symlink\" not specified, "
"and link refers to file NOT in archive; preferring abs_path.\n");
((uint8_t *)temp_to_write->buf)[1] |= 0x4;
} else {
// Safe links, do not store symlink!
fprintf(stderr,
"WARNING: Symlink \"%s\" points to outside archive contents, "
"will not be stored! (Use \"--no-safe-links\" to disable this "
"behavior)\n",
file_info->filename);
((uint8_t *)temp_to_write->buf)[1] |= 0x8;
}
} }
// Store the 4 byte bit-flags for file. // Store the 4 byte bit-flags for file.
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write); simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
if ((((uint8_t *)temp_to_write->buf)[1] & 0x8) != 0) {
// Skipped symlink.
simple_archiver_list_get(to_write, write_list_datas_fn, state->out_f);
simple_archiver_list_free(&to_write);
char format_str[64];
snprintf(format_str, 64, FILE_COUNTS_OUTPUT_FORMAT_STR_1, state->digits,
state->digits);
fprintf(stderr, format_str, ++(state->count), state->max);
return 0;
}
// Store the absolute and relative paths. // Store the absolute and relative paths.
if (!abs_path) { if (!abs_path) {
fprintf(stderr, fprintf(stderr,
@ -836,6 +907,10 @@ int write_files_fn(void *data, void *ud) {
snprintf(format_str, 64, FILE_COUNTS_OUTPUT_FORMAT_STR_1, state->digits, snprintf(format_str, 64, FILE_COUNTS_OUTPUT_FORMAT_STR_1, state->digits,
state->digits); state->digits);
fprintf(stderr, format_str, ++(state->count), state->max); fprintf(stderr, format_str, ++(state->count), state->max);
if (is_sig_int_occurred) {
return 1;
}
return 0; return 0;
} }
@ -1094,12 +1169,18 @@ int read_decomp_to_out_file(const char *out_filename, int in_pipe,
ssize_t read_ret; ssize_t read_ret;
size_t fwrite_ret; size_t fwrite_ret;
while (written_amt < file_size) { while (written_amt < file_size) {
if (is_sig_pipe_occurred) {
fprintf(stderr, "ERROR: SIGPIPE while decompressing!\n");
return SDAS_INTERNAL_ERROR;
}
int ret = try_write_to_decomp(to_dec_pipe, chunk_remaining, in_f, read_buf, int ret = try_write_to_decomp(to_dec_pipe, chunk_remaining, in_f, read_buf,
read_buf_size, hold_buf, has_hold); read_buf_size, hold_buf, has_hold);
if (ret != SDAS_SUCCESS) { if (ret != SDAS_SUCCESS) {
return ret; return ret;
} } else if (is_sig_pipe_occurred) {
if (file_size - written_amt >= read_buf_size) { fprintf(stderr, "ERROR: SIGPIPE while decompressing!\n");
return SDAS_INTERNAL_ERROR;
} else if (file_size - written_amt >= read_buf_size) {
read_ret = read(in_pipe, read_buf, read_buf_size); read_ret = read(in_pipe, read_buf, read_buf_size);
if (read_ret > 0) { if (read_ret > 0) {
if (out_fd) { if (out_fd) {
@ -1130,6 +1211,7 @@ int read_decomp_to_out_file(const char *out_filename, int in_pipe,
} else { } else {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
// Non-blocking read from pipe. // Non-blocking read from pipe.
nanosleep(&nonblock_sleep, NULL);
continue; continue;
} else { } else {
// Error. // Error.
@ -1169,6 +1251,7 @@ int read_decomp_to_out_file(const char *out_filename, int in_pipe,
} else { } else {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
// Non-blocking read from pipe. // Non-blocking read from pipe.
nanosleep(&nonblock_sleep, NULL);
continue; continue;
} else { } else {
// Error. // Error.
@ -1469,6 +1552,7 @@ int symlinks_and_files_from_files(void *data, void *ud) {
SDArchiverLinkedList *symlinks_list = ptr_array[0]; SDArchiverLinkedList *symlinks_list = ptr_array[0];
SDArchiverLinkedList *files_list = ptr_array[1]; SDArchiverLinkedList *files_list = ptr_array[1];
const char *user_cwd = ptr_array[2]; const char *user_cwd = ptr_array[2];
SDArchiverPHeap *pheap = ptr_array[3];
if (file_info->filename) { if (file_info->filename) {
if (file_info->link_dest) { if (file_info->link_dest) {
@ -1555,8 +1639,14 @@ int symlinks_and_files_from_files(void *data, void *ud) {
return 1; return 1;
} }
file_info_struct->file_size = (uint64_t)ftell_ret; file_info_struct->file_size = (uint64_t)ftell_ret;
simple_archiver_list_add(files_list, file_info_struct, if (pheap) {
free_internal_file_info); simple_archiver_priority_heap_insert(
pheap, (int64_t)file_info_struct->file_size, file_info_struct,
free_internal_file_info);
} else {
simple_archiver_list_add(files_list, file_info_struct,
free_internal_file_info);
}
} }
} }
@ -1588,6 +1678,8 @@ int files_to_chunk_count(void *data, void *ud) {
return 0; return 0;
} }
int greater_fn(int64_t a, int64_t b) { return a > b; }
char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) { char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) {
switch (error) { switch (error) {
case SDAS_SUCCESS: case SDAS_SUCCESS:
@ -1614,6 +1706,8 @@ char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) {
return "Failed to change current working directory"; return "Failed to change current working directory";
case SDAS_INVALID_WRITE_VERSION: case SDAS_INVALID_WRITE_VERSION:
return "Unsupported write version file format"; return "Unsupported write version file format";
case SDAS_SIGINT:
return "Interrupt signal SIGINT recieved";
default: default:
return "Unknown error"; return "Unknown error";
} }
@ -1645,6 +1739,11 @@ void simple_archiver_free_state(SDArchiverState **state) {
int simple_archiver_write_all(FILE *out_f, SDArchiverState *state, int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
const SDArchiverLinkedList *filenames) { const SDArchiverLinkedList *filenames) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
signal(SIGINT, handle_sig_int);
#endif
switch (state->parsed->write_version) { switch (state->parsed->write_version) {
case 0: case 0:
return simple_archiver_write_v0(out_f, state, filenames); return simple_archiver_write_v0(out_f, state, filenames);
@ -1764,6 +1863,10 @@ int simple_archiver_write_v0(FILE *out_f, SDArchiverState *state,
} }
} }
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
// Iterate over files in list to write. // Iterate over files in list to write.
state->count = 0; state->count = 0;
state->max = filenames->count; state->max = filenames->count;
@ -1788,6 +1891,9 @@ int simple_archiver_write_v0(FILE *out_f, SDArchiverState *state,
state->digits); state->digits);
fprintf(stderr, format_str, state->count, state->max); fprintf(stderr, format_str, state->count, state->max);
if (simple_archiver_list_get(filenames, write_files_fn, state)) { if (simple_archiver_list_get(filenames, write_files_fn, state)) {
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
// Error occurred. // Error occurred.
fprintf(stderr, "Error ocurred writing file(s) to archive.\n"); fprintf(stderr, "Error ocurred writing file(s) to archive.\n");
return SDAS_FAILED_TO_WRITE; return SDAS_FAILED_TO_WRITE;
@ -1817,11 +1923,17 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
SDArchiverLinkedList *symlinks_list = simple_archiver_list_init(); SDArchiverLinkedList *symlinks_list = simple_archiver_list_init();
__attribute__((cleanup(simple_archiver_list_free))) __attribute__((cleanup(simple_archiver_list_free)))
SDArchiverLinkedList *files_list = simple_archiver_list_init(); SDArchiverLinkedList *files_list = simple_archiver_list_init();
__attribute__((cleanup(simple_archiver_priority_heap_free)))
SDArchiverPHeap *files_pheap =
(state->parsed->flags & 0x40)
? simple_archiver_priority_heap_init_less_fn(greater_fn)
: NULL;
ptr_array = malloc(sizeof(void *) * 3); ptr_array = malloc(sizeof(void *) * 4);
ptr_array[0] = symlinks_list; ptr_array[0] = symlinks_list;
ptr_array[1] = files_list; ptr_array[1] = files_list;
ptr_array[2] = (void *)state->parsed->user_cwd; ptr_array[2] = (void *)state->parsed->user_cwd;
ptr_array[3] = files_pheap;
if (simple_archiver_list_get(filenames, symlinks_and_files_from_files, if (simple_archiver_list_get(filenames, symlinks_and_files_from_files,
ptr_array)) { ptr_array)) {
@ -1830,6 +1942,22 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
} }
free(ptr_array); free(ptr_array);
if (files_pheap) {
while (files_pheap->size > 0) {
simple_archiver_list_add(files_list,
simple_archiver_priority_heap_pop(files_pheap),
free_internal_file_info);
}
simple_archiver_priority_heap_free(&files_pheap);
}
if (symlinks_list->count + files_list->count != filenames->count) {
fprintf(stderr,
"ERROR: Count mismatch between files and symlinks and files from "
"parser!\n");
return SDAS_INTERNAL_ERROR;
}
if (fwrite("SIMPLE_ARCHIVE_VER", 1, 18, out_f) != 18) { if (fwrite("SIMPLE_ARCHIVE_VER", 1, 18, out_f) != 18) {
return SDAS_FAILED_TO_WRITE; return SDAS_FAILED_TO_WRITE;
} }
@ -1951,11 +2079,25 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
abs_path); abs_path);
} }
} }
uint_fast8_t is_invalid = 0;
if (abs_path && (state->parsed->flags & 0x20) == 0 && if (abs_path && (state->parsed->flags & 0x20) == 0 &&
!simple_archiver_hash_map_get(abs_filenames, abs_path, !simple_archiver_hash_map_get(abs_filenames, abs_path,
strlen(abs_path) + 1)) { strlen(abs_path) + 1)) {
// Is not a filename being archived, set preference to absolute path. // Is not a filename being archived.
buf[0] |= 1; if ((state->parsed->flags & 0x80) == 0) {
// Not a "safe link", mark invalid and continue.
is_invalid = 1;
fprintf(stderr,
"WARNING: \"%s\" points to outside of archived files (or is "
"invalid) and \"--no-safe-links\" not specified, will not "
"store abs/rel-links to this entry!\n",
(const char *)node->data);
} else {
// Safe links disabled, set preference to absolute path.
buf[0] |= 1;
}
} }
// Get symlink stats for permissions. // Get symlink stats for permissions.
@ -1998,6 +2140,11 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
buf[0] = 0xFE; buf[0] = 0xFE;
buf[1] = 3; buf[1] = 3;
#endif #endif
if (is_invalid) {
buf[1] |= 4;
}
if (fwrite(buf, 1, 2, out_f) != 2) { if (fwrite(buf, 1, 2, out_f) != 2) {
return SDAS_FAILED_TO_WRITE; return SDAS_FAILED_TO_WRITE;
} }
@ -2018,7 +2165,7 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
return SDAS_FAILED_TO_WRITE; return SDAS_FAILED_TO_WRITE;
} }
if (abs_path && (state->parsed->flags & 0x20) == 0) { if (abs_path && (state->parsed->flags & 0x20) == 0 && !is_invalid) {
len = strlen(abs_path); len = strlen(abs_path);
if (len >= 0xFFFF) { if (len >= 0xFFFF) {
fprintf(stderr, fprintf(stderr,
@ -2042,7 +2189,7 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
} }
} }
if (rel_path) { if (rel_path && !is_invalid) {
len = strlen(rel_path); len = strlen(rel_path);
if (len >= 0xFFFF) { if (len >= 0xFFFF) {
fprintf(stderr, fprintf(stderr,
@ -2073,6 +2220,10 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
} }
} }
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
__attribute__((cleanup(simple_archiver_list_free))) __attribute__((cleanup(simple_archiver_list_free)))
SDArchiverLinkedList *chunk_counts = simple_archiver_list_init(); SDArchiverLinkedList *chunk_counts = simple_archiver_list_init();
@ -2140,6 +2291,9 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
uint64_t chunk_count = 0; uint64_t chunk_count = 0;
for (SDArchiverLLNode *chunk_c_node = chunk_counts->head->next; for (SDArchiverLLNode *chunk_c_node = chunk_counts->head->next;
chunk_c_node != chunk_counts->tail; chunk_c_node = chunk_c_node->next) { chunk_c_node != chunk_counts->tail; chunk_c_node = chunk_c_node->next) {
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
fprintf(stderr, "CHUNK %3lu of %3lu\n", ++chunk_count, chunk_counts->count); fprintf(stderr, "CHUNK %3lu of %3lu\n", ++chunk_count, chunk_counts->count);
// Write file count before iterating through files. // Write file count before iterating through files.
if (non_c_chunk_size) { if (non_c_chunk_size) {
@ -2307,6 +2461,9 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
int_fast8_t to_temp_finished = 0; int_fast8_t to_temp_finished = 0;
for (uint64_t file_idx = 0; file_idx < *((uint64_t *)chunk_c_node->data); for (uint64_t file_idx = 0; file_idx < *((uint64_t *)chunk_c_node->data);
++file_idx) { ++file_idx) {
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
file_node = file_node->next; file_node = file_node->next;
if (file_node == files_list->tail) { if (file_node == files_list->tail) {
return SDAS_INTERNAL_ERROR; return SDAS_INTERNAL_ERROR;
@ -2321,6 +2478,10 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
char hold_buf[SIMPLE_ARCHIVER_BUFFER_SIZE]; char hold_buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
ssize_t has_hold = -1; ssize_t has_hold = -1;
while (!to_comp_finished) { while (!to_comp_finished) {
if (is_sig_pipe_occurred) {
fprintf(stderr, "ERROR: SIGPIPE while compressing!\n");
return SDAS_INTERNAL_ERROR;
}
if (!to_comp_finished) { if (!to_comp_finished) {
// Write to compressor. // Write to compressor.
if (ferror(fd)) { if (ferror(fd)) {
@ -2336,6 +2497,7 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
// Non-blocking write. // Non-blocking write.
has_hold = (int)fread_ret; has_hold = (int)fread_ret;
memcpy(hold_buf, buf, fread_ret); memcpy(hold_buf, buf, fread_ret);
nanosleep(&nonblock_sleep, NULL);
} else { } else {
fprintf( fprintf(
stderr, stderr,
@ -2362,6 +2524,7 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
if (write_ret < 0) { if (write_ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
// Non-blocking write. // Non-blocking write.
nanosleep(&nonblock_sleep, NULL);
} else { } else {
return SDAS_INTERNAL_ERROR; return SDAS_INTERNAL_ERROR;
} }
@ -2384,6 +2547,7 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
if (read_ret < 0) { if (read_ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
// Non-blocking read. // Non-blocking read.
nanosleep(&nonblock_sleep, NULL);
} else { } else {
fprintf(stderr, fprintf(stderr,
"ERROR: Reading from compressor, pipe read error!\n"); "ERROR: Reading from compressor, pipe read error!\n");
@ -2409,11 +2573,16 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
// Finish writing. // Finish writing.
if (!to_temp_finished) { if (!to_temp_finished) {
while (1) { while (1) {
if (is_sig_pipe_occurred) {
fprintf(stderr, "ERROR: SIGPIPE while compressing!\n");
return SDAS_INTERNAL_ERROR;
}
ssize_t read_ret = ssize_t read_ret =
read(pipe_outof_read, buf, SIMPLE_ARCHIVER_BUFFER_SIZE); read(pipe_outof_read, buf, SIMPLE_ARCHIVER_BUFFER_SIZE);
if (read_ret < 0) { if (read_ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
// Non-blocking read. // Non-blocking read.
nanosleep(&nonblock_sleep, NULL);
} else { } else {
fprintf(stderr, fprintf(stderr,
"ERROR: Reading from compressor, pipe read error!\n"); "ERROR: Reading from compressor, pipe read error!\n");
@ -2456,7 +2625,9 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
// Write compressed chunk. // Write compressed chunk.
while (!feof(temp_fd)) { while (!feof(temp_fd)) {
if (ferror(temp_fd)) { if (is_sig_int_occurred) {
return SDAS_SIGINT;
} else if (ferror(temp_fd)) {
return SDAS_INTERNAL_ERROR; return SDAS_INTERNAL_ERROR;
} }
size_t fread_ret = fread(buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, temp_fd); size_t fread_ret = fread(buf, 1, SIMPLE_ARCHIVER_BUFFER_SIZE, temp_fd);
@ -2489,6 +2660,9 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
fwrite(non_c_chunk_size, 8, 1, out_f); fwrite(non_c_chunk_size, 8, 1, out_f);
for (uint64_t file_idx = 0; file_idx < *((uint64_t *)chunk_c_node->data); for (uint64_t file_idx = 0; file_idx < *((uint64_t *)chunk_c_node->data);
++file_idx) { ++file_idx) {
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
file_node = file_node->next; file_node = file_node->next;
if (file_node == files_list->tail) { if (file_node == files_list->tail) {
return SDAS_INTERNAL_ERROR; return SDAS_INTERNAL_ERROR;
@ -2521,6 +2695,12 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract, int simple_archiver_parse_archive_info(FILE *in_f, int_fast8_t do_extract,
const SDArchiverState *state) { const SDArchiverState *state) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
signal(SIGINT, handle_sig_int);
#endif
uint8_t buf[32]; uint8_t buf[32];
memset(buf, 0, 32); memset(buf, 0, 32);
uint16_t u16; uint16_t u16;
@ -2637,6 +2817,10 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
simple_archiver_helper_32_bit_be(&u32); simple_archiver_helper_32_bit_be(&u32);
fprintf(stderr, "File count is %u\n", u32); fprintf(stderr, "File count is %u\n", u32);
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
const uint32_t size = u32; const uint32_t size = u32;
const size_t digits = simple_archiver_helper_num_digits(size); const size_t digits = simple_archiver_helper_num_digits(size);
char format_str[128]; char format_str[128];
@ -2659,6 +2843,9 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
} }
} }
for (uint32_t idx = 0; idx < size; ++idx) { for (uint32_t idx = 0; idx < size; ++idx) {
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
skip = 0; skip = 0;
fprintf(stderr, format_str, idx + 1, size); fprintf(stderr, format_str, idx + 1, size);
if (feof(in_f) || ferror(in_f)) { if (feof(in_f) || ferror(in_f)) {
@ -2671,6 +2858,9 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
simple_archiver_helper_cleanup_malloced))) void *out_f_name = NULL; simple_archiver_helper_cleanup_malloced))) void *out_f_name = NULL;
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE))) FILE *out_f = __attribute__((cleanup(simple_archiver_helper_cleanup_FILE))) FILE *out_f =
NULL; NULL;
__attribute__((cleanup(
cleanup_overwrite_filename_delete_simple))) char *to_overwrite_dest =
NULL;
if (u16 < SIMPLE_ARCHIVER_BUFFER_SIZE) { if (u16 < SIMPLE_ARCHIVER_BUFFER_SIZE) {
if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) { if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
return SDAS_INVALID_FILE; return SDAS_INVALID_FILE;
@ -2700,12 +2890,14 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
if (fd == -1) { if (fd == -1) {
if (errno == ELOOP) { if (errno == ELOOP) {
// Is an existing symbolic file. // Is an existing symbolic file.
unlink((const char *)buf); // Defer deletion to after "is invalid" check.
to_overwrite_dest = strdup((const char *)buf);
} }
} else { } else {
close(fd); close(fd);
// Is an existing file. // Is an existing file.
unlink((const char *)buf); // Defer deletion to after "is invalid" check.
to_overwrite_dest = strdup((const char *)buf);
} }
} }
if (!skip) { if (!skip) {
@ -2747,12 +2939,14 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
if (fd == -1) { if (fd == -1) {
if (errno == ELOOP) { if (errno == ELOOP) {
// Is an existing symbolic file. // Is an existing symbolic file.
unlink((const char *)uc_heap_buf); // Defer deletion to after "is invalid" check.
to_overwrite_dest = strdup((const char *)uc_heap_buf);
} }
} else { } else {
close(fd); close(fd);
// Is an existing file. // Is an existing file.
unlink((const char *)uc_heap_buf); // Defer deletion to after "is invalid" check.
to_overwrite_dest = strdup((const char *)uc_heap_buf);
} }
} }
if (!skip) { if (!skip) {
@ -2767,6 +2961,17 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
return SDAS_INVALID_FILE; return SDAS_INVALID_FILE;
} }
// Check for "invalid entry" flag.
if ((buf[1] & 0x8) != 0) {
free(to_overwrite_dest);
to_overwrite_dest = NULL;
fprintf(stderr, " This file entry was marked invalid, skipping...\n");
continue;
} else {
// Do deferred overwrite action: remove existing file/symlink.
cleanup_overwrite_filename_delete_simple(&to_overwrite_dest);
}
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \ #if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
@ -2988,11 +3193,21 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
char recv_buf[SIMPLE_ARCHIVER_BUFFER_SIZE]; char recv_buf[SIMPLE_ARCHIVER_BUFFER_SIZE];
size_t amount_to_read; size_t amount_to_read;
while (!write_pipe_done || !read_pipe_done) { while (!write_pipe_done || !read_pipe_done) {
if (is_sig_pipe_occurred) { if (is_sig_int_occurred) {
if (pipe_into_cmd[1] >= 0) {
close(pipe_into_cmd[1]);
pipe_into_cmd[1] = -1;
}
if (pipe_outof_cmd[0] >= 0) {
close(pipe_outof_cmd[0]);
pipe_outof_cmd[0] = -1;
}
return SDAS_SIGINT;
} else if (is_sig_pipe_occurred) {
fprintf(stderr, fprintf(stderr,
"WARNING: Failed to write to decompressor (SIGPIPE)! " "WARNING: Failed to write to decompressor (SIGPIPE)! "
"Invalid decompressor cmd?\n"); "Invalid decompressor cmd?\n");
return 1; return SDAS_INTERNAL_ERROR;
} }
// Read from file. // Read from file.
@ -3017,10 +3232,12 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
write_again = 0; write_again = 0;
if (compressed_file_size == 0) { if (compressed_file_size == 0) {
close(pipe_into_cmd[1]); close(pipe_into_cmd[1]);
pipe_into_cmd[1] = -1;
write_pipe_done = 1; write_pipe_done = 1;
} }
} else if (write_ret == -1) { } else if (write_ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
nanosleep(&nonblock_sleep, NULL);
write_again = 1; write_again = 1;
} else { } else {
// Error. // Error.
@ -3064,6 +3281,7 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
} else if (read_ret == -1) { } else if (read_ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
// No bytes to read yet. // No bytes to read yet.
nanosleep(&nonblock_sleep, NULL);
} else { } else {
// Error. // Error.
fprintf(stderr, fprintf(stderr,
@ -3075,6 +3293,7 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
// EOF. // EOF.
read_pipe_done = 1; read_pipe_done = 1;
close(pipe_outof_cmd[0]); close(pipe_outof_cmd[0]);
pipe_outof_cmd[0] = -1;
simple_archiver_helper_cleanup_FILE(&out_f); simple_archiver_helper_cleanup_FILE(&out_f);
} else { } else {
// Invalid state (unreachable?), error. // Invalid state (unreachable?), error.
@ -3374,6 +3593,10 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
} }
} }
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
return SDAS_SUCCESS; return SDAS_SUCCESS;
} }
@ -3458,6 +3681,10 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
} }
} }
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
// Link count. // Link count.
if (fread(buf, 1, 4, in_f) != 4) { if (fread(buf, 1, 4, in_f) != 4) {
return SDAS_INVALID_FILE; return SDAS_INVALID_FILE;
@ -3466,11 +3693,21 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
simple_archiver_helper_32_bit_be(&u32); simple_archiver_helper_32_bit_be(&u32);
for (uint32_t idx = 0; idx < u32; ++idx) { for (uint32_t idx = 0; idx < u32; ++idx) {
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
fprintf(stderr, "SYMLINK %3u of %3u\n", idx + 1, u32); fprintf(stderr, "SYMLINK %3u of %3u\n", idx + 1, u32);
if (fread(buf, 1, 2, in_f) != 2) { if (fread(buf, 1, 2, in_f) != 2) {
return SDAS_INVALID_FILE; return SDAS_INVALID_FILE;
} }
const uint_fast8_t absolute_preferred = (buf[0] & 1) ? 1 : 0; const uint_fast8_t absolute_preferred = (buf[0] & 1) ? 1 : 0;
const uint_fast8_t is_invalid = (buf[1] & 4) ? 1 : 0;
if (is_invalid) {
fprintf(stderr,
" WARNING: This symlink entry was marked invalid (not a safe "
"link)!\n");
}
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \ #if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
@ -3480,7 +3717,7 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
uint_fast8_t link_extracted = 0; uint_fast8_t link_extracted = 0;
uint_fast8_t skip_due_to_map = 0; uint_fast8_t skip_due_to_map = 0;
uint_fast8_t skip_due_to_invalid = 0; uint_fast8_t skip_due_to_invalid = is_invalid ? 1 : 0;
if (fread(buf, 1, 2, in_f) != 2) { if (fread(buf, 1, 2, in_f) != 2) {
return SDAS_INVALID_FILE; return SDAS_INVALID_FILE;
@ -3685,6 +3922,9 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
const uint32_t chunk_count = u32; const uint32_t chunk_count = u32;
for (uint32_t chunk_idx = 0; chunk_idx < chunk_count; ++chunk_idx) { for (uint32_t chunk_idx = 0; chunk_idx < chunk_count; ++chunk_idx) {
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
fprintf(stderr, "CHUNK %3u of %3u\n", chunk_idx + 1, chunk_count); fprintf(stderr, "CHUNK %3u of %3u\n", chunk_idx + 1, chunk_count);
if (fread(buf, 1, 4, in_f) != 4) { if (fread(buf, 1, 4, in_f) != 4) {
@ -3890,6 +4130,9 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
ssize_t has_hold = -1; ssize_t has_hold = -1;
while (node->next != file_info_list->tail) { while (node->next != file_info_list->tail) {
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
node = node->next; node = node->next;
const SDArchiverInternalFileInfo *file_info = node->data; const SDArchiverInternalFileInfo *file_info = node->data;
fprintf(stderr, " FILE %3u of %3u: %s\n", ++file_idx, file_count, fprintf(stderr, " FILE %3u of %3u: %s\n", ++file_idx, file_count,
@ -3921,11 +4164,14 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
fprintf(stderr, fprintf(stderr,
" WARNING: File already exists and " " WARNING: File already exists and "
"\"--overwrite-extract\" is not specified, skipping!\n"); "\"--overwrite-extract\" is not specified, skipping!\n");
read_decomp_to_out_file(NULL, pipe_outof_read, (char *)buf, int ret = read_decomp_to_out_file(
SIMPLE_ARCHIVER_BUFFER_SIZE, NULL, pipe_outof_read, (char *)buf,
file_info->file_size, &pipe_into_write, SIMPLE_ARCHIVER_BUFFER_SIZE, file_info->file_size,
&chunk_remaining, in_f, hold_buf, &pipe_into_write, &chunk_remaining, in_f, hold_buf,
&has_hold); &has_hold);
if (ret != SDAS_SUCCESS) {
return ret;
}
continue; continue;
} }
} }
@ -3989,10 +4235,13 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
} }
} else { } else {
#else #else
// } (This comment exists so that vim can correctly match curly-braces. // } (This comment exists so that vim can correctly match curly-braces).
if (!is_compressed) { if (!is_compressed) {
#endif #endif
while (node->next != file_info_list->tail) { while (node->next != file_info_list->tail) {
if (is_sig_int_occurred) {
return SDAS_SIGINT;
}
node = node->next; node = node->next;
const SDArchiverInternalFileInfo *file_info = node->data; const SDArchiverInternalFileInfo *file_info = node->data;
fprintf(stderr, " FILE %3u of %3u: %s\n", ++file_idx, file_count, fprintf(stderr, " FILE %3u of %3u: %s\n", ++file_idx, file_count,

View file

@ -52,7 +52,8 @@ typedef enum SDArchiverStateReturns {
SDAS_FAILED_TO_CREATE_MAP, SDAS_FAILED_TO_CREATE_MAP,
SDAS_FAILED_TO_EXTRACT_SYMLINK, SDAS_FAILED_TO_EXTRACT_SYMLINK,
SDAS_FAILED_TO_CHANGE_CWD, SDAS_FAILED_TO_CHANGE_CWD,
SDAS_INVALID_WRITE_VERSION SDAS_INVALID_WRITE_VERSION,
SDAS_SIGINT
} SDArchiverStateReturns; } SDArchiverStateReturns;
/// Returned pointer must not be freed. /// Returned pointer must not be freed.

View file

@ -168,6 +168,9 @@ void simple_archiver_print_usage(void) {
fprintf(stderr, "--overwrite-extract : allows overwriting when extracting\n"); fprintf(stderr, "--overwrite-extract : allows overwriting when extracting\n");
fprintf(stderr, fprintf(stderr,
"--no-abs-symlink : do not store absolute paths for symlinks\n"); "--no-abs-symlink : do not store absolute paths for symlinks\n");
fprintf(stderr,
"--no-safe-links : keep symlinks that link to outside archive "
"contents\n");
fprintf(stderr, fprintf(stderr,
"--temp-files-dir <dir> : where to store temporary files created " "--temp-files-dir <dir> : where to store temporary files created "
"when compressing (defaults to current working directory)\n"); "when compressing (defaults to current working directory)\n");
@ -177,6 +180,9 @@ void simple_archiver_print_usage(void) {
fprintf(stderr, fprintf(stderr,
"--chunk-min-size <bytes> : v1 file format minimum chunk size " "--chunk-min-size <bytes> : v1 file format minimum chunk size "
"(default 4194304 or 4MiB)\n"); "(default 4194304 or 4MiB)\n");
fprintf(stderr,
"--no-pre-sort-files : do NOT pre-sort files by size (by default "
"enabled so that the first file is the largest)\n");
fprintf(stderr, fprintf(stderr,
"-- : specifies remaining arguments are files to archive/extract\n"); "-- : specifies remaining arguments are files to archive/extract\n");
fprintf( fprintf(
@ -190,7 +196,7 @@ void simple_archiver_print_usage(void) {
SDArchiverParsed simple_archiver_create_parsed(void) { SDArchiverParsed simple_archiver_create_parsed(void) {
SDArchiverParsed parsed; SDArchiverParsed parsed;
parsed.flags = 0; parsed.flags = 0x40;
parsed.filename = NULL; parsed.filename = NULL;
parsed.compressor = NULL; parsed.compressor = NULL;
parsed.decompressor = NULL; parsed.decompressor = NULL;
@ -300,6 +306,11 @@ int simple_archiver_parse_args(int argc, const char **argv,
out->flags |= 0x8; out->flags |= 0x8;
} else if (strcmp(argv[0], "--no-abs-symlink") == 0) { } else if (strcmp(argv[0], "--no-abs-symlink") == 0) {
out->flags |= 0x20; out->flags |= 0x20;
} else if (strcmp(argv[0], "--no-safe-links") == 0) {
out->flags |= 0x80;
fprintf(stderr,
"NOTICE: Disabling safe-links, symlinks that point to outside "
"archived files will be preserved!\n");
} else if (strcmp(argv[0], "--temp-files-dir") == 0) { } else if (strcmp(argv[0], "--temp-files-dir") == 0) {
if (argc < 2) { if (argc < 2) {
fprintf(stderr, "ERROR: --temp-files-dir is missing an argument!\n"); fprintf(stderr, "ERROR: --temp-files-dir is missing an argument!\n");
@ -344,6 +355,8 @@ int simple_archiver_parse_args(int argc, const char **argv,
} }
--argc; --argc;
++argv; ++argv;
} else if (strcmp(argv[0], "--no-pre-sort-files") == 0) {
out->flags &= 0xFFFFFFBF;
} else if (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) { } else if (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) {
is_remaining_args = 1; is_remaining_args = 1;
} else if (argv[0][0] != '-') { } else if (argv[0][0] != '-') {

View file

@ -35,6 +35,8 @@ typedef struct SDArchiverParsed {
/// 0b xxxx 1xxx - Allow extract overwrite. /// 0b xxxx 1xxx - Allow extract overwrite.
/// 0b xxx1 xxxx - Create archive to stdout or read archive from stdin. /// 0b xxx1 xxxx - Create archive to stdout or read archive from stdin.
/// 0b xx1x xxxx - Do not save absolute paths for symlinks. /// 0b xx1x xxxx - Do not save absolute paths for symlinks.
/// 0b x1xx xxxx - Sort files by size before archiving.
/// 0b 1xxx xxxx - No safe links.
uint32_t flags; uint32_t flags;
/// Null-terminated string. /// Null-terminated string.
char *filename; char *filename;

View file

@ -109,7 +109,7 @@ int main(void) {
CHECK_TRUE(strcmp("doop", parsed.working_files[1]) == 0); CHECK_TRUE(strcmp("doop", parsed.working_files[1]) == 0);
CHECK_TRUE(parsed.working_files[2] == NULL); CHECK_TRUE(parsed.working_files[2] == NULL);
CHECK_TRUE(parsed.filename == NULL); CHECK_TRUE(parsed.filename == NULL);
CHECK_TRUE(parsed.flags == 0); CHECK_TRUE(parsed.flags == 0x40);
simple_archiver_free_parsed(&parsed); simple_archiver_free_parsed(&parsed);
@ -126,7 +126,7 @@ int main(void) {
CHECK_TRUE(strcmp("../../.prev_dir_file", parsed.working_files[2]) == 0); CHECK_TRUE(strcmp("../../.prev_dir_file", parsed.working_files[2]) == 0);
CHECK_TRUE(parsed.working_files[3] == NULL); CHECK_TRUE(parsed.working_files[3] == NULL);
CHECK_TRUE(strcmp("the_filename", parsed.filename) == 0); CHECK_TRUE(strcmp("the_filename", parsed.filename) == 0);
CHECK_TRUE(parsed.flags == 1); CHECK_TRUE(parsed.flags == 0x41);
simple_archiver_free_parsed(&parsed); simple_archiver_free_parsed(&parsed);
} }