1/********************************************************** 2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of version 2 of the GNU General Public License as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it would be useful, but 9 * WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 11 * 12 * Further, this software is distributed without any warranty that it is 13 * free of the rightful claim of any third person regarding infringement 14 * or the like. Any license provided herein, whether implied or 15 * otherwise, applies only to this software file. Patent licenses, if 16 * any, provided herein do not apply to combinations of this program with 17 * other software, or any other product whatsoever. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with this program; if not, write the Free Software Foundation, Inc., 21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, 24 * Mountain View, CA 94043, or: 25 * 26 * http://www.sgi.com 27 * 28 * For further information regarding this notice, see: 29 * 30 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ 31 *********************************************************/ 32 33/********************************************************** 34 * 35 * OS Testing - Silicon Graphics, Inc. 36 * 37 * FUNCTION NAME : tst_tmpdir, tst_rmdir 38 * 39 * FUNCTION TITLE : Create/remove a testing temp dir 40 * 41 * SYNOPSIS: 42 * void tst_tmpdir(); 43 * void tst_rmdir(); 44 * 45 * AUTHOR : Dave Fenner 46 * 47 * INITIAL RELEASE : UNICOS 8.0 48 * 49 * DESCRIPTION 50 * tst_tmpdir() is used to create a unique, temporary testing 51 * directory, and make it the current working directory. 52 * tst_rmdir() is used to remove the directory created by 53 * tst_tmpdir(). 54 * 55 * RETURN VALUE 56 * Neither tst_tmpdir() or tst_rmdir() has a return value. 57 * 58 *********************************************************/ 59#define _GNU_SOURCE 60#include <sys/mman.h> 61#include <sys/types.h> 62#include <sys/stat.h> 63#include <assert.h> 64#include <errno.h> 65#include <libgen.h> 66#include <stdio.h> 67#include <stdlib.h> 68#include <string.h> 69#include <unistd.h> 70#include <dirent.h> 71#include <fcntl.h> 72 73#include "test.h" 74#include "safe_macros.h" 75#include "ltp_priv.h" 76#include "lapi/futex.h" 77 78/* 79 * Define some useful macros. 80 */ 81#define DIR_MODE (S_IRWXU|S_IRWXG|S_IRWXO) 82 83#ifndef PATH_MAX 84#ifdef MAXPATHLEN 85#define PATH_MAX MAXPATHLEN 86#else 87#define PATH_MAX 1024 88#endif 89#endif 90 91/* 92 * Define global variables. 93 */ 94extern char *TCID; /* defined/initialized in main() */ 95static char *TESTDIR = NULL; /* the directory created */ 96 97static char test_start_work_dir[PATH_MAX]; 98 99/* lib/tst_checkpoint.c */ 100extern futex_t *tst_futexes; 101 102int tst_tmpdir_created(void) 103{ 104 return TESTDIR != NULL; 105} 106 107char *tst_get_tmpdir(void) 108{ 109 if (TESTDIR == NULL) { 110 tst_brkm(TBROK, NULL, "you must call tst_tmpdir() first"); 111 return NULL; 112 } 113 114 return strdup(TESTDIR); 115} 116 117const char *tst_get_startwd(void) 118{ 119 return test_start_work_dir; 120} 121 122static int rmobj(char *obj, char **errmsg) 123{ 124 int ret_val = 0; 125 DIR *dir; 126 struct dirent *dir_ent; 127 char dirobj[PATH_MAX]; 128 struct stat statbuf; 129 static char err_msg[1024]; 130 int fd; 131 132 fd = open(obj, O_DIRECTORY | O_NOFOLLOW); 133 if (fd != -1) { 134 close(fd); 135 136 /* Do NOT perform the request if the directory is "/" */ 137 if (!strcmp(obj, "/")) { 138 if (errmsg != NULL) { 139 sprintf(err_msg, "Cannot remove /"); 140 *errmsg = err_msg; 141 } 142 return -1; 143 } 144 145 /* Open the directory to get access to what is in it */ 146 if ((dir = opendir(obj)) == NULL) { 147 if (rmdir(obj) != 0) { 148 if (errmsg != NULL) { 149 sprintf(err_msg, 150 "rmdir(%s) failed; errno=%d: %s", 151 obj, errno, tst_strerrno(errno)); 152 *errmsg = err_msg; 153 } 154 return -1; 155 } else { 156 return 0; 157 } 158 } 159 160 /* Loop through the entries in the directory, removing each one */ 161 for (dir_ent = (struct dirent *)readdir(dir); 162 dir_ent != NULL; dir_ent = (struct dirent *)readdir(dir)) { 163 164 /* Don't remove "." or ".." */ 165 if (!strcmp(dir_ent->d_name, ".") 166 || !strcmp(dir_ent->d_name, "..")) 167 continue; 168 169 /* Recursively call this routine to remove the current entry */ 170 sprintf(dirobj, "%s/%s", obj, dir_ent->d_name); 171 if (rmobj(dirobj, errmsg) != 0) 172 ret_val = -1; 173 } 174 175 closedir(dir); 176 177 /* If there were problems removing an entry, don't attempt to 178 remove the directory itself */ 179 if (ret_val == -1) 180 return -1; 181 182 /* Get the link count, now that all the entries have been removed */ 183 if (lstat(obj, &statbuf) < 0) { 184 if (errmsg != NULL) { 185 sprintf(err_msg, 186 "lstat(%s) failed; errno=%d: %s", obj, 187 errno, tst_strerrno(errno)); 188 *errmsg = err_msg; 189 } 190 return -1; 191 } 192 193 /* Remove the directory itself */ 194 if (statbuf.st_nlink >= 3) { 195 /* The directory is linked; unlink() must be used */ 196 if (unlink(obj) < 0) { 197 if (errmsg != NULL) { 198 sprintf(err_msg, 199 "unlink(%s) failed; errno=%d: %s", 200 obj, errno, tst_strerrno(errno)); 201 *errmsg = err_msg; 202 } 203 return -1; 204 } 205 } else { 206 /* The directory is not linked; remove() can be used */ 207 if (remove(obj) < 0) { 208 if (errmsg != NULL) { 209 sprintf(err_msg, 210 "remove(%s) failed; errno=%d: %s", 211 obj, errno, tst_strerrno(errno)); 212 *errmsg = err_msg; 213 } 214 return -1; 215 } 216 } 217 } else { 218 if (unlink(obj) < 0) { 219 if (errmsg != NULL) { 220 sprintf(err_msg, 221 "unlink(%s) failed; errno=%d: %s", obj, 222 errno, tst_strerrno(errno)); 223 *errmsg = err_msg; 224 } 225 return -1; 226 } 227 } 228 229 return 0; 230} 231 232void tst_tmpdir(void) 233{ 234 char template[PATH_MAX]; 235 char *env_tmpdir; 236 char *errmsg, *c; 237 238 /* 239 * Create a template for the temporary directory. Use the 240 * environment variable TMPDIR if it is available, otherwise 241 * use our default TEMPDIR. 242 */ 243 env_tmpdir = getenv("TMPDIR"); 244 if (env_tmpdir) { 245 c = strchr(env_tmpdir, '/'); 246 /* 247 * Now we force environment variable TMPDIR to be an absolute 248 * pathname, which dose not make much sense, but it will 249 * greatly simplify code in tst_rmdir(). 250 */ 251 if (c != env_tmpdir) { 252 tst_brkm(TBROK, NULL, "You must specify an absolute " 253 "pathname for environment variable TMPDIR"); 254 return; 255 } 256 snprintf(template, PATH_MAX, "%s/%.3sXXXXXX", env_tmpdir, TCID); 257 } else { 258 snprintf(template, PATH_MAX, "%s/%.3sXXXXXX", TEMPDIR, TCID); 259 } 260 261 /* Make the temporary directory in one shot using mkdtemp. */ 262 if (mkdtemp(template) == NULL) { 263 tst_brkm(TBROK | TERRNO, NULL, 264 "%s: mkdtemp(%s) failed", __func__, template); 265 return; 266 } 267 268 if ((TESTDIR = strdup(template)) == NULL) { 269 tst_brkm(TBROK | TERRNO, NULL, 270 "%s: strdup(%s) failed", __func__, template); 271 return; 272 } 273 274 SAFE_CHOWN(NULL, TESTDIR, -1, getgid()); 275 276 SAFE_CHMOD(NULL, TESTDIR, DIR_MODE); 277 278 if (getcwd(test_start_work_dir, sizeof(test_start_work_dir)) == NULL) { 279 tst_resm(TINFO, "Failed to record test working dir"); 280 test_start_work_dir[0] = '\0'; 281 } 282 283 /* 284 * Change to the temporary directory. If the chdir() fails, issue 285 * TBROK messages for all test cases, attempt to remove the 286 * directory (if it was created), and exit. If the removal also 287 * fails, also issue a TWARN message. 288 */ 289 if (chdir(TESTDIR) == -1) { 290 tst_resm(TERRNO, "%s: chdir(%s) failed", __func__, TESTDIR); 291 292 /* Try to remove the directory */ 293 if (rmobj(TESTDIR, &errmsg) == -1) { 294 tst_resm(TWARN, "%s: rmobj(%s) failed: %s", 295 __func__, TESTDIR, errmsg); 296 } 297 298 tst_exit(); 299 } 300} 301 302void tst_rmdir(void) 303{ 304 char *errmsg; 305 306 /* 307 * Check that TESTDIR is not NULL. 308 */ 309 if (TESTDIR == NULL) { 310 tst_resm(TWARN, 311 "%s: TESTDIR was NULL; no removal attempted", 312 __func__); 313 return; 314 } 315 316 /* 317 * Unmap the backend file. 318 * This is needed to overcome the NFS "silly rename" feature. 319 */ 320 if (tst_futexes) { 321 msync((void *)tst_futexes, getpagesize(), MS_SYNC); 322 munmap((void *)tst_futexes, getpagesize()); 323 } 324 325 /* 326 * Attempt to remove the "TESTDIR" directory, using rmobj(). 327 */ 328 if (rmobj(TESTDIR, &errmsg) == -1) { 329 tst_resm(TWARN, "%s: rmobj(%s) failed: %s", 330 __func__, TESTDIR, errmsg); 331 } 332} 333