Adds 2017 to copyrights
[src/app-framework-main.git] / src / wgtpkg-workdir.c
index 59da1e6..8695ae2 100644 (file)
@@ -1,5 +1,7 @@
 /*
- Copyright 2015 IoT.bzh
+ Copyright 2015, 2016, 2017 IoT.bzh
+
+ author: José Bollo <jose.bollo@iot.bzh>
 
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
 
 #include <unistd.h>
 #include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
 #include <dirent.h>
-#include <syslog.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <assert.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <limits.h>
 
 #include "verbose.h"
-#include "wgtpkg.h"
+#include "wgtpkg-workdir.h"
+#include "utils-dir.h"
 
-static int mode = 0755;
+static const mode_t dirmode = 0755;
 char workdir[PATH_MAX] = { 0, };
 int workdirfd = -1;
 
-/* removes recursively the content of a directory */
-static int clean_dirfd(int dirfd)
-{
-       int cr, fd;
-       DIR *dir;
-       struct dirent *ent;
-       struct {
-               struct dirent entry;
-               char spare[PATH_MAX];
-       } entry;
-
-       dirfd = dup(dirfd);
-       if (dirfd < 0) {
-               syslog(LOG_ERR, "failed to dup the dirfd");
-               return -1;
-       }
-       dir = fdopendir(dirfd);
-       if (dir == NULL) {
-               syslog(LOG_ERR, "fdopendir failed in clean_dirfd");
-               return -1;
-       }
-
-       cr = -1;
-       for (;;) {
-               if (readdir_r(dir, &entry.entry, &ent) != 0) {
-                       syslog(LOG_ERR, "readdir_r failed in clean_dirfd");
-                       goto error;
-               }
-               if (ent == NULL)
-                       break;
-               if (ent->d_name[0] == '.' && (ent->d_name[1] == 0
-                               || (ent->d_name[1] == '.' && ent->d_name[2] == 0)))
-                       continue;
-               cr = unlinkat(dirfd, ent->d_name, 0);
-               if (!cr)
-                       continue;
-               if (errno != EISDIR) {
-                       syslog(LOG_ERR, "unlink of %s failed in clean_dirfd", ent->d_name);
-                       goto error;
-               }
-               fd = openat(dirfd, ent->d_name, O_DIRECTORY|O_RDONLY);
-               if (fd < 0) {
-                       syslog(LOG_ERR, "opening directory %s failed in clean_dirfd", ent->d_name);
-                       goto error;
-               }
-               cr = clean_dirfd(fd);
-               close(fd);
-               if (cr)
-                       goto error;
-               cr = unlinkat(dirfd, ent->d_name, AT_REMOVEDIR);
-               if (cr) {
-                       syslog(LOG_ERR, "rmdir of %s failed in clean_dirfd", ent->d_name);
-                       goto error;
-               }
-       }
-       cr = 0;
-error:
-       closedir(dir);
-       return cr;
-}
-
-/* removes recursively the content of a directory */
-static int clean_dir(const char *directory)
-{
-       int fd, rc;
-
-       fd = openat(AT_FDCWD, directory, O_DIRECTORY|O_RDONLY);
-       if (fd < 0) {
-               syslog(LOG_ERR, "opening directory %s failed in clean_dir", directory);
-               return fd;
-       }
-       rc = clean_dirfd(fd);
-       close(fd);
-       return rc;
-}
-
 /* removes the working directory */
 void remove_workdir()
 {
        assert(workdirfd >= 0);
-       clean_dirfd(workdirfd);
+       remove_directory_content_fd(workdirfd);
        close(workdirfd);
        workdirfd = -1;
        rmdir(workdir);
        workdir[0] = 0;
 }
 
