Compare commits
13 commits
4115e70d14
...
f89e5f7640
Author | SHA1 | Date | |
---|---|---|---|
f89e5f7640 | |||
0ae80168e3 | |||
d93f62a0cb | |||
8f33d62392 | |||
52bbfa4e65 | |||
8bedc750ba | |||
aaebe09347 | |||
0a776cae37 | |||
f472ad1a6b | |||
dcd2e531e6 | |||
ef49b37648 | |||
6ad609e7fc | |||
b098fd6d69 |
8 changed files with 1092 additions and 16 deletions
106
file_format.md
106
file_format.md
|
@ -76,3 +76,109 @@ 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"
|
1. 8 bytes 64-bit unsigned integer "size of filename in this archive file"
|
||||||
in big-endian.
|
in big-endian.
|
||||||
2. X bytes file data (length defined by previous value).
|
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 byte.
|
||||||
|
1. Currently unused.
|
||||||
|
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.
956
src/archiver.c
956
src/archiver.c
File diff suppressed because it is too large
Load diff
|
@ -69,6 +69,14 @@ int simple_archiver_write_all(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);
|
||||||
|
|
||||||
|
/// 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.
|
/// Returns zero on success.
|
||||||
int simple_archiver_de_compress(int pipe_fd_in[2], int pipe_fd_out[2],
|
int simple_archiver_de_compress(int pipe_fd_in[2], int pipe_fd_out[2],
|
||||||
const char *cmd, void *pid_out);
|
const char *cmd, void *pid_out);
|
||||||
|
|
|
@ -67,12 +67,13 @@ int main(int argc, const char **argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDArchiverParsedStatus parsed_status;
|
||||||
__attribute__((cleanup(simple_archiver_list_free)))
|
__attribute__((cleanup(simple_archiver_list_free)))
|
||||||
SDArchiverLinkedList *filenames =
|
SDArchiverLinkedList *filenames =
|
||||||
simple_archiver_parsed_to_filenames(&parsed);
|
simple_archiver_parsed_to_filenames(&parsed, &parsed_status);
|
||||||
if (!filenames) {
|
if (!filenames || parsed_status != SDAPS_SUCCESS) {
|
||||||
fprintf(stderr,
|
fprintf(stderr, "ERROR: %s!\n",
|
||||||
"ERROR: Failed to resolve filenames from positional arguments!\n");
|
simple_archiver_parsed_status_to_str(parsed_status));
|
||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
src/parser.c
19
src/parser.c
|
@ -131,6 +131,17 @@ int list_remove_same_str_fn(void *data, void *ud) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *simple_archiver_parsed_status_to_str(SDArchiverParsedStatus status) {
|
||||||
|
switch (status) {
|
||||||
|
case SDAPS_SUCCESS:
|
||||||
|
return "Success";
|
||||||
|
case SDAPS_NO_USER_CWD:
|
||||||
|
return "No user current working directory (-C <dir>)";
|
||||||
|
default:
|
||||||
|
return "Unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void simple_archiver_print_usage(void) {
|
void simple_archiver_print_usage(void) {
|
||||||
fprintf(stderr, "Usage flags:\n");
|
fprintf(stderr, "Usage flags:\n");
|
||||||
fprintf(stderr, "-c : create archive file\n");
|
fprintf(stderr, "-c : create archive file\n");
|
||||||
|
@ -367,7 +378,7 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
const SDArchiverParsed *parsed) {
|
const SDArchiverParsed *parsed, SDArchiverParsedStatus *status_out) {
|
||||||
SDArchiverLinkedList *files_list = simple_archiver_list_init();
|
SDArchiverLinkedList *files_list = simple_archiver_list_init();
|
||||||
__attribute__((cleanup(simple_archiver_hash_map_free)))
|
__attribute__((cleanup(simple_archiver_hash_map_free)))
|
||||||
SDArchiverHashMap *hash_map = simple_archiver_hash_map_init();
|
SDArchiverHashMap *hash_map = simple_archiver_hash_map_init();
|
||||||
|
@ -381,6 +392,9 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
original_cwd = realpath(".", NULL);
|
original_cwd = realpath(".", NULL);
|
||||||
if (chdir(parsed->user_cwd)) {
|
if (chdir(parsed->user_cwd)) {
|
||||||
simple_archiver_list_free(&files_list);
|
simple_archiver_list_free(&files_list);
|
||||||
|
if (status_out) {
|
||||||
|
*status_out = SDAPS_NO_USER_CWD;
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -607,5 +621,8 @@ SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status_out) {
|
||||||
|
*status_out = SDAPS_SUCCESS;
|
||||||
|
}
|
||||||
return files_list;
|
return files_list;
|
||||||
}
|
}
|
||||||
|
|
10
src/parser.h
10
src/parser.h
|
@ -59,6 +59,14 @@ typedef struct SDArchiverFileInfo {
|
||||||
char *link_dest;
|
char *link_dest;
|
||||||
} SDArchiverFileInfo;
|
} SDArchiverFileInfo;
|
||||||
|
|
||||||
|
typedef enum SDArchiverParsedStatus {
|
||||||
|
SDAPS_SUCCESS,
|
||||||
|
SDAPS_NO_USER_CWD,
|
||||||
|
} SDArchiverParsedStatus;
|
||||||
|
|
||||||
|
/// Returned c-string does not need to be free'd.
|
||||||
|
char *simple_archiver_parsed_status_to_str(SDArchiverParsedStatus status);
|
||||||
|
|
||||||
void simple_archiver_print_usage(void);
|
void simple_archiver_print_usage(void);
|
||||||
|
|
||||||
SDArchiverParsed simple_archiver_create_parsed(void);
|
SDArchiverParsed simple_archiver_create_parsed(void);
|
||||||
|
@ -74,6 +82,6 @@ void simple_archiver_free_parsed(SDArchiverParsed *parsed);
|
||||||
|
|
||||||
/// Each entry in the linked list is an SDArchiverFileInfo object.
|
/// Each entry in the linked list is an SDArchiverFileInfo object.
|
||||||
SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
SDArchiverLinkedList *simple_archiver_parsed_to_filenames(
|
||||||
const SDArchiverParsed *parsed);
|
const SDArchiverParsed *parsed, SDArchiverParsedStatus *status_out);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue