tClean pack from syystem if update fails - pm - barely a pack manager
 (HTM) git clone git://z3bra.org/pm
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 11ca8f6728acf195601b904ac0c90cbfff2f2f8f
 (DIR) parent 714472bbfbcc98d91cd52a7ebc7ca205a0601666
 (HTM) Author: z3bra <willyatmailoodotorg>
       Date:   Wed, 22 Jun 2016 09:54:01 +0200
       
       Clean pack from syystem if update fails
       
       Packs should not be left "half-installed". In case the update fails during
       installation, the pack will simply be removed from the system.
       
       This commit also fixes a few cases where mmap() would fail, and put metadata
       removal in its own function, for easier use.
       
       Diffstat:
         M pm.c                                |     116 ++++++++++++++++++++++---------
       
       1 file changed, 82 insertions(+), 34 deletions(-)
       ---
 (DIR) diff --git a/pm.c b/pm.c
       t@@ -68,6 +68,7 @@ int pack_extract(const char *rootfs, const char *datadir, struct pack *p);
        int pack_install(const char *rootfs, const char *datadir, struct pack *p);
        int pack_delete(const char *rootfs, const char *datadir, struct pack *p);
        
       +int inspect_installed(const char *datadir, const char *name);
        int inspect_version(const char *datadir, const char *name, char version[]);
        int inspect_collision(const char *rootfs, struct pack *p);
        int inspect_files(int fd, const char *datadir, const char *name);
       t@@ -77,6 +78,7 @@ int write_metadata(const char *datadir, struct pack *pack);
        int write_entry(struct archive *a, struct archive *w);
        int delete_node(char *path);
        int delete_content(const char *rootfs, char *map, size_t size);
       +int delete_metadata(const char *datadir, char *name);
        
        /* action wrappers around CLI arguments */
        int install(const char *rootfs, const char *datadir, char *path);
       t@@ -236,13 +238,12 @@ pack_load_tarball(char *path)
        struct pack *
        pack_load_metadata(const char *datadir, char *name)
        {
       -        struct stat st;
                struct pack *pack = NULL;
                char tmp[PATH_MAX] = "";
        
                snprintf(tmp, PATH_MAX, "%s/%s", datadir, name);
        
       -        if (stat(tmp, &st) < 0) {
       +        if (inspect_installed(datadir, name)) {
                        fprintf(stderr, "%s: No such pack installed\n", name);
                        return NULL;
                }
       t@@ -315,6 +316,7 @@ pack_extract(const char *rootfs, const char *datadir, struct pack *p)
                if ((r = archive_read_open_filename(a, p->path, 0)) != ARCHIVE_OK) {
                        fprintf(stderr, "%s: %s\n", p->path, archive_error_string(a));
                        archive_read_free(a);
       +                close(fd);
                        return r;
                }
        
       t@@ -347,6 +349,7 @@ pack_extract(const char *rootfs, const char *datadir, struct pack *p)
                        dprintf(fd, "%s\n", archive_entry_pathname(e));
                        archive_write_finish_entry(w);
                }
       +        close(fd);
                chdir(cwd);
        
                archive_write_free(w);
       t@@ -399,14 +402,14 @@ pack_install(const char *rootfs, const char *datadir, struct pack *p)
        int
        pack_delete(const char *rootfs, const char *datadir, struct pack *p)
        {
       -        int fd;
       +        int fd, r = 0;
                char *addr = NULL;
                char tmp[PATH_MAX];
                struct stat st;
        
                snprintf(tmp, PATH_MAX, "%s/%s/files", datadir, p->name);
       -        if (stat(tmp, &st) < 0 && errno == ENOENT) {
       -                fprintf(stderr, "%s: No installed files\n", p->name);
       +        if (stat(tmp, &st) < 0) {
       +                perror(tmp);
                        return -1;
                }
                if ((fd = open(tmp, O_RDONLY)) < 0) {
       t@@ -414,42 +417,46 @@ pack_delete(const char *rootfs, const char *datadir, struct pack *p)
                        return ERR_DELETE;
                }
        
       -        addr = mmap(0, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
       -        if (addr == MAP_FAILED) {
       -                perror(tmp);
       -                close(fd);
       -        }
       -
       -        log(LOG_VERBOSE, "deleting installed files\n");
       -        /* ignore errors so everything gets deleted */
       -        if (delete_content(rootfs, addr, st.st_size) < 0) {
       -                fprintf(stderr, "%s: Pack removal failed\n", p->name);
       -                close(fd);
       -                return ERR_DELETE;
       +        if (st.st_size > 0) {
       +                addr = mmap(0, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
       +                if (addr == MAP_FAILED) {
       +                        perror("mmap");
       +                        close(fd);
       +                        return ERR_DELETE;
       +                }
       +        
       +                log(LOG_VERBOSE, "deleting installed files\n");
       +                /* ignore errors so everything gets deleted */
       +                if (delete_content(rootfs, addr, st.st_size) < 0) {
       +                        fprintf(stderr, "%s: Pack removal failed\n", p->name);
       +                        close(fd);
       +                        r = ERR_DELETE;
       +                }
       +                munmap(addr, st.st_size);
                }
                close(fd);
        
                log(LOG_VERBOSE, "cleaning metadata\n");
       -        log(LOG_DEBUG, "- %s\n", tmp);
       -        if (unlink(tmp) < 0) {
       -                perror(tmp);
       -                return ERR_DELETE;
       +        if (delete_metadata(datadir, p->name) < 0) {
       +                fprintf(stderr, "%s: Metadata removal failed\n", p->name);
       +                r = ERR_DELETE;
                }
        
       -        snprintf(tmp, PATH_MAX, "%s/%s/version", datadir, p->name);
       +        return r;
       +}
        
       -        log(LOG_DEBUG, "- %s\n", tmp);
       -        if (unlink(tmp) < 0) {
       -                perror(tmp);
       -                return ERR_DELETE;
       -        }
        
       -        /* remove metadata directory, no matter what */
       -        snprintf(tmp, PATH_MAX, "%s/%s", datadir, p->name);
       -        log(LOG_DEBUG, "- %s\n", tmp);
       -        rmdir(tmp);
       +/*
       + * Returns 0 if "name" is installed, non-zero otherwise
       + */
       +int
       +inspect_installed(const char *datadir, const char *name)
       +{
       +        char tmp[PATH_MAX] = "";
       +        struct stat st;
        
       -        return 0;
       +        snprintf(tmp, PATH_MAX, "%s/%s", datadir, name);
       +        return stat(tmp, &st) || S_ISDIR(st.st_mode);
        }
        
        
       t@@ -507,7 +514,7 @@ inspect_collision(const char *rootfs, struct pack *p)
                while (archive_read_next_header(a, &e) == ARCHIVE_OK) {
                        if (stat(archive_entry_pathname(e), &st) == 0 && !S_ISDIR(st.st_mode)) {
                                fprintf(stderr, "%s: File exists\n", archive_entry_pathname(e));
       -                        r = -1;
       +                        r++;
                                break;
                        }
                        archive_read_data_skip(a);
       t@@ -724,6 +731,32 @@ delete_content(const char *rootfs, char *map, size_t size)
        
        
        /*
       + * Delete the metadata stored for a given pack name
       + */
       +int
       +delete_metadata(const char *datadir, char *name)
       +{
       +        int i;
       +        char path[PATH_MAX] = "";
       +        char *meta[] = { "files", "version", NULL };
       +        
       +        for (i = 0; meta[i] != NULL; i++) {
       +                snprintf(path, PATH_MAX, "%s/%s/%s", datadir, name, meta[i]);
       +                log(LOG_DEBUG, "- %s\n", path);
       +                if (unlink(path) < 0) {
       +                        perror(path);
       +                        return ERR_DELETE;
       +                }
       +        }
       +
       +        /* remove metadata directory, no matter what */
       +        snprintf(path, PATH_MAX, "%s/%s", datadir, name);
       +        log(LOG_DEBUG, "- %s\n", path);
       +        return rmdir(path);
       +}
       +
       +
       +/*
         * Install a pack from the given path. This wraps load/install of a pack
         */
        int
       t@@ -760,6 +793,12 @@ update(const char *rootfs, const char *datadir, char *path)
                if ((p = pack_load_tarball(path)) == NULL)
                        return ERR_PACK_LOAD;
        
       +        if (inspect_installed(datadir, p->name)) {
       +                fprintf(stderr, "%s: No such pack installed\n", p->name);
       +                pack_free(p);
       +                return ERR_DELETE;
       +        }
       +
                if (pack_delete(rootfs, datadir, p) != 0)
                        return ERR_DELETE;
        
       t@@ -767,8 +806,12 @@ update(const char *rootfs, const char *datadir, char *path)
                r += pack_install(rootfs, datadir, p);
                overwrite = tmp;
        
       -        if (r == 0)
       +        if (r == 0) {
                        log(LOG_INFO, "updated %s to %s\n", p->name, p->version);
       +        } else {
       +                fprintf(stderr, "%s: Update failed. Cleaning filesystem\n", p->name);
       +                pack_delete(rootfs, datadir, p);
       +        }
        
                pack_free(p);
        
       t@@ -785,6 +828,11 @@ delete(const char *rootfs, const char *datadir, char *name)
                int r = 0;
                struct pack *p = NULL;
        
       +        if (inspect_installed(datadir, name)) {
       +                fprintf(stderr, "%s: No such pack installed\n", name);
       +                return ERR_DELETE;
       +        }
       +
                if ((p = pack_load_metadata(datadir, name)) == NULL)
                        return ERR_PACK_LOAD;