Compare commits
16 commits
0f9c8efe63
...
c7cd445139
Author | SHA1 | Date | |
---|---|---|---|
c7cd445139 | |||
b09948d245 | |||
6376be2840 | |||
da18464d5d | |||
8fa430f842 | |||
53fefb7ae8 | |||
45fdffdc9c | |||
7407972450 | |||
3d58f466af | |||
b273d91896 | |||
c71f4f45c7 | |||
c1faae90e9 | |||
d625c1b1cb | |||
a7aa31fc89 | |||
f76e383e78 | |||
efde02b4ab |
10 changed files with 1626 additions and 80 deletions
|
@ -83,6 +83,7 @@ add_executable(test_simplearchiver
|
|||
src/test.c
|
||||
src/parser.c
|
||||
src/helpers.c
|
||||
src/archiver.c
|
||||
src/algorithms/linear_congruential_gen.c
|
||||
src/data_structures/linked_list.c
|
||||
src/data_structures/hash_map.c
|
||||
|
|
114
file_format.md
114
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. 2 bytes that are a 16-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.
|
||||
|
|
BIN
file_format_1_example_0
Normal file
BIN
file_format_1_example_0
Normal file
Binary file not shown.
BIN
file_format_1_example_1
Normal file
BIN
file_format_1_example_1
Normal file
Binary file not shown.
1506
src/archiver.c
1506
src/archiver.c
File diff suppressed because it is too large
Load diff
|
@ -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,30 @@ 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);
|
||||
|
||||
#endif
|
||||
|
|
18
src/main.c
18
src/main.c
|
@ -97,7 +97,8 @@ int main(int argc, const char **argv) {
|
|||
int ret = simple_archiver_write_all(file, state, filenames);
|
||||
if (ret != SDAS_SUCCESS) {
|
||||
fprintf(stderr, "Error during writing.\n");
|
||||
char *error_str = simple_archiver_error_to_string(ret);
|
||||
char *error_str =
|
||||
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
|
||||
fprintf(stderr, " %s\n", error_str);
|
||||
}
|
||||
fclose(file);
|
||||
|
@ -113,7 +114,8 @@ int main(int argc, const char **argv) {
|
|||
int ret = simple_archiver_write_all(stdout, state, filenames);
|
||||
if (ret != SDAS_SUCCESS) {
|
||||
fprintf(stderr, "Error during writing.\n");
|
||||
char *error_str = simple_archiver_error_to_string(ret);
|
||||
char *error_str =
|
||||
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
|
||||
fprintf(stderr, " %s\n", error_str);
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +132,8 @@ int main(int argc, const char **argv) {
|
|||
int ret = simple_archiver_parse_archive_info(file, 0, NULL);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error during archive checking/examining.\n");
|
||||
char *error_str = simple_archiver_error_to_string(ret);
|
||||
char *error_str =
|
||||
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
|
||||
fprintf(stderr, " %s\n", error_str);
|
||||
}
|
||||
fclose(file);
|
||||
|
@ -138,7 +141,8 @@ int main(int argc, const char **argv) {
|
|||
int ret = simple_archiver_parse_archive_info(stdin, 0, NULL);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error during archive checking/examining.\n");
|
||||
char *error_str = simple_archiver_error_to_string(ret);
|
||||
char *error_str =
|
||||
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
|
||||
fprintf(stderr, " %s\n", error_str);
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +161,8 @@ int main(int argc, const char **argv) {
|
|||
int ret = simple_archiver_parse_archive_info(file, 1, state);
|
||||
if (ret != SDAS_SUCCESS) {
|
||||
fprintf(stderr, "Error during archive extracting.\n");
|
||||
char *error_str = simple_archiver_error_to_string(ret);
|
||||
char *error_str =
|
||||
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
|
||||
fprintf(stderr, " %s\n", error_str);
|
||||
}
|
||||
fclose(file);
|
||||
|
@ -165,7 +170,8 @@ int main(int argc, const char **argv) {
|
|||
int ret = simple_archiver_parse_archive_info(stdin, 1, state);
|
||||
if (ret != SDAS_SUCCESS) {
|
||||
fprintf(stderr, "Error during archive extracting.\n");
|
||||
char *error_str = simple_archiver_error_to_string(ret);
|
||||
char *error_str =
|
||||
simple_archiver_error_to_string((SDArchiverStateReturns)ret);
|
||||
fprintf(stderr, " %s\n", error_str);
|
||||
}
|
||||
}
|
||||
|
|
24
src/parser.c
24
src/parser.c
|
@ -169,6 +169,9 @@ void simple_archiver_print_usage(void) {
|
|||
fprintf(stderr,
|
||||
"--temp-files-dir <dir> : where to store temporary files created "
|
||||
"when compressing (defaults to current working directory)\n");
|
||||
fprintf(stderr,
|
||||
"--write-version <version> : Force write version file format "
|
||||
"(default 1)\n");
|
||||
fprintf(stderr,
|
||||
"-- : specifies remaining arguments are files to archive/extract\n");
|
||||
fprintf(
|
||||
|
@ -189,6 +192,7 @@ SDArchiverParsed simple_archiver_create_parsed(void) {
|
|||
parsed.working_files = NULL;
|
||||
parsed.temp_dir = NULL;
|
||||
parsed.user_cwd = NULL;
|
||||
parsed.write_version = 0;
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
@ -299,6 +303,26 @@ 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 (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) {
|
||||
is_remaining_args = 1;
|
||||
} else if (argv[0][0] != '-') {
|
||||
|
|
|
@ -51,6 +51,8 @@ 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;
|
||||
} SDArchiverParsed;
|
||||
|
||||
typedef struct SDArchiverFileInfo {
|
||||
|
|
16
src/test.c
16
src/test.c
|
@ -23,6 +23,7 @@
|
|||
#include <string.h>
|
||||
|
||||
// Local includes.
|
||||
#include "archiver.h"
|
||||
#include "helpers.h"
|
||||
#include "parser_internal.h"
|
||||
|
||||
|
@ -241,6 +242,21 @@ int main(void) {
|
|||
free(out);
|
||||
}
|
||||
|
||||
// Test archiver.
|
||||
{
|
||||
__attribute__((
|
||||
cleanup(simple_archiver_helper_cleanup_c_string))) char *rel_path =
|
||||
simple_archiver_filenames_to_relative_path(
|
||||
"/one/two/three/four/five", "/one/two/branch/other/path");
|
||||
CHECK_STREQ(rel_path, "../../branch/other/path");
|
||||
simple_archiver_helper_cleanup_c_string(&rel_path);
|
||||
|
||||
rel_path = simple_archiver_filenames_to_relative_path(
|
||||
"/one/two/three/four/five", "/one/two/three/other/dir/");
|
||||
CHECK_STREQ(rel_path, "../other/dir/");
|
||||
simple_archiver_helper_cleanup_c_string(&rel_path);
|
||||
}
|
||||
|
||||
printf("Checks checked: %u\n", checks_checked);
|
||||
printf("Checks passed: %u\n", checks_passed);
|
||||
return checks_passed == checks_checked ? 0 : 1;
|
||||
|
|
Loading…
Reference in a new issue