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_startwd(void)125 const char *tst_get_startwd(void)
126 {
127 return test_start_work_dir;
128 }
129
purge_dir(const char * path,char ** errptr)130 static int purge_dir(const char *path, char **errptr)
131 {
132 int ret_val = 0;
133 DIR *dir;
134 struct dirent *dir_ent;
135 char dirobj[PATH_MAX];
136 static char err_msg[PATH_MAX + 1280];
137
138 /* Do NOT perform the request if the directory is "/" */
139 if (!strcmp(path, "/")) {
140 if (errptr) {
141 strcpy(err_msg, "Cannot purge system root directory");
142 *errptr = err_msg;
143 }
144
145 return -1;
146 }
147
148 errno = 0;
149
150 /* Open the directory to get access to what is in it */
151 if (!(dir = opendir(path))) {
152 if (errptr) {
153 sprintf(err_msg,
154 "Cannot open directory %s; errno=%d: %s",
155 path, errno, tst_strerrno(errno));
156 *errptr = err_msg;
157 }
158 return -1;
159 }
160
161 /* Loop through the entries in the directory, removing each one */
162 for (dir_ent = readdir(dir); dir_ent; dir_ent = readdir(dir)) {
163 /* Don't remove "." or ".." */
164 if (!strcmp(dir_ent->d_name, ".")
165 || !strcmp(dir_ent->d_name, ".."))
166 continue;
167
168 /* Recursively remove the current entry */
169 sprintf(dirobj, "%s/%s", path, dir_ent->d_name);
170 if (rmobj(dirobj, errptr) != 0)
171 ret_val = -1;
172 }
173
174 closedir(dir);
175 return ret_val;
176 }
177
rmobj(const char * obj,char ** errmsg)178 static int rmobj(const char *obj, char **errmsg)
179 {
180 int ret_val = 0;
181 struct stat statbuf;
182 static char err_msg[PATH_MAX + 1280];
183 int fd;
184
185 fd = open(obj, O_DIRECTORY | O_NOFOLLOW);
186 if (fd >= 0) {
187 close(fd);
188 ret_val = purge_dir(obj, errmsg);
189
190 /* If there were problems removing an entry, don't attempt to
191 remove the directory itself */
192 if (ret_val == -1)
193 return -1;
194
195 /* Get the link count, now that all the entries have been removed */
196 if (lstat(obj, &statbuf) < 0) {
197 if (errmsg != NULL) {
198 sprintf(err_msg,
199 "lstat(%s) failed; errno=%d: %s", obj,
200 errno, tst_strerrno(errno));
201 *errmsg = err_msg;
202 }
203 return -1;
204 }
205
206 /* Remove the directory itself */
207 if (statbuf.st_nlink >= 3) {
208 /* The directory is linked; unlink() must be used */
209 if (unlink(obj) < 0) {
210 if (errmsg != NULL) {
211 sprintf(err_msg,
212 "unlink(%s) failed; errno=%d: %s",
213 obj, errno, tst_strerrno(errno));
214 *errmsg = err_msg;
215 }
216 return -1;
217 }
218 } else {
219 /* The directory is not linked; remove() can be used */
220 if (remove(obj) < 0) {
221 if (errmsg != NULL) {
222 sprintf(err_msg,
223 "remove(%s) failed; errno=%d: %s",
224 obj, errno, tst_strerrno(errno));
225 *errmsg = err_msg;
226 }
227 return -1;
228 }
229 }
230 } else {
231 if (unlink(obj) < 0) {
232 if (errmsg != NULL) {
233 sprintf(err_msg,
234 "unlink(%s) failed; errno=%d: %s", obj,
235 errno, tst_strerrno(errno));
236 *errmsg = err_msg;
237 }
238 return -1;
239 }
240 }
241
242 return 0;
243 }
244
tst_tmpdir(void)245 void tst_tmpdir(void)
246 {
247 char template[PATH_MAX];
248 char *env_tmpdir;
249 char *errmsg, *c;
250
251 /*
252 * Create a template for the temporary directory. Use the
253 * environment variable TMPDIR if it is available, otherwise
254 * use our default TEMPDIR.
255 */
256 env_tmpdir = getenv("TMPDIR");
257 if (env_tmpdir) {
258 c = strchr(env_tmpdir, '/');
259 /*
260 * Now we force environment variable TMPDIR to be an absolute
261 * pathname, which dose not make much sense, but it will
262 * greatly simplify code in tst_rmdir().
263 */
264 if (c != env_tmpdir) {
265 tst_brkm(TBROK, NULL, "You must specify an absolute "
266 "pathname for environment variable TMPDIR");
267 return;
268 }
269 snprintf(template, PATH_MAX, "%s/%.3sXXXXXX", env_tmpdir, TCID);
270 } else {
271 snprintf(template, PATH_MAX, "%s/%.3sXXXXXX", TEMPDIR, TCID);
272 }
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