Compare commits
27 commits
Author | SHA1 | Date | |
---|---|---|---|
9d31798da6 | |||
1c06462ca7 | |||
50bc4a8a07 | |||
df37f68bcf | |||
edcaee803c | |||
50b1f4b274 | |||
cef3e4184a | |||
7ee54bddf2 | |||
9d84b28efe | |||
3dfc258fa4 | |||
1b7bfde458 | |||
04f4897dd3 | |||
fd5e9ab47d | |||
925ea46877 | |||
d68c7caf41 | |||
2a63793bcd | |||
adfa6836f5 | |||
553cf2a6ec | |||
78ba5acd70 | |||
28c09b0232 | |||
d54fc441a3 | |||
d2d202235c | |||
52c78b2fb4 | |||
9add2a650d | |||
ccbb1acd95 | |||
c4e5fbf8ce | |||
e7e4736aa5 |
16 changed files with 372 additions and 35 deletions
|
@ -87,4 +87,5 @@ add_executable(test_simplearchiver
|
|||
src/algorithms/linear_congruential_gen.c
|
||||
src/data_structures/linked_list.c
|
||||
src/data_structures/hash_map.c
|
||||
src/data_structures/priority_heap.c
|
||||
)
|
||||
|
|
56
Changelog.md
Normal file
56
Changelog.md
Normal 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.
|
11
README.md
11
README.md
|
@ -1,7 +1,8 @@
|
|||
# Simple Archiver
|
||||
|
||||
This program ~~is not yet~~ almost finished! Basic functionality is implemented
|
||||
and only some advanced features are missing. You can track progress
|
||||
This program ~~is not yet~~ ~~almost~~ basically finished! ~~Basic~~ Necessary
|
||||
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).
|
||||
|
||||
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-extract : allows overwriting when extracting
|
||||
--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)
|
||||
--write-version <version> : Force write version file format (default 1)
|
||||
--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
|
||||
If creating archive file, remaining args specify files to archive.
|
||||
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
|
||||
and return processed data to stdout.
|
||||
|
||||
## Changes
|
||||
|
||||
See the [Changelog](https://git.seodisparate.com/stephenseo/SimpleArchiver/src/branch/main/Changelog.md).
|
||||
|
||||
## LICENSE Information
|
||||
|
||||
Uses the [ISC License](https://choosealicense.com/licenses/isc/).
|
||||
|
|
|
@ -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".
|
||||
3. The third bit is UNSET if relative links are preferred, and is SET
|
||||
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.
|
||||
1. Currently unused.
|
||||
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.
|
||||
1. The first bit is "other write 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
|
||||
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
|
||||
|
|
307
src/archiver.c
307
src/archiver.c
|
@ -38,6 +38,7 @@
|
|||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "data_structures/priority_heap.h"
|
||||
#include "helpers.h"
|
||||
|
||||
#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_LINUX
|
||||
volatile int is_sig_pipe_occurred = 0;
|
||||
volatile int is_sig_int_occurred = 0;
|
||||
|
||||
void handle_sig_pipe(int sig) {
|
||||
if (sig == SIGPIPE) {
|
||||
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
|
||||
|
||||
typedef struct SDArchiverInternalToWrite {
|
||||
|
@ -104,7 +114,25 @@ void cleanup_temp_filename_delete(void ***ptrs_array) {
|
|||
#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) {
|
||||
if (is_sig_int_occurred) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const SDArchiverFileInfo *file_info = data;
|
||||
SDArchiverState *state = ud;
|
||||
|
||||
|
@ -257,6 +285,17 @@ int write_files_fn(void *data, void *ud) {
|
|||
size_t read_count;
|
||||
ssize_t ret;
|
||||
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) {
|
||||
fprintf(stderr,
|
||||
"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);
|
||||
if (ret == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
nanosleep(&nonblock_sleep, NULL);
|
||||
write_again = 1;
|
||||
} else {
|
||||
// Error during write.
|
||||
|
@ -297,6 +337,7 @@ int write_files_fn(void *data, void *ud) {
|
|||
simple_archiver_helper_cleanup_FILE(&file_fd);
|
||||
write_done = 1;
|
||||
close(pipe_into_cmd[1]);
|
||||
pipe_into_cmd[1] = -1;
|
||||
// fprintf(stderr, "write_done\n");
|
||||
} else if (ferror(file_fd)) {
|
||||
// Error during read file.
|
||||
|
@ -326,10 +367,11 @@ int write_files_fn(void *data, void *ud) {
|
|||
read_done = 1;
|
||||
simple_archiver_helper_cleanup_FILE(&tmp_fd);
|
||||
close(pipe_outof_cmd[0]);
|
||||
pipe_outof_cmd[0] = -1;
|
||||
// fprintf(stderr, "read_done\n");
|
||||
} else if (ret == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
// Nop.
|
||||
nanosleep(&nonblock_sleep, NULL);
|
||||
} else {
|
||||
// Read error.
|
||||
fprintf(stderr,
|
||||
|
@ -348,6 +390,8 @@ int write_files_fn(void *data, void *ud) {
|
|||
"WARNING: Failed to write to compressor (SIGPIPE)! Invalid "
|
||||
"compressor cmd?\n");
|
||||
return 1;
|
||||
} else if (is_sig_int_occurred) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
waitpid(compressor_pid, NULL, 0);
|
||||
|
@ -605,6 +649,10 @@ int write_files_fn(void *data, void *ud) {
|
|||
|
||||
simple_archiver_list_free(&to_write);
|
||||
|
||||
if (is_sig_int_occurred) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Write file.
|
||||
fprintf(stderr, "Writing file: %s\n", file_info->filename);
|
||||
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 &&
|
||||
!simple_archiver_hash_map_get(state->map, abs_path,
|
||||
strlen(abs_path) + 1)) {
|
||||
// Is not a filename being archived, set preference to absolute path.
|
||||
fprintf(stderr,
|
||||
"NOTICE: abs_path exists, \"--no-abs-symlink\" not specified, "
|
||||
"and link refers to file NOT in archive; preferring abs_path.\n");
|
||||
((uint8_t *)temp_to_write->buf)[1] |= 0x4;
|
||||
// Is not a filename being archived.
|
||||
if ((state->parsed->flags & 0x80) != 0) {
|
||||
// No safe links, set preference to absolute path.
|
||||
fprintf(
|
||||
stderr,
|
||||
"NOTICE: abs_path exists, \"--no-abs-symlink\" not specified, "
|
||||
"and link refers to file NOT in archive; preferring abs_path.\n");
|
||||
((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.
|
||||
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.
|
||||
if (!abs_path) {
|
||||
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,
|
||||
state->digits);
|
||||
fprintf(stderr, format_str, ++(state->count), state->max);
|
||||
|
||||
if (is_sig_int_occurred) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1094,12 +1169,18 @@ int read_decomp_to_out_file(const char *out_filename, int in_pipe,
|
|||
ssize_t read_ret;
|
||||
size_t fwrite_ret;
|
||||
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,
|
||||
read_buf_size, hold_buf, has_hold);
|
||||
if (ret != SDAS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
if (file_size - written_amt >= read_buf_size) {
|
||||
} else if (is_sig_pipe_occurred) {
|
||||
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);
|
||||
if (read_ret > 0) {
|
||||
if (out_fd) {
|
||||
|
@ -1130,6 +1211,7 @@ int read_decomp_to_out_file(const char *out_filename, int in_pipe,
|
|||
} else {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
// Non-blocking read from pipe.
|
||||
nanosleep(&nonblock_sleep, NULL);
|
||||
continue;
|
||||
} else {
|
||||
// Error.
|
||||
|
@ -1169,6 +1251,7 @@ int read_decomp_to_out_file(const char *out_filename, int in_pipe,
|
|||
} else {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
// Non-blocking read from pipe.
|
||||
nanosleep(&nonblock_sleep, NULL);
|
||||
continue;
|
||||
} else {
|
||||
// Error.
|
||||
|
@ -1469,6 +1552,7 @@ int symlinks_and_files_from_files(void *data, void *ud) {
|
|||
SDArchiverLinkedList *symlinks_list = ptr_array[0];
|
||||
SDArchiverLinkedList *files_list = ptr_array[1];
|
||||
const char *user_cwd = ptr_array[2];
|
||||
SDArchiverPHeap *pheap = ptr_array[3];
|
||||
|
||||
if (file_info->filename) {
|
||||
if (file_info->link_dest) {
|
||||
|
@ -1555,8 +1639,14 @@ int symlinks_and_files_from_files(void *data, void *ud) {
|
|||
return 1;
|
||||
}
|
||||
file_info_struct->file_size = (uint64_t)ftell_ret;
|
||||
simple_archiver_list_add(files_list, file_info_struct,
|
||||
free_internal_file_info);
|
||||
if (pheap) {
|
||||
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;
|
||||
}
|
||||
|
||||
int greater_fn(int64_t a, int64_t b) { return a > b; }
|
||||
|
||||
char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) {
|
||||
switch (error) {
|
||||
case SDAS_SUCCESS:
|
||||
|
@ -1614,6 +1706,8 @@ char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) {
|
|||
return "Failed to change current working directory";
|
||||
case SDAS_INVALID_WRITE_VERSION:
|
||||
return "Unsupported write version file format";
|
||||
case SDAS_SIGINT:
|
||||
return "Interrupt signal SIGINT recieved";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
|
@ -1645,6 +1739,11 @@ void simple_archiver_free_state(SDArchiverState **state) {
|
|||
|
||||
int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
|
||||
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) {
|
||||
case 0:
|
||||
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.
|
||||
state->count = 0;
|
||||
state->max = filenames->count;
|
||||
|
@ -1788,6 +1891,9 @@ int simple_archiver_write_v0(FILE *out_f, SDArchiverState *state,
|
|||
state->digits);
|
||||
fprintf(stderr, format_str, state->count, state->max);
|
||||
if (simple_archiver_list_get(filenames, write_files_fn, state)) {
|
||||
if (is_sig_int_occurred) {
|
||||
return SDAS_SIGINT;
|
||||
}
|
||||
// Error occurred.
|
||||
fprintf(stderr, "Error ocurred writing file(s) to archive.\n");
|
||||
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();
|
||||
__attribute__((cleanup(simple_archiver_list_free)))
|
||||
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[1] = files_list;
|
||||
ptr_array[2] = (void *)state->parsed->user_cwd;
|
||||
ptr_array[3] = files_pheap;
|
||||
|
||||
if (simple_archiver_list_get(filenames, symlinks_and_files_from_files,
|
||||
ptr_array)) {
|
||||
|
@ -1830,6 +1942,22 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
|
|||
}
|
||||
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) {
|
||||
return SDAS_FAILED_TO_WRITE;
|
||||
}
|
||||
|
@ -1951,11 +2079,25 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
|
|||
abs_path);
|
||||
}
|
||||
}
|
||||
|
||||
uint_fast8_t is_invalid = 0;
|
||||
|
||||
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;
|
||||
// Is not a filename being archived.
|
||||
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.
|
||||
|
@ -1998,6 +2140,11 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
|
|||
buf[0] = 0xFE;
|
||||
buf[1] = 3;
|
||||
#endif
|
||||
|
||||
if (is_invalid) {
|
||||
buf[1] |= 4;
|
||||
}
|
||||
|
||||
if (fwrite(buf, 1, 2, out_f) != 2) {
|
||||
return SDAS_FAILED_TO_WRITE;
|
||||
}
|
||||
|
@ -2018,7 +2165,7 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
|
|||
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);
|
||||
if (len >= 0xFFFF) {
|
||||
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);
|
||||
if (len >= 0xFFFF) {
|
||||
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)))
|
||||
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;
|
||||
for (SDArchiverLLNode *chunk_c_node = chunk_counts->head->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);
|
||||
// Write file count before iterating through files.
|
||||
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;
|
||||
for (uint64_t file_idx = 0; file_idx < *((uint64_t *)chunk_c_node->data);
|
||||
++file_idx) {
|
||||
if (is_sig_int_occurred) {
|
||||
return SDAS_SIGINT;
|
||||
}
|
||||
file_node = file_node->next;
|
||||
if (file_node == files_list->tail) {
|
||||
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];
|
||||
ssize_t has_hold = -1;
|
||||
while (!to_comp_finished) {
|
||||
if (is_sig_pipe_occurred) {
|
||||
fprintf(stderr, "ERROR: SIGPIPE while compressing!\n");
|
||||
return SDAS_INTERNAL_ERROR;
|
||||
}
|
||||
if (!to_comp_finished) {
|
||||
// Write to compressor.
|
||||
if (ferror(fd)) {
|
||||
|
@ -2336,6 +2497,7 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
|
|||
// Non-blocking write.
|
||||
has_hold = (int)fread_ret;
|
||||
memcpy(hold_buf, buf, fread_ret);
|
||||
nanosleep(&nonblock_sleep, NULL);
|
||||
} else {
|
||||
fprintf(
|
||||
stderr,
|
||||
|
@ -2362,6 +2524,7 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
|
|||
if (write_ret < 0) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
// Non-blocking write.
|
||||
nanosleep(&nonblock_sleep, NULL);
|
||||
} else {
|
||||
return SDAS_INTERNAL_ERROR;
|
||||
}
|
||||
|
@ -2384,6 +2547,7 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
|
|||
if (read_ret < 0) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
// Non-blocking read.
|
||||
nanosleep(&nonblock_sleep, NULL);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"ERROR: Reading from compressor, pipe read error!\n");
|
||||
|
@ -2409,11 +2573,16 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state,
|
|||
// Finish writing.
|
||||
if (!to_temp_finished) {
|
||||
while (1) {
|
||||
if (is_sig_pipe_occurred) {
|
||||
fprintf(stderr, "ERROR: SIGPIPE while compressing!\n");
|
||||
return SDAS_INTERNAL_ERROR;
|
||||
}
|
||||
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.
|
||||
nanosleep(&nonblock_sleep, NULL);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"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.
|
||||
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;
|
||||
}
|
||||
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);
|
||||
for (uint64_t file_idx = 0; file_idx < *((uint64_t *)chunk_c_node->data);
|
||||
++file_idx) {
|
||||
if (is_sig_int_occurred) {
|
||||
return SDAS_SIGINT;
|
||||
}
|
||||
file_node = file_node->next;
|
||||
if (file_node == files_list->tail) {
|
||||
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,
|
||||
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];
|
||||
memset(buf, 0, 32);
|
||||
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);
|
||||
fprintf(stderr, "File count is %u\n", u32);
|
||||
|
||||
if (is_sig_int_occurred) {
|
||||
return SDAS_SIGINT;
|
||||
}
|
||||
|
||||
const uint32_t size = u32;
|
||||
const size_t digits = simple_archiver_helper_num_digits(size);
|
||||
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) {
|
||||
if (is_sig_int_occurred) {
|
||||
return SDAS_SIGINT;
|
||||
}
|
||||
skip = 0;
|
||||
fprintf(stderr, format_str, idx + 1, size);
|
||||
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;
|
||||
__attribute__((cleanup(simple_archiver_helper_cleanup_FILE))) FILE *out_f =
|
||||
NULL;
|
||||
__attribute__((cleanup(
|
||||
cleanup_overwrite_filename_delete_simple))) char *to_overwrite_dest =
|
||||
NULL;
|
||||
if (u16 < SIMPLE_ARCHIVER_BUFFER_SIZE) {
|
||||
if (fread(buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
|
||||
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 (errno == ELOOP) {
|
||||
// Is an existing symbolic file.
|
||||
unlink((const char *)buf);
|
||||
// Defer deletion to after "is invalid" check.
|
||||
to_overwrite_dest = strdup((const char *)buf);
|
||||
}
|
||||
} else {
|
||||
close(fd);
|
||||
// Is an existing file.
|
||||
unlink((const char *)buf);
|
||||
// Defer deletion to after "is invalid" check.
|
||||
to_overwrite_dest = strdup((const char *)buf);
|
||||
}
|
||||
}
|
||||
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 (errno == ELOOP) {
|
||||
// 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 {
|
||||
close(fd);
|
||||
// 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) {
|
||||
|
@ -2767,6 +2961,17 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
|
|||
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 || \
|
||||
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
|
||||
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];
|
||||
size_t amount_to_read;
|
||||
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,
|
||||
"WARNING: Failed to write to decompressor (SIGPIPE)! "
|
||||
"Invalid decompressor cmd?\n");
|
||||
return 1;
|
||||
return SDAS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (compressed_file_size == 0) {
|
||||
close(pipe_into_cmd[1]);
|
||||
pipe_into_cmd[1] = -1;
|
||||
write_pipe_done = 1;
|
||||
}
|
||||
} else if (write_ret == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
nanosleep(&nonblock_sleep, NULL);
|
||||
write_again = 1;
|
||||
} else {
|
||||
// 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) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
// No bytes to read yet.
|
||||
nanosleep(&nonblock_sleep, NULL);
|
||||
} else {
|
||||
// Error.
|
||||
fprintf(stderr,
|
||||
|
@ -3075,6 +3293,7 @@ int simple_archiver_parse_archive_version_0(FILE *in_f, int_fast8_t do_extract,
|
|||
// EOF.
|
||||
read_pipe_done = 1;
|
||||
close(pipe_outof_cmd[0]);
|
||||
pipe_outof_cmd[0] = -1;
|
||||
simple_archiver_helper_cleanup_FILE(&out_f);
|
||||
} else {
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
if (fread(buf, 1, 4, in_f) != 4) {
|
||||
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);
|
||||
|
||||
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);
|
||||
if (fread(buf, 1, 2, in_f) != 2) {
|
||||
return SDAS_INVALID_FILE;
|
||||
}
|
||||
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 || \
|
||||
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 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) {
|
||||
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;
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
while (node->next != file_info_list->tail) {
|
||||
if (is_sig_int_occurred) {
|
||||
return SDAS_SIGINT;
|
||||
}
|
||||
node = node->next;
|
||||
const SDArchiverInternalFileInfo *file_info = node->data;
|
||||
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,
|
||||
" 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);
|
||||
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;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -3989,10 +4235,13 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract,
|
|||
}
|
||||
} 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) {
|
||||
#endif
|
||||
while (node->next != file_info_list->tail) {
|
||||
if (is_sig_int_occurred) {
|
||||
return SDAS_SIGINT;
|
||||
}
|
||||
node = node->next;
|
||||
const SDArchiverInternalFileInfo *file_info = node->data;
|
||||
fprintf(stderr, " FILE %3u of %3u: %s\n", ++file_idx, file_count,
|
||||
|
|
|
@ -52,7 +52,8 @@ typedef enum SDArchiverStateReturns {
|
|||
SDAS_FAILED_TO_CREATE_MAP,
|
||||
SDAS_FAILED_TO_EXTRACT_SYMLINK,
|
||||
SDAS_FAILED_TO_CHANGE_CWD,
|
||||
SDAS_INVALID_WRITE_VERSION
|
||||
SDAS_INVALID_WRITE_VERSION,
|
||||
SDAS_SIGINT
|
||||
} SDArchiverStateReturns;
|
||||
|
||||
/// Returned pointer must not be freed.
|
||||
|
|
15
src/parser.c
15
src/parser.c
|
@ -168,6 +168,9 @@ void simple_archiver_print_usage(void) {
|
|||
fprintf(stderr, "--overwrite-extract : allows overwriting when extracting\n");
|
||||
fprintf(stderr,
|
||||
"--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,
|
||||
"--temp-files-dir <dir> : where to store temporary files created "
|
||||
"when compressing (defaults to current working directory)\n");
|
||||
|
@ -177,6 +180,9 @@ void simple_archiver_print_usage(void) {
|
|||
fprintf(stderr,
|
||||
"--chunk-min-size <bytes> : v1 file format minimum chunk size "
|
||||
"(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,
|
||||
"-- : specifies remaining arguments are files to archive/extract\n");
|
||||
fprintf(
|
||||
|
@ -190,7 +196,7 @@ void simple_archiver_print_usage(void) {
|
|||
SDArchiverParsed simple_archiver_create_parsed(void) {
|
||||
SDArchiverParsed parsed;
|
||||
|
||||
parsed.flags = 0;
|
||||
parsed.flags = 0x40;
|
||||
parsed.filename = NULL;
|
||||
parsed.compressor = NULL;
|
||||
parsed.decompressor = NULL;
|
||||
|
@ -300,6 +306,11 @@ int simple_archiver_parse_args(int argc, const char **argv,
|
|||
out->flags |= 0x8;
|
||||
} else if (strcmp(argv[0], "--no-abs-symlink") == 0) {
|
||||
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) {
|
||||
if (argc < 2) {
|
||||
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;
|
||||
++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) {
|
||||
is_remaining_args = 1;
|
||||
} else if (argv[0][0] != '-') {
|
||||
|
|
|
@ -35,6 +35,8 @@ typedef struct SDArchiverParsed {
|
|||
/// 0b xxxx 1xxx - Allow extract overwrite.
|
||||
/// 0b xxx1 xxxx - Create archive to stdout or read archive from stdin.
|
||||
/// 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;
|
||||
/// Null-terminated string.
|
||||
char *filename;
|
||||
|
|
|
@ -109,7 +109,7 @@ int main(void) {
|
|||
CHECK_TRUE(strcmp("doop", parsed.working_files[1]) == 0);
|
||||
CHECK_TRUE(parsed.working_files[2] == NULL);
|
||||
CHECK_TRUE(parsed.filename == NULL);
|
||||
CHECK_TRUE(parsed.flags == 0);
|
||||
CHECK_TRUE(parsed.flags == 0x40);
|
||||
|
||||
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(parsed.working_files[3] == NULL);
|
||||
CHECK_TRUE(strcmp("the_filename", parsed.filename) == 0);
|
||||
CHECK_TRUE(parsed.flags == 1);
|
||||
CHECK_TRUE(parsed.flags == 0x41);
|
||||
|
||||
simple_archiver_free_parsed(&parsed);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue