tUse mmap to read metadata files in delete() - pm - barely a pack manager
 (HTM) git clone git://z3bra.org/pm
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit c293e2c77808dc0c1f1b292111dd18dd19280a46
 (DIR) parent d66413a9f2f5c91fb4f7e7b5e147e71f6478a881
 (HTM) Author: z3bra <willyatmailoodotorg>
       Date:   Sat, 30 Jan 2016 15:24:02 +0100
       
       Use mmap to read metadata files in delete()
       
       Diffstat:
         M pm.c                                |     118 ++++++++++++++++++-------------
       
       1 file changed, 68 insertions(+), 50 deletions(-)
       ---
 (DIR) diff --git a/pm.c b/pm.c
       t@@ -5,6 +5,7 @@
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
       +#include <sys/mman.h>
        #include <sys/stat.h>
        #include <sys/types.h>
        
       t@@ -49,7 +50,6 @@ void usage(char *name);
        int is_empty(char *dir);
        int mkdir_parents(char *dir, mode_t mode);
        char *base_name(char *path);
       -char *lread(int fd, char *buf, size_t len);
        
        struct pack *pack_load(char *path);
        void pack_free(struct pack *p);
       t@@ -63,7 +63,8 @@ int write_metadata(char *datadir, struct pack *pack);
        int write_entry(struct archive *a, struct archive *w);
        int unpack(char *rootfs, char *datadir, struct pack *p);
        int install(char *rootfs, char *datadir, struct pack *p);
       -int delete_content(int fd);
       +int delete_node(char *path);
       +int delete_content(char *map, size_t size);
        int delete(const char *rootfs, const char *datadir, const char *name);
        
        char *argv0;
       t@@ -141,23 +142,6 @@ base_name(char *path)
        
        
        /*
       - * Reads a line which is at least 'len' bytes long. It will load
       - * the line into "buf". The user has to ensure the addressed
       - * pointed to by 'buf' is big enough to contain the line.
       - */
       -char *
       -lread(int fd, char *buf, size_t len)
       -{
       -        size_t i;
       -        int r, c = 0;
       -        while ((r = read(fd, &c, 1)) > 0 && c != '\n' && i < len) {
       -                buf[i++] = c;
       -        }
       -        buf[i] = '\0';
       -        return r > 0 ? buf : NULL;
       -}
       -
       -/*
         * Check for collisions between the filesystem and the tarball
         */
        int
       t@@ -450,51 +434,80 @@ install(char *rootfs, char *datadir, struct pack *p)
        
        
        /*
       - * Delete all entries listed in the given file.
       - * if the entry doesn't exist, it will be ignored
       + * Deletes a node, be it a file or a directory.
       + * In case the node doesn't exists, we're done
         */
        int
       -delete_content(int fd)
       +delete_node(char *path)
        {
                int r = 0;
       -        char file[PATH_MAX] = "";
                struct stat st;
                size_t len = 0;
        
       -        if (lread(fd, file, PATH_MAX))
       -                if ((r = delete_content(fd)) < 0)
       -                        return r;
       -
       -        /* remove trailing '\n' */
       -        if ((len = strnlen(file, PATH_MAX)) == 0)
       -                return 0;
       -
       -        file[len - 1] = 0;
       -        len--;
       -
       +        len = strnlen(path, PATH_MAX);
                /* remove potential trailing '/' */
       -        if (file[len - 1] == '/') {
       -                file[len - 1] = 0;
       -                len--;
       -        }
       +        if (path[len - 1] == '/')
       +                path[--len] = 0;
        
                /*
       -         * if file doesn't exist anymore, it's all good :)
       +         * if path doesn't exist anymore, it's all good :)
                 * we use lstat here so that dangling links can be delt with too
                 */
       -        if (lstat(file, &st) < 0 && errno == ENOENT)
       +        if (lstat(path, &st) < 0 && errno == ENOENT)
                        return 0;
        
       -        if (S_ISDIR(st.st_mode) && is_empty(file) == 0) {
       -                if ((r = rmdir(file)) < 0)
       -                        perror(file);
       +        if (verbose == 1)
       +                printf("- %s\n", path);
       +
       +        if (S_ISDIR(st.st_mode) && is_empty(path) == 0) {
       +                if ((r = rmdir(path)) < 0) {
       +                        perror(path);
       +                        return r;
       +                }
       +        }
       +
       +        if (!S_ISDIR(st.st_mode) && (r = unlink(path)) < 0) {
       +                perror(path);
                        return r;
                }
        
       -        if (!S_ISDIR(st.st_mode) && (r = unlink(file)) < 0)
       -                perror(file);
       +        return 0;
       +}
       +
       +/*
       + * Delete all entries listed in the given file.
       + * if the entry doesn't exist, it will be ignored
       + */
       +int
       +delete_content(char *map, size_t size)
       +{
       +        char *path = NULL;
       +        size_t off;
        
       -        return r;
       +        if (size < 1)
       +                return -1;
       +
       +        do {
       +                /*
       +                 * it is assumed here that the file is POSIX and thus that
       +                 * the last char will be \n.
       +                 * This might sound stupid; but this is what unpack() will do.
       +                 * We read the mmap file backward until we encounter \n or the
       +                 * beginning of the file. If it's an \n, we replace it by 0 and
       +                 * process to the deletion of the inode, either with rmdir or
       +                 * unlink.
       +                 */
       +                for (off=size-1; off>0 && map[off] != '\n'; off--);
       +                map[off] = (off > 0 ? 0 : *map);
       +                path     = (off < size-1 ? &map[off] + 1 : NULL);
       +
       +                if (path != NULL && strnlen(path, PATH_MAX) > 0) {
       +                        if (delete_node(path) < 0)
       +                                return ERR_DELETE;
       +                }
       +        } while (off > 0);
       +
       +        return 0;
        }
        
        
       t@@ -507,30 +520,35 @@ int
        delete(const char *rootfs, const char *datadir, const char *packname)
        {
                int fd;
       +        char *addr = NULL;
                char tmp[PATH_MAX];
                struct stat st;
        
       -        snprintf(tmp, PATH_MAX, "%s/%s", datadir, packname);
       +        snprintf(tmp, PATH_MAX, "%s/%s/files", datadir, packname);
                if (stat(tmp, &st) < 0 && errno == ENOENT) {
                        fprintf(stderr, "%s: not installed\n", packname);
                        return -1;
                }
       -
       -        snprintf(tmp, PATH_MAX, "%s/%s/files", datadir, packname);
                if ((fd = open(tmp, O_RDONLY)) < 0) {
                        perror(tmp);
                        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);
       +        }
        
                if (verbose == 1)
                        printf("%s: deleting from %s\n", packname, rootfs);
                if (chdir(rootfs) < 0) {
                        perror(rootfs);
       +                close(fd);
                        return ERR_DELETE;
                }
        
                /* ignore errors so everything gets deleted */
       -        if (delete_content(fd) < 0) {
       +        if (delete_content(addr, st.st_size) < 0) {
                        fprintf(stderr, "%s: cannot remove pack\n", packname);
                        close(fd);
                        return ERR_DELETE;