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