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