blob: b11c8b669e15910594a226c0d3d08d7f3801fc30 [file] [log] [blame]
/*
* Copyright 2011-2012 the Redfish authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "test/temp_dir.h"
#include "util/log.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
// Globals
static int g_tempdir_nonce = 0;
static int g_num_tempdirs = 0;
static char **g_tempdirs = NULL;
static pthread_mutex_t tempdir_lock = PTHREAD_MUTEX_INITIALIZER;
// Functions
static void cleanup_registered_tempdirs(void)
{
int i;
const char *skip_cleanup;
skip_cleanup = getenv("SKIP_CLEANUP");
if (skip_cleanup)
return;
pthread_mutex_lock(&tempdir_lock);
for (i = 0; i < g_num_tempdirs; ++i) {
recursive_unlink(g_tempdirs[i]);
free(g_tempdirs[i]);
}
free(g_tempdirs);
g_tempdirs = NULL;
pthread_mutex_unlock(&tempdir_lock);
}
char *create_tempdir(const char *name, int mode, char *err, size_t err_len)
{
char *tdir = NULL, tmp[PATH_MAX];
int nonce, pid;
const char *base = getenv("TMPDIR");
err[0] = '\0';
if (!base) {
base = "/tmp";
}
if (base[0] != '/') {
// canonicalize non-abosolute TMPDIR
if (realpath(base, tmp) == NULL) {
int e = errno;
snprintf(err, err_len, "realpath(%s) failed: %s",
base, terror(e));
return NULL;
}
base = tmp;
}
pthread_mutex_lock(&tempdir_lock);
nonce = g_tempdir_nonce++;
pthread_mutex_unlock(&tempdir_lock);
pid = getpid();
if (asprintf(&tdir, "%s/%s.tmp.%08d.%08d",
base, name, pid, nonce) < 0) {
snprintf(err, err_len, "asprintf failed");
return NULL;
}
if (mkdir(tdir, mode) == -1) {
int e = errno;
snprintf(err, err_len, "mkdir(%s) failed: %s", tdir, terror(e));
free(tdir);
return NULL;
}
return tdir;
}
int register_tempdir_for_cleanup(const char *tdir)
{
char **tempdirs;
pthread_mutex_lock(&tempdir_lock);
tempdirs = realloc(g_tempdirs, sizeof(char*) * (g_num_tempdirs + 1));
if (!tempdirs)
return -ENOMEM;
g_tempdirs = tempdirs;
g_tempdirs[g_num_tempdirs] = strdup(tdir);
if (g_num_tempdirs == 0)
atexit(cleanup_registered_tempdirs);
g_num_tempdirs++;
pthread_mutex_unlock(&tempdir_lock);
return 0;
}
void unregister_tempdir_for_cleanup(const char *tdir)
{
int i;
char **tempdirs;
pthread_mutex_lock(&tempdir_lock);
if (g_num_tempdirs == 0) {
pthread_mutex_unlock(&tempdir_lock);
return;
}
for (i = 0; i < g_num_tempdirs; ++i) {
if (strcmp(g_tempdirs[i], tdir) == 0)
break;
}
if (i == g_num_tempdirs) {
pthread_mutex_unlock(&tempdir_lock);
return;
}
free(g_tempdirs[i]);
g_tempdirs[i] = g_tempdirs[g_num_tempdirs - 1];
tempdirs = realloc(g_tempdirs, sizeof(char*) * g_num_tempdirs - 1);
if (tempdirs) {
g_tempdirs = tempdirs;
}
g_num_tempdirs--;
pthread_mutex_unlock(&tempdir_lock);
}
static int recursive_unlink_helper(int dirfd, const char *name)
{
int fd = -1, ret = 0;
DIR *dfd = NULL;
struct stat stat;
struct dirent *de;
if (dirfd >= 0) {
fd = openat(dirfd, name, O_RDONLY);
} else {
fd = open(name, O_RDONLY);
}
if (fd < 0) {
ret = errno;
fprintf(stderr, "error opening %s: %s\n", name, terror(ret));
goto done;
}
if (fstat(fd, &stat) < 0) {
ret = errno;
fprintf(stderr, "failed to stat %s: %s\n", name, terror(ret));
goto done;
}
if (!(S_ISDIR(stat.st_mode))) {
if (unlinkat(dirfd, name, 0)) {
ret = errno;
fprintf(stderr, "failed to unlink %s: %s\n", name, terror(ret));
goto done;
}
} else {
dfd = fdopendir(fd);
if (!dfd) {
ret = errno;
fprintf(stderr, "fopendir(%s) failed: %s\n", name, terror(ret));
goto done;
}
while ((de = readdir(dfd))) {
if (!strcmp(de->d_name, "."))
continue;
if (!strcmp(de->d_name, ".."))
continue;
ret = recursive_unlink_helper(fd, de->d_name);
if (ret)
goto done;
}
if (unlinkat(dirfd, name, AT_REMOVEDIR) < 0) {
ret = errno;
goto done;
}
}
done:
if (fd >= 0) {
close(fd);
}
if (dfd) {
closedir(dfd);
}
return -ret;
}
int recursive_unlink(const char *path)
{
return recursive_unlink_helper(-1, path);
}
// vim:ts=4:sw=4:et