diff options
Diffstat (limited to 'src/kernel/tests/lib/tst_tmpdir.c')
-rw-r--r-- | src/kernel/tests/lib/tst_tmpdir.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/src/kernel/tests/lib/tst_tmpdir.c b/src/kernel/tests/lib/tst_tmpdir.c new file mode 100644 index 0000000..0c39eb8 --- /dev/null +++ b/src/kernel/tests/lib/tst_tmpdir.c @@ -0,0 +1,347 @@ +/********************************************************** + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + *********************************************************/ + +/********************************************************** + * + * OS Testing - Silicon Graphics, Inc. + * + * FUNCTION NAME : tst_tmpdir, tst_rmdir + * + * FUNCTION TITLE : Create/remove a testing temp dir + * + * SYNOPSIS: + * void tst_tmpdir(); + * void tst_rmdir(); + * + * AUTHOR : Dave Fenner + * + * INITIAL RELEASE : UNICOS 8.0 + * + * DESCRIPTION + * tst_tmpdir() is used to create a unique, temporary testing + * directory, and make it the current working directory. + * tst_rmdir() is used to remove the directory created by + * tst_tmpdir(). + * + * RETURN VALUE + * Neither tst_tmpdir() or tst_rmdir() has a return value. + * + *********************************************************/ +#define _GNU_SOURCE +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <assert.h> +#include <errno.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <dirent.h> +#include <fcntl.h> + +#include "test.h" +#include "safe_macros.h" +#include "ltp_priv.h" +#include "lapi/futex.h" + +/* + * Define some useful macros. + */ +#define DIR_MODE (S_IRWXU|S_IRWXG|S_IRWXO) + +#ifndef PATH_MAX +#ifdef MAXPATHLEN +#define PATH_MAX MAXPATHLEN +#else +#define PATH_MAX 1024 +#endif +#endif + +/* + * Define global variables. + */ +extern char *TCID; /* defined/initialized in main() */ +static char *TESTDIR = NULL; /* the directory created */ + +static char test_start_work_dir[PATH_MAX]; + +/* lib/tst_checkpoint.c */ +extern futex_t *tst_futexes; + +static int rmobj(const char *obj, char **errmsg); + +int tst_tmpdir_created(void) +{ + return TESTDIR != NULL; +} + +char *tst_get_tmpdir(void) +{ + if (TESTDIR == NULL) { + tst_brkm(TBROK, NULL, "you must call tst_tmpdir() first"); + return NULL; + } + + return strdup(TESTDIR); +} + +const char *tst_get_startwd(void) +{ + return test_start_work_dir; +} + +static int purge_dir(const char *path, char **errptr) +{ + int ret_val = 0; + DIR *dir; + struct dirent *dir_ent; + char dirobj[PATH_MAX]; + static char err_msg[PATH_MAX + 1280]; + + /* Do NOT perform the request if the directory is "/" */ + if (!strcmp(path, "/")) { + if (errptr) { + strcpy(err_msg, "Cannot purge system root directory"); + *errptr = err_msg; + } + + return -1; + } + + errno = 0; + + /* Open the directory to get access to what is in it */ + if (!(dir = opendir(path))) { + if (errptr) { + sprintf(err_msg, + "Cannot open directory %s; errno=%d: %s", + path, errno, tst_strerrno(errno)); + *errptr = err_msg; + } + return -1; + } + + /* Loop through the entries in the directory, removing each one */ + for (dir_ent = readdir(dir); dir_ent; dir_ent = readdir(dir)) { + /* Don't remove "." or ".." */ + if (!strcmp(dir_ent->d_name, ".") + || !strcmp(dir_ent->d_name, "..")) + continue; + + /* Recursively remove the current entry */ + sprintf(dirobj, "%s/%s", path, dir_ent->d_name); + if (rmobj(dirobj, errptr) != 0) + ret_val = -1; + } + + closedir(dir); + return ret_val; +} + +static int rmobj(const char *obj, char **errmsg) +{ + int ret_val = 0; + struct stat statbuf; + static char err_msg[PATH_MAX + 1280]; + int fd; + + fd = open(obj, O_DIRECTORY | O_NOFOLLOW); + if (fd >= 0) { + close(fd); + ret_val = purge_dir(obj, errmsg); + + /* If there were problems removing an entry, don't attempt to + remove the directory itself */ + if (ret_val == -1) + return -1; + + /* Get the link count, now that all the entries have been removed */ + if (lstat(obj, &statbuf) < 0) { + if (errmsg != NULL) { + sprintf(err_msg, + "lstat(%s) failed; errno=%d: %s", obj, + errno, tst_strerrno(errno)); + *errmsg = err_msg; + } + return -1; + } + + /* Remove the directory itself */ + if (statbuf.st_nlink >= 3) { + /* The directory is linked; unlink() must be used */ + if (unlink(obj) < 0) { + if (errmsg != NULL) { + sprintf(err_msg, + "unlink(%s) failed; errno=%d: %s", + obj, errno, tst_strerrno(errno)); + *errmsg = err_msg; + } + return -1; + } + } else { + /* The directory is not linked; remove() can be used */ + if (remove(obj) < 0) { + if (errmsg != NULL) { + sprintf(err_msg, + "remove(%s) failed; errno=%d: %s", + obj, errno, tst_strerrno(errno)); + *errmsg = err_msg; + } + return -1; + } + } + } else { + if (unlink(obj) < 0) { + if (errmsg != NULL) { + sprintf(err_msg, + "unlink(%s) failed; errno=%d: %s", obj, + errno, tst_strerrno(errno)); + *errmsg = err_msg; + } + return -1; + } + } + + return 0; +} + +void tst_tmpdir(void) +{ + char template[PATH_MAX]; + char *env_tmpdir; + char *errmsg, *c; + + /* + * Create a template for the temporary directory. Use the + * environment variable TMPDIR if it is available, otherwise + * use our default TEMPDIR. + */ + env_tmpdir = getenv("TMPDIR"); + if (env_tmpdir) { + c = strchr(env_tmpdir, '/'); + /* + * Now we force environment variable TMPDIR to be an absolute + * pathname, which dose not make much sense, but it will + * greatly simplify code in tst_rmdir(). + */ + if (c != env_tmpdir) { + tst_brkm(TBROK, NULL, "You must specify an absolute " + "pathname for environment variable TMPDIR"); + return; + } + snprintf(template, PATH_MAX, "%s/%.3sXXXXXX", env_tmpdir, TCID); + } else { + snprintf(template, PATH_MAX, "%s/%.3sXXXXXX", TEMPDIR, TCID); + } + + /* Make the temporary directory in one shot using mkdtemp. */ + if (mkdtemp(template) == NULL) { + tst_brkm(TBROK | TERRNO, NULL, + "%s: mkdtemp(%s) failed", __func__, template); + return; + } + + if ((TESTDIR = strdup(template)) == NULL) { + tst_brkm(TBROK | TERRNO, NULL, + "%s: strdup(%s) failed", __func__, template); + return; + } + + SAFE_CHOWN(NULL, TESTDIR, -1, getgid()); + + SAFE_CHMOD(NULL, TESTDIR, DIR_MODE); + + if (getcwd(test_start_work_dir, sizeof(test_start_work_dir)) == NULL) { + tst_resm(TINFO, "Failed to record test working dir"); + test_start_work_dir[0] = '\0'; + } + + /* + * Change to the temporary directory. If the chdir() fails, issue + * TBROK messages for all test cases, attempt to remove the + * directory (if it was created), and exit. If the removal also + * fails, also issue a TWARN message. + */ + if (chdir(TESTDIR) == -1) { + tst_resm(TERRNO, "%s: chdir(%s) failed", __func__, TESTDIR); + + /* Try to remove the directory */ + if (rmobj(TESTDIR, &errmsg) == -1) { + tst_resm(TWARN, "%s: rmobj(%s) failed: %s", + __func__, TESTDIR, errmsg); + } + + tst_exit(); + } +} + +void tst_rmdir(void) +{ + char *errmsg; + + /* + * Check that TESTDIR is not NULL. + */ + if (TESTDIR == NULL) { + tst_resm(TWARN, + "%s: TESTDIR was NULL; no removal attempted", + __func__); + return; + } + + /* + * Unmap the backend file. + * This is needed to overcome the NFS "silly rename" feature. + */ + if (tst_futexes) { + msync((void *)tst_futexes, getpagesize(), MS_SYNC); + munmap((void *)tst_futexes, getpagesize()); + } + + /* + * Attempt to remove the "TESTDIR" directory, using rmobj(). + */ + if (rmobj(TESTDIR, &errmsg) == -1) { + tst_resm(TWARN, "%s: rmobj(%s) failed: %s", + __func__, TESTDIR, errmsg); + } +} + +void tst_purge_dir(const char *path) +{ + char *err; + + if (purge_dir(path, &err)) + tst_brkm(TBROK, NULL, "%s: %s", __func__, err); +} |