Home | History | Annotate | Download | only in lib
      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  */
     94 extern char *TCID;		/* defined/initialized in main() */
     95 static char *TESTDIR = NULL;	/* the directory created */
     96 
     97 static char test_start_work_dir[PATH_MAX];
     98 
     99 /* lib/tst_checkpoint.c */
    100 extern futex_t *tst_futexes;
    101 
    102 int tst_tmpdir_created(void)
    103 {
    104 	return TESTDIR != NULL;
    105 }
    106 
    107 char *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 
    117 const char *tst_get_startwd(void)
    118 {
    119 	return test_start_work_dir;
    120 }
    121 
    122 static 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 
    232 void 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 
    302 void 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