commit 17d46e6d155f2c24a562a495ad592bbdaa908b2e
parent 12eda77969230f52c43a249bde3d3bdad6a83715
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Sat, 3 Apr 2021 20:10:56 -0500
broken stuff
Diffstat:
M | Makefile | | | 6 | +++--- |
M | kiss.c | | | 17 | ++++++++++++++++- |
M | pkg.c | | | 212 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | pkg.h | | | 1 | + |
M | util.c | | | 6 | ++++++ |
M | util.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);