Line data Source code
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 0 : const char *path_utils_error_string(int error)
78 : {
79 0 : switch(error) {
80 0 : case SUCCESS: return _("Success");
81 0 : case PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED: return _("Path could not be fully normalized");
82 : }
83 0 : return NULL;
84 : }
85 :
86 27 : static int dot_to_absolute(char *rel_path, int rel_path_size)
87 : {
88 : char tmp_path[PATH_MAX];
89 :
90 27 : if (strcmp(rel_path, ".") == 0) {
91 10 : if (getcwd(rel_path, rel_path_size) == NULL) {
92 0 : if (errno == ERANGE)
93 0 : return ENOBUFS;
94 : else
95 0 : return errno;
96 : }
97 17 : } else if (strcmp(rel_path, "..") == 0) {
98 0 : if (getcwd(tmp_path, sizeof(tmp_path)) == NULL) {
99 0 : if (errno == ERANGE)
100 0 : return ENOBUFS;
101 : else
102 0 : return errno;
103 : }
104 0 : strncpy(rel_path, dirname(tmp_path), rel_path_size);
105 0 : if (rel_path[rel_path_size-1] != '\0') return ENOBUFS;
106 : }
107 :
108 27 : return SUCCESS;
109 : }
110 :
111 8 : 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 8 : if (!path) return EINVAL;
117 7 : if (!base_name || base_name_size < 1) return ENOBUFS;
118 :
119 7 : strncpy(tmp_path, path, sizeof(tmp_path));
120 7 : if (tmp_path[sizeof(tmp_path)-1] != '\0') return ENOBUFS;
121 7 : strncpy(base_name, basename(tmp_path), base_name_size);
122 7 : if (base_name[base_name_size-1] != '\0') return ENOBUFS;
123 :
124 6 : ret = dot_to_absolute(base_name, base_name_size);
125 6 : if (ret != SUCCESS) {
126 0 : return ret;
127 : }
128 :
129 6 : return SUCCESS;
130 : }
131 :
132 17 : 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 17 : if (!path) return EINVAL;
138 16 : if (!dir_path || dir_path_size < 1) return ENOBUFS;
139 :
140 16 : strncpy(tmp_path, path, sizeof(tmp_path));
141 16 : if (tmp_path[sizeof(tmp_path)-1] != '\0') return ENOBUFS;
142 16 : strncpy(dir_path, dirname(tmp_path), dir_path_size);
143 16 : if (dir_path[dir_path_size-1] != '\0') return ENOBUFS;
144 :
145 15 : ret = dot_to_absolute(dir_path, dir_path_size);
146 15 : if (ret != SUCCESS) {
147 0 : return ret;
148 : }
149 :
150 15 : return SUCCESS;
151 : }
152 :
153 7 : 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 7 : if (!path) return EINVAL;
161 6 : if (!dir_path || dir_path_size < 1) return ENOBUFS;
162 6 : if (!base_name || base_name_size < 1) return ENOBUFS;
163 :
164 6 : strncpy(tmp_path, path, sizeof(tmp_path));
165 6 : if (tmp_path[sizeof(tmp_path)-1] != '\0') return ENOBUFS;
166 6 : strncpy(base_name, basename(tmp_path), base_name_size);
167 6 : if (base_name[base_name_size-1] != '\0') return ENOBUFS;
168 :
169 6 : strncpy(tmp_path, path, sizeof(tmp_path));
170 6 : if (tmp_path[sizeof(tmp_path)-1] != '\0') return ENOBUFS;
171 6 : strncpy(dir_path, dirname(tmp_path), dir_path_size);
172 6 : if (dir_path[dir_path_size-1] != '\0') return ENOBUFS;
173 :
174 6 : ret = dot_to_absolute(dir_path, dir_path_size);
175 6 : if (ret != SUCCESS) {
176 0 : return ret;
177 : }
178 :
179 6 : if (strcmp(base_name, ".") == 0) {
180 2 : strncpy(base_name, "", base_name_size);
181 2 : if (base_name[base_name_size-1] != '\0') return ENOBUFS;
182 : }
183 :
184 6 : return SUCCESS;
185 : }
186 :
187 231 : bool is_absolute_path(const char *path)
188 : {
189 231 : if (!path) return false;
190 231 : return path[0] == '/';
191 : }
192 :
193 19 : int path_concat(char *path, size_t path_size, const char *head, const char *tail)
194 : {
195 : int ret;
196 : const char *p, *src;
197 : char *dst, *dst_end;
198 :
199 19 : if (!path || path_size < 1) return ENOBUFS;
200 :
201 19 : dst = path;
202 19 : dst_end = path + path_size - 1; /* -1 allows for NULL terminator */
203 :
204 19 : if (head && *head) {
205 : /* walk to end of head */
206 17 : for (p = head; *p; p++);
207 :
208 : /* skip any trailing slashes in head */
209 17 : for (p--; p > head && *p == '/'; p--);
210 :
211 : /* If the length of head exceeds the buffer size, fail */
212 17 : if ((p - head) > path_size-1) {
213 1 : ret = ENOBUFS;
214 1 : goto fail;
215 : }
216 :
217 : /* Copy head into path */
218 223 : for (src = head; src <= p && dst < dst_end;) {
219 191 : *dst++ = *src++;
220 : }
221 : }
222 18 : if (tail && *tail) {
223 : /* skip any leading slashes in tail */
224 16 : for (p = tail; *p && *p == '/'; p++);
225 :
226 16 : if (dst > path)
227 : /* insert single slash between head & tail
228 : * Making sure not to add an extra if the
229 : * preceding character is also a slash
230 : * (such as the case where head was the
231 : * special-case "/".
232 : */
233 14 : if (dst < dst_end && *(dst-1) != '/') {
234 12 : *dst++ = '/';
235 : }
236 :
237 : /* Copy the tail into the path */
238 167 : for (src = p; *src && dst < dst_end;) {
239 135 : *dst++ = *src++;
240 : }
241 :
242 : /* If we got past dst_end and there is more data
243 : * in the src buffer, we should return ENOBUFS
244 : */
245 16 : if (*src) {
246 3 : ret = ENOBUFS; /* failed to copy everything */
247 3 : goto fail;
248 : }
249 : }
250 15 : *dst = 0;
251 :
252 15 : return SUCCESS;
253 :
254 : fail:
255 : /* On failure, set the buffer to the empty string for safety */
256 4 : *path = '\0';
257 4 : return ret;
258 : }
259 :
260 228 : int make_path_absolute(char *absolute_path, size_t absolute_path_size, const char *path)
261 : {
262 228 : int result = SUCCESS;
263 : const char *src;
264 : char *dst, *dst_end;
265 :
266 228 : if (!absolute_path || absolute_path_size < 1) return ENOBUFS;
267 :
268 226 : dst = absolute_path;
269 226 : dst_end = absolute_path + absolute_path_size - 1; /* -1 allows for NULL terminator */
270 :
271 226 : if (is_absolute_path(path)) {
272 5 : for (src = path; *src && dst < dst_end;) *dst++ = *src++;
273 5 : *dst = 0;
274 5 : if (dst > dst_end || *src) result = ENOBUFS;
275 5 : return result;
276 : }
277 :
278 221 : if ((getcwd(absolute_path, absolute_path_size) == NULL)) {
279 0 : if (errno == ERANGE)
280 0 : return ENOBUFS;
281 : else
282 0 : return errno;
283 : }
284 :
285 221 : for (dst = absolute_path; *dst && dst < dst_end; dst++);
286 221 : if (!(path && *path)) return result;
287 220 : if (dst > dst_end) {
288 0 : *absolute_path = 0;
289 0 : return ENOBUFS;
290 : }
291 :
292 220 : *dst++ = '/';
293 220 : if (dst > dst_end) {
294 1 : *absolute_path = 0;
295 1 : return ENOBUFS;
296 : }
297 :
298 219 : for (src = path; *src && dst < dst_end;) *dst++ = *src++;
299 219 : if (*src) return ENOBUFS; /* failed to copy everything */
300 218 : *dst = 0;
301 :
302 218 : return result;
303 : }
304 :
305 41 : char **split_path(const char *path, int *count)
306 : {
307 : int n_components, component_len, total_component_len, alloc_len;
308 : const char *start, *end;
309 : char *mem_block, **array_ptr, *component_ptr;
310 :
311 41 : if (count) *count = 0;
312 41 : if (!path) return NULL;
313 :
314 : /* If path is absolute add in special "/" root component */
315 34 : if (*path == '/') {
316 26 : n_components = 1;
317 26 : total_component_len = 2;
318 : } else {
319 8 : n_components = 0;
320 8 : total_component_len = 0;
321 : }
322 :
323 : /* Scan for components, keep several counts */
324 102 : for (start = end = path; *start; start = end) {
325 70 : for (start = end; *start && *start == '/'; start++);
326 70 : for (end = start; *end && *end != '/'; end++);
327 70 : if ((component_len = end - start) == 0) break;
328 68 : n_components++;
329 68 : total_component_len += component_len + 1;
330 : }
331 :
332 : /*
333 : * Allocate a block big enough for component array (with trailing NULL
334 : * entry, hence n_components+1) and enough room for a copy of each NULL
335 : * terminated component. We'll copy the components into the same allocation
336 : * block after the end of the pointer array.
337 : */
338 34 : alloc_len = ((n_components+1) * sizeof(char *)) + total_component_len;
339 :
340 34 : if ((mem_block = malloc(alloc_len)) == NULL) {
341 0 : if (count) *count = -1;
342 0 : return NULL;
343 : }
344 :
345 : /* component array */
346 34 : array_ptr = (char **)mem_block;
347 : /* components copied after end of array */
348 34 : component_ptr = mem_block + ((n_components+1)*sizeof(char *));
349 :
350 : /* If path is absolute add in special "/" root component */
351 34 : if (*path == '/') {
352 26 : *array_ptr++ = component_ptr;
353 26 : *component_ptr++ = '/';
354 26 : *component_ptr++ = 0;
355 : }
356 :
357 102 : for (start = end = path; *start; start = end) {
358 70 : for (start = end; *start && *start == '/'; start++);
359 70 : for (end = start; *end && *end != '/'; end++);
360 70 : if ((end - start) == 0) break;
361 :
362 68 : *array_ptr++ = component_ptr;
363 68 : while (start < end) *component_ptr++ = *start++;
364 68 : *component_ptr++ = 0;
365 : }
366 34 : *array_ptr = NULL;
367 34 : if (count) *count = n_components;
368 34 : return (char **)mem_block;
369 : }
370 :
371 229 : int normalize_path(char *normalized_path, size_t normalized_path_size, const char *path)
372 : {
373 229 : int result = SUCCESS;
374 : int component_len;
375 : bool is_absolute, can_backup;
376 : const char *start, *end;
377 : char *dst, *dst_end, *p, *limit;
378 :
379 229 : if (!normalized_path || normalized_path_size < 1) return ENOBUFS;
380 :
381 229 : dst = normalized_path;
382 229 : dst_end = normalized_path + normalized_path_size - 1; /* -1 allows for NULL terminator */
383 229 : can_backup = true;
384 :
385 229 : if (!path || !*path) {
386 1 : if (dst > dst_end) {
387 0 : *dst = 0;
388 0 : return ENOBUFS;
389 : }
390 1 : *dst++ = '.';
391 1 : *dst = 0;
392 1 : return result;
393 : }
394 :
395 228 : if ((is_absolute = *path == '/')) {
396 225 : if (dst < dst_end) {
397 225 : *dst++ = '/';
398 : } else {
399 0 : *dst = 0;
400 0 : return ENOBUFS;
401 : }
402 : }
403 :
404 1649 : for (start = end = path; *start; start = end) {
405 1422 : for (start = end; *start && *start == '/'; start++);
406 1422 : for (end = start; *end && *end != '/'; end++);
407 1422 : if ((component_len = end - start) == 0) break;
408 1422 : if (component_len == 1 && start[0] == '.') continue;
409 1206 : if (component_len == 2 && start[0] == '.' && start[1] == '.' && can_backup) {
410 : /* back up one level */
411 13 : if ((is_absolute && dst == normalized_path+1) || (!is_absolute && dst == normalized_path)) {
412 3 : if (is_absolute) continue;
413 1 : can_backup = false;
414 1 : result = PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED;
415 : } else {
416 10 : if (is_absolute)
417 7 : limit = normalized_path+1;
418 : else
419 3 : limit = normalized_path;
420 10 : for (p = dst-1; p >= limit && *p != '/'; p--);
421 10 : if (p < limit)
422 7 : dst = limit;
423 : else
424 3 : dst = p;
425 10 : continue;
426 : }
427 : }
428 :
429 1194 : if ((end-start) > (dst_end-dst)) {
430 1 : return ENOBUFS;
431 : }
432 :
433 1193 : if ((dst > normalized_path) && (dst < dst_end) && (dst[-1] != '/')) *dst++ = '/';
434 1193 : while ((start < end) && (dst < dst_end)) *dst++ = *start++;
435 : }
436 :
437 227 : if (dst == normalized_path) {
438 0 : if (is_absolute)
439 0 : *dst++ = '/';
440 : else
441 0 : *dst++ = '.';
442 : }
443 227 : *dst = 0;
444 227 : return result;
445 : }
446 :
447 10 : int common_path_prefix(char *common_path,
448 : size_t common_path_size,
449 : int *common_count,
450 : const char *path1, const char *path2)
451 : {
452 : int count1, count2, min_count, i, n_common, result;
453 : char **split1, **split2;
454 : char *dst, *dst_end, *src;
455 :
456 10 : if (!common_path || common_path_size < 1) return ENOBUFS;
457 :
458 9 : result = SUCCESS;
459 9 : n_common = 0;
460 9 : split1 = split_path(path1, &count1);
461 9 : split2 = split_path(path2, &count2);
462 :
463 9 : if (count1 <= count2)
464 9 : min_count = count1;
465 : else
466 0 : min_count = count2;
467 :
468 9 : if (min_count <= 0 || !split1 || !split2 ) {
469 1 : result = SUCCESS;
470 1 : *common_path = 0;
471 1 : goto done;
472 : }
473 :
474 21 : for (n_common = 0; n_common < min_count; n_common++) {
475 20 : if (strcmp(split1[n_common], split2[n_common]) != 0) break;
476 : }
477 :
478 8 : if (n_common == 0) {
479 1 : result = SUCCESS;
480 1 : *common_path = 0;
481 1 : goto done;
482 : }
483 :
484 7 : dst = common_path;
485 7 : dst_end = common_path + common_path_size - 1; /* -1 allows for NULL terminator */
486 17 : for (i = 0; i < n_common; i++) {
487 12 : for (src = split1[i]; *src && dst < dst_end;) *dst++ = *src++;
488 12 : if (dst == dst_end && *src) {
489 2 : *dst = 0;
490 2 : result = ENOBUFS;
491 2 : goto done;
492 : }
493 10 : if (dst[-1] != '/' && i < n_common-1) { /* insert path separator */
494 0 : if (dst == dst_end) {
495 0 : *dst = 0;
496 0 : result = ENOBUFS;
497 0 : goto done;
498 : }
499 0 : *dst++ = '/';
500 : }
501 : }
502 5 : *dst = 0;
503 :
504 : done:
505 9 : free(split1);
506 9 : free(split2);
507 9 : if (common_count) *common_count = n_common;
508 9 : return result;
509 : }
510 :
511 218 : int make_normalized_absolute_path(char *result_path, size_t result_path_size, const char *path)
512 : {
513 : int error;
514 : char absolute_path[PATH_MAX];
515 :
516 218 : if (!result_path || result_path_size < 1) return ENOBUFS;
517 218 : *result_path = 0;
518 218 : if ((error = make_path_absolute(absolute_path, sizeof(absolute_path), path)) != SUCCESS) return error;
519 218 : if ((error = normalize_path(result_path, result_path_size, absolute_path)) != SUCCESS) return error;
520 218 : return SUCCESS;
521 : }
522 :
523 4 : int find_existing_directory_ancestor(char *ancestor, size_t ancestor_size, const char *path)
524 : {
525 : int error;
526 : char dir_path[PATH_MAX];
527 : struct stat info;
528 :
529 4 : if (!ancestor || ancestor_size < 1) return ENOBUFS;
530 3 : *ancestor = 0;
531 3 : strncpy(dir_path, path, sizeof(dir_path));
532 3 : if (dir_path[sizeof(dir_path)-1] != '\0') return ENOBUFS;
533 :
534 10 : while (strcmp(dir_path, "/") != 0) {
535 7 : if (lstat(dir_path, &info) < 0) {
536 2 : error = errno;
537 2 : if (error != ENOENT) return error;
538 : } else {
539 5 : if (S_ISDIR(info.st_mode)) break;
540 : }
541 4 : error = get_dirname(dir_path, sizeof(dir_path), dir_path);
542 4 : if (error != SUCCESS) {
543 0 : return error;
544 : }
545 : }
546 :
547 3 : strncpy(ancestor, dir_path, ancestor_size);
548 3 : if (ancestor[ancestor_size-1] != '\0') return ENOBUFS;
549 2 : return SUCCESS;
550 : }
551 :
552 7 : int directory_list(const char *path, bool recursive,
553 : directory_list_callback_t callback, void *user_data)
554 : {
555 : DIR *dir;
556 : struct dirent *entry;
557 : struct stat info;
558 7 : int error = 0;
559 : char entry_path[PATH_MAX];
560 7 : bool prune = false;
561 :
562 7 : if (!(dir = opendir(path))) {
563 2 : error = errno;
564 2 : return error;
565 : }
566 :
567 19 : for (entry = readdir(dir); entry; entry = readdir(dir)) {
568 :
569 23 : if (strcmp(entry->d_name, ".") == 0 ||
570 9 : strcmp(entry->d_name, "..") == 0) {
571 10 : continue;
572 : }
573 :
574 4 : error = path_concat(entry_path, sizeof(entry_path),
575 4 : path, entry->d_name);
576 4 : if (error != SUCCESS) {
577 0 : closedir(dir);
578 : /* Don't bother checking the return here.
579 : * The path_concat error is more important
580 : */
581 0 : return error;
582 : }
583 :
584 4 : if (lstat(entry_path, &info) < 0) {
585 0 : continue;
586 : }
587 :
588 4 : prune = !callback(path, entry->d_name, entry_path, &info, user_data);
589 4 : if (S_ISDIR(info.st_mode)) {
590 4 : if (recursive && !prune) {
591 2 : error = directory_list(entry_path, recursive,
592 : callback, user_data);
593 2 : if (error != SUCCESS) {
594 0 : closedir(dir);
595 : /* Don't bother checking the return here.
596 : * The directory_list error is more important
597 : */
598 0 : return error;
599 : }
600 : }
601 : }
602 : }
603 5 : error = closedir(dir);
604 5 : if (error) {
605 0 : return error;
606 : }
607 5 : return SUCCESS;
608 : }
609 :
610 7 : bool is_ancestor_path(const char *ancestor, const char *path)
611 : {
612 : char **path_components, **ancestor_components;
613 : int i, path_count, ancestor_count;
614 7 : bool result = false;
615 :
616 7 : path_components = split_path(path, &path_count);
617 7 : ancestor_components = split_path(ancestor, &ancestor_count);
618 :
619 7 : if (!path_components || !ancestor_components) {
620 : goto exit;
621 : }
622 :
623 4 : if (ancestor_count >= path_count) {
624 2 : goto exit;
625 : }
626 :
627 8 : for (i = 0; i < ancestor_count; i++) {
628 7 : if (strcmp(path_components[i], ancestor_components[i]) != 0) {
629 1 : goto exit;
630 : }
631 : }
632 :
633 1 : result = true;
634 :
635 : exit:
636 7 : free(path_components);
637 7 : free(ancestor_components);
638 7 : return result;
639 : }
640 :
|