From 70c0620cfe058ac3ec31c599ea79634d03f069dc Mon Sep 17 00:00:00 2001 From: Emmaker Date: Thu, 13 Mar 2025 20:56:39 -0400 Subject: [PATCH] Begin zyb/extract --- .gitignore | 2 - include/zyb/extract.h | 28 ++++++++ include/zyb/header.h | 58 ++++++++++++++++ meson.build | 22 ++++-- {src => source}/crypt/LICENSE | 0 {src => source}/crypt/sha256.c | 0 {src => source}/crypt/utils.c | 0 source/zyb/extract.c | 119 +++++++++++++++++++++++++++++++++ 8 files changed, 220 insertions(+), 9 deletions(-) delete mode 100644 .gitignore create mode 100644 include/zyb/extract.h create mode 100644 include/zyb/header.h rename {src => source}/crypt/LICENSE (100%) rename {src => source}/crypt/sha256.c (100%) rename {src => source}/crypt/utils.c (100%) create mode 100644 source/zyb/extract.c diff --git a/.gitignore b/.gitignore deleted file mode 100644 index a5fe92c..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Meson build output -build diff --git a/include/zyb/extract.h b/include/zyb/extract.h new file mode 100644 index 0000000..5474026 --- /dev/null +++ b/include/zyb/extract.h @@ -0,0 +1,28 @@ +#ifndef ZYB_EXTRACT_H +#define ZYB_EXTRACT_H + +// libc +#include + +// libzyb +#include + +// Extract leader from an open zyb +int zyb_extract_leader(FILE *fptr, struct lead *leader); + +// Extract (next) dependency from an open zyb +int zyb_extract_dependency(FILE *fptr, struct dependency *dep); + +// Extract full header from an open zyb +int zyb_extract_header(FILE *fptr, struct zyb_header *header); + +// Extract (next) file from an open zyb +int zyb_extract_file(FILE *fptr, char *dir); + +// Extract full zyb archive +int zyb_extract_FILE(FILE *fptr, char *dir); + +// Extract full zyb archive at `file` +int zyb_extract(char *file, char *dir); + +#endif diff --git a/include/zyb/header.h b/include/zyb/header.h new file mode 100644 index 0000000..05d6d09 --- /dev/null +++ b/include/zyb/header.h @@ -0,0 +1,58 @@ +#ifndef ZYB_HEADERS_H +#define ZYB_HEADERS_H + +// libc +#include + +#define ZYB_MAGIC "ZYB\0" +#define ZYB_DEP_MAGIC "DEP\0" +#define ZYB_FILE_MAGIC "ZIP\0" +#define ZYB_FORMAT_VER 1 +#define ZYB_NAME_LENGTH 100 + +/* + * This struct is the very first thing in any .zyb file. + * It contains package metadata, such as the name and dependencies. + */ +struct __attribute__((__packed__, aligned(2))) zyb_header { + /* + * This struct is the first part of the header, + * containing critical information about the package. + */ + struct __attribute__((__packed__, aligned(2))) lead { + char magic[4]; // Magic (ZYB_MAGIC) + unsigned short version; // Format version (ZYB_FORMAT_VER) + char arch[6]; // Magic identifying arch + char os[6]; // Magic identifying os + char name[ZYB_NAME_LENGTH]; // Name of the package + unsigned short major, minor, patch; // SemVer + unsigned short dependencies; // Number of dependencies + unsigned char reserved[2]; // Padding + } leader; + /* + * This struct contains information about each dependency. + * The number of these struct should EXACTLY be lead.dependencies. + */ + struct __attribute__((__packed__, aligned(2))) dependency { + char magic[4]; // Magic (ZYB_DEP_MAGIC) + char name[ZYB_NAME_LENGTH]; // Name of the dependency + unsigned short major, minor, patch; // Minimum SemVer + unsigned char reserved[2]; // Padding + } dependencies[0]; +}; + +/* + * This struct appends every single file in the .zyb. + * It contains metadata for the individual files in the package. + * WARNING: CHECKSUM IS FOR THE UNZIPPED FILE. + */ +struct __attribute__((__packed__, aligned(2))) zyb_file_header { + char magic[4]; // Magic (ZYB_FILE_MAGIC) + char *path; // Destination path of the file + mode_t permissions; // UNIX permission attributes (32 bits) + off_t length; // File length (64 bits) + unsigned char checksum[32]; // Checksum of the unzipped file + unsigned char reserved[2]; // Padding +}; + +#endif diff --git a/meson.build b/meson.build index 5d5c499..cacee18 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,5 @@ -project('libzepkg', 'c') +project('libzepkg', 'c', + version : '0.1.0') cmake = import('cmake') pkg = import('pkgconfig') @@ -6,19 +7,26 @@ pkg = import('pkgconfig') # The minizip-ng project, used for gzipping files minizip_ng = cmake.subproject('minizip-ng').dependency('minizip') +# External dependencies +glib = dependency('glib-2.0', method : 'pkg-config', required : true) +gio = dependency('gio-unix-2.0', method : 'pkg-config', required : true) +ostree = dependency('ostree-1', method : 'pkg-config', required : true) + files = files( - 'src/crypt/sha256.c', - 'src/crypt/utils.c') + 'source/crypt/sha256.c', + 'source/crypt/utils.c', + + 'source/zyb/extract.c') include = include_directories('include') -# Build the library (statically) +# Build the library libzepkg = static_library('zepkg', files, include_directories : include, - dependencies : [minizip_ng]) + dependencies : [ minizip_ng, glib, gio, ostree ]) # Generate a pkg-config file pkg.generate( - libraries : [libzepkg], - version : '0.1.0', + libraries : [ libzepkg ], + subdirs : [ 'zyb' ], name : 'libzepkg', description : 'Library for extracting zyb files, and interacting with the zepkg database.') diff --git a/src/crypt/LICENSE b/source/crypt/LICENSE similarity index 100% rename from src/crypt/LICENSE rename to source/crypt/LICENSE diff --git a/src/crypt/sha256.c b/source/crypt/sha256.c similarity index 100% rename from src/crypt/sha256.c rename to source/crypt/sha256.c diff --git a/src/crypt/utils.c b/source/crypt/utils.c similarity index 100% rename from src/crypt/utils.c rename to source/crypt/utils.c diff --git a/source/zyb/extract.c b/source/zyb/extract.c new file mode 100644 index 0000000..62f0159 --- /dev/null +++ b/source/zyb/extract.c @@ -0,0 +1,119 @@ +#include + +// libc +#include +#include +#include + +// libzepkg +#include + +int zyb_extract_leader(FILE *fptr, struct lead *leader) { + if (fptr == NULL) + return 1; + + char magic[4]; + size_t bytes = fread(magic, 1, 4, fptr); + if (bytes != 4) + return 1; + + if (strcmp(magic, ZYB_MAGIC)) + return 1; + + bytes = fread(&leader + 4, 1, sizeof(struct lead) - 4, fptr); + if (bytes != sizeof(struct lead) - 4) + return 1; + + if (leader->version != ZYB_FORMAT_VER) + return 1; + + return 0; +} + +int zyb_extract_dependency(FILE *fptr, struct dependency *dep) { + if (fptr == NULL) + return 1; + + char magic[4]; + size_t bytes = fread(magic, 1, 4, fptr); + if (bytes != 4) + return 1; + + if (strcmp(magic, ZYB_DEP_MAGIC)) + return 1; + + bytes = fread(&dep + 4, 1, sizeof(struct dependency) - 4, fptr); + if (bytes != sizeof(struct dependency) - 4) + return 1; + + return 0; +} + +int zyb_extract_header(FILE *fptr, struct zyb_header *header) { + if (fptr == NULL) + return 1; + + struct lead leader; + int ret; + if ((ret = zyb_extract_leader(fptr, &leader))) + return ret; + header->leader = leader; + + if (header->leader.dependencies > 0) { + for (int i = 0; i < header->leader.dependencies; i++) { + header = realloc(header, sizeof(header) + sizeof(struct dependency)); + + struct dependency dep; + if ((ret = zyb_extract_dependency(fptr, &dep))) + return ret; + header->dependencies[i] = dep; + } + } + + return 0; +} + +int zyb_extract_file(FILE *fptr, char *dir) { + if (fptr == NULL) + return 1; + + char magic[4]; + size_t bytes = fread(magic, 1, 4, fptr); + if (bytes != 4) + return 1; + + if (strcmp(magic, ZYB_FILE_MAGIC)) + return 1; + + // Read path in chunks of 2 bytes (to account for alignment) + char *path = malloc(PATH_MAX); + for (int i = 0; i < PATH_MAX; i += 2) { + fread(path + i, 1, 2, fptr); + if (path[i] == '\0' || path[i + 1] == '\0') + goto eos; + } + // If it reaches this point, means the string is longer than PATH_MAX + return 1; + +eos: // End of string; + // Read rest of header + mode_t permissions; + bytes = fread(&permissions, sizeof(mode_t), 1, fptr); + if (bytes != sizeof(mode_t)) + return 1; + + off_t size; + bytes = fread(&size, sizeof(off_t), 1, fptr); + if (bytes != sizeof(off_t)) + return 1; + + unsigned char checksum[32]; + bytes = fread(&checksum, 1, 32, fptr); + if (bytes != 32) + return 1; + + // Move pointer 2 bytes to account for padding + fseek(fptr, 2, SEEK_CUR); + + return 0; +}