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