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