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