• 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 "tst_buffers.h"
75 #include "safe_macros.h"
76 #include "tst_tmpdir.h"
77 #include "ltp_priv.h"
78 #include "lapi/futex.h"
79 
80 /*
81  * Define some useful macros.
82  */
83 #define DIR_MODE	(S_IRWXU|S_IRWXG|S_IRWXO)
84 
85 #ifndef PATH_MAX
86 #ifdef MAXPATHLEN
87 #define PATH_MAX	MAXPATHLEN
88 #else
89 #define PATH_MAX	1024
90 #endif
91 #endif
92 
93 /*
94  * Define global variables.
95  */
96 extern char *TCID;		/* defined/initialized in main() */
97 static char *TESTDIR;	/* the directory created */
98 
99 static char test_start_work_dir[PATH_MAX];
100 
101 /* lib/tst_checkpoint.c */
102 extern futex_t *tst_futexes;
103 
104 static int rmobj(const char *obj, char **errmsg);
105 
tst_tmpdir_created(void)106 int tst_tmpdir_created(void)
107 {
108 	return TESTDIR != NULL;
109 }
110 
tst_get_tmpdir(void)111 char *tst_get_tmpdir(void)
112 {
113 	char *ret = NULL;
114 
115 	if (TESTDIR == NULL) {
116 		tst_brkm(TBROK, NULL, "you must call tst_tmpdir() first");
117 		return NULL;
118 	}
119 
120 	ret = strdup(TESTDIR);
121 	if (!ret)
122 		tst_brkm(TBROK, NULL, "strdup() failed");
123 
124 	return ret;
125 }
126 
tst_get_tmpdir_root(void)127 const char *tst_get_tmpdir_root(void)
128 {
129 	const char *env_tmpdir = getenv("TMPDIR");
130 
131 	if (!env_tmpdir)
132 		env_tmpdir = TEMPDIR;
133 
134 	if (env_tmpdir[0] != '/') {
135 		tst_brkm(TBROK, NULL, "You must specify an absolute "
136 				"pathname for environment variable TMPDIR");
137 		return NULL;
138 	}
139 	return env_tmpdir;
140 }
141 
tst_get_startwd(void)142 const char *tst_get_startwd(void)
143 {
144 	return test_start_work_dir;
145 }
146 
purge_dir(const char * path,char ** errptr)147 static int purge_dir(const char *path, char **errptr)
148 {
149 	int ret_val = 0;
150 	DIR *dir;
151 	struct dirent *dir_ent;
152 	char dirobj[PATH_MAX];
153 	static char err_msg[PATH_MAX + 1280];
154 
155 	/* Do NOT perform the request if the directory is "/" */
156 	if (!strcmp(path, "/")) {
157 		if (errptr) {
158 			strcpy(err_msg, "Cannot purge system root directory");
159 			*errptr = err_msg;
160 		}
161 
162 		return -1;
163 	}
164 
165 	errno = 0;
166 
167 	/* Open the directory to get access to what is in it */
168 	if (!(dir = opendir(path))) {
169 		if (errptr) {
170 			sprintf(err_msg,
171 				"Cannot open directory %s; errno=%d: %s",
172 				path, errno, tst_strerrno(errno));
173 			*errptr = err_msg;
174 		}
175 		return -1;
176 	}
177 
178 	/* Loop through the entries in the directory, removing each one */
179 	for (dir_ent = readdir(dir); dir_ent; dir_ent = readdir(dir)) {
180 		/* Don't remove "." or ".." */
181 		if (!strcmp(dir_ent->d_name, ".")
182 		    || !strcmp(dir_ent->d_name, ".."))
183 			continue;
184 
185 		/* Recursively remove the current entry */
186 		sprintf(dirobj, "%s/%s", path, dir_ent->d_name);
187 		if (rmobj(dirobj, errptr) != 0)
188 			ret_val = -1;
189 	}
190 
191 	closedir(dir);
192 	return ret_val;
193 }
194 
rmobj(const char * obj,char ** errmsg)195 static int rmobj(const char *obj, char **errmsg)
196 {
197 	int ret_val = 0;
198 	struct stat statbuf;
199 	static char err_msg[PATH_MAX + 1280];
200 	int fd;
201 
202 	fd = open(obj, O_DIRECTORY | O_NOFOLLOW);
203 	if (fd >= 0) {
204 		close(fd);
205 		ret_val = purge_dir(obj, errmsg);
206 
207 		/* If there were problems removing an entry, don't attempt to
208 		   remove the directory itself */
209 		if (ret_val == -1)
210 			return -1;
211 
212 		/* Get the link count, now that all the entries have been removed */
213 		if (lstat(obj, &statbuf) < 0) {
214 			if (errmsg != NULL) {
215 				sprintf(err_msg,
216 					"lstat(%s) failed; errno=%d: %s", obj,
217 					errno, tst_strerrno(errno));
218 				*errmsg = err_msg;
219 			}
220 			return -1;
221 		}
222 
223 		/* Remove the directory itself */
224 		if (statbuf.st_nlink >= 3) {
225 			/* The directory is linked; unlink() must be used */
226 			if (unlink(obj) < 0) {
227 				if (errmsg != NULL) {
228 					sprintf(err_msg,
229 						"unlink(%s) failed; errno=%d: %s",
230 						obj, errno, tst_strerrno(errno));
231 					*errmsg = err_msg;
232 				}
233 				return -1;
234 			}
235 		} else {
236 			/* The directory is not linked; remove() can be used */
237 			if (remove(obj) < 0) {
238 				if (errmsg != NULL) {
239 					sprintf(err_msg,
240 						"remove(%s) failed; errno=%d: %s",
241 						obj, errno, tst_strerrno(errno));
242 					*errmsg = err_msg;
243 				}
244 				return -1;
245 			}
246 		}
247 	} else {
248 		if (unlink(obj) < 0) {
249 			if (errmsg != NULL) {
250 				sprintf(err_msg,
251 					"unlink(%s) failed; errno=%d: %s", obj,
252 					errno, tst_strerrno(errno));
253 				*errmsg = err_msg;
254 			}
255 			return -1;
256 		}
257 	}
258 
259 	return 0;
260 }
261 
tst_tmpdir(void)262 void tst_tmpdir(void)
263 {
264 	char template[PATH_MAX];
265 	const char *env_tmpdir;
266 	char *errmsg;
267 
268 	/*
269 	 * Create a template for the temporary directory.  Use the
270 	 * environment variable TMPDIR if it is available, otherwise
271 	 * use our default TEMPDIR.
272 	 */
273 	env_tmpdir = tst_get_tmpdir_root();
274 	snprintf(template, PATH_MAX, "%s/LTP_%.3sXXXXXX", env_tmpdir, TCID);
275 
276 	/* Make the temporary directory in one shot using mkdtemp. */
277 	if (mkdtemp(template) == NULL) {
278 		tst_brkm(TBROK | TERRNO, NULL,
279 			 "%s: mkdtemp(%s) failed", __func__, template);
280 		return;
281 	}
282 
283 	if ((TESTDIR = strdup(template)) == NULL) {
284 		tst_brkm(TBROK | TERRNO, NULL,
285 			 "%s: strdup(%s) failed", __func__, template);
286 		return;
287 	}
288 
289 	SAFE_CHOWN(NULL, TESTDIR, -1, getgid());
290 
291 	SAFE_CHMOD(NULL, TESTDIR, DIR_MODE);
292 
293 	if (getcwd(test_start_work_dir, sizeof(test_start_work_dir)) == NULL) {
294 		tst_resm(TINFO, "Failed to record test working dir");
295 		test_start_work_dir[0] = '\0';
296 	}
297 
298 	/*
299 	 * Change to the temporary directory.  If the chdir() fails, issue
300 	 * TBROK messages for all test cases, attempt to remove the
301 	 * directory (if it was created), and exit.  If the removal also
302 	 * fails, also issue a TWARN message.
303 	 */
304 	if (chdir(TESTDIR) == -1) {
305 		tst_resm(TERRNO, "%s: chdir(%s) failed", __func__, TESTDIR);
306 
307 		/* Try to remove the directory */
308 		if (rmobj(TESTDIR, &errmsg) == -1) {
309 			tst_resm(TWARN, "%s: rmobj(%s) failed: %s",
310 				 __func__, TESTDIR, errmsg);
311 		}
312 
313 		tst_exit();
314 	}
315 
316 	tst_resm(TINFO, "Using %s as tmpdir (%s filesystem)", TESTDIR,
317 			 tst_fs_type_name(tst_fs_type(NULL, TESTDIR)));
318 }
319 
tst_rmdir(void)320 void tst_rmdir(void)
321 {
322 	char *errmsg;
323 
324 	/*
325 	 * Check that TESTDIR is not NULL.
326 	 */
327 	if (TESTDIR == NULL) {
328 		tst_resm(TWARN,
329 			 "%s: TESTDIR was NULL; no removal attempted",
330 			 __func__);
331 		return;
332 	}
333 
334 	/*
335 	 * Unmap the backend file.
336 	 * This is needed to overcome the NFS "silly rename" feature.
337 	 */
338 	if (tst_futexes) {
339 		msync((void *)tst_futexes, getpagesize(), MS_SYNC);
340 		munmap((void *)tst_futexes, getpagesize());
341 	}
342 
343 	/*
344 	 * Attempt to remove the "TESTDIR" directory, using rmobj().
345 	 */
346 	if (rmobj(TESTDIR, &errmsg) == -1) {
347 		tst_resm(TWARN, "%s: rmobj(%s) failed: %s",
348 			 __func__, TESTDIR, errmsg);
349 	}
350 }
351 
tst_purge_dir(const char * path)352 void tst_purge_dir(const char *path)
353 {
354 	char *err;
355 
356 	if (purge_dir(path, &err))
357 		tst_brkm(TBROK, NULL, "%s: %s", __func__, err);
358 }
359 
tst_tmpdir_path(void)360 char *tst_tmpdir_path(void)
361 {
362 	static char *tmpdir;
363 
364 	if (!TESTDIR)
365 		tst_brkm(TBROK, NULL, ".needs_tmpdir must be set!");
366 
367 	if (tmpdir)
368 		return tmpdir;
369 
370 	tmpdir = tst_strdup(TESTDIR);
371 
372 	return tmpdir;
373 }
374 
tst_tmpdir_genpath(const char * fmt,...)375 char *tst_tmpdir_genpath(const char *fmt, ...)
376 {
377 	size_t testdir_len, path_len;
378 	va_list va, vac;
379 	char *ret;
380 
381 	if (!TESTDIR)
382 		tst_brkm(TBROK, NULL, ".needs_tmpdir must be set!");
383 
384 	testdir_len = strlen(TESTDIR);
385 	path_len = testdir_len;
386 
387 	va_start(va, fmt);
388 	va_copy(vac, va);
389 	path_len += vsnprintf(NULL, 0, fmt, va) + 2;
390 	va_end(va);
391 
392 	ret = tst_alloc(path_len);
393 
394 	strcpy(ret, TESTDIR);
395 
396 	ret[testdir_len] = '/';
397 
398 	vsprintf(ret + testdir_len + 1, fmt, vac);
399 	va_end(vac);
400 
401 	return ret;
402 }
403