nkiss

WIP
git clone git://git.nihaljere.xyz/nkiss
Log | Files | Refs

commit 17d46e6d155f2c24a562a495ad592bbdaa908b2e
parent 12eda77969230f52c43a249bde3d3bdad6a83715
Author: Nihal Jere <nihal@nihaljere.xyz>
Date:   Sat,  3 Apr 2021 20:10:56 -0500

broken stuff

Diffstat:
MMakefile | 6+++---
Mkiss.c | 17++++++++++++++++-
Mpkg.c | 212+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpkg.h | 1+
Mutil.c | 6++++++
Mutil.h | 1+
6 files changed, 239 insertions(+), 4 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,8 +1,8 @@ CC = c99 -CFLAGS = -Wall -Werror -LFLAGS = -ltls +CFLAGS = -Werror +LFLAGS = -ltls -lz -SRC = alt.c http.c kiss.c manifest.c pkg.c sha256.c util.c +SRC = alt.c http.c kiss.c manifest.c pkg.c sha256.c tar.c util.c OBJ = $(SRC:.c=.o) EXE = nkiss diff --git a/kiss.c b/kiss.c @@ -11,6 +11,9 @@ #include "alt.h" #include "util.h" +#include <stdint.h> +#include "sha256.h" + char *argv0; void @@ -74,6 +77,17 @@ kiss_download_names(char *kiss_path, char *names[], int len) } void +kiss_install_names(char *kiss_path, char *names[], int len) +{ + char *repo, kp[KISS_PATH_MAX], path[PATH_LEN], binpath[PATH_LEN]; + + if (strlen(kiss_path) >= KISS_PATH_MAX) + die("%s: KISS_PATH too long", __func__); + + pkg_install(names[0]); +} + +void kiss_checksum_names(char *kiss_path, char *names[], int len) { char *repo, kp[KISS_PATH_MAX], path[PATH_LEN]; @@ -220,12 +234,13 @@ main(int argc, char *argv[]) kiss_checksum_names(kiss_path, &argv[2], argc-2); } else if (strcmp(argv[1], "remove") == 0 && argc > 2) { kiss_remove_names(&argv[2], argc-2); + } else if (strcmp(argv[1], "install") == 0 && argc > 2) { + kiss_install_names(kiss_path, &argv[2], argc-2); } else if (strcmp(argv[1], "alt") == 0) { if (argc == 2) kiss_alt(); else if (argc == 4) kiss_alt_swap(argv[2], argv[3]); } - return 0; } diff --git a/pkg.c b/pkg.c @@ -1,20 +1,232 @@ +#define _XOPEN_SOURCE 500 + #include <dirent.h> #include <errno.h> #include <fnmatch.h> +#include <ftw.h> #include <libgen.h> #include <stdlib.h> #include <stdint.h> #include <stdio.h> #include <string.h> +#include <string.h> +#include <sys/types.h> #include <unistd.h> +#include <zlib.h> #include "common.h" #include "http.h" #include "manifest.h" #include "pkg.h" #include "sha256.h" +#include "tar.h" #include "util.h" +/* This function can be passed into nftw to recursive delete all files in a + * directory */ +static int +rm(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) +{ + if (typeflag & FTW_DP) { + if (rmdir(fpath) == -1) + warn("%s: failed to remove %s:", __func__, fpath); + } else if (typeflag & (FTW_F | FTW_SL)) { + if (unlink(fpath) == -1) { + warn("%s: failed to remove %s:", __func__, fpath); + } + } + return 0; +} + +static char **tblist; +static size_t tbc; +static size_t tbmax; + +/* loads package tarball file names into tblist */ +static int +tbload(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) +{ + const char *installpath = fpath+1; + char **temp; + if (strcmp(fpath, ".") == 0) + return 0; + + if (tbc == tbmax) { + if ((temp = realloc(tblist, tbmax*2*sizeof(char *))) == NULL) + return -1; + tblist = temp; + tbmax *= 2; + } + + /* we want to add a trailing slash on dirs, as the manifest has these */ + if (typeflag & FTW_D) { + tblist[tbc] = malloc(strlen(fpath) + 2); + if (tblist[tbc] == NULL) + return -1; + + strcpy(tblist[tbc], fpath+1); + strcat(tblist[tbc], "/"); + } + else { + tblist[tbc] = strdup(fpath+1); + if (tblist[tbc] == NULL) + return -1; + } + + tbc++; + return 0; +} + +void +pkg_install(char *binpath) +{ + gzFile gz; + char cwd[PATH_LEN], cachedir[PATH_LEN], epath[PATH_LEN], + mpath[PATH_LEN], name[PKG_NAME_MAX]; + char *e, **temp, **pptr; + struct diff_t tbdiff, idiff; + struct manifest_t tbmanifest, imanifest; + struct tar_t *archive = NULL; + + tbmanifest.start = NULL; + imanifest.start = NULL; + + get_cache_dir(cachedir); + + if (getcwd(cwd, PATH_LEN) == NULL) + die("%s: cwd path too long", __func__); + + if (snprintf(epath, PATH_LEN, "%s/proc/%d/extract", cachedir, getpid()) > PATH_LEN) + die("%s: extract path too long", __func__); + + /* TODO it would be better to do this in kiss_install as it can be given + * the name and would have to derive the binpath from that, and we would + * just be repeating work */ + /* we need package name to construct path to manifest... */ + if ((e = strchr(binpath, '@')) == NULL) + die("%s: invalid binpath %s", __func__, binpath); + memcpy(name, binpath, e - binpath); + name[e - binpath] = '\0'; + + if (snprintf(mpath, PATH_LEN, ".%s/%s/manifest", KISS_INSTALLED, name) > PATH_LEN) + die("%s: manifest path too long", __func__); + + if ((gz = gzopen(binpath, "r")) == NULL) + die("%s: failed to open gz archive %s", __func__, binpath); + + if (tar_read(gz, &archive, 1) < 0) { + tar_free(archive); + die("%s: failed to read tar archive %s", __func__, binpath); + } + + mkdirs(epath); + + /* we chdir so we can use a relative path in nftw */ + chdir(epath); + + tar_extract(gz, archive, 0, NULL, 0); + + if (manifest_open(&tbmanifest, mpath) == -1) + die("%s: failed to open tarball manifest %s", __func__, mpath); + + /* TODO use list implementation for this */ + if ((tblist = malloc(MANIFEST_LINES*sizeof(char *))) == NULL) + die("%s: tarball initial malloc:", __func__); + + tbmax = MANIFEST_LINES; + tbc = 0; + + /* load a list of files in the tarball */ + if (nftw(".", &tbload, 100, FTW_DEPTH | FTW_PHYS) == -1) + die("%s: failed to walk tarball tree\n", __func__); + + /* shrink to what is necessary */ + if ((temp = realloc(tblist, (tbc+1)*sizeof(char *))) == NULL) + die("%s: tarball shrink realloc:", __func__); + + tblist = temp; + + qsort(tblist, tbc, sizeof(char *), &prstrcmpcb); + + if (diff(&tbdiff, tbmanifest.lines, tblist) == -1) + die("%s: failed to generate diff for %s", __func__, name); + + if (*tbdiff.left != NULL || *tbdiff.right != NULL) + die("%s: tarball doesn't match manifest for %s", __func__, name); + + /* At this point, we know the tarball is a valid kiss package, so we can + * proceed to installation. We use the same installation method as kiss. */ + + /* This means that we first generate a diff between the manifest for the + * installed package and the new one. */ + if (manifest_open(&imanifest, mpath+1) == -1) + die("%s: failed to open installed manifest %s", __func__, mpath); + + if (diff(&idiff, imanifest.lines, tbmanifest.lines) == -1) + die("%s: failed to generate diff for %s", __func__, name); + + /* We then swap to the new version of files which are shared between the + * manifests. */ + for (pptr = idiff.both; *pptr != NULL; pptr++) { + int l = strlen(*pptr); + + /* Skip over directories, as directories shared between the old and new + * packages should already exist. TODO Maybe we should have a mode that + * forces directory creation in case of a corrupt package or something + */ + if ((*pptr)[l-1] == '/') + continue; + + printf("installing %s\n", *pptr); + } + + /* Next, we remove files that are in the old manifest but not the new + * one. */ + for (pptr = idiff.left; *pptr != NULL; pptr++) { + int l = strlen(*pptr); + + if ((*pptr)[l-1] == '/') { + if (rmdir(*pptr) == -1) + warn("%s: failed to remove %s:", __func__, pptr); + continue; + } + + if (unlink(*pptr) == -1) + warn("%s: failed to remove %s:", __func__, pptr); + } + + /* Finally we install files that are in the new manifest but not the old + * one. */ + /* TODO alternatives */ + for (pptr = idiff.right; *pptr != NULL; pptr++) { + int l = strlen(*pptr); + + if ((*pptr)[l-1] == '/') { + if (mkdir(*pptr) == -1) + warn("%s: failed to install %s:", __func__, pptr); + continue; + } + + if (cp(*pptr) == -1) + warn("%s: failed to install %s:", __func__, pptr); + } + + /* Since mmap is used to load manifests into memory, we wait until the end + * to swap out the manifest, as it is unspecified what happens if the + * underlying file changes. */ + + /* cleanup */ + for (int i = 0; i < tbc; i++) + free(tblist[i]); + free(tblist); + + manifest_close(&imanifest); + manifest_close(&tbmanifest); + chdir(cwd); + tar_free(archive); + gzclose(gz); +} + void pkg_remove(char *name) { diff --git a/pkg.h b/pkg.h @@ -8,6 +8,7 @@ struct source_t { char checksum[HASH_LEN]; }; +void pkg_install(char *binpath); void pkg_remove(char *name); void pkg_retrieve(struct source_t *sources); int pkg_sources(char *repo, char *name, struct source_t *); diff --git a/util.c b/util.c @@ -54,6 +54,12 @@ die(const char *fmt, ...) } int +prstrcmpcb(const void *p1, const void *p2) +{ + return -strcmp(*((const char **) p1), *((const char **) p2)); +} + +int strcmpcb(const void *p1, const void *p2) { return strcmp((const char *) p1, (const char *) p2); diff --git a/util.h b/util.h @@ -19,6 +19,7 @@ struct diff_t { void warn(const char *, ...); void die(const char *, ...); +int prstrcmpcb(const void *p1, const void *p2); int strcmpcb(const void *p1, const void *p2); void strrepl(char *str, const char a, const char b); int mkdirs(const char *path);