1 /*
2 Authors:
3 John Dennis <jdennis.redhat.com>
4
5 Copyright (C) 2009 Red Hat
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*****************************************************************************/
22 /******************************** Documentation ******************************/
23 /*****************************************************************************/
24
25 /*****************************************************************************/
26 /******************************* Include Files *******************************/
27 /*****************************************************************************/
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <dirent.h>
34 #include <sys/errno.h>
35 #include <sys/stat.h>
36
37 #include <libgen.h>
38
39 #include "path_utils.h"
40
41 /*****************************************************************************/
42 /****************************** Internal Defines *****************************/
43 /*****************************************************************************/
44
45 /*****************************************************************************/
46 /************************** Internal Type Definitions ************************/
47 /*****************************************************************************/
48
49 /*****************************************************************************/
50 /********************** External Function Declarations *********************/
51 /*****************************************************************************/
52
53 /*****************************************************************************/
54 /********************** Internal Function Declarations *********************/
55 /*****************************************************************************/
56
57 /*****************************************************************************/
58 /************************* External Global Variables ***********************/
59 /*****************************************************************************/
60
61 /*****************************************************************************/
62 /************************* Internal Global Variables ***********************/
63 /*****************************************************************************/
64
65 /*****************************************************************************/
66 /**************************** Inline Functions *****************************/
67 /*****************************************************************************/
68
69 /*****************************************************************************/
70 /*************************** Internal Functions ****************************/
71 /*****************************************************************************/
72
73 /*****************************************************************************/
74 /**************************** Exported Functions ***************************/
75 /*****************************************************************************/
76
77 const char *path_utils_error_string(int error)
78 {
79 switch(error) {
80 case SUCCESS: return _("Success");
81 case PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED: return _("Path could not be fully normalized");
82 }
83 return NULL;
84 }
85
86 static int dot_to_absolute(char *rel_path, int rel_path_size)
87 {
88 char tmp_path[PATH_MAX];
89
90 if (strcmp(rel_path, ".") == 0) {
91 if (getcwd(rel_path, rel_path_size) == NULL) {
92 if (errno == ERANGE)
93 return ENOBUFS;
94 else
95 return errno;
96 }
97 } else if (strcmp(rel_path, "..") == 0) {
98 if (getcwd(tmp_path, sizeof(tmp_path)) == NULL) {
99 if (errno == ERANGE)
100 return ENOBUFS;
101 else
102 return errno;
103 }
104 strncpy(rel_path, dirname(tmp_path), rel_path_size);
105 if (rel_path[rel_path_size-1] != 0) return ENOBUFS;
106 }
107
108 return SUCCESS;
109 }
110
111 int get_basename(char *base_name, size_t base_name_size, const char *path)
112 {
113 char tmp_path[PATH_MAX];
114 int ret;
115
116 if (!path) return EINVAL;
117 if (!base_name || base_name_size < 1) return ENOBUFS;
118
119 strncpy(tmp_path, path, sizeof(tmp_path));
120 if (tmp_path[sizeof(tmp_path)-1] != 0) return ENOBUFS;
121 strncpy(base_name, basename(tmp_path), base_name_size);
122 if (base_name[base_name_size-1] != 0) return ENOBUFS;
123
124 ret = dot_to_absolute(base_name, base_name_size);
125 if (ret != SUCCESS) {
126 return ret;
127 }
128
129 return SUCCESS;
130 }
131
132 int get_dirname(char *dir_path, size_t dir_path_size, const char *path)
133 {
134 char tmp_path[PATH_MAX];
135 int ret;
136
137 if (!path) return EINVAL;
138 if (!dir_path || dir_path_size < 1) return ENOBUFS;
139
140 strncpy(tmp_path, path, sizeof(tmp_path));
141 if (tmp_path[sizeof(tmp_path)-1] != 0) return ENOBUFS;
142 strncpy(dir_path, dirname(tmp_path), dir_path_size);
143 if (dir_path[dir_path_size-1] != 0) return ENOBUFS;
144
145 ret = dot_to_absolute(dir_path, dir_path_size);
146 if (ret != SUCCESS) {
147 return ret;
148 }
149
150 return SUCCESS;
151 }
152
153 int get_directory_and_base_name(char *dir_path, size_t dir_path_size,
154 char *base_name, size_t base_name_size,
155 const char *path)
156 {
157 char tmp_path[PATH_MAX];
158 int ret;
159
160 if (!path) return EINVAL;
161 if (!dir_path || dir_path_size < 1) return ENOBUFS;
162 if (!base_name || base_name_size < 1) return ENOBUFS;
163
164 strncpy(tmp_path, path, sizeof(tmp_path));
165 if (tmp_path[sizeof(tmp_path)-1] != 0) return ENOBUFS;
166 strncpy(base_name, basename(tmp_path), base_name_size);
167 if (base_name[base_name_size-1] != 0) return ENOBUFS;
168
169 strncpy(tmp_path, path, sizeof(tmp_path));
170 if (tmp_path[sizeof(tmp_path)-1] != 0) return ENOBUFS;
171 strncpy(dir_path, dirname(tmp_path), dir_path_size);
172 if (dir_path[dir_path_size-1] != 0) return ENOBUFS;
173
174 ret = dot_to_absolute(dir_path, dir_path_size);
175 if (ret != SUCCESS) {
176 return ret;
177 }
178
179 if (strcmp(base_name, ".") == 0) {
180 strncpy(base_name, "", base_name_size);
181 if (base_name[base_name_size-1] != 0) return ENOBUFS;
182 }
183
184 return SUCCESS;
185 }
186
187 bool is_absolute_path(const char *path)
188 {
189 if (!path) return false;
190 return path[0] == '/';
191 }
192
193 int path_concat(char *path, size_t path_size, const char *head, const char *tail)
194 {
195 const char *p, *src;
196 char *dst, *dst_end;
197
198 if (!path || path_size < 1) return ENOBUFS;
199
200 dst = path;
201 dst_end = path + path_size - 1; /* -1 allows for NULL terminator */
202
203 if (head && *head) {
204 for (p = head; *p; p++); /* walk to end of head */
205 for (p--; p >= head && *p == '/'; p--); /* skip any trailing slashes in head */
206 if ((p - head) > path_size-1) return ENOBUFS;
207 for (src = head; src <= p && dst < dst_end;) *dst++ = *src++; /* copy head */
208 }
209 if (tail && *tail) {
210 for (p = tail; *p && *p == '/'; p++); /* skip any leading slashes in tail */
211 if (dst > path)
212 if (dst < dst_end) *dst++ = '/'; /* insert single slash between head & tail */
213 for (src = p; *src && dst <= dst_end;) *dst++ = *src++; /* copy tail */
214 if (*src) return ENOBUFS; /* failed to copy everything */
215 }
216 *dst = 0;
217 if (dst > dst_end) {
218 return ENOBUFS;
219 }
220 return SUCCESS;
221
222 }
223
224 int make_path_absolute(char *absolute_path, size_t absolute_path_size, const char *path)
225 {
226 int result = SUCCESS;
227 const char *src;
228 char *dst, *dst_end;
229
230 if (!absolute_path || absolute_path_size < 1) return ENOBUFS;
231
232 dst = absolute_path;
233 dst_end = absolute_path + absolute_path_size - 1; /* -1 allows for NULL terminator */
234
235 if (is_absolute_path(path)) {
236 for (src = path; *src && dst < dst_end;) *dst++ = *src++;
237 *dst = 0;
238 if (dst > dst_end || *src) result = ENOBUFS;
239 return result;
240 }
241
242 if ((getcwd(absolute_path, absolute_path_size) == NULL)) {
243 if (errno == ERANGE)
244 return ENOBUFS;
245 else
246 return errno;
247 }
248
249 for (dst = absolute_path; *dst && dst < dst_end; dst++);
250 if (!(path && *path)) return result;
251 if (dst > dst_end) {
252 *absolute_path = 0;
253 return ENOBUFS;
254 }
255
256 *dst++ = '/';
257 if (dst > dst_end) {
258 *absolute_path = 0;
259 return ENOBUFS;
260 }
261
262 for (src = path; *src && dst < dst_end;) *dst++ = *src++;
263 if (*src) return ENOBUFS; /* failed to copy everything */
264 *dst = 0;
265
266 return result;
267 }
268
269 char **split_path(const char *path, int *count)
270 {
271 int n_components, component_len, total_component_len, alloc_len;
272 const char *start, *end;
273 char *mem_block, **array_ptr, *component_ptr;
274
275 if (count) *count = 0;
276 if (!path) return NULL;
277
278 /* If path is absolute add in special "/" root component */
279 if (*path == '/') {
280 n_components = 1;
281 total_component_len = 2;
282 } else {
283 n_components = 0;
284 total_component_len = 0;
285 }
286
287 /* Scan for components, keep several counts */
288 for (start = end = path; *start; start = end) {
289 for (start = end; *start && *start == '/'; start++);
290 for (end = start; *end && *end != '/'; end++);
291 if ((component_len = end - start) == 0) break;
292 n_components++;
293 total_component_len += component_len + 1;
294 }
295
296 /*
297 * Allocate a block big enough for component array (with trailing NULL
298 * entry, hence n_components+1) and enough room for a copy of each NULL
299 * terminated component. We'll copy the components into the same allocation
300 * block after the end of the pointer array.
301 */
302 alloc_len = ((n_components+1) * sizeof(char *)) + total_component_len;
303
304 if ((mem_block = malloc(alloc_len)) == NULL) {
305 if (count) *count = -1;
306 return NULL;
307 }
308
309 /* component array */
310 array_ptr = (char **)mem_block;
311 /* components copied after end of array */
312 component_ptr = mem_block + ((n_components+1)*sizeof(char *));
313
314 /* If path is absolute add in special "/" root component */
315 if (*path == '/') {
316 *array_ptr++ = component_ptr;
317 *component_ptr++ = '/';
318 *component_ptr++ = 0;
319 }
320
321 for (start = end = path; *start; start = end) {
322 for (start = end; *start && *start == '/'; start++);
323 for (end = start; *end && *end != '/'; end++);
324 if ((component_len = end - start) == 0) break;
325
326 *array_ptr++ = component_ptr;
327 while (start < end) *component_ptr++ = *start++;
328 *component_ptr++ = 0;
329 }
330 *array_ptr++ = NULL;
331 if (count) *count = n_components;
332 return (char **)mem_block;
333 }
334
335 int normalize_path(char *normalized_path, size_t normalized_path_size, const char *path)
336 {
337 int result = SUCCESS;
338 int component_len;
339 bool is_absolute, can_backup;
340 const char *start, *end;
341 char *dst, *dst_end, *p, *limit;
342
343 if (!normalized_path || normalized_path_size < 1) return ENOBUFS;
344
345 dst = normalized_path;
346 dst_end = normalized_path + normalized_path_size - 1; /* -1 allows for NULL terminator */
347 can_backup = true;
348
349 if (!path || !*path) {
350 if (dst > dst_end) {
351 *dst = 0;
352 return ENOBUFS;
353 }
354 *dst++ = '.';
355 *dst = 0;
356 return result;
357 }
358
359 if ((is_absolute = *path == '/')) {
360 if (dst < dst_end) {
361 *dst++ = '/';
362 } else {
363 *dst = 0;
364 return ENOBUFS;
365 }
366 }
367
368 for (start = end = path; *start; start = end) {
369 for (start = end; *start && *start == '/'; start++);
370 for (end = start; *end && *end != '/'; end++);
371 if ((component_len = end - start) == 0) break;
372 if (component_len == 1 && start[0] == '.') continue;
373 if (component_len == 2 && start[0] == '.' && start[1] == '.' && can_backup) {
374 /* back up one level */
375 if ((is_absolute && dst == normalized_path+1) || (!is_absolute && dst == normalized_path)) {
376 if (is_absolute) continue;
377 can_backup = false;
378 result = PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED;
379 } else {
380 if (is_absolute)
381 limit = normalized_path+1;
382 else
383 limit = normalized_path;
384 for (p = dst-1; p >= limit && *p != '/'; p--);
385 if (p < limit)
386 dst = limit;
387 else
388 dst = p;
389 continue;
390 }
391 }
392
393 if ((end-start) > (dst_end-dst)) {
394 return ENOBUFS;
395 }
396
397 if ((dst > normalized_path) && (dst < dst_end) && (dst[-1] != '/')) *dst++ = '/';
398 while ((start < end) && (dst < dst_end)) *dst++ = *start++;
399 }
400
401 if (dst == normalized_path) {
402 if (is_absolute)
403 *dst++ = '/';
404 else
405 *dst++ = '.';
406 }
407 *dst = 0;
408 return result;
409 }
410
411 int common_path_prefix(char *common_path,
412 size_t common_path_size,
413 int *common_count,
414 const char *path1, const char *path2)
415 {
416 int count1, count2, min_count, i, n_common, result;
417 char **split1, **split2;
418 char *dst, *dst_end, *src;
419
420 if (!common_path || common_path_size < 1) return ENOBUFS;
421
422 result = SUCCESS;
423 n_common = 0;
424 split1 = split_path(path1, &count1);
425 split2 = split_path(path2, &count2);
426
427 if (count1 <= count2)
428 min_count = count1;
429 else
430 min_count = count2;
431
432 if (min_count <= 0 || !split1 || !split2 ) {
433 result = SUCCESS;
434 *common_path = 0;
435 goto done;
436 }
437
438 for (n_common = 0; n_common < min_count; n_common++) {
439 if (strcmp(split1[n_common], split2[n_common]) != 0) break;
440 }
441
442 if (n_common == 0) {
443 result = SUCCESS;
444 *common_path = 0;
445 goto done;
446 }
447
448 dst = common_path;
449 dst_end = common_path + common_path_size - 1; /* -1 allows for NULL terminator */
450 for (i = 0; i < n_common; i++) {
451 for (src = split1[i]; *src && dst < dst_end;) *dst++ = *src++;
452 if (dst == dst_end && *src) {
453 *dst = 0;
454 result = ENOBUFS;
455 goto done;
456 }
457 if (dst[-1] != '/' && i < n_common-1) { /* insert path separator */
458 if (dst == dst_end) {
459 *dst = 0;
460 result = ENOBUFS;
461 goto done;
462 }
463 *dst++ = '/';
464 }
465 }
466 *dst = 0;
467
468 done:
469 free(split1);
470 free(split2);
471 if (common_count) *common_count = n_common;
472 return result;
473 }
474
475 int make_normalized_absolute_path(char *result_path, size_t result_path_size, const char *path)
476 {
477 int error;
478 char absolute_path[PATH_MAX];
479
480 if (!result_path || result_path_size < 1) return ENOBUFS;
481 *result_path = 0;
482 if ((error = make_path_absolute(absolute_path, sizeof(absolute_path), path)) != SUCCESS) return error;
483 if ((error = normalize_path(result_path, result_path_size, absolute_path)) != SUCCESS) return error;
484 return SUCCESS;
485 }
486
487 int find_existing_directory_ancestor(char *ancestor, size_t ancestor_size, const char *path)
488 {
489 int error;
490 char dir_path[PATH_MAX];
491 struct stat info;
492
493 if (!ancestor || ancestor_size < 1) return ENOBUFS;
494 *ancestor = 0;
495 strncpy(dir_path, path, sizeof(dir_path));
496 if (dir_path[sizeof(dir_path)-1] != 0) return ENOBUFS;
497
498 while (strcmp(dir_path, "/") != 0) {
499 if (lstat(dir_path, &info) < 0) {
500 error = errno;
501 if (error != ENOENT) return error;
502 } else {
503 if (S_ISDIR(info.st_mode)) break;
504 }
505 error = get_dirname(dir_path, sizeof(dir_path), dir_path);
506 if (error != SUCCESS) {
507 return error;
508 }
509 }
510
511 strncpy(ancestor, dir_path, ancestor_size);
512 if (ancestor[ancestor_size-1] != 0) return ENOBUFS;
513 return SUCCESS;
514 }
515
516 int directory_list(const char *path, bool recursive,
517 directory_list_callback_t callback, void *user_data)
518 {
519 DIR *dir;
520 struct dirent *entry;
521 struct stat info;
522 int error = 0;
523 char entry_path[PATH_MAX];
524 bool prune = false;
525
Event alloc_fn: Calling allocation function "opendir". Event var_assign: Assigning: "dir" = storage returned from "opendir(path)".
Also see events: [leaked_storage][leaked_storage][noescape]At conditional (1): "!(dir = opendir(path))": Taking false branch.
| | |
526 if (!(dir = opendir(path))) {
527 error = errno;
528 return error;
529 }
530
Event noescape: Variable "dir" is not freed or pointed-to in function "readdir".
Also see events: [alloc_fn][var_assign][leaked_storage][leaked_storage]At conditional (2): "entry": Taking true branch.
At conditional (4): "entry": Taking true branch.
| | | |
531 for (entry = readdir(dir); entry; entry = readdir(dir)) {
532 prune = false;
At conditional (3): "strcmp(&entry->d_name, &".") == 0": Taking true branch.
At conditional (5): "strcmp(&entry->d_name, &".") == 0": Taking false branch.
At conditional (6): "strcmp(&entry->d_name, &"..") == 0": Taking false branch.
| | |
533 if (strcmp(entry->d_name, ".") == 0 ||
534 strcmp(entry->d_name, "..") == 0) {
535 continue;
536 }
537
538 error = path_concat(entry_path, sizeof(entry_path),
539 path, entry->d_name);
At conditional (7): "error != 0": Taking true branch.
|
540 if (error != SUCCESS) {
541 return error;
542 }
543
544 if (lstat(entry_path, &info) < 0) {
545 continue;
546 }
547
548 prune = !callback(path, entry->d_name, entry_path, &info, user_data);
549 if (S_ISDIR(info.st_mode)) {
550 if (recursive && !prune) {
551 error = directory_list(entry_path, recursive,
552 callback, user_data);
553 if (error != SUCCESS) {
554 return error;
555 }
556 }
557 }
558 }
559 error = closedir(dir);
560 if (error) {
561 return error;
562 }
563 return SUCCESS;
564 }
565
566 bool is_ancestor_path(const char *ancestor, const char *path)
567 {
568 char **path_components, **ancestor_components;
569 int i, path_count, ancestor_count;
570 bool result;
571
572 result = false;
573 path_components = split_path(path, &path_count);
574 ancestor_components = split_path(ancestor, &ancestor_count);
575
576 if (!path_components || !ancestor_components) {
577 result = false;
578 goto exit;
579 }
580
581 if (ancestor_count >= path_count) {
582 result = false;
583 goto exit;
584 }
585
586 for (i = 0; i < ancestor_count; i++) {
587 if (strcmp(path_components[i], ancestor_components[i]) != 0) {
588 result = false;
589 goto exit;
590 }
591 }
592
593 result = true;
594
595 exit:
596 free(path_components);
597 free(ancestor_components);
598 return result;
599 }
600