1 /*
2 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
3 * University Research and Technology
4 * Corporation. All rights reserved.
5 * Copyright (c) 2004-2005 The University of Tennessee and The University
6 * of Tennessee Research Foundation. All rights
7 * reserved.
8 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
9 * University of Stuttgart. All rights reserved.
10 * Copyright (c) 2004-2005 The Regents of the University of California.
11 * All rights reserved.
12 * Copyright (c) 2015-2017 Research Organization for Information Science
13 * and Technology (RIST). All rights reserved.
14 * Copyright (c) 2016-2017 Intel, Inc. All rights reserved.
15 * $COPYRIGHT$
16 *
17 * Additional copyrights may follow
18 *
19 * $HEADER$
20 */
21
22
23 #include "opal_config.h"
24
25 #include <errno.h>
26 #include <string.h>
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif /* HAVE_UNISTD_H */
30 #include <stdlib.h>
31 #if HAVE_SYS_STAT_H
32 #include <sys/stat.h>
33 #endif /* HAVE_SYS_STAT_H */
34 #ifdef HAVE_SYS_TYPES_H
35 #include <sys/types.h>
36 #endif /* HAVE_SYS_TYPES_H */
37 #ifdef HAVE_DIRENT_H
38 #include <dirent.h>
39 #endif /* HAVE_DIRENT_H */
40
41 #include "opal/util/output.h"
42 #include "opal/util/os_dirpath.h"
43 #include "opal/util/show_help.h"
44 #include "opal/util/argv.h"
45 #include "opal/util/os_path.h"
46 #include "opal/constants.h"
47
48 static const char path_sep[] = OPAL_PATH_SEP;
49
50 int opal_os_dirpath_create(const char *path, const mode_t mode)
51 {
52 struct stat buf;
53 char **parts, *tmp;
54 int i, len;
55 int ret;
56
57 if (NULL == path) { /* protect ourselves from errors */
58 return(OPAL_ERR_BAD_PARAM);
59 }
60
61 if (0 == (ret = stat(path, &buf))) { /* already exists */
62 if (mode == (mode & buf.st_mode)) { /* has correct mode */
63 return(OPAL_SUCCESS);
64 }
65 if (0 == (ret = chmod(path, (buf.st_mode | mode)))) { /* successfully change mode */
66 return(OPAL_SUCCESS);
67 }
68 opal_show_help("help-opal-util.txt", "dir-mode", true,
69 path, mode, strerror(errno));
70 return(OPAL_ERR_PERM); /* can't set correct mode */
71 }
72
73 /* quick -- try to make directory */
74 if (0 == mkdir(path, mode)) {
75 return(OPAL_SUCCESS);
76 }
77
78 /* didnt work, so now have to build our way down the tree */
79 /* Split the requested path up into its individual parts */
80
81 parts = opal_argv_split(path, path_sep[0]);
82
83 /* Ensure to allocate enough space for tmp: the strlen of the
84 incoming path + 1 (for \0) */
85
86 tmp = (char*)malloc(strlen(path) + 1);
87 tmp[0] = '\0';
88
89 /* Iterate through all the subdirectory names in the path,
90 building up a directory name. Check to see if that dirname
91 exists. If it doesn't, create it. */
92
93 len = opal_argv_count(parts);
94 for (i = 0; i < len; ++i) {
95 if (i == 0) {
96 /* If in POSIX-land, ensure that we never end a directory
97 name with path_sep */
98
99 if ('/' == path[0]) {
100 strcat(tmp, path_sep);
101 }
102 strcat(tmp, parts[i]);
103 }
104
105 /* If it's not the first part, ensure that there's a
106 preceeding path_sep and then append this part */
107
108 else {
109 if (path_sep[0] != tmp[strlen(tmp) - 1]) {
110 strcat(tmp, path_sep);
111 }
112 strcat(tmp, parts[i]);
113 }
114
115 /* Now that we have the name, try to create it */
116 mkdir(tmp, mode);
117 ret = errno; // save the errno for an error msg, if needed
118 if (0 != stat(tmp, &buf)) {
119 opal_show_help("help-opal-util.txt", "mkdir-failed", true,
120 tmp, strerror(ret));
121 opal_argv_free(parts);
122 free(tmp);
123 return OPAL_ERROR;
124 } else if (i == (len-1) && (mode != (mode & buf.st_mode)) && (0 > chmod(tmp, (buf.st_mode | mode)))) {
125 opal_show_help("help-opal-util.txt", "dir-mode", true,
126 tmp, mode, strerror(errno));
127 opal_argv_free(parts);
128 free(tmp);
129 return(OPAL_ERR_PERM); /* can't set correct mode */
130 }
131 }
132
133 /* All done */
134
135 opal_argv_free(parts);
136 free(tmp);
137 return OPAL_SUCCESS;
138 }
139
140 /**
141 * This function attempts to remove a directory along with all the
142 * files in it. If the recursive variable is non-zero, then it will
143 * try to recursively remove all directories. If provided, the
144 * callback function is executed prior to the directory or file being
145 * removed. If the callback returns non-zero, then no removal is
146 * done.
147 */
148 int opal_os_dirpath_destroy(const char *path,
149 bool recursive,
150 opal_os_dirpath_destroy_callback_fn_t cbfunc)
151 {
152 int rc, exit_status = OPAL_SUCCESS;
153 bool is_dir = false;
154 DIR *dp;
155 struct dirent *ep;
156 char *filenm;
157 struct stat buf;
158
159 if (NULL == path) { /* protect against error */
160 return OPAL_ERROR;
161 }
162
163 /*
164 * Make sure we have access to the the base directory
165 */
166 if (OPAL_SUCCESS != (rc = opal_os_dirpath_access(path, 0))) {
167 exit_status = rc;
168 goto cleanup;
169 }
170
171 /* Open up the directory */
172 dp = opendir(path);
173 if (NULL == dp) {
174 return OPAL_ERROR;
175 }
176
177 while (NULL != (ep = readdir(dp))) {
178 /* skip:
179 * - . and ..
180 */
181 if ((0 == strcmp(ep->d_name, ".")) ||
182 (0 == strcmp(ep->d_name, ".."))) {
183 continue;
184 }
185
186 /* Check to see if it is a directory */
187 is_dir = false;
188
189 /* Create a pathname. This is not always needed, but it makes
190 * for cleaner code just to create it here. Note that we are
191 * allocating memory here, so we need to free it later on.
192 */
193 filenm = opal_os_path(false, path, ep->d_name, NULL);
194
195 rc = stat(filenm, &buf);
196 if (0 > rc) {
197 /* Handle a race condition. filenm might have been deleted by an
198 * other process running on the same node. That typically occurs
199 * when one task is removing the job_session_dir and an other task
200 * is still removing its proc_session_dir.
201 */
202 free(filenm);
203 continue;
204 }
205 if (S_ISDIR(buf.st_mode)) {
206 is_dir = true;
207 }
208
209 /*
210 * If not recursively decending, then if we find a directory then fail
211 * since we were not told to remove it.
212 */
213 if (is_dir && !recursive) {
214 /* Set the error indicating that we found a directory,
215 * but continue removing files
216 */
217 exit_status = OPAL_ERROR;
218 free(filenm);
219 continue;
220 }
221
222 /* Will the caller allow us to remove this file/directory? */
223 if (NULL != cbfunc) {
224 /*
225 * Caller does not wish to remove this file/directory,
226 * continue with the rest of the entries
227 */
228 if (!(cbfunc(path, ep->d_name))) {
229 free(filenm);
230 continue;
231 }
232 }
233 /* Directories are recursively destroyed */
234 if (is_dir) {
235 rc = opal_os_dirpath_destroy(filenm, recursive, cbfunc);
236 free(filenm);
237 if (OPAL_SUCCESS != rc) {
238 exit_status = rc;
239 closedir(dp);
240 goto cleanup;
241 }
242 } else {
243 /* Files are removed right here */
244 if (0 != (rc = unlink(filenm))) {
245 exit_status = OPAL_ERROR;
246 }
247 free(filenm);
248 }
249 }
250
251 /* Done with this directory */
252 closedir(dp);
253
254 cleanup:
255
256 /*
257 * If the directory is empty, them remove it
258 */
259 if(opal_os_dirpath_is_empty(path)) {
260 rmdir(path);
261 }
262
263 return exit_status;
264 }
265
266 bool opal_os_dirpath_is_empty(const char *path ) {
267 DIR *dp;
268 struct dirent *ep;
269
270 if (NULL != path) { /* protect against error */
271 dp = opendir(path);
272 if (NULL != dp) {
273 while ((ep = readdir(dp))) {
274 if ((0 != strcmp(ep->d_name, ".")) &&
275 (0 != strcmp(ep->d_name, ".."))) {
276 closedir(dp);
277 return false;
278 }
279 }
280 closedir(dp);
281 return true;
282 }
283 return false;
284 }
285
286 return true;
287 }
288
289 int opal_os_dirpath_access(const char *path, const mode_t in_mode ) {
290 struct stat buf;
291 mode_t loc_mode = S_IRWXU; /* looking for full rights */
292
293 /*
294 * If there was no mode specified, use the default mode
295 */
296 if (0 != in_mode) {
297 loc_mode = in_mode;
298 }
299
300 if (0 == stat(path, &buf)) { /* exists - check access */
301 if ((buf.st_mode & loc_mode) == loc_mode) { /* okay, I can work here */
302 return(OPAL_SUCCESS);
303 } else {
304 /* Don't have access rights to the existing path */
305 return(OPAL_ERROR);
306 }
307 } else {
308 /* We could not find the path */
309 return( OPAL_ERR_NOT_FOUND );
310 }
311 }