pkgtools

morpheus pkg tools
git clone git://git.2f30.org/pkgtools.git
Log | Files | Refs | README | LICENSE

commit 155d25f88664709355a98723d96aa886180ed9d9
parent 945efae19305d4416f44b666b37340901d5055ce
Author: sin <sin@2f30.org>
Date:   Mon Jun 16 00:14:59 +0100

Add db interface

Diffstat:
Makefile | 2+-
db.c | 571+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
db.h | 28++++++++++++++++++++++++++++
infopkg.c | 109+++++++++++++++++++------------------------------------------------------------
installpkg.c | 304++++++-------------------------------------------------------------------------
lock.c | 32--------------------------------
removepkg.c | 262+++++++------------------------------------------------------------------------
strlcat.c | 17++++++++++++++++-
strlcpy.c | 17++++++++++++++++-
util.h | 2++
10 files changed, 704 insertions(+), 640 deletions(-)
diff --git a/Makefile b/Makefile @@ -4,7 +4,7 @@ include config.mk .SUFFIXES: .c .o LIB = \ - lock.o \ + db.o \ strlcat.o \ strlcpy.o diff --git a/db.c b/db.c @@ -0,0 +1,571 @@ +/* See LICENSE file for copyright and license details. */ +#define _XOPEN_SOURCE 500 +#include <archive.h> +#include <archive_entry.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <ftw.h> +#include <limits.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/file.h> +#include <sys/types.h> +#include <unistd.h> +#include "db.h" +#include "util.h" + +int fflag = 0; +int vflag = 0; + +struct dbentry { + struct pkg *pkg; + int deleted; + struct dbentry *next; +}; + +struct db { + DIR *pkgdir; + char prefix[PATH_MAX]; + char path[PATH_MAX]; + struct dbentry *head; +}; + +struct db * +dbinit(const char *prefix) +{ + struct db *db; + struct sigaction sa; + + db = malloc(sizeof(*db)); + if (!db) { + fprintf(stderr, "out of memory\n"); + return NULL; + } + db->head = NULL; + + realpath(prefix, db->prefix); + + estrlcpy(db->path, db->prefix, sizeof(db->path)); + estrlcat(db->path, "/var/pkg", sizeof(db->path)); + + db->pkgdir = opendir(db->path); + if (!db->pkgdir) { + fprintf(stderr, "opendir %s: %s\n", db->path, + strerror(errno)); + return NULL; + } + + if (flock(dirfd(db->pkgdir), LOCK_EX | LOCK_NB) < 0) { + if (errno == EWOULDBLOCK) + fprintf(stderr, "package db already locked\n"); + else + fprintf(stderr, "flock %s: %s\n", db->path, + strerror(errno)); + return NULL; + } + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigaction(SIGHUP, &sa, 0); + sigaction(SIGINT, &sa, 0); + sigaction(SIGQUIT, &sa, 0); + sigaction(SIGTERM, &sa, 0); + + return db; +} + +int +dbload(struct db *db) +{ + struct dbentry *de; + struct dirent *dp; + + while ((dp = readdir(db->pkgdir))) { + if (strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0) + continue; + de = malloc(sizeof(*de)); + if (!de) { + fprintf(stderr, "out of memory\n"); + return -1; + } + de->pkg = pkgnew(dp->d_name); + if (!de->pkg) + return -1; + if (dbpkgload(db, de->pkg) < 0) + return -1; + de->next = db->head; + db->head = de; + } + + return 0; +} + +struct pkg * +dbfind(struct db *db, const char *name) +{ + struct dbentry *de; + + for (de = db->head; de; de = de->next) + if (de->deleted == 0 && strcmp(de->pkg->name, name) == 0) + break; + if (!de) + return NULL; + return de->pkg; +} + +int +dbcollide(struct db *db, const char *name) +{ + char pkgpath[PATH_MAX]; + char path[PATH_MAX]; + struct archive *ar; + struct archive_entry *entry; + struct stat sb; + int ok = 0; + int r; + + realpath(name, pkgpath); + + ar = archive_read_new(); + + archive_read_support_filter_gzip(ar); + archive_read_support_format_tar(ar); + + r = archive_read_open_filename(ar, pkgpath, 10240); + if (r < 0) { + fprintf(stderr, "%s\n", archive_error_string(ar)); + return -1; + } + + while (1) { + r = archive_read_next_header(ar, &entry); + if (r == ARCHIVE_EOF) + break; + if (r != ARCHIVE_OK) { + fprintf(stderr, "%s\n", archive_error_string(ar)); + return -1; + } + estrlcpy(path, db->prefix, sizeof(path)); + estrlcat(path, "/", sizeof(path)); + estrlcat(path, archive_entry_pathname(entry), sizeof(path)); + if (access(path, F_OK) == 0) { + r = stat(path, &sb); + if (r < 0) { + fprintf(stderr, "lstat %s: %s\n", + archive_entry_pathname(entry), + strerror(errno)); + return -1; + } + if (S_ISDIR(sb.st_mode) == 0) { + fprintf(stderr, "%s exists\n", path); + ok = -1; + } + } + } + + archive_read_free(ar); + + return ok; +} + +int +dbadd(struct db *db, const char *name) +{ + char pkgpath[PATH_MAX], tmppath[PATH_MAX]; + char path[PATH_MAX]; + FILE *fp; + struct archive *ar; + struct archive_entry *entry; + int r; + + realpath(name, pkgpath); + + estrlcpy(tmppath, pkgpath, sizeof(tmppath)); + estrlcpy(path, db->path, sizeof(path)); + estrlcat(path, "/", sizeof(path)); + estrlcat(path, basename(tmppath), sizeof(path)); + + ar = archive_read_new(); + + archive_read_support_filter_gzip(ar); + archive_read_support_format_tar(ar); + + r = archive_read_open_filename(ar, pkgpath, 10240); + if (r < 0) { + fprintf(stderr, "%s\n", archive_error_string(ar)); + return -1; + } + + fp = fopen(path, "w"); + if (!fp) { + fprintf(stderr, "fopen %s: %s\n", path, + strerror(errno)); + return -1; + } + + while (1) { + r = archive_read_next_header(ar, &entry); + if (r == ARCHIVE_EOF) + break; + if (r != ARCHIVE_OK) { + fprintf(stderr, "%s\n", archive_error_string(ar)); + return -1; + } + estrlcpy(tmppath, db->prefix, sizeof(tmppath)); + estrlcat(tmppath, "/", sizeof(tmppath)); + estrlcat(tmppath, archive_entry_pathname(entry), sizeof(tmppath)); + if (vflag == 1) + printf("installed %s\n", tmppath); + fputs(archive_entry_pathname(entry), fp); + fputc('\n', fp); + } + + if (vflag == 1) + printf("updating %s\n", path); + fflush(fp); + r = fsync(fileno(fp)); + if (r < 0) + fprintf(stderr, "fsync: %s\n", strerror(errno)); + fclose(fp); + + archive_read_free(ar); + + return 0; +} + +int +dbwalk(struct db *db, int (*cb)(struct db *, struct pkg *, void *), void *data) +{ + struct dbentry *de; + int r; + + for (de = db->head; de; de = de->next) { + if (de->deleted == 1) + continue; + r = cb(db, de->pkg, data); + if (r < 0) + return -1; + /* terminate traversal */ + if (r > 0) + return 1; + } + return 0; +} + +int +dblinks(struct db *db, const char *path) +{ + struct dbentry *de; + struct pkgentry *pe; + int links = 0; + + for (de = db->head; de; de = de->next) { + if (de->deleted == 1) + continue; + for (pe = de->pkg->head; pe; pe = pe->next) + if (strcmp(pe->path, path) == 0) + links++; + } + return links; +} + +int +dbfree(struct db *db) +{ + struct dbentry *de, *tmp; + + de = db->head; + while (de) { + tmp = de->next; + pkgfree(de->pkg); + free(de); + de = tmp; + } + if (flock(dirfd(db->pkgdir), LOCK_UN) < 0) { + fprintf(stderr, "flock %s: %s\n", db->path, + strerror(errno)); + return -1; + } + closedir(db->pkgdir); + free(db); + return 0; +} + +void +dbdump(struct db *db) +{ + struct dbentry *de; + + for (de = db->head; de; de = de->next) { + if (de->deleted == 1) + continue; + puts(de->pkg->name); + } +} + +int +dbpkgload(struct db *db, struct pkg *pkg) +{ + struct pkgentry *pe; + FILE *fp; + char path[PATH_MAX]; + char *buf = NULL, *p; + size_t sz = 0; + + if (pkg->head) + return 0; + + estrlcpy(path, db->path, sizeof(path)); + estrlcat(path, "/", sizeof(path)); + estrlcat(path, pkg->name, sizeof(path)); + + fp = fopen(path, "r"); + if (!fp) { + fprintf(stderr, "fopen %s: %s\n", pkg->name, + strerror(errno)); + return -1; + } + + while (getline(&buf, &sz, fp) != -1) { + p = strrchr(buf, '\n'); + if (p) + *p = '\0'; + + if (buf[0] == '\0') { + fprintf(stderr, "malformed pkg file: %s\n", + path); + return -1; + } + + pe = malloc(sizeof(*pe)); + if (!pe) { + fprintf(stderr, "out of memory\n"); + return -1; + } + + estrlcpy(pe->path, db->prefix, sizeof(pe->path)); + estrlcat(pe->path, "/", sizeof(pe->path)); + estrlcat(pe->path, buf, sizeof(pe->path)); + + pe->next = pkg->head; + pkg->head = pe; + } + if (ferror(fp)) { + fprintf(stderr, "input error %s: %s\n", pkg->name, + strerror(errno)); + fclose(fp); + return -1; + } + + free(buf); + fclose(fp); + return 0; +} + +int +dbpkginstall(struct db *db, const char *name) +{ + char cwd[PATH_MAX]; + char pkgpath[PATH_MAX]; + struct archive *ar; + struct archive_entry *entry; + int flags; + int r; + + realpath(name, pkgpath); + + ar = archive_read_new(); + + archive_read_support_filter_gzip(ar); + archive_read_support_format_tar(ar); + + r = archive_read_open_filename(ar, pkgpath, 10240); + if (r < 0) { + fprintf(stderr, "%s\n", archive_error_string(ar)); + return -1; + } + + getcwd(cwd, sizeof(cwd)); + r = chdir(db->prefix); + if (r < 0) { + fprintf(stderr, "chdir %s: %s\n", db->prefix, + strerror(errno)); + return -1; + } + + while (1) { + r = archive_read_next_header(ar, &entry); + if (r == ARCHIVE_EOF) + break; + if (r != ARCHIVE_OK) { + fprintf(stderr, "%s\n", archive_error_string(ar)); + r = chdir(cwd); + if (r < 0) + fprintf(stderr, "chdir %s: %s\n", cwd, strerror(errno)); + return -1; + } + flags = ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | + ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_UNLINK | + ARCHIVE_EXTRACT_SECURE_NODOTDOT; + if (archive_read_extract(ar, entry, flags) != ARCHIVE_OK) + fprintf(stderr, "%s\n", archive_error_string(ar)); + } + + archive_read_free(ar); + + r = chdir(cwd); + if (r < 0) { + fprintf(stderr, "chdir %s: %s\n", cwd, strerror(errno)); + return -1; + } + + return 0; +} + +static int +rmemptydir(const char *f, const struct stat *sb, int typeflag, + struct FTW *ftwbuf) +{ + (void) sb; + (void) ftwbuf; + + if (typeflag == FTW_DP) { + if (vflag == 1) + printf("removing %s\n", f); + rmdir(f); + } + return 0; +} + +int +dbpkgremove(struct db *db, const char *name) +{ + struct dbentry *de; + struct pkg *pkg; + struct pkgentry *pe; + struct stat sb; + char tmppath[PATH_MAX], *p; + int r; + + estrlcpy(tmppath, name, sizeof(tmppath)); + p = basename(tmppath); + + for (de = db->head; de; de = de->next) { + if (de->deleted == 1) + continue; + if (strcmp(de->pkg->name, p) == 0) { + pkg = de->pkg; + break; + } + } + if (!de) { + fprintf(stderr, "%s is not installed\n", name); + return -1; + } + + for (pe = pkg->head; pe; pe = pe->next) { + r = lstat(pe->path, &sb); + if (r < 0) { + fprintf(stderr, "lstat %s: %s\n", + pe->path, strerror(errno)); + continue; + } + + if (S_ISDIR(sb.st_mode) == 1) { + if (fflag == 0) + printf("ignoring directory %s\n", pe->path); + continue; + } + + if (S_ISLNK(sb.st_mode) == 1) { + if (fflag == 0) { + printf("ignoring link %s\n", pe->path); + continue; + } + } + + if (vflag == 1) + printf("removing %s\n", pe->path); + r = remove(pe->path); + if (r < 0) { + fprintf(stderr, "remove %s: %s\n", pe->path, + strerror(errno)); + continue; + } + } + + if (fflag == 1) { + /* prune empty directories as well */ + for (pe = pkg->head; pe; pe = pe->next) { + if (dblinks(db, pe->path) > 1) + continue; + nftw(pe->path, rmemptydir, 1, FTW_DEPTH); + } + } + + de->deleted = 1; + + return 0; +} + +int +dbrm(struct db *db, const char *name) +{ + struct dbentry *de; + char path[PATH_MAX], tmpname[PATH_MAX], *p; + int r; + + estrlcpy(tmpname, name, sizeof(tmpname)); + p = basename(tmpname); + for (de = db->head; de; de = de->next) { + if (de->deleted == 1 && strcmp(de->pkg->name, p) == 0) { + estrlcpy(path, db->path, sizeof(path)); + estrlcat(path, "/", sizeof(path)); + estrlcat(path, de->pkg->name, sizeof(path)); + if (vflag == 1) + printf("removing %s\n", path); + /* nuke db entry for this package */ + r = remove(path); + if (r < 0) { + fprintf(stderr, "remove %s: %s\n", path, + strerror(errno)); + return -1; + } + break; + } + } + return 0; +} + +struct pkg * +pkgnew(char *name) +{ + struct pkg *pkg; + + pkg = malloc(sizeof(*pkg)); + if (!pkg) { + fprintf(stderr, "out of memory\n"); + return NULL; + } + pkg->name = name; + pkg->head = NULL; + return pkg; +} + +void +pkgfree(struct pkg *pkg) +{ + struct pkgentry *pe, *tmp; + + pe = pkg->head; + while (pe) { + tmp = pe->next; + free(pe); + pe = tmp; + } + free(pkg); +} diff --git a/db.h b/db.h @@ -0,0 +1,28 @@ +/* See LICENSE file for copyright and license details. */ +struct pkgentry { + char path[PATH_MAX]; + struct pkgentry *next; +}; + +struct pkg { + char *name; + struct pkgentry *head; +}; + +extern int fflag; +extern int vflag; + +struct db *dbinit(const char *); +int dbload(struct db *); +int dbcollide(struct db *, const char *); +int dbadd(struct db *, const char *); +int dbwalk(struct db *, int (*)(struct db *, struct pkg *, void *), void *); +int dblinks(struct db *, const char *); +int dbfree(struct db *); +void dbdump(struct db *); +int dbpkgload(struct db *, struct pkg *); +int dbpkginstall(struct db *, const char *); +int dbpkgremove(struct db *, const char *); +int dbrm(struct db *, const char *); +struct pkg *pkgnew(char *); +void pkgfree(struct pkg *); diff --git a/infopkg.c b/infopkg.c @@ -1,7 +1,5 @@ /* See LICENSE file for copyright and license details. */ -#include <dirent.h> #include <errno.h> -#include <libgen.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> @@ -9,9 +7,10 @@ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> +#include "db.h" #include "util.h" -static void ownpkg(const char *, const char *); +static int ownpkg(struct db *, struct pkg *, void *); char *argv0; @@ -27,10 +26,8 @@ usage(void) int main(int argc, char *argv[]) { - DIR *dir; - struct dirent *dp; + struct db *db; char *prefix = "/"; - char path[PATH_MAX]; int Oflag = 0; int i; int r; @@ -49,110 +46,56 @@ main(int argc, char *argv[]) if (Oflag == 0 || argc < 1) usage(); - r = chdir(prefix); - if (r < 0) { - fprintf(stderr, "chdir %s: %s\n", prefix, - strerror(errno)); - return EXIT_FAILURE; - } - - lockdb(); - - dir = opendir("var/pkg"); - if (!dir) { - fprintf(stderr, "opendir %s: %s\n", "var/pkg", - strerror(errno)); - return EXIT_FAILURE; - } + db = dbinit(prefix); + if (!db) + exit(EXIT_FAILURE); + r = dbload(db); + if (r < 0) + exit(EXIT_FAILURE); for (i = 0; i < argc; i++) { - while ((dp = readdir(dir))) { - if (strcmp(dp->d_name, ".") == 0 || - strcmp(dp->d_name, "..") == 0) - continue; - if (strlcpy(path, dp->d_name, sizeof(path)) >= sizeof(path)) { - fprintf(stderr, "path too long\n"); - exit(EXIT_FAILURE); - } - ownpkg(basename(path), argv[i]); - } - rewinddir(dir); + r = dbwalk(db, ownpkg, argv[i]); + if (r < 0) + exit(EXIT_FAILURE); } - closedir(dir); + dbfree(db); return EXIT_SUCCESS; } -static void -ownpkg(const char *pkg, const char *f) +static int +ownpkg(struct db *db, struct pkg *pkg, void *file) { + struct pkgentry *pe; struct stat sb1, sb2; - char buf[BUFSIZ], *p; - char path[PATH_MAX], filename[PATH_MAX]; - FILE *fp; + char path[PATH_MAX]; int r; - for (; *f == '/'; f++) - ; - if (strlcpy(filename, f, sizeof(filename)) >= sizeof(filename)) { - fprintf(stderr, "path too long\n"); - exit(EXIT_FAILURE); - } + realpath(file, path); - r = lstat(filename, &sb1); + r = lstat(path, &sb1); if (r < 0) { - fprintf(stderr, "lstat %s: %s\n", f, strerror(errno)); - exit(EXIT_FAILURE); - } - if (S_ISREG(sb1.st_mode) == 0) { - fprintf(stderr, "%s is not a regular file\n", filename); + fprintf(stderr, "lstat %s: %s\n", path, strerror(errno)); exit(EXIT_FAILURE); } - if (strlcpy(path, "var/pkg/", sizeof(path)) >= sizeof(path)) { - fprintf(stderr, "path too long\n"); - exit(EXIT_FAILURE); - } - if (strlcat(path, pkg, sizeof(path)) >= sizeof(path)) { - fprintf(stderr, "path too long\n"); + if (dbpkgload(db, pkg) < 0) exit(EXIT_FAILURE); - } - - fp = fopen(path, "r"); - if (!fp) { - fprintf(stderr, "fopen %s: %s\n", path, - strerror(errno)); - exit(EXIT_FAILURE); - } - while (fgets(buf, sizeof(buf), fp)) { - p = strrchr(buf, '\n'); - if (p) - *p = '\0'; - - if (buf[0] == '\0') { - fprintf(stderr, "nil file entry in %s, skipping\n", path); - continue; - } - - r = lstat(buf, &sb2); + for (pe = pkg->head; pe; pe = pe->next) { + r = lstat(pe->path, &sb2); if (r < 0) { fprintf(stderr, "lstat %s: %s\n", - buf, strerror(errno)); + pe->path, strerror(errno)); continue; } - if (sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino) { - printf("/%s is owned by %s\n", filename, pkg); + printf("%s is owned by %s\n", path, pkg->name); break; } } - if (ferror(fp)) { - fprintf(stderr, "I/O error while processing %s\n", path); - exit(EXIT_FAILURE); - } - fclose(fp); + return 0; } diff --git a/installpkg.c b/installpkg.c @@ -1,25 +1,13 @@ /* See LICENSE file for copyright and license details. */ -#include <archive.h> -#include <archive_entry.h> -#include <errno.h> -#include <libgen.h> #include <limits.h> -#include <stdint.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> +#include "db.h" #include "util.h" -static void checkdb(const char *); -static void updatedb(const char *, const char *); -static int collisions(const char *, const char *); -static int extract(const char *, const char *); +static int collidepkg(struct db *, struct pkg *, void *); char *argv0; -static int vflag = 0; static void usage(void) @@ -34,11 +22,11 @@ usage(void) int main(int argc, char *argv[]) { + struct db *db; + char path[PATH_MAX]; + char *prefix = "/"; int r; int i; - char cwd[PATH_MAX]; - char *prefix = "/"; - int fflag = 0; ARGBEGIN { case 'v': @@ -57,287 +45,37 @@ main(int argc, char *argv[]) if (argc < 1) usage(); - getcwd(cwd, sizeof(cwd)); - - r = chdir(prefix); - if (r < 0) { - fprintf(stderr, "chdir %s: %s\n", prefix, - strerror(errno)); + db = dbinit(prefix); + if (!db) exit(EXIT_FAILURE); - } - - lockdb(); - - r = chdir(cwd); - if (r < 0) { - fprintf(stderr, "chdir %s: %s\n", prefix, - strerror(errno)); + r = dbload(db); + if (r < 0) exit(EXIT_FAILURE); - } - checkdb(prefix); for (i = 0; i < argc; i++) { + realpath(argv[i], path); if (vflag == 1) - printf("installing %s\n", argv[i]); + printf("installing %s\n", path); if (fflag == 0) { - if (collisions(prefix, argv[i]) != 0) { - fprintf(stderr, - "aborting %s, use -f to override\n", - argv[i]); + r = dbwalk(db, collidepkg, path); + if (r < 0) exit(EXIT_FAILURE); - } } - updatedb(prefix, argv[i]); - extract(prefix, argv[i]); - printf("installed %s\n", argv[i]); + dbadd(db, path); + dbpkginstall(db, path); + printf("installed %s\n", path); } - r = chdir(prefix); - if (r < 0) { - fprintf(stderr, "chdir %s: %s\n", prefix, - strerror(errno)); - exit(EXIT_FAILURE); - } + dbfree(db); return EXIT_SUCCESS; } -static void -checkdb(const char *prefix) -{ - char cwd[PATH_MAX]; - char *dbpath = "var/pkg"; - int r; - - getcwd(cwd, sizeof(cwd)); - - r = chdir(prefix); - if (r < 0) { - fprintf(stderr, "chdir %s: %s\n", prefix, - strerror(errno)); - exit(EXIT_FAILURE); - } - - r = access(dbpath, R_OK | W_OK | X_OK); - if (r < 0) { - fprintf(stderr, "access %s: %s\n", dbpath, strerror(errno)); - exit(EXIT_FAILURE); - } - - chdir(cwd); -} - -static void -updatedb(const char *prefix, const char *f) -{ - char cwd[PATH_MAX]; - char path[PATH_MAX], filename[PATH_MAX]; - FILE *fp; - struct archive *in; - struct archive_entry *entry; - int r; - - getcwd(cwd, sizeof(cwd)); - - in = archive_read_new(); - - archive_read_support_filter_gzip(in); - archive_read_support_format_tar(in); - - r = archive_read_open_filename(in, f, BUFSIZ); - if (r < 0) { - fprintf(stderr, "%s\n", archive_error_string(in)); - exit(EXIT_FAILURE); - } - - r = chdir(prefix); - if (r < 0) { - fprintf(stderr, "chdir %s: %s\n", prefix, - strerror(errno)); - exit(EXIT_FAILURE); - } - - if (strlcpy(path, "var/pkg/", sizeof(path)) >= sizeof(path)) { - fprintf(stderr, "path too long\n"); - exit(EXIT_FAILURE); - } - if (strlcpy(filename, f, sizeof(filename)) >= sizeof(filename)) { - fprintf(stderr, "path too long\n"); - exit(EXIT_FAILURE); - } - if (strlcat(path, basename(filename), sizeof(path)) >= sizeof(path)) { - fprintf(stderr, "path too long\n"); - exit(EXIT_FAILURE); - } - - fp = fopen(path, "w"); - if (!fp) { - fprintf(stderr, "fopen %s: %s\n", path, - strerror(errno)); - exit(EXIT_FAILURE); - } - - while (1) { - r = archive_read_next_header(in, &entry); - if (r == ARCHIVE_EOF) - break; - if (r != ARCHIVE_OK) { - fprintf(stderr, "%s\n", archive_error_string(in)); - exit(EXIT_FAILURE); - } - if (vflag == 1) - printf("installed %s\n", archive_entry_pathname(entry)); - fputs(archive_entry_pathname(entry), fp); - fputc('\n', fp); - } - - if (vflag == 1) - printf("updating %s\n", path); - fflush(fp); - r = fsync(fileno(fp)); - if (r < 0) - fprintf(stderr, "fsync: %s\n", strerror(errno)); - fclose(fp); - - archive_read_close(in); - archive_read_free(in); - - chdir(cwd); -} - -static int -collisions(const char *prefix, const char *f) -{ - char cwd[PATH_MAX]; - struct archive *in; - struct archive_entry *entry; - struct stat sb; - int ok = 0; - int r; - - getcwd(cwd, sizeof(cwd)); - - in = archive_read_new(); - - archive_read_support_filter_gzip(in); - archive_read_support_format_tar(in); - - r = archive_read_open_filename(in, f, BUFSIZ); - if (r < 0) { - fprintf(stderr, "%s\n", archive_error_string(in)); - exit(EXIT_FAILURE); - } - - r = chdir(prefix); - if (r < 0) { - fprintf(stderr, "chdir %s: %s\n", prefix, - strerror(errno)); - exit(EXIT_FAILURE); - } - - while (1) { - r = archive_read_next_header(in, &entry); - if (r == ARCHIVE_EOF) - break; - if (r != ARCHIVE_OK) { - fprintf(stderr, "%s\n", archive_error_string(in)); - exit(EXIT_FAILURE); - } - if (access(archive_entry_pathname(entry), F_OK) == 0) { - r = stat(archive_entry_pathname(entry), &sb); - if (r < 0) { - fprintf(stderr, "lstat %s: %s\n", - archive_entry_pathname(entry), - strerror(errno)); - exit(EXIT_FAILURE); - } - if (S_ISDIR(sb.st_mode) == 0) { - fprintf(stderr, "%s/%s exists\n", prefix, - archive_entry_pathname(entry)); - ok = 1; - } - } - } - - archive_read_close(in); - archive_read_free(in); - - chdir(cwd); - return ok; -} - static int -extract(const char *prefix, const char *f) +collidepkg(struct db *db, struct pkg *pkg, void *file) { - char cwd[PATH_MAX]; - const void *buf; - size_t size; - int64_t offset; - struct archive *in; - struct archive *out; - struct archive_entry *entry; - int r; - - getcwd(cwd, sizeof(cwd)); - - in = archive_read_new(); - out = archive_write_disk_new(); - - archive_write_disk_set_options(out, - ARCHIVE_EXTRACT_OWNER | - ARCHIVE_EXTRACT_PERM | - ARCHIVE_EXTRACT_TIME | - ARCHIVE_EXTRACT_UNLINK | - ARCHIVE_EXTRACT_SECURE_NODOTDOT); - - archive_read_support_filter_gzip(in); - archive_read_support_format_tar(in); - - r = archive_read_open_filename(in, f, BUFSIZ); - if (r < 0) { - fprintf(stderr, "%s\n", archive_error_string(in)); - exit(EXIT_FAILURE); - } - - r = chdir(prefix); - if (r < 0) { - fprintf(stderr, "chdir %s: %s\n", prefix, - strerror(errno)); - exit(EXIT_FAILURE); - } - - while (1) { - r = archive_read_next_header(in, &entry); - if (r == ARCHIVE_EOF) - break; - if (r != ARCHIVE_OK) { - fprintf(stderr, "%s\n", archive_error_string(in)); - exit(EXIT_FAILURE); - } - r = archive_write_header(out, entry); - if (r != ARCHIVE_OK) { - fprintf(stderr, "%s\n", archive_error_string(out)); - } else { - while (1) { - r = archive_read_data_block(in, &buf, - &size, &offset); - if (r == ARCHIVE_EOF) - break; - if (r != ARCHIVE_OK) - break; - r = archive_write_data_block(out, buf, size, - offset); - if (r != ARCHIVE_OK) - break; - } - } - } - - archive_read_close(in); - archive_read_free(in); - archive_write_close(out); - archive_write_free(out); - - chdir(cwd); + (void)pkg; + if (dbcollide(db, file) < 0) + return -1; return 0; } diff --git a/lock.c b/lock.c @@ -1,32 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <unistd.h> - -/* lock the package dabatase - assumes cwd is the root prefix */ -void -lockdb(void) -{ - DIR *dir; - - dir = opendir("var/pkg"); - if (!dir) { - fprintf(stderr, "opendir %s: %s\n", "var/pkg", - strerror(errno)); - exit(EXIT_FAILURE); - } - - if (flock(dirfd(dir), LOCK_EX | LOCK_NB) < 0) { - if (errno == EWOULDBLOCK) - fprintf(stderr, "package db already locked\n"); - else - fprintf(stderr, "flock %s: %s\n", "var/pkg", - strerror(errno)); - exit(EXIT_FAILURE); - } -} diff --git a/removepkg.c b/removepkg.c @@ -1,25 +1,14 @@ /* See LICENSE file for copyright and license details. */ -#define _XOPEN_SOURCE 500 -#include <dirent.h> -#include <errno.h> -#include <ftw.h> -#include <libgen.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> +#include "db.h" #include "util.h" -static int numrefs(const char *); -static int rmemptydir(const char *, const struct stat *, int, struct FTW *); -static int removepkg(const char *); +static int removepkg(struct db *, struct pkg *, void *); char *argv0; -static int vflag; -static int fflag; static void usage(void) @@ -34,11 +23,9 @@ usage(void) int main(int argc, char *argv[]) { - DIR *dir; - struct dirent *dp; - char filename[PATH_MAX]; + struct db *db; + char path[PATH_MAX]; char *prefix = "/"; - int found = 0; int r; int i; @@ -59,239 +46,36 @@ main(int argc, char *argv[]) if (argc < 1) usage(); - r = chdir(prefix); - if (r < 0) { - fprintf(stderr, "chdir %s: %s\n", prefix, - strerror(errno)); - return EXIT_FAILURE; - } - - lockdb(); - - dir = opendir("var/pkg"); - if (!dir) { - fprintf(stderr, "opendir %s: %s\n", "var/pkg", - strerror(errno)); - return EXIT_FAILURE; - } + db = dbinit(prefix); + if (!db) + exit(EXIT_FAILURE); + r = dbload(db); + if (r < 0) + exit(EXIT_FAILURE); for (i = 0; i < argc; i++) { - if (strlcpy(filename, argv[i], sizeof(filename)) >= sizeof(filename)) { - fprintf(stderr, "path too long\n"); + realpath(argv[i], path); + r = dbwalk(db, removepkg, path); + if (r < 0) exit(EXIT_FAILURE); - } - found = 0; - - while ((dp = readdir(dir))) { - if (strcmp(dp->d_name, ".") == 0 || - strcmp(dp->d_name, "..") == 0) - continue; - if (strcmp(dp->d_name, basename(filename)) == 0) { - if (vflag == 1) - printf("removing %s\n", argv[i]); - if (removepkg(argv[i]) != 0) - return EXIT_FAILURE; - printf("removed %s\n", argv[i]); - found = 1; - break; - } - } - if (found == 0) { - fprintf(stderr, "package %s not installed\n", argv[i]); - return EXIT_FAILURE; - } - - rewinddir(dir); + dbrm(db, path); + printf("removed %s\n", path); } - closedir(dir); + dbfree(db); return EXIT_SUCCESS; } static int -numrefs(const char *f) -{ - DIR *dir; - struct dirent *dp; - FILE *fp; - char path[PATH_MAX]; - char buf[BUFSIZ], *p; - int refs = 0; - - dir = opendir("var/pkg"); - if (!dir) { - fprintf(stderr, "opendir %s: %s\n", "var/pkg", - strerror(errno)); - exit(EXIT_FAILURE); - } - - while ((dp = readdir(dir))) { - if (strcmp(dp->d_name, ".") == 0 || - strcmp(dp->d_name, "..") == 0) - continue; - - if (strlcpy(path, "var/pkg/", sizeof(path)) >= sizeof(path)) { - fprintf(stderr, "path too long\n"); - exit(EXIT_FAILURE); - } - if (strlcat(path, dp->d_name, sizeof(path)) >= sizeof(path)) { - fprintf(stderr, "path too long\n"); - exit(EXIT_FAILURE); - } - - fp = fopen(path, "r"); - if (!fp) { - fprintf(stderr, "fopen %s: %s\n", path, - strerror(errno)); - exit(EXIT_FAILURE); - } - - while (fgets(buf, sizeof(buf), fp)) { - p = strrchr(buf, '\n'); - if (p) - *p = '\0'; - if (buf[0] == '\0') - continue; - if (strcmp(buf, f) == 0) - refs++; - } - if (ferror(fp)) { - fprintf(stderr, "I/O error while processing %s\n", path); - fclose(fp); - return 1; - } - - fclose(fp); - } - - closedir(dir); - - return refs; -} - -static int -rmemptydir(const char *f, const struct stat *sb, int typeflag, - struct FTW *ftwbuf) -{ - (void) sb; - (void) ftwbuf; - - if (typeflag == FTW_DP) { - if (vflag == 1) - printf("removing %s\n", f); - rmdir(f); - } - return 0; -} - -static int -removepkg(const char *f) +removepkg(struct db *db, struct pkg *pkg, void *file) { - FILE *fp; - struct stat sb; - char buf[BUFSIZ], *p; - char path[PATH_MAX], filename[PATH_MAX]; - int r; - - if (strlcpy(path, "var/pkg/", sizeof(path)) >= sizeof(path)) { - fprintf(stderr, "path too long\n"); - exit(EXIT_FAILURE); - } - if (strlcpy(filename, f, sizeof(filename)) >= sizeof(filename)) { - fprintf(stderr, "path too long\n"); - exit(EXIT_FAILURE); - } - if (strlcat(path, basename(filename), sizeof(path)) >= sizeof(path)) { - fprintf(stderr, "path too long\n"); - exit(EXIT_FAILURE); - } - - fp = fopen(path, "r"); - if (!fp) { - fprintf(stderr, "fopen %s: %s\n", path, - strerror(errno)); - return 1; - } - - while (fgets(buf, sizeof(buf), fp)) { - p = strrchr(buf, '\n'); - if (p) - *p = '\0'; - - if (buf[0] == '\0') { - fprintf(stderr, "nil file entry in %s, skipping\n", - path); - continue; - } - - r = lstat(buf, &sb); - if (r < 0) { - fprintf(stderr, "lstat %s: %s\n", - buf, strerror(errno)); - continue; - } - - if (S_ISDIR(sb.st_mode) == 1) { - if (fflag == 0) - printf("ignoring directory %s\n", buf); - continue; - } - - if (S_ISLNK(sb.st_mode) == 1) { - if (fflag == 0) { - printf("ignoring link %s\n", buf); - continue; - } - } - - if (vflag == 1) - printf("removing %s\n", buf); - r = remove(buf); - if (r < 0) { - fprintf(stderr, "remove %s: %s\n", buf, - strerror(errno)); - continue; - } - } - if (ferror(fp)) { - fprintf(stderr, "I/O error while processing %s\n", path); - fclose(fp); - return 1; - } - - if (fflag == 1) { - /* prune empty directories as well */ - rewind(fp); - while (fgets(buf, sizeof(buf), fp)) { - p = strrchr(buf, '\n'); - if (p) - *p = '\0'; - if (buf[0] == '\0') - continue; - if (numrefs(buf) > 1) - continue; - nftw(buf, rmemptydir, 1, FTW_DEPTH); - } - if (ferror(fp)) { - fprintf(stderr, "I/O error while processing %s\n", path); - fclose(fp); - return 1; - } - } - - fclose(fp); - - if (vflag == 1) - printf("removing %s\n", path); - /* nuke db entry for this package */ - r = remove(path); - if (r < 0) { - fprintf(stderr, "remove %s: %s\n", path, - strerror(errno)); - return 1; - } + char name[PATH_MAX], *p; - sync(); + estrlcpy(name, file, sizeof(name)); + p = basename(name); + if (strcmp(pkg->name, p) == 0) + if (dbpkgremove(db, p) < 0) + return -1; return 0; } diff --git a/strlcat.c b/strlcat.c @@ -14,8 +14,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <sys/types.h> #include "util.h" /* @@ -49,3 +51,16 @@ strlcat(char *dst, const char *src, size_t siz) *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } + +size_t +estrlcat(char *dst, const char *src, size_t siz) +{ + size_t r; + + r = strlcat(dst, src, siz); + if (r >= siz) { + fprintf(stderr, "destination buffer too small\n"); + exit(EXIT_FAILURE); + } + return r; +} diff --git a/strlcpy.c b/strlcpy.c @@ -14,8 +14,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <sys/types.h> #include "util.h" /* @@ -45,3 +47,16 @@ strlcpy(char *dst, const char *src, size_t siz) } return(s - src - 1); /* count does not include NUL */ } + +size_t +estrlcpy(char *dst, const char *src, size_t siz) +{ + size_t r; + + r = strlcpy(dst, src, siz); + if (r >= siz) { + fprintf(stderr, "destination buffer too small\n"); + exit(EXIT_FAILURE); + } + return r; +} diff --git a/util.h b/util.h @@ -10,5 +10,7 @@ void lockdb(void); #undef strlcat size_t strlcat(char *, const char *, size_t); +size_t estrlcat(char *, const char *, size_t); #undef strlcpy size_t strlcpy(char *, const char *, size_t); +size_t estrlcpy(char *, const char *, size_t);