-static int set_real_workdir(const char *name, int create)
+static void put_workdir(int fd, const char *name, size_t length)
+{
+       /* close the previous directory if any */
+       if (workdirfd >= 0)
+               close(workdirfd);
+
+       /* copy the name and the fd if existing */
+       if (fd < 0) {
+               workdir[0] = '.';
+               workdir[1] = 0;
+               workdirfd = AT_FDCWD;
+       } else {
+               
+               assert(length < sizeof workdir);
+               memcpy(workdir, name, 1 + length);
+               workdirfd = fd;
+       }
+}
+
+int set_workdir(const char *name, int create)
 {
        int rc, dirfd;
        size_t length;
@@ -128,88 +76,69 @@ static int set_real_workdir(const char *name, int create)
        /* check the length */
        length = strlen(name);
        if (length >= sizeof workdir) {
-               syslog(LOG_ERR, "workdir name too long");
+               ERROR("workdir name too long");
                errno = EINVAL;
                return -1;
        }
 
        /* opens the directory */
-       dirfd = openat(AT_FDCWD, name, O_DIRECTORY|O_RDONLY);
+       dirfd = openat(AT_FDCWD, name, O_PATH|O_DIRECTORY|O_RDONLY);
        if (dirfd < 0) {
-               if (!create || errno != ENOENT) {
-                       syslog(LOG_ERR, "no workdir %s", name);
+               if (errno != ENOENT) {
+                       ERROR("error while opening workdir %s: %m", name);
                        return -1;
                }
-               rc = mkdir(name, mode);
+               if (!create) {
+                       ERROR("workdir %s doesn't exist", name);
+                       return -1;
+               }
+               rc = mkdirat(AT_FDCWD, name, dirmode);
                if (rc) {
-                       syslog(LOG_ERR, "can't create workdir %s", name);
+                       ERROR("can't create workdir %s", name);
                        return -1;
                }
-               dirfd = open(name, O_PATH|O_DIRECTORY);
+               dirfd = openat(AT_FDCWD, name, O_PATH|O_DIRECTORY|O_RDONLY);
                if (dirfd < 0) {
-                       syslog(LOG_ERR, "can't open workdir %s", name);
+                       ERROR("can't open workdir %s", name);
                        return -1;
                }
        }
 
-       /* close the previous directory if any */
-       if (workdirfd >= 0)
-               close(workdirfd);
-       workdirfd = dirfd;
-       memcpy(workdir, name, 1+length);
+       /* record new workdir */
+       put_workdir(dirfd, name, length);
        return 0;
 }
 
-int set_workdir(const char *name, int create)
-{
-       char *rp;
-       int rc;
-
-       if (name[0] == '/')
-               return set_real_workdir(name, create);
-
-       rp = realpath(name, NULL);
-       if (!rp) {
-               syslog(LOG_ERR, "realpath failed for %s", name);
-               return -1;
-       }
-       rc = set_real_workdir(rp, create);
-       free(rp);
-       return rc;
-}
-
-static int make_real_workdir_base(const char *root, const char *prefix, int reuse)
+int make_workdir(const char *root, const char *prefix, int reuse)
 {
        int i, n, r, l;
 
+       put_workdir(AT_FDCWD, ".", 1);
+
        n = snprintf(workdir, sizeof workdir, "%s/%s", root, prefix);
-       if (n >= sizeof workdir) {
-               syslog(LOG_ERR, "workdir prefix too long");
+       if (n >= (int)sizeof workdir) {
+               ERROR("workdir prefix too long");
                errno = EINVAL;
                return -1;
        }
        r = (int)(sizeof workdir) - n;
 
-       if (workdirfd >= 0)
-               close(workdirfd);
-       workdirfd = -1;
-
        /* create a temporary directory */
        for (i = 0 ; ; i++) {
                if (i == INT_MAX) {
-                       syslog(LOG_ERR, "exhaustion of workdirs");
+                       ERROR("exhaustion of workdirs");
                        return -1;
                }
-               l = snprintf(workdir + n, r, "%d", i);
+               l = snprintf(workdir + n, (unsigned)r, "%d", i);
                if (l >= r) {
-                       syslog(LOG_ERR, "computed workdir too long");
+                       ERROR("computed workdir too long");
                        errno = EINVAL;
                        return -1;
                }
-               if (!mkdir(workdir, mode))
+               if (!mkdirat(AT_FDCWD, workdir, dirmode))
                        break;
                if (errno != EEXIST) {
-                       syslog(LOG_ERR, "error in creation of workdir %s: %m", workdir);
+                       ERROR("error in creation of workdir %s: %m", workdir);
                        return -1;
                }
                if (reuse)
@@ -217,7 +146,7 @@ static int make_real_workdir_base(const char *root, const char *prefix, int reus
        }
        workdirfd = openat(AT_FDCWD, workdir, O_RDONLY|O_DIRECTORY);
        if (workdirfd < 0) {
-               syslog(LOG_ERR, "error in onnection to workdir %s: %m", workdir);
+               ERROR("error in onnection to workdir %s: %m", workdir);
                rmdir(workdir);
                return -1;
        }
@@ -225,39 +154,17 @@ static int make_real_workdir_base(const char *root, const char *prefix, int reus
        return 0;
 }
 
-int make_workdir_base(const char *root, const char *prefix, int reuse)
+int move_workdir(const char *dest, int parents, int force)
 {
-       char *rp;
        int rc;
-
-       if (root[0] == '/')
-               return make_real_workdir_base(root, prefix, reuse);
-
-       rp = realpath(root, NULL);
-       if (!rp) {
-               syslog(LOG_ERR, "realpath failed for %s", root);
-               return -1;
-       }
-       rc = make_real_workdir_base(rp, prefix, reuse);
-       free(rp);
-       return rc;
-}
-
-int make_workdir(int reuse)
-{
-       return make_workdir_base(".", "PACK", reuse);
-}
-
-static int move_real_workdir(const char *dest, int parents, int force)
-{
-       int rc, len, l;
+       size_t len;
        struct stat s;
        char *copy;
        const char *iter;
 
        /* check length */
        if (strlen(dest) >= sizeof workdir) {
-               syslog(LOG_ERR, "destination dirname too long");
+               ERROR("destination dirname too long");
                errno = EINVAL;
                return -1;
        }
@@ -266,29 +173,29 @@ static int move_real_workdir(const char *dest, int parents, int force)
        rc = stat(dest, &s);
        if (rc == 0) {
                if (!S_ISDIR(s.st_mode)) {
-                       syslog(LOG_ERR, "in move_real_workdir, can't overwrite regular file %s", dest);
+                       ERROR("in move_workdir, can't overwrite regular file %s", dest);
                        errno = EEXIST;
                        return -1;
                }
                if (!force) {
-                       syslog(LOG_ERR, "in move_real_workdir, can't overwrite regular file %s", dest);
+                       ERROR("in move_workdir, can't overwrite regular file %s", dest);
                        errno = EEXIST;
                        return -1;
                }
-               rc = clean_dir(dest);
+               rc = remove_directory_content(dest);
                if (rc) {
-                       syslog(LOG_ERR, "in move_real_workdir, can't clean dir %s", dest);
+                       ERROR("in move_workdir, can't clean dir %s", dest);
                        return rc;
                }
                rc = rmdir(dest);
                if (rc) {
-                       syslog(LOG_ERR, "in move_real_workdir, can't remove dir %s", dest);
+                       ERROR("in move_workdir, can't remove dir %s", dest);
                        return rc;
                }
        } else {
                /* check parent of dest */
                iter = strrchr(dest, '/');
-               len = iter ? iter - dest : 0;
+               len = iter ? (size_t)(iter - dest) : 0;
                if (len) {
                        /* is parent existing? */
                        copy = strndupa(dest, len);
@@ -296,45 +203,17 @@ static int move_real_workdir(const char *dest, int parents, int force)
                        if (!rc) {
                                /* found an entry */
                                if (!S_ISDIR(s.st_mode)) {
-                                       syslog(LOG_ERR, "in move_real_workdir, '%s' isn't a directory", copy);
+                                       ERROR("in move_workdir, '%s' isn't a directory", copy);
                                        errno = ENOTDIR;
                                        return -1;
                                }
                        } else if (!parents) {
                                /* parent entry not found but not allowed to create it */
-                               syslog(LOG_ERR, "in move_real_workdir, parent directory '%s' not found: %m", copy);
+                               ERROR("in move_workdir, parent directory '%s' not found: %m", copy);
+                               return -1;
+                       } else if (create_directory(copy, dirmode, 1)) {
+                               ERROR("in move_workdir, creation of directory %s failed: %m", copy);
                                return -1;
-                       } else {
-                               /* parent entries to be created */
-                               l = len;
-                               for(;;) {
-                                       /* backward loop */
-                                       rc = mkdir(copy, mode);
-                                       if (!rc)
-                                               break;
-                                       if (errno != ENOENT) {
-                                               syslog(LOG_ERR, "in move_real_workdir, mkdir '%s' failed: %m", copy);
-                                               return -1;
-                                       }
-                                       while (l && copy[l] != '/')
-                                               l--;
-                                       if (l == 0) {
-                                               syslog(LOG_ERR, "in move_real_workdir, internal error");
-                                               errno = EINVAL;
-                                               return -1;
-                                       }
-                                       copy[l] = 0;
-                               }
-                               while(l < len) {
-                                       /* forward loop */
-                                       copy[l] = '/';
-                                       while (copy[++l]);
-                                       rc = mkdir(copy, mode);
-                                       if (rc && errno != EEXIST) {
-                                               syslog(LOG_ERR, "in move_real_workdir, mkdir '%s' failed: %m", copy);
-                                               return -1;
-                                       }
-                               }
                        }
                }
        }
@@ -344,28 +223,10 @@ static int move_real_workdir(const char *dest, int parents, int force)
        workdirfd = -1;
        rc = renameat(AT_FDCWD, workdir, AT_FDCWD, dest);
        if (rc) {
-               syslog(LOG_ERR, "in move_real_workdir, renameat failed %s -> %s: %m", workdir, dest);
+               ERROR("in move_workdir, renameat failed %s -> %s: %m", workdir, dest);
                return -1;
        }
 
-       return set_real_workdir(dest, 0);
-}
-
-int move_workdir(const char *dest, int parents, int force)
-{
-       char *rp;
-       int rc;
-
-       if (dest[0] == '/')
-               return move_real_workdir(dest, parents, force);
-
-       rp = realpath(dest, NULL);
-       if (!rp) {
-               syslog(LOG_ERR, "realpath failed for %s", dest);
-               return -1;
-       }
-       rc = move_real_workdir(rp, parents, force);
-       free(rp);
-       return rc;
+       return set_workdir(dest, 0);
 }