Compare commits

..

8 commits

Author SHA1 Message Date
d8068a6f5f Impl. link extracting
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 52s
2024-07-24 14:52:35 +09:00
b28e384149 Add abs_path check when referring out of archive
If absolute paths are enabled, and a link points to something outside of
the archive, a flag is set on the symlink entry to prefer absolute
paths.
2024-07-24 14:31:38 +09:00
c6d2d50c0f Create "set" of filenames for use when archiving 2024-07-24 14:05:05 +09:00
2f4c0d3679 Split off code into internal helper function
Function gets absolute path to given filename.
2024-07-24 14:04:19 +09:00
9d105bc3a5 Minor tweak to includes in archiver 2024-07-24 14:03:03 +09:00
b90b7ae64f Update file_format.md 2024-07-24 13:33:46 +09:00
e9c327a62f Add "--no-abs-symlink" to not store absolute paths 2024-07-24 13:32:39 +09:00
d939939723 Impl. archiving abs/rel-paths for symbolic links
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 4s
TODO:
    Choosing the ideal link based on if link points to an archived file.
    Extracting (creating) symbolic links from archive.
2024-07-23 15:28:59 +09:00
5 changed files with 315 additions and 33 deletions

View file

@ -55,6 +55,8 @@ Following the file-count bytes, the following bytes are added for each file:
2. The second byte.
1. The first bit is "other write permission".
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.
3. The third byte.
1. Currently unused.
4. The fourth byte.

View file

