• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static int rmobj(const char *obj, char **errmsg);
103 
tst_tmpdir_created(void)104 int tst_tmpdir_created(void)
105 {
106 	return TESTDIR != NULL;
107 }
108 
tst_get_tmpdir(void)109 char *tst_get_tmpdir(void)
110 {
111 	char *ret = NULL;
112 
113 	if (TESTDIR == NULL) {
114 		tst_brkm(TBROK, NULL, "you must call tst_tmpdir() first");
115 		return NULL;
116 	}
117 
118 	ret = strdup(TESTDIR);
119 	if (!ret)
120 		tst_brkm(TBROK, NULL, "strdup() failed");
121 
122 	return ret;
123 }
124 
tst_get_tmpdir_root(void)125 const char *tst_get_tmpdir_root(void)
126 {
127 	const char *env_tmpdir = getenv("TMPDIR");
128 
129 	if (!env_tmpdir)
130 		env_tmpdir = TEMPDIR;
131 
132 	if (env_tmpdir[0] != '/') {
133 		tst_brkm(TBROK, NULL, "You must specify an absolute "
134 				"pathname for environment variable TMPDIR");
135 		return NULL;
136 	}
137 	return env_tmpdir;
138 }
139 
tst_get_startwd(void)140 const char *tst_get_startwd(void)
141 {
142 	return test_start_work_dir;
143 }
144 
purge_dir(const char * path,char ** errptr)145 static int purge_dir(const char *path, char **errptr)
146 {
147 	int ret_val = 0;
148 	DIR *dir;
149 	struct dirent *dir_ent;
150 	char dirobj[PATH_MAX];
151 	static char err_msg[PATH_MAX + 1280];
152 
153 	/* Do NOT perform the request if the directory is "/" */
154 	if (!strcmp(path, "/")) {
155 		if (errptr) {
156 			strcpy(err_msg, "Cannot purge system root directory");
157 			*errptr = err_msg;
158 		}
159 
160 		return -1;
161 	}
162 
163 	errno = 0;
164 
165 	/* Open the directory to get access to what is in it */
166 	if (!(dir = opendir(path))) {
167 		if (errptr) {
168 			sprintf(err_msg,
169 				"Cannot open directory %s; errno=%d: %s",
170 				path, errno, tst_strerrno(errno));
171 			*errptr = err_msg;
172 		}
173 		return -1;
174 	}
175 
176 	/* Loop through the entries in the directory, removing each one */
177 	for (dir_ent = readdir(dir); dir_ent; dir_ent = readdir(dir)) {
178 		/* Don't remove "." or ".." */
179 		if (!strcmp(dir_ent->d_name, ".")
180 		    || !strcmp(dir_ent->d_name, ".."))
181 			continue;
182 
183 		/* Recursively remove the current entry */
184 		sprintf(dirobj, "%s/%s", path, dir_ent->d_name);
185 		if (rmobj(dirobj, errptr) != 0)
186 			ret_val = -1;
187 	}
188 
189 	closedir(dir);
190 	return ret_val;
191 }
192 
rmobj(const char * obj,char ** errmsg)193 static int rmobj(const char *obj, char **errmsg)
194 {
195 	int ret_val = 0;
196 	struct stat statbuf;
197 	static char err_msg[PATH_MAX + 1280];
198 	int fd;
199 
200 	fd = open(obj, O_DIRECTORY | O_NOFOLLOW);
201 	if (fd >= 0) {
202 		close(fd);
203 		ret_val = purge_dir(obj, errmsg);
204 
205 		/* If there were problems removing an entry, don't attempt to
206 		   remove the directory itself */
207 		if (ret_val == -1)
208 			return -1;
209 
210 		/* Get the link count, now that all the entries have been removed */
211 		if (lstat(obj, &statbuf) < 0) {
212 			if (errmsg != NULL) {
213 				sprintf(err_msg,
214 					"lstat(%s) failed; errno=%d: %s", obj,
215 					errno, tst_strerrno(errno));
216 				*errmsg = err_msg;
217 			}
218 			return -1;
219 		}
220 
221 		/* Remove the directory itself */
222 		if (statbuf.st_nlink >= 3) {
223 			/* The directory is linked; unlink() must be used */
224 			if (unlink(obj) < 0) {
225 				if (errmsg != NULL) {
226 					sprintf(err_msg,
227 						"unlink(%s) failed; errno=%d: %s",
228 						obj, errno, tst_strerrno(errno));
229 					*errmsg = err_msg;
230 				}
231 				return -1;
232 			}
233 		} else {
234 			/* The directory is not linked; remove() can be used */
235 			if (remove(obj) < 0) {
236 				if (errmsg != NULL) {
237 					sprintf(err_msg,
238 						"remove(%s) failed; errno=%d: %s",
239 						obj, errno, tst_strerrno(errno));
240 					*errmsg = err_msg;
241 				}
242 				return -1;
243 			}
244 		}
245 	} else {
246 		if (unlink(obj) < 0) {
247 			if (errmsg != NULL) {
248 				sprintf(err_msg,
249 					"unlink(%s) failed; errno=%d: %s", obj,
250 					errno, tst_strerrno(errno));
251 				*errmsg = err_msg;
252 			}
253 			return -1;
254 		}
255 	}
256 
257 	return 0;
258 }
259 
tst_tmpdir(void)260 void tst_tmpdir(void)
261 {
262 	char template[PATH_MAX];
263 	const char *env_tmpdir;
264 	char *errmsg;
265 
266 	/*
267 	 * Create a template for the temporary directory.  Use the
268 	 * environment variable TMPDIR if it is available, otherwise
269 	 * use our default TEMPDIR.
270 	 */
271 	env_tmpdir = tst_get_tmpdir_root();
272 	snprintf(template, PATH_MAX, "%s/LTP_%.3sXXXXXX", env_tmpdir, TCID);
273 
274 	/* Make the temporary directory in one shot using mkdtemp. */
275 	if (mkdtemp(template) == NULL) {
276 		tst_brkm(TBROK | TERRNO, NULL,
277 			 "%s: mkdtemp(%s) failed", __func__, template);
278 		return;
279 	}
280 
281 	if ((TESTDIR = strdup(template)) == NULL) {
282 		tst_brkm(TBROK | TERRNO, NULL,
283 			 "%s: strdup(%s) failed", __func__, template);
284 		return;
285 	}
286 
287 	SAFE_CHOWN(NULL, TESTDIR, -1, getgid());
288 
289 	SAFE_CHMOD(NULL, TESTDIR, DIR_MODE);
290 
291 	if (getcwd(test_start_work_dir, sizeof(test_start_work_dir)) == NULL) {
292 		tst_resm(TINFO, "Failed to record test working dir");
293 		test_start_work_dir[0] = '\0';
294 	}
295 
296 	/*
297 	 * Change to the temporary directory.  If the chdir() fails, issue
298 	 * TBROK messages for all test cases, attempt to remove the
299 	 * directory (if it was created), and exit.  If the removal also
300 	 * fails, also issue a TWARN message.
301 	 */
302 	if (chdir(TESTDIR) == -1) {
303 		tst_resm(TERRNO, "%s: chdir(%s) failed", __func__, TESTDIR);
304 
305 		/* Try to remove the directory */
306 		if (rmobj(TESTDIR, &errmsg) == -1) {
307 			tst_resm(TWARN, "%s: rmobj(%s) failed: %s",
308 				 __func__, TESTDIR, errmsg);
309 		}
310 
311 		tst_exit();
312 	}
313 }
314 
tst_rmdir(void)315 void tst_rmdir(void)
316 {
317 	char *errmsg;
318 
319 	/*
320 	 * Check that TESTDIR is not NULL.
321 	 */
322 	if (TESTDIR == NULL) {
323 		tst_resm(TWARN,
324 			 "%s: TESTDIR was NULL; no removal attempted",
325 			 __func__);
326 		return;
327 	}
328 
329 	/*
330 	 * Unmap the backend file.
331 	 * This is needed to overcome the NFS "silly rename" feature.
332 	 */
333 	if (tst_futexes) {
334 		msync((void *)tst_futexes, getpagesize(), MS_SYNC);
335 		munmap((void *)tst_futexes, getpagesize());
336 	}
337 
338 	/*
339 	 * Attempt to remove the "TESTDIR" directory, using rmobj().
340 	 */
341 	if (rmobj(TESTDIR, &errmsg) == -1) {
342 		tst_resm(TWARN, "%s: rmobj(%s) failed: %s",
343 			 __func__, TESTDIR, errmsg);
344 	}
345 }
346 
tst_purge_dir(const char * path)347 void tst_purge_dir(const char *path)
348 {
349 	char *err;
350 
351 	if (purge_dir(path, &err))
352 		tst_brkm(TBROK, NULL, "%s: %s", __func__, err);
353 }
354