Update date in copyrights
[src/app-framework-main.git] / src / wgtpkg-workdir.c
index 12cfa24..d0f1472 100644 (file)
@@ -1,5 +1,7 @@
 /*
- Copyright 2015 IoT.bzh
+ Copyright (C) 2015-2019 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 "wgtpkg.h"
+#include "verbose.h"
+#include "wgtpkg-workdir.h"
+#include "utils-dir.h"
 
-static int mode = 0700;
-static char workdir[PATH_MAX];
+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)
+/* removes the working directory */
+void remove_workdir()
 {
-       int cr, fd;
-       DIR *dir;
-       struct dirent *ent;
-       struct {
-               struct dirent entry;
-               char spare[PATH_MAX];
-       } entry;
-
-       dir = fdopendir(dirfd);
-       if (dir == NULL) {
-               syslog(LOG_ERR, "opendir 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;
+       assert(workdirfd >= 0);
+       remove_directory_content_fd(workdirfd);
+       close(workdirfd);
+       workdirfd = -1;
+       rmdir(workdir);
+       workdir[0] = 0;
 }
 
-/* removes recursively the content of a directory */
-static int clean_dir(const char *directory)
+static void put_workdir(int fd, const char *name, size_t length)
 {
-       int fd, rc;
+       /* close the previous directory if any */
+       if (workdirfd >= 0)
+               close(workdirfd);
 
-       fd = openat(AT_FDCWD, directory, O_DIRECTORY|O_RDONLY);
+       /* copy the name and the fd if existing */
        if (fd < 0) {
-               syslog(LOG_ERR, "opening directory %s failed in clean_dir", directory);
-               return fd;
+               workdir[0] = '.';
+               workdir[1] = 0;
+               workdirfd = AT_FDCWD;
+       } else {
+
+               assert(length < sizeof workdir);
+               memcpy(workdir, name, 1 + length);
+               workdirfd = fd;
        }
-       rc = clean_dirfd(fd);
-       close(fd);
-       return rc;
-}
-
-/* removes the content of the working directory */
-int enter_workdir(int clean)
-{
-       int rc = chdir(workdir);
-       if (rc)
-               syslog(LOG_ERR, "entring workdir %s failed", workdir);
-       else if (clean)
-               rc = clean_dir(workdir);
-       return rc;
-}
-
-/* removes the working directory */
-void remove_workdir()
-{
-       enter_workdir(1);
-       chdir("..");
-       rmdir(workdir);
 }
 
 int set_workdir(const char *name, int create)
 {
-       int rc;
+       int rc, dirfd;
        size_t length;
-       struct stat s;
 
        /* 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;
        }
 
-       rc = stat(name, &s);
-       if (rc) {
+       /* check if . */
+       if (length == 1 && name[0] == '.') {
+               put_workdir(AT_FDCWD, name, length);
+               return 0;
+       }
+
+       /* opens the directory */
+       dirfd = openat(AT_FDCWD, name, O_PATH|O_DIRECTORY|O_RDONLY);
+       if (dirfd < 0) {
+               if (errno != ENOENT) {
+                       ERROR("error while opening workdir %s: %m", name);
+                       return -1;
+               }
                if (!create) {
-                       syslog(LOG_ERR, "no workdir %s", name);
+                       ERROR("workdir %s doesn't exist", name);
                        return -1;
                }
-               rc = mkdir(name, mode);
+               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 = openat(AT_FDCWD, name, O_PATH|O_DIRECTORY|O_RDONLY);
+               if (dirfd < 0) {
+                       ERROR("can't open workdir %s", name);
                        return -1;
                }
-
-       } else if (!S_ISDIR(s.st_mode)) {
-               syslog(LOG_ERR, "%s isn't a directory", name);
-               errno = ENOTDIR;
-               return -1;
        }
-       memcpy(workdir, name, 1+length);
+
+       /* record new workdir */
+       put_workdir(dirfd, name, length);
        return 0;
 }
 
-int make_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;
        }
@@ -170,43 +132,107 @@ int make_workdir_base(const char *root, const char *prefix, int reuse)
        /* 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)
                        break;
        }
+       workdirfd = openat(AT_FDCWD, workdir, O_RDONLY|O_DIRECTORY);
+       if (workdirfd < 0) {
+               ERROR("error in onnection to workdir %s: %m", workdir);
+               rmdir(workdir);
+               return -1;
+       }
 
        return 0;
 }
 
-int make_workdir(int reuse)
+int move_workdir(const char *dest, int parents, int force)
 {
-       return make_workdir_base(".", "PACK", reuse);
-}
+       int rc;
+       size_t len;
+       struct stat s;
+       char *copy;
+       const char *iter;
 
-int workdirfd()
-{
-       int result = open(workdir, O_PATH|O_DIRECTORY);
-       if (result < 0)
-               syslog(LOG_ERR, "can't get fd for workdir %.*s: %m", PATH_MAX, workdir);
-       return result;
-}
+       /* check length */
+       if (strlen(dest) >= sizeof workdir) {
+               ERROR("destination dirname too long");
+               errno = EINVAL;
+               return -1;
+       }
 
-int move_workdir(const char *dest, int parents, int force)
-{
-       
+       /* if an existing directory exist remove it if force */
+       rc = stat(dest, &s);
+       if (rc == 0) {
+               if (!S_ISDIR(s.st_mode)) {
+                       ERROR("in move_workdir, can't overwrite regular file %s", dest);
+                       errno = EEXIST;
+                       return -1;
+               }
+               if (!force) {
+                       ERROR("in move_workdir, can't overwrite regular file %s", dest);
+                       errno = EEXIST;
+                       return -1;
+               }
+               rc = remove_directory_content(dest);
+               if (rc) {
+                       ERROR("in move_workdir, can't clean dir %s", dest);
+                       return rc;
+               }
+               rc = rmdir(dest);
+               if (rc) {
+                       ERROR("in move_workdir, can't remove dir %s", dest);
+                       return rc;
+               }
+       } else {
+               /* check parent of dest */
+               iter = strrchr(dest, '/');
+               len = iter ? (size_t)(iter - dest) : 0;
+               if (len) {
+                       /* is parent existing? */
+                       copy = strndupa(dest, len);
+                       rc = stat(copy, &s);
+                       if (!rc) {
+                               /* found an entry */
+                               if (!S_ISDIR(s.st_mode)) {
+                                       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 */
+                               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;
+                       }
+               }
+       }
+
+       /* try to rename now */
+       close(workdirfd);
+       workdirfd = -1;
+       rc = renameat(AT_FDCWD, workdir, AT_FDCWD, dest);
+       if (rc) {
+               ERROR("in move_workdir, renameat failed %s -> %s: %m", workdir, dest);
+               return -1;
+       }
+
+       return set_workdir(dest, 0);
 }