@ -28,6 +28,8 @@
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <spawn.h>
#include <sys/stat.h>
@ -35,7 +37,6 @@
#include <unistd.h>
#endif
#include "data_structures/hash_map.h"
#include "helpers.h"
#define TEMP_FILENAME_CMP "simple_archiver_compressed_%u.tmp"
@ -102,6 +103,43 @@ void cleanup_temp_filename_delete(void ***ptrs_array) {
#endif
}
char *filename_to_absolute_path(const char *filename) {
__attribute__((cleanup(free_malloced_memory))) void *path =
malloc(strlen(filename) + 1);
strncpy(path, filename, strlen(filename) + 1);
char *path_dir = dirname(path);
if (!path_dir) {
return NULL;
}
__attribute__((cleanup(free_malloced_memory))) void *dir_realpath =
realpath(path_dir, NULL);
if (!dir_realpath) {
return NULL;
}
// Recreate "path" since it may have been modified by dirname().
free_malloced_memory(&path);
path = malloc(strlen(filename) + 1);
strncpy(path, filename, strlen(filename) + 1);
char *filename_basename = basename(path);
if (!filename_basename) {
return NULL;
}
// Get combined full path to file.
char *fullpath =
malloc(strlen(dir_realpath) + 1 + strlen(filename_basename) + 1);
strncpy(fullpath, dir_realpath, strlen(dir_realpath) + 1);
fullpath[strlen(dir_realpath)] = '/';
strncpy(fullpath + strlen(dir_realpath) + 1, filename_basename,
strlen(filename_basename) + 1);
return fullpath;
}
int write_files_fn(void *data, void *ud) {
const SDArchiverFileInfo *file_info = data;
SDArchiverState *state = ud;
@ -666,33 +704,149 @@ int write_files_fn(void *data, void *ud) {
// Unsupported platform. Just set the permission bits for user.
((unsigned char *)temp_to_write->buf)[0] |= 0xE;
#endif
// Need to get abs_path for checking/setting a flag before storing flags.
// Get absolute path.
__attribute__((cleanup(free_malloced_memory))) void *abs_path =
realpath(file_info->filename, NULL);
__attribute__((cleanup(free_malloced_memory))) void *rel_path = NULL;
if (abs_path) {
// Get relative path.
// First get absolute path of link.
__attribute__((cleanup(free_malloced_memory))) void *link_abs_path =
filename_to_absolute_path(file_info->filename);
if (!link_abs_path) {
fprintf(stderr, "WARNING: Failed to get absolute path of link!\n");
} else {
// fprintf(stderr, "DEBUG: abs_path: %s\nDEBUG: link_abs_path: %s\n",
// (char*)abs_path, (char*)link_abs_path);
// Compare paths to get relative path.
// Get first non-common char.
unsigned int idx;
unsigned int last_slash;
for (idx = 0, last_slash = 0;
idx < strlen(abs_path) && idx < strlen(link_abs_path); ++idx) {
if (((const char *)abs_path)[idx] !=
((const char *)link_abs_path)[idx]) {
break;
} else if (((const char *)abs_path)[idx] == '/') {
last_slash = idx + 1;
}
}
// Get substrings of both paths.
char *link_substr = (char *)link_abs_path + last_slash;
char *dest_substr = (char *)abs_path + last_slash;
rel_path = malloc(strlen(dest_substr) + 1);
strncpy(rel_path, dest_substr, strlen(dest_substr) + 1);
// fprintf(stderr, "DEBUG: link_substr: %s\nDEBUG: dest_substr: %s\n",
// link_substr, dest_substr);
// Generate the relative path.
int has_slash = 0;
idx = 0;
do {
for (; link_substr[idx] != '/' && link_substr[idx] != 0; ++idx);
if (link_substr[idx] == 0) {
has_slash = 0;
} else {
has_slash = 1;
char *new_rel_path = malloc(strlen(rel_path) + 1 + 3);
new_rel_path[0] = '.';
new_rel_path[1] = '.';
new_rel_path[2] = '/';
strncpy(new_rel_path + 3, rel_path, strlen(rel_path) + 1);
free(rel_path);
rel_path = new_rel_path;
++idx;
}
} while (has_slash);
}
}
// Check if absolute path refers to one of the filenames.
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");
((unsigned char *)temp_to_write->buf)[1] |= 0x4;
}
// Store the 4 byte bit-flags for file.
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
// Write absolute path length.
// TODO actually store absolute path.
// u16 = 0;
// No need to convert to big-endian since 0 is 0.
// simple_archiver_helper_16_bit_be(&u16);
// Store the absolute and relative paths.
if (!abs_path) {
fprintf(stderr,
"WARNING: Failed to get absolute path of link destination!\n");
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
temp_to_write->buf = malloc(2);
temp_to_write->size = 2;
memset(temp_to_write->buf, 0, 2);
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
} else if ((state->parsed->flags & 0x20) == 0) {
// Write absolute path length.
u16 = strlen(abs_path);
simple_archiver_helper_16_bit_be(&u16);
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
temp_to_write->buf = malloc(2);
temp_to_write->size = 2;
((unsigned char *)temp_to_write->buf)[0] = 0;
((unsigned char *)temp_to_write->buf)[1] = 0;
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
temp_to_write->buf = malloc(2);
temp_to_write->size = 2;
memcpy(temp_to_write->buf, &u16, 2);
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
// Write relative path length.
// TODO actually store relative path.
// Write absolute path.
simple_archiver_helper_16_bit_be(&u16);
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
temp_to_write->buf = malloc(u16 + 1);
temp_to_write->size = u16 + 1;
strncpy(temp_to_write->buf, abs_path, u16 + 1);
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
} else {
fprintf(stderr,
"NOTICE: Not saving absolute path since \"--no-abs-symlink\" "
"was specified.\n");
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
temp_to_write->buf = malloc(2);
temp_to_write->size = 2;
memset(temp_to_write->buf, 0, 2);
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
}
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
temp_to_write->buf = malloc(2);
temp_to_write->size = 2;
((unsigned char *)temp_to_write->buf)[0] = 0;
((unsigned char *)temp_to_write->buf)[1] = 0;
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
if (rel_path) {
// Write relative path length.
u16 = strlen(rel_path);
simple_archiver_helper_16_bit_be(&u16);
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
temp_to_write->buf = malloc(2);
temp_to_write->size = 2;
memcpy(temp_to_write->buf, &u16, 2);
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
// Write relative path.
simple_archiver_helper_16_bit_be(&u16);
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
temp_to_write->buf = malloc(u16 + 1);
temp_to_write->size = u16 + 1;
strncpy(temp_to_write->buf, rel_path, u16 + 1);
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
} else {
temp_to_write = malloc(sizeof(SDArchiverInternalToWrite));
temp_to_write->buf = malloc(2);
temp_to_write->size = 2;
memset(temp_to_write->buf, 0, 2);
simple_archiver_list_add(to_write, temp_to_write, free_internal_to_write);
}
// Write all previously set data.
fprintf(stderr, "Writing symlink info: %s\n", file_info->filename);
if ((state->parsed->flags & 0x20) == 0) {
fprintf(stderr, " abs path: %s\n", (char *)abs_path);
}
fprintf(stderr, " rel path: %s\n", (char *)rel_path);
simple_archiver_list_get(to_write, write_list_datas_fn, state->out_f);
simple_archiver_list_free(&to_write);
}
@ -704,6 +858,54 @@ int write_files_fn(void *data, void *ud) {
void cleanup_nop_fn(__attribute__((unused)) void *unused) {}
void cleanup_free_fn(void *data) { free(data); }
int filenames_to_abs_map_fn(void *data, void *ud) {
SDArchiverFileInfo *file_info = data;
SDArchiverHashMap **abs_filenames = ud;
// Get combined full path to file.
char *fullpath = filename_to_absolute_path(file_info->filename);
if (!fullpath) {
return 1;
}
simple_archiver_hash_map_insert(abs_filenames, fullpath, fullpath,
strlen(fullpath) + 1, cleanup_nop_fn, NULL);
// Try putting all parent dirs up to current working directory.
// First get absolute path to current working directory.
__attribute__((cleanup(free_malloced_memory))) void *cwd_dirname =
realpath(".", NULL);
if (!cwd_dirname) {
return 1;
}
// Use copy of fullpath to avoid clobbering it.
__attribute__((cleanup(free_malloced_memory))) void *fullpath_copy =
malloc(strlen(fullpath) + 1);
strncpy(fullpath_copy, fullpath, strlen(fullpath) + 1);
// Get dirnames.
char *prev = fullpath_copy;
char *fullpath_dirname;
while (1) {
fullpath_dirname = dirname(prev);
if (!fullpath_dirname || strlen(fullpath_dirname) <= strlen(cwd_dirname)) {
break;
} else {
// Make and store copy of fullpath_dirname.
char *fullpath_dirname_copy = malloc(strlen(fullpath_dirname) + 1);
strncpy(fullpath_dirname_copy, fullpath_dirname,
strlen(fullpath_dirname) + 1);
simple_archiver_hash_map_insert(
abs_filenames, fullpath_dirname_copy, fullpath_dirname_copy,
strlen(fullpath_dirname_copy) + 1, cleanup_nop_fn, NULL);
}
prev = fullpath_dirname;
}
return 0;
}
char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) {
switch (error) {
case SDAS_SUCCESS:
@ -722,6 +924,10 @@ char *simple_archiver_error_to_string(enum SDArchiverStateReturns error) {
return "Invalid file";
case SDAS_INTERNAL_ERROR:
return "Internal error";
case SDAS_FAILED_TO_CREATE_MAP:
return "Failed to create set of filenames (internal error)";
case SDAS_FAILED_TO_EXTRACT_SYMLINK:
return "Failed to extract symlink (internal error)";
default:
return "Unknown error";
}
@ -736,6 +942,7 @@ SDArchiverState *simple_archiver_init_state(const SDArchiverParsed *parsed) {
state->flags = 0;
state->parsed = parsed;
state->out_f = NULL;
state->map = NULL;
return state;
}
@ -749,6 +956,14 @@ void simple_archiver_free_state(SDArchiverState **state) {
int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
const SDArchiverLinkedList *filenames) {
// First create a "set" of absolute paths to given filenames.
__attribute__((cleanup(simple_archiver_hash_map_free)))
SDArchiverHashMap *abs_filenames = simple_archiver_hash_map_init();
if (simple_archiver_list_get(filenames, filenames_to_abs_map_fn,
&abs_filenames)) {
return SDAS_FAILED_TO_CREATE_MAP;
}
if (fwrite("SIMPLE_ARCHIVE_VER", 1, 18, out_f) != 18) {
return SDAS_FAILED_TO_WRITE;
}
@ -832,6 +1047,7 @@ int simple_archiver_write_all(FILE *out_f, SDArchiverState *state,
state->count = 0;
state->max = filenames->count;
state->out_f = out_f;
state->map = abs_filenames;
fprintf(stderr, "Begin archiving...\n");
fprintf(stderr, "[%10u/%10u]\n", state->count, state->max);
if (simple_archiver_list_get(filenames, write_files_fn, state)) {
@ -1399,6 +1615,14 @@ int simple_archiver_parse_archive_info(FILE *in_f, int do_extract,
}
} else {
// Is a symbolic link.
int abs_preferred = (buf[1] & 0x4) != 0 ? 1 : 0;
fprintf(stderr, " Absolute path is %s\n",
(abs_preferred ? "preferred" : "NOT preferred"));
__attribute__((cleanup(free_malloced_memory))) void *abs_path = NULL;
__attribute__((cleanup(free_malloced_memory))) void *rel_path = NULL;
if (fread(&u16, 2, 1, in_f) != 1) {
return SDAS_INVALID_FILE;
}
@ -1411,15 +1635,15 @@ int simple_archiver_parse_archive_info(FILE *in_f, int do_extract,
}
buf[1023] = 0;
fprintf(stderr, " Link absolute path: %s\n", buf);
abs_path = malloc((size_t)u16 + 1);
strncpy(abs_path, (char *)buf, (size_t)u16 + 1);
} else {
__attribute__((cleanup(free_malloced_memory))) void *heap_buf =
malloc(u16 + 1);
unsigned char *uc_heap_buf = heap_buf;
if (fread(uc_heap_buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
abs_path = malloc(u16 + 1);
if (fread(abs_path, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
return SDAS_INVALID_FILE;
}
uc_heap_buf[u16 - 1] = 0;
fprintf(stderr, " Link absolute path: %s\n", uc_heap_buf);
((char *)abs_path)[u16 - 1] = 0;
fprintf(stderr, " Link absolute path: %s\n", (char *)abs_path);
}
if (fread(&u16, 2, 1, in_f) != 1) {
@ -1434,15 +1658,62 @@ int simple_archiver_parse_archive_info(FILE *in_f, int do_extract,
}
buf[1023] = 0;
fprintf(stderr, " Link relative path: %s\n", buf);
rel_path = malloc((size_t)u16 + 1);
strncpy(rel_path, (char *)buf, (size_t)u16 + 1);
} else {
__attribute__((cleanup(free_malloced_memory))) void *heap_buf =
malloc(u16 + 1);
unsigned char *uc_heap_buf = heap_buf;
if (fread(uc_heap_buf, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
rel_path = malloc(u16 + 1);
if (fread(rel_path, 1, u16 + 1, in_f) != (size_t)u16 + 1) {
return SDAS_INVALID_FILE;
}
uc_heap_buf[u16 - 1] = 0;
fprintf(stderr, " Link relative path: %s\n", uc_heap_buf);
((char *)rel_path)[u16 - 1] = 0;
fprintf(stderr, " Link relative path: %s\n", (char *)rel_path);
}
if (do_extract) {
simple_archiver_helper_make_dirs((const char *)out_f_name);
if (abs_path && rel_path) {
if (abs_preferred) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
int ret = symlink(abs_path, out_f_name);
if (ret == -1) {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
#endif
} else {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
int ret = symlink(rel_path, out_f_name);
if (ret == -1) {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
#endif
}
} else if (abs_path) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
int ret = symlink(abs_path, out_f_name);
if (ret == -1) {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
#endif
} else if (rel_path) {
#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \
SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX
int ret = symlink(rel_path, out_f_name);
if (ret == -1) {
return SDAS_FAILED_TO_EXTRACT_SYMLINK;
}
#endif
} else {
fprintf(
stderr,
"WARNING: Symlink entry in archive has no paths to link to!\n");
}
}
}
}

View file

@ -21,6 +21,7 @@
#include <stdio.h>
#include "data_structures/hash_map.h"
#include "data_structures/linked_list.h"
#include "parser.h"
@ -30,6 +31,7 @@ typedef struct SDArchiverState {
unsigned int flags;
const SDArchiverParsed *parsed;
FILE *out_f;
SDArchiverHashMap *map;
unsigned int count;
unsigned int max;
} SDArchiverState;
@ -42,7 +44,9 @@ enum SDArchiverStateReturns {
SDAS_NO_DECOMPRESSOR,
SDAS_INVALID_PARSED_STATE,
SDAS_INVALID_FILE,
SDAS_INTERNAL_ERROR
SDAS_INTERNAL_ERROR,
SDAS_FAILED_TO_CREATE_MAP,
SDAS_FAILED_TO_EXTRACT_SYMLINK
};
/// Returned pointer must not be freed.

View file

@ -151,6 +151,8 @@ void simple_archiver_print_usage(void) {
"file's stored decompressor\n");
fprintf(stderr, "--overwrite-create : allows overwriting an archive file\n");
fprintf(stderr, "--overwrite-extract : allows overwriting when extracting\n");
fprintf(stderr,
"--no-abs-symlink : do not store absolute paths for symlinks\n");
fprintf(stderr,
"-- : specifies remaining arguments are files to archive/extract\n");
fprintf(
@ -243,6 +245,8 @@ int simple_archiver_parse_args(int argc, const char **argv,
out->flags |= 0x4;
} else if (strcmp(argv[0], "--overwrite-extract") == 0) {
out->flags |= 0x8;
} else if (strcmp(argv[0], "--no-abs-symlink") == 0) {
out->flags |= 0x20;
} else if (argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == 0) {
is_remaining_args = 1;
} else if (argv[0][0] != '-') {

View file

@ -30,6 +30,7 @@ typedef struct SDArchiverParsed {
/// 0b xxxx x1xx - Allow create archive overwrite.
/// 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.
unsigned int flags;
/// Null-terminated string.
char *filename;