/*
- 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 "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) {
+ /* 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;
}
/* 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);
}