From 6a65f0c024c56caa0a9f81db42f4881be6838f1d Mon Sep 17 00:00:00 2001 From: Chloe M Date: Wed, 19 Aug 2026 21:54:30 -0500 Subject: project: core -> packer Signed-off-by: Chloe M --- core/.bop/posthook.sh | 1 - core/rdar.c | 453 ------------------------------------------------ packer/.bop/posthook.sh | 1 + packer/rdar.c | 453 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 454 insertions(+), 454 deletions(-) delete mode 100644 core/.bop/posthook.sh delete mode 100644 core/rdar.c create mode 100644 packer/.bop/posthook.sh create mode 100644 packer/rdar.c diff --git a/core/.bop/posthook.sh b/core/.bop/posthook.sh deleted file mode 100644 index 3f8d158..0000000 --- a/core/.bop/posthook.sh +++ /dev/null @@ -1 +0,0 @@ -clang core/*.c -o rdar diff --git a/core/rdar.c b/core/rdar.c deleted file mode 100644 index ba67c69..0000000 --- a/core/rdar.c +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Copyright (c) 2026, Chloe M. - * Provided under the BSD-3 clause. - */ - -#define _DEFAULT_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Forward declarations */ -struct idata_desc; - -/* Data descriptor constants */ -#define RDAR_MAX_PATHLEN 32 -#define RDAR_MAGIC ">RDAR!" -#define RDAR_MAGIC_LEN 6 - -/* Data descriptor file flags */ -#define DATA_ROOT (1 << 0) /* Is root data descriptor */ -#define DATA_AVL (1 << 1) /* Available to consumer software; not touched by us */ - -/* Version conversion macros */ -#define VERSION_U16(MAJOR, MINOR) \ - (((MINOR) << 8) | (MAJOR)) -#define VERSION_MAJOR(VERSION) \ - ((VERSION) & 0xFF) -#define VERSION_MINOR(VERSION) \ - (((VERSION) >> 8) & 0xFF) - -/* Pushes idata descriptor to table */ -#define IDATA_PUSH(IDATA_P) \ - ++d_desc_count; \ - TAILQ_INSERT_TAIL(&d_desc_table, (IDATA_P), link); - -/* - * Software version - * - * XXX: We encode it as a U16 so that we may bundle it - * with the file data as binary. - */ -#define RDAR_VERSION VERSION_U16(1, 0) /* v1.0 */ - -/* Fallback path if '-o' is not specified */ -#define FALLBACK_OUTPUT_PATH "out.rdar" - -/* Globals */ -static const char *input_dir = NULL; -static const char *output_path = NULL; -static TAILQ_HEAD(, idata_desc) d_desc_table; -static size_t d_desc_count = 0; - -/* - * The root data region (RDR) contains information about the - * entire archive. - * - * @desc_count: Number of descriptors - * @revision: Format revision - */ -struct root_data_region { - size_t desc_count; - uint16_t revision; -}; - -/* - * Represents a file that has been mapped into memory - * - * @data: File data - * @length: Length of data - */ -struct file_view { - char *data; - size_t length; -}; - -/* - * A data descriptor holds information about a specific - * file along with an offset to which it is located at - * in the offset. - * - * @magic: Format magic - * @path: File path - * @off: File data offset - * @flags: File flags - * @length: File length in bytes - */ -struct data_desc { - char magic[RDAR_MAGIC_LEN]; - char path[RDAR_MAX_PATHLEN]; - off_t off; - uint16_t flags; - size_t length; -}; - -/* - * Internal data descriptor, used to hold data - * before being flushed to the archive - * - * @data: Actual data descriptor - * @file: File view this descriptor references - * @link: TAILQ link - */ -struct idata_desc { - struct data_desc data; - struct file_view file; - TAILQ_ENTRY(idata_desc) link; -}; - -static void -help(void) -{ - printf("usage: ./rdar <... flags>\n"); - printf("[-h] Display this help menu\n"); - printf("[-v] Display the program version\n"); - printf("[-i] Input directory to pack\n"); - printf("[-o] Output path for archive file\n"); -} - -static void -version(void) -{ - uint16_t Major, Minor; - - Major = VERSION_MAJOR(RDAR_VERSION); - Minor = VERSION_MINOR(RDAR_VERSION); - printf("Version v%d.%d\n", Major, Minor); -} - -/* - * Destroy a file view - * - * @view: View to destory - */ -static void -view_destroy(struct file_view *view) -{ - munmap(view->data, view->length); - view->data = NULL; - view->length = 0; -} - -/* - * Create a mapped file view from a file path - */ -static int -view_from_path(const char *path, struct file_view *result) -{ - struct stat sb; - int fd; - - if (path == NULL || result == NULL) { - return -1; - } - - fd = open(path, O_RDONLY); - if (fd < 0) { - printf("fatal: failed to open '%s'\n", path); - perror("open"); - return -1; - } - - if (stat(path, &sb) < 0) { - printf("fatal: failed to stat '%s'\n", path); - perror("stat"); - close(fd); - return -1; - } - - result->data = mmap( - NULL, - sb.st_size, - PROT_READ, - MAP_SHARED, - fd, - 0 - ); - - if (result->data == NULL) { - printf("fatal: failed to map '%s'\n", path); - perror("mmap"); - close(fd); - return -1; - } - - result->length = sb.st_size; - close(fd); - return 0; -} - -/* - * Allocate an internal data descriptor - * - * @path: Path of file - * @is_root: If set, don't create a view - */ -static struct idata_desc * -idata_from_file(const char *path, bool is_root) -{ - struct idata_desc *idata_desc; - struct data_desc *data_desc; - size_t path_len; - const char *p; - - path_len = strlen(path); - if (path_len >= RDAR_MAX_PATHLEN - 1) { - printf("fatal: path '%s' is too long!\n", path); - return NULL; - } - - idata_desc = malloc(sizeof(*idata_desc)); - if (idata_desc == NULL) { - printf("fatal: failed to allocate idata_desc\n"); - printf("* last file processed: %s\n", path); - return NULL; - } - - if (!is_root) { - if (view_from_path(path, &idata_desc->file) < 0) { - free(idata_desc); - return NULL; - } - } - - p = path; - - /* Strip leading path stuff */ - while (*p == '.' && *p != '\0') - ++p; - while (*p == '/' && *p != '\0') - ++p; - - /* Initialize the data descriptor */ - data_desc = &idata_desc->data; - memcpy(data_desc->magic, RDAR_MAGIC, RDAR_MAGIC_LEN); - memcpy(data_desc->path, p, strlen(p)); - data_desc->path[path_len] = '\0'; - data_desc->flags = 0; - data_desc->off = 0; - return idata_desc; -} - -/* - * Push the root descriptor to the table. - * - * The root descriptor is to contain an offset into a special - * %root% file entry which contains data (e.g., version, file count) - * encoded within. - */ -static int -idata_push_root(void) -{ - struct idata_desc *root; - struct data_desc *desc; - - root = idata_from_file("%root%", true); - if (root == NULL) { - return -1; - } - - desc = &root->data; - desc->length = sizeof(struct root_data_region); - desc->flags = DATA_ROOT; - IDATA_PUSH(root); - return 0; -} - -static void -idata_table_destroy(void) -{ - struct idata_desc *desc, *next; - - desc = TAILQ_FIRST(&d_desc_table); - while (desc != NULL) { - view_destroy(&desc->file); - next = TAILQ_NEXT(desc, link); - TAILQ_REMOVE(&d_desc_table, desc, link); - desc = next; - } -} - -static void -flush_root_desc(struct data_desc *d_desc, int out_fd) -{ - struct root_data_region rdr = { - .desc_count = d_desc_count, - .revision = RDAR_VERSION - }; - - d_desc->off = lseek(out_fd, 0, SEEK_CUR); - write(out_fd, &rdr, sizeof(rdr)); -} - -static void -idata_table_flush(void) -{ - const struct file_view *view; - struct idata_desc *desc_iter; - struct data_desc *d_desc; - off_t new_off; - int fd; - - fd = open(output_path, O_WRONLY | O_TRUNC | O_CREAT, 0666); - if (fd < 0) { - printf("fatal: failed to open output path\n"); - perror("open"); - return; - } - - new_off = d_desc_count * sizeof(struct data_desc); - lseek(fd, new_off, SEEK_SET); - - /* First pass write the file data */ - TAILQ_FOREACH(desc_iter, &d_desc_table, link) { - view = &desc_iter->file; - d_desc = &desc_iter->data; - - /* - * Check if we need to flush the root descriptor, we ignore any files that start - * with the '%' modifier. - */ - if (d_desc->path[0] == '%') { - if (strcmp(d_desc->path, "%root%") == 0) - flush_root_desc(d_desc, fd); - - continue; - } - - d_desc->off = lseek(fd, 0, SEEK_CUR); - d_desc->length = view->length; - write(fd, view->data, view->length); - } - - /* Data descriptors go at the start */ - lseek(fd, 0, SEEK_SET); - - /* Second pass write the data descriptors */ - TAILQ_FOREACH(desc_iter, &d_desc_table, link) { - view = &desc_iter->file; - d_desc = &desc_iter->data; - write(fd, d_desc, sizeof(*d_desc)); - } - - close(fd); -} - -static void -recurse_dir(const char *dirpath) -{ - DIR *dir; - struct dirent *dirent; - struct idata_desc *idata_desc; - char pathbuf[264]; - - dir = opendir(dirpath); - if (dir == NULL) { - printf("fatal: failed to open '%s'\n", input_dir); - perror("opendir"); - return; - } - - while ((dirent = readdir(dir)) != NULL) { - if (dirent->d_name[0] == '.') { - continue; - } - - switch (dirent->d_type) { - case DT_REG: - snprintf(pathbuf, sizeof(pathbuf), "%s/%s", dirpath, dirent->d_name); - printf("[f] %s\n", pathbuf); - - idata_desc = idata_from_file(pathbuf, false); - if (idata_desc == NULL) { - printf("warn: ignoring '%s'\n", pathbuf); - break; - } - - IDATA_PUSH(idata_desc); - break; - case DT_DIR: - snprintf(pathbuf, sizeof(pathbuf), "%s/%s", dirpath, dirent->d_name); - printf("[d] %s\n", pathbuf); - recurse_dir(pathbuf); - break; - } - } - - closedir(dir); -} - -int -main(int argc, char **argv) -{ - int opt; - - if (argc < 2) { - printf("fatal: too few arguments\n"); - help(); - return -1; - } - - while ((opt = getopt(argc, argv, "hvi:o:")) != -1) { - switch (opt) { - case 'h': - help(); - return -1; - case 'v': - version(); - return -1; - case 'i': - input_dir = strdup(optarg); - if (input_dir == NULL) { - printf("fatal: failed to allocate input_dir\n"); - return -1; - } - - break; - case 'o': - output_path = strdup(optarg); - if (output_path == NULL) { - printf("fatal: failed to allocate output dir\n"); - return -1; - } - - break; - } - } - - if (input_dir == NULL) { - printf("fatal: input path not specified\n"); - help(); - return -1; - } - - if (output_path == NULL) { - output_path = FALLBACK_OUTPUT_PATH; - } - - TAILQ_INIT(&d_desc_table); - idata_push_root(); - recurse_dir(input_dir); - idata_table_flush(); - idata_table_destroy(); - return 0; -} diff --git a/packer/.bop/posthook.sh b/packer/.bop/posthook.sh new file mode 100644 index 0000000..42df38b --- /dev/null +++ b/packer/.bop/posthook.sh @@ -0,0 +1 @@ +clang packer/*.c -o rdar diff --git a/packer/rdar.c b/packer/rdar.c new file mode 100644 index 0000000..ba67c69 --- /dev/null +++ b/packer/rdar.c @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2026, Chloe M. + * Provided under the BSD-3 clause. + */ + +#define _DEFAULT_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Forward declarations */ +struct idata_desc; + +/* Data descriptor constants */ +#define RDAR_MAX_PATHLEN 32 +#define RDAR_MAGIC ">RDAR!" +#define RDAR_MAGIC_LEN 6 + +/* Data descriptor file flags */ +#define DATA_ROOT (1 << 0) /* Is root data descriptor */ +#define DATA_AVL (1 << 1) /* Available to consumer software; not touched by us */ + +/* Version conversion macros */ +#define VERSION_U16(MAJOR, MINOR) \ + (((MINOR) << 8) | (MAJOR)) +#define VERSION_MAJOR(VERSION) \ + ((VERSION) & 0xFF) +#define VERSION_MINOR(VERSION) \ + (((VERSION) >> 8) & 0xFF) + +/* Pushes idata descriptor to table */ +#define IDATA_PUSH(IDATA_P) \ + ++d_desc_count; \ + TAILQ_INSERT_TAIL(&d_desc_table, (IDATA_P), link); + +/* + * Software version + * + * XXX: We encode it as a U16 so that we may bundle it + * with the file data as binary. + */ +#define RDAR_VERSION VERSION_U16(1, 0) /* v1.0 */ + +/* Fallback path if '-o' is not specified */ +#define FALLBACK_OUTPUT_PATH "out.rdar" + +/* Globals */ +static const char *input_dir = NULL; +static const char *output_path = NULL; +static TAILQ_HEAD(, idata_desc) d_desc_table; +static size_t d_desc_count = 0; + +/* + * The root data region (RDR) contains information about the + * entire archive. + * + * @desc_count: Number of descriptors + * @revision: Format revision + */ +struct root_data_region { + size_t desc_count; + uint16_t revision; +}; + +/* + * Represents a file that has been mapped into memory + * + * @data: File data + * @length: Length of data + */ +struct file_view { + char *data; + size_t length; +}; + +/* + * A data descriptor holds information about a specific + * file along with an offset to which it is located at + * in the offset. + * + * @magic: Format magic + * @path: File path + * @off: File data offset + * @flags: File flags + * @length: File length in bytes + */ +struct data_desc { + char magic[RDAR_MAGIC_LEN]; + char path[RDAR_MAX_PATHLEN]; + off_t off; + uint16_t flags; + size_t length; +}; + +/* + * Internal data descriptor, used to hold data + * before being flushed to the archive + * + * @data: Actual data descriptor + * @file: File view this descriptor references + * @link: TAILQ link + */ +struct idata_desc { + struct data_desc data; + struct file_view file; + TAILQ_ENTRY(idata_desc) link; +}; + +static void +help(void) +{ + printf("usage: ./rdar <... flags>\n"); + printf("[-h] Display this help menu\n"); + printf("[-v] Display the program version\n"); + printf("[-i] Input directory to pack\n"); + printf("[-o] Output path for archive file\n"); +} + +static void +version(void) +{ + uint16_t Major, Minor; + + Major = VERSION_MAJOR(RDAR_VERSION); + Minor = VERSION_MINOR(RDAR_VERSION); + printf("Version v%d.%d\n", Major, Minor); +} + +/* + * Destroy a file view + * + * @view: View to destory + */ +static void +view_destroy(struct file_view *view) +{ + munmap(view->data, view->length); + view->data = NULL; + view->length = 0; +} + +/* + * Create a mapped file view from a file path + */ +static int +view_from_path(const char *path, struct file_view *result) +{ + struct stat sb; + int fd; + + if (path == NULL || result == NULL) { + return -1; + } + + fd = open(path, O_RDONLY); + if (fd < 0) { + printf("fatal: failed to open '%s'\n", path); + perror("open"); + return -1; + } + + if (stat(path, &sb) < 0) { + printf("fatal: failed to stat '%s'\n", path); + perror("stat"); + close(fd); + return -1; + } + + result->data = mmap( + NULL, + sb.st_size, + PROT_READ, + MAP_SHARED, + fd, + 0 + ); + + if (result->data == NULL) { + printf("fatal: failed to map '%s'\n", path); + perror("mmap"); + close(fd); + return -1; + } + + result->length = sb.st_size; + close(fd); + return 0; +} + +/* + * Allocate an internal data descriptor + * + * @path: Path of file + * @is_root: If set, don't create a view + */ +static struct idata_desc * +idata_from_file(const char *path, bool is_root) +{ + struct idata_desc *idata_desc; + struct data_desc *data_desc; + size_t path_len; + const char *p; + + path_len = strlen(path); + if (path_len >= RDAR_MAX_PATHLEN - 1) { + printf("fatal: path '%s' is too long!\n", path); + return NULL; + } + + idata_desc = malloc(sizeof(*idata_desc)); + if (idata_desc == NULL) { + printf("fatal: failed to allocate idata_desc\n"); + printf("* last file processed: %s\n", path); + return NULL; + } + + if (!is_root) { + if (view_from_path(path, &idata_desc->file) < 0) { + free(idata_desc); + return NULL; + } + } + + p = path; + + /* Strip leading path stuff */ + while (*p == '.' && *p != '\0') + ++p; + while (*p == '/' && *p != '\0') + ++p; + + /* Initialize the data descriptor */ + data_desc = &idata_desc->data; + memcpy(data_desc->magic, RDAR_MAGIC, RDAR_MAGIC_LEN); + memcpy(data_desc->path, p, strlen(p)); + data_desc->path[path_len] = '\0'; + data_desc->flags = 0; + data_desc->off = 0; + return idata_desc; +} + +/* + * Push the root descriptor to the table. + * + * The root descriptor is to contain an offset into a special + * %root% file entry which contains data (e.g., version, file count) + * encoded within. + */ +static int +idata_push_root(void) +{ + struct idata_desc *root; + struct data_desc *desc; + + root = idata_from_file("%root%", true); + if (root == NULL) { + return -1; + } + + desc = &root->data; + desc->length = sizeof(struct root_data_region); + desc->flags = DATA_ROOT; + IDATA_PUSH(root); + return 0; +} + +static void +idata_table_destroy(void) +{ + struct idata_desc *desc, *next; + + desc = TAILQ_FIRST(&d_desc_table); + while (desc != NULL) { + view_destroy(&desc->file); + next = TAILQ_NEXT(desc, link); + TAILQ_REMOVE(&d_desc_table, desc, link); + desc = next; + } +} + +static void +flush_root_desc(struct data_desc *d_desc, int out_fd) +{ + struct root_data_region rdr = { + .desc_count = d_desc_count, + .revision = RDAR_VERSION + }; + + d_desc->off = lseek(out_fd, 0, SEEK_CUR); + write(out_fd, &rdr, sizeof(rdr)); +} + +static void +idata_table_flush(void) +{ + const struct file_view *view; + struct idata_desc *desc_iter; + struct data_desc *d_desc; + off_t new_off; + int fd; + + fd = open(output_path, O_WRONLY | O_TRUNC | O_CREAT, 0666); + if (fd < 0) { + printf("fatal: failed to open output path\n"); + perror("open"); + return; + } + + new_off = d_desc_count * sizeof(struct data_desc); + lseek(fd, new_off, SEEK_SET); + + /* First pass write the file data */ + TAILQ_FOREACH(desc_iter, &d_desc_table, link) { + view = &desc_iter->file; + d_desc = &desc_iter->data; + + /* + * Check if we need to flush the root descriptor, we ignore any files that start + * with the '%' modifier. + */ + if (d_desc->path[0] == '%') { + if (strcmp(d_desc->path, "%root%") == 0) + flush_root_desc(d_desc, fd); + + continue; + } + + d_desc->off = lseek(fd, 0, SEEK_CUR); + d_desc->length = view->length; + write(fd, view->data, view->length); + } + + /* Data descriptors go at the start */ + lseek(fd, 0, SEEK_SET); + + /* Second pass write the data descriptors */ + TAILQ_FOREACH(desc_iter, &d_desc_table, link) { + view = &desc_iter->file; + d_desc = &desc_iter->data; + write(fd, d_desc, sizeof(*d_desc)); + } + + close(fd); +} + +static void +recurse_dir(const char *dirpath) +{ + DIR *dir; + struct dirent *dirent; + struct idata_desc *idata_desc; + char pathbuf[264]; + + dir = opendir(dirpath); + if (dir == NULL) { + printf("fatal: failed to open '%s'\n", input_dir); + perror("opendir"); + return; + } + + while ((dirent = readdir(dir)) != NULL) { + if (dirent->d_name[0] == '.') { + continue; + } + + switch (dirent->d_type) { + case DT_REG: + snprintf(pathbuf, sizeof(pathbuf), "%s/%s", dirpath, dirent->d_name); + printf("[f] %s\n", pathbuf); + + idata_desc = idata_from_file(pathbuf, false); + if (idata_desc == NULL) { + printf("warn: ignoring '%s'\n", pathbuf); + break; + } + + IDATA_PUSH(idata_desc); + break; + case DT_DIR: + snprintf(pathbuf, sizeof(pathbuf), "%s/%s", dirpath, dirent->d_name); + printf("[d] %s\n", pathbuf); + recurse_dir(pathbuf); + break; + } + } + + closedir(dir); +} + +int +main(int argc, char **argv) +{ + int opt; + + if (argc < 2) { + printf("fatal: too few arguments\n"); + help(); + return -1; + } + + while ((opt = getopt(argc, argv, "hvi:o:")) != -1) { + switch (opt) { + case 'h': + help(); + return -1; + case 'v': + version(); + return -1; + case 'i': + input_dir = strdup(optarg); + if (input_dir == NULL) { + printf("fatal: failed to allocate input_dir\n"); + return -1; + } + + break; + case 'o': + output_path = strdup(optarg); + if (output_path == NULL) { + printf("fatal: failed to allocate output dir\n"); + return -1; + } + + break; + } + } + + if (input_dir == NULL) { + printf("fatal: input path not specified\n"); + help(); + return -1; + } + + if (output_path == NULL) { + output_path = FALLBACK_OUTPUT_PATH; + } + + TAILQ_INIT(&d_desc_table); + idata_push_root(); + recurse_dir(input_dir); + idata_table_flush(); + idata_table_destroy(); + return 0; +} -- cgit v1.2.3