pm

barely a pack manager
git clone git://z3bra.org/pm
Log | Files | Refs | README | LICENSE

commit b4f5118c0b65eea1050fbce7eee1fda92749a1e2
parent 124b13d5f0ddef0e683c239655108a1fadb38c66
Author: z3bra <willyatmailoodotorg>
Date:   Mon Jan 11 14:12:47 2016

Refactoring and improvement over installation code

Fixed some issues regarding memory freeing, pack struct handling
and inconsistencies across functions names.

Pack will not get installed if they're already installed, unless
the -f flag (force) has been provided.

Deletion of packs will now occur in the following order:

* files installed
* version file
* content file

So that if an error occur, we're still able to list the pack
content, and try to fix errors by hand.

Diffstat:
 pm.c | 391 ++++++++++++++++++++++++++++++++------------------------------------
 1 file changed, 188 insertions(+), 203 deletions(-)

diff --git a/pm.c b/pm.c @@ -13,10 +13,10 @@ #include "arg.h" -#define PACKAGE_ROOT (getenv("ROOT")?getenv("ROOT"):"/ns/pm/rootfs") -#define PACKAGE_DATA (getenv("META")?getenv("META"):"/ns/pm/rootfs/metadata") -#define PACKAGE_BUFF_SIZE 8192 -#define PACKAGE_EXTENSION ".tar.bz2" +#define PACK_ROOT (getenv("ROOT")?getenv("ROOT"):"/ns/pm/rootfs") +#define PACK_DATA (getenv("META")?getenv("META"):"/ns/pm/rootfs/metadata") +#define PACK_BUFF_SIZE 8192 +#define PACK_EXTENSION ".tar.bz2" struct pack { char *path; @@ -37,7 +37,7 @@ enum { enum { ERR_INVALID_ACTION = 1, ERR_METADATA = 2, - ERR_PACKAGE_LOAD = 3, + ERR_PACK_LOAD = 3, ERR_DELETE = 4, ERR_INSPECT = 5, ERR_UNPACK = 6, @@ -47,18 +47,21 @@ void usage(char *name); int is_empty(char *dir); int mkdir_parents(char *dir, mode_t mode); char *base_name(char *path); -int inspect(char *name); -int list_archive(int fd, char *filename); -int list_local(int fd, char *packname); -int list_content(int fd, char *packname); -int metadata(char *datadir, struct pack *pack); -int write_on_disk(struct archive *a, struct archive *w); -int unpack(char *root, char *in); + +struct pack *pack_load(char *path); +void pack_free(struct pack *p); + +int inspect_system(int fd, char *datadir); +int inspect_files(int fd, char *datadir, char *packname); +int inspect(char *datadir, char *packname); + +int write_metadata(char *datadir, struct pack *pack); +int write_entry(struct archive *a, struct archive *w); +int unpack(struct archive *a, char *metafile, int mask); +int install(char *rootfs, char *datadir, struct pack *p, int overwrite); int delete_content(FILE *metafile); -int delete(const char *datadir, const char *rootfs, const char *name); -struct pack *pack_load_file(char *file); -struct pack *pack_load(char *name); -void pack_unload(struct pack *p); +int delete(const char *rootfs, const char *datadir, const char *name); + char *argv0; @@ -73,22 +76,23 @@ usage(char *name) * Returns 0 if a directory is empty, -1 otherwise */ int -is_empty(char *dir) +is_empty(char *path) { DIR *d; struct dirent *p; - if (!(d = opendir(dir))) { - perror(dir); + if (!(d = opendir(path))) { + perror(path); return -1; } while ((p = readdir(d))) - if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) { + if (!strcmp(p->d_name, ".") && !strcmp(p->d_name, "..")) { closedir(d); return -1; } + closedir(d); return 0; } @@ -98,13 +102,13 @@ is_empty(char *dir) * http://nion.modprobe.de/blog/archives/357-Recursive-directory-creation.html */ int -mkdir_parents(char *dir, mode_t mode) +mkdir_parents(char *path, mode_t mode) { char tmp[PATH_MAX] = ""; char *p = NULL; size_t len; - snprintf(tmp, sizeof(tmp),"%s",dir); + snprintf(tmp, sizeof(tmp), "%s", path); len = strlen(tmp); if(tmp[len - 1] == '/') tmp[len - 1] = 0; @@ -128,72 +132,18 @@ base_name(char *path) return b ? b + 1 : path; } -/* - * Inspect the system, either by looking into a pack, checking installed - * files, or listing packs actually installed - */ -int -inspect(char *name) -{ - struct stat st; - - /* name is NULL, list packs installed */ - if (!name) - return list_local(1, PACKAGE_DATA); - - /* if name isn't a filename, list installed files */ - if (stat(name, &st) < 0) - return list_content(1, name); - - /* otherwise, list the content of the pack */ - return list_archive(1, name); -} - - -/* - * Write the content of an archive to a filedes - */ -int -list_archive(int fd, char *filename) -{ - struct archive *a; - struct archive_entry *e; - int r; - - /* configure archive to support all types */ - a = archive_read_new(); - archive_read_support_filter_bzip2(a); - archive_read_support_format_tar(a); - - /* set blocksize to 0 as libarchive will choose the best size anyway */ - r = archive_read_open_filename(a, filename, 0); - if (r != ARCHIVE_OK) - return r; - - /* output each filename to stdout */ - while(archive_read_next_header(a, &e) == ARCHIVE_OK) { - dprintf(fd, "%s\n", archive_entry_pathname(e)); - } - - r = archive_read_free(a); - if (r != ARCHIVE_OK) - return r; - - return 0; -} - /* - * Write files installed by a pack to a filedes + * Write files installed by a pack to a file descriptor */ int -list_content(int fd, char *name) +inspect_files(int fd, char *datadir, char *packname) { int meta; char tmp[PATH_MAX] = ""; size_t len; - snprintf(tmp, PATH_MAX, "%s/%s/files", PACKAGE_DATA, name); + snprintf(tmp, PATH_MAX, "%s/%s/files", datadir, packname); if ((meta = open(tmp, O_RDONLY)) < 0) { perror(tmp); return -1; @@ -204,16 +154,15 @@ list_content(int fd, char *name) dprintf(fd, "%s", tmp); } - return 0; } /* - * Write packs installed in PACKAGE_ROOT to a filedes + * Write packs installed in datadir to a file descriptor */ int -list_local(int fd, char *datadir) +inspect_system(int fd, char *datadir) { DIR *d; struct dirent *p; @@ -232,12 +181,11 @@ list_local(int fd, char *datadir) datadir, p->d_name); if ((meta = open(tmp, O_RDONLY)) < 0) { - perror(tmp); - closedir(d); - return 1; + sprintf(tmp, "(unknown)"); + } else { + len = read(meta, tmp, 32); + tmp[len - 1] = 0; } - len = read(meta, tmp, 32); - tmp[len - 1] = 0; dprintf(fd, "%s\t%s\n", p->d_name, tmp); } @@ -247,60 +195,63 @@ list_local(int fd, char *datadir) /* - * Write metadata about a pack file - * - * TODO: - * + /deps /asdep + * Inspect the system, either by looking into a pack, checking installed + * files, or listing packs actually installed */ int -metadata(char *datadir, struct pack *pack) +inspect(char *datadir, char *packname) { - int fd, r; - struct stat st; - char tmp[PATH_MAX], *name; + /* name is NULL, list packs installed */ + if (!packname) + return inspect_system(1, datadir); + + /* otherwise, list files installed by a pack */ + return inspect_files(1, datadir, packname); +} - name = strdup(pack->path); - name = strtok(name, ":"); - sprintf(tmp, "%s/%s", datadir, base_name(name)); - free(name); +/* + * Write metadata about a pack file: + * + datadir/packname/version - version of the pack installed + */ +int +write_metadata(char *datadir, struct pack *p) +{ + int fd, r; + struct stat st; + char tmp[PATH_MAX]; + snprintf(tmp, PATH_MAX, "%s/%s", datadir, p->name); if (stat(tmp, &st) < 0) { - r = mkdir_parents(tmp, 0750); - if (r < 0) + if ((r = mkdir_parents(tmp, 0750)) < 0) return r; } - snprintf(tmp, PATH_MAX, "%s/%s/files", datadir, pack->name); - fd = open(tmp, O_CREAT|O_WRONLY|O_TRUNC, 0644); - if (fd < 0) { + snprintf(tmp, PATH_MAX, "%s/%s/version", datadir, p->name); + if ((fd = open(tmp, O_CREAT|O_WRONLY|O_TRUNC, 0644)) < 0) { perror(tmp); return -1; } - list_archive(fd, pack->path); - close(fd); - snprintf(tmp, PATH_MAX, "%s/%s/version", datadir, pack->name); - fd = open(tmp, O_CREAT|O_WRONLY|O_TRUNC, 0644); - if (fd < 0) { - perror(tmp); - return -1; - } - - r = write(fd, pack->version, strlen(pack->version)); + r = write(fd, p->version, strnlen(p->version, LINE_MAX)); r += write(fd, "\n", 1); if (r < 1) { perror(tmp); + close(fd); return -1; } - close(fd); + close(fd); return 0; } + +/* + * Write an archive entry on the disk, thus creating the file + */ int -write_on_disk(struct archive *a, struct archive *w) +write_entry(struct archive *a, struct archive *w) { int r; const void *buf; @@ -321,65 +272,111 @@ write_on_disk(struct archive *a, struct archive *w) } } + +int +unpack(struct archive *a, char *meta, int mask) +{ + int r, fd; + struct stat st; + struct archive *w; + struct archive_entry *e; + + if (stat(meta, &st) == 0 && (mask & ARCHIVE_EXTRACT_NO_OVERWRITE)) { + fprintf(stderr, "%s: File exists\n", meta); + return -1; + } + + if ((fd = open(meta, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) { + perror(meta); + return -1; + } + + w = archive_write_disk_new(); + archive_write_disk_set_options(w, mask); + + while ((r = archive_read_next_header(a, &e)) != ARCHIVE_EOF) { + if (r != ARCHIVE_OK) { + fprintf(stderr, "%s: %s\n", archive_entry_pathname(e), + archive_error_string(a)); + break; + } + + if ((r = archive_write_header(w, e)) != ARCHIVE_OK) { + fprintf(stderr, "%s: %s\n", archive_entry_pathname(e), + archive_error_string(w)); + break; + } + + if ((r = write_entry(a, w)) != ARCHIVE_OK) { + fprintf(stderr, "%s: %s\n", archive_entry_pathname(e), + archive_error_string(w)); + break; + } + + dprintf(fd, "%s\n", archive_entry_pathname(e)); + archive_write_finish_entry(w); + } + + archive_write_close(w); + archive_write_free(w); + + return r == ARCHIVE_EOF ? ARCHIVE_OK : r; +} + + /* - * Extract files into the given directory + * Install a pack to rootfs, writing metadata to datadir + * If overwrite is set to 1, it will overwrite all files */ int -unpack(char *root, char *in) +install(char *rootfs, char *datadir, struct pack *p, int overwrite) { struct archive *a; - struct archive *disk; - struct archive_entry *e; + char meta[PATH_MAX] = ""; int r; int mask = ARCHIVE_EXTRACT_PERM - |ARCHIVE_EXTRACT_NO_OVERWRITE |ARCHIVE_EXTRACT_SECURE_NODOTDOT; + if (overwrite == 0) + mask |= ARCHIVE_EXTRACT_NO_OVERWRITE; + + /* + * ensure we can chdir to the rootfs and write metadata + * to the datadir + */ + if (chdir(rootfs) < 0) { + perror(rootfs); + return -1; + } + + if (write_metadata(datadir, p) < 0) { + perror(datadir); + return -1; + } + a = archive_read_new(); archive_read_support_filter_bzip2(a); archive_read_support_format_tar(a); - disk = archive_write_disk_new(); - archive_write_disk_set_options(disk, mask); - - r = archive_read_open_filename(a, in, 0); - if (r != ARCHIVE_OK) { - fprintf(stderr, "%s\n", archive_error_string(a)); + /* try and open the tarball of our pack */ + if ((r = archive_read_open_filename(a, p->path, 0)) != ARCHIVE_OK) { + fprintf(stderr, "%s: %s\n", p->name, archive_error_string(a)); return r; } - /* extract the pack at the specified root */ - if (chdir(root) < 0) { - perror(root); - return -1; - } + snprintf(meta, PATH_MAX, "%s/%s/files", datadir, p->name); + r = unpack(a, meta, mask); - while ((r = archive_read_next_header(a, &e)) != ARCHIVE_EOF) { - if (r < ARCHIVE_OK) { - fprintf(stderr, "%s\n", archive_error_string(a)); - return r; - } - - r = archive_write_header(disk, e); - if (r != ARCHIVE_OK) { - fprintf(stderr, "%s\n", archive_error_string(a)); - return r; - } - if (write_on_disk(a, disk) != ARCHIVE_OK) { - fprintf(stderr, "%s\n", archive_error_string(disk)); - } - archive_write_finish_entry(disk); - } archive_read_close(a); archive_read_free(a); - return 0; + return r; } /* - * Delete entries listed in a file recursively - * This will also remove directories if they are empty + * Delete all entries listed in the given file. + * if the entry doesn't exist, it will be ignored */ int delete_content(FILE *f) @@ -389,7 +386,9 @@ delete_content(FILE *f) size_t len = 0; if (fgets(file, PATH_MAX, f)) - delete_content(f); + if (delete_content(f) < 0) { + return -1; + } /* remove trailing '\n' */ if ((len = strnlen(file, PATH_MAX)) == 0) @@ -411,50 +410,56 @@ delete_content(FILE *f) if (lstat(file, &st) < 0 && errno == ENOENT) return 0; - if (S_ISDIR(st.st_mode) && is_empty(file)) + if (S_ISDIR(st.st_mode) && is_empty(file) == 0) { return rmdir(file); - - if (unlink(file) < 0) { - perror(file); - return -1; } - return 0; + return unlink(file); } /* - * Delete all files related to a pack. Installed files, but also metadata + * Delete a pack from the system. + * This will read the file datadir/name/files, change directory to rootfs + * and call delete_content() */ int -delete(const char *datadir, const char *rootfs, const char *name) +delete(const char *rootfs, const char *datadir, const char *packname) { FILE *f; char meta[PATH_MAX]; - snprintf(meta, PATH_MAX, "%s/%s/version", datadir, name); - if (unlink(meta) < 0) - return ERR_DELETE; - - snprintf(meta, PATH_MAX, "%s/%s/files", datadir, name); + snprintf(meta, PATH_MAX, "%s/%s/files", datadir, packname); if ((f = fopen(meta, "r")) == NULL) { perror(meta); - return -1; + return ERR_DELETE; } if (chdir(rootfs) < 0) { perror(rootfs); - return -1; + return ERR_DELETE; } /* ignore errors so everything gets deleted */ - delete_content(f); + if (delete_content(f) < 0) { + fprintf(stderr, "%s: Could not remove pack\n", packname); + fclose(f); + return ERR_DELETE; + } fclose(f); - if (unlink(meta) < 0) + if (unlink(meta) < 0) { + perror(meta); + return ERR_DELETE; + } + + snprintf(meta, PATH_MAX, "%s/%s/version", datadir, packname); + if (unlink(meta) < 0) { + perror(meta); return ERR_DELETE; + } - snprintf(meta, PATH_MAX, "%s/%s", datadir, name); + snprintf(meta, PATH_MAX, "%s/%s", datadir, packname); return rmdir(meta); } @@ -463,20 +468,20 @@ delete(const char *datadir, const char *rootfs, const char *name) * Load a pack from a tarball and return a pack structure */ struct pack * -pack_load_file(char *path) +pack_load(char *path) { int fd; - struct pack *pack = malloc(sizeof(struct pack)); + struct pack *pack = NULL; char tmp[PATH_MAX] = ""; char *p; if ((fd = open(path, O_RDONLY)) < 0) { perror(path); - free(pack); return NULL; } close(fd); + pack = malloc(sizeof(struct pack)); pack->path = strdup(path); snprintf(tmp, PATH_MAX, "%s", base_name(path)); @@ -490,7 +495,7 @@ pack_load_file(char *path) *p = ':'; snprintf(tmp, PATH_MAX, "%s", p + 1); - for (p = tmp; *p && strcmp(p, PACKAGE_EXTENSION); p++); + for (p = tmp; *p && strcmp(p, PACK_EXTENSION); p++); if (!*p) { free(pack); return NULL; @@ -502,33 +507,12 @@ pack_load_file(char *path) return pack; } -/* - * Build a package structure from the argument - */ -struct pack * -pack_load(char *name) -{ - struct pack *p = NULL; - struct stat st; - - if (stat(name, &st) < 0) - return NULL; - - p = pack_load_file(name); - - if (!p) { - fprintf(stderr, "could not load pack %s\n", name); - return NULL; - } - - return p; -} /* * Free a pack structure */ void -pack_unload(struct pack *p) +pack_free(struct pack *p) { if (!p) return; @@ -542,6 +526,7 @@ pack_unload(struct pack *p) int main (int argc, char **argv) { + int overwrite = 0; char *n = NULL; struct pack *p = NULL; uint8_t action = ACTION_INSPECT; @@ -553,6 +538,9 @@ main (int argc, char **argv) case 'd': action = ACTION_DELETE; break; + case 'f': + overwrite = 1; + break; case 'i': action = ACTION_INSPECT; if (argc > 1) @@ -566,22 +554,19 @@ main (int argc, char **argv) case ACTION_INSTALL: while (*argv) { if ((p = pack_load(*(argv++)))) { - metadata(PACKAGE_DATA, p); - if (unpack(PACKAGE_ROOT, p->path) != 0) { - delete(PACKAGE_DATA, PACKAGE_ROOT, p->name); - } - pack_unload(p); + install(PACK_ROOT, PACK_DATA, p, overwrite); + pack_free(p); } } break; case ACTION_DELETE: while (*argv) - delete(PACKAGE_DATA, PACKAGE_ROOT, *argv++); + delete(PACK_ROOT, PACK_DATA, *argv++); break; case ACTION_INSPECT: - if (inspect(n) != 0) + if (inspect(PACK_DATA, n) != 0) return ERR_INSPECT; break;