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
path_utils_error_string(int error)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
dot_to_absolute(char * rel_path,int rel_path_size)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
get_basename(char * base_name,size_t base_name_size,const char * path)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
get_dirname(char * dir_path,size_t dir_path_size,const char * path)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
get_directory_and_base_name(char * dir_path,size_t dir_path_size,char * base_name,size_t base_name_size,const char * path)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
is_absolute_path(const char * path)187 bool is_absolute_path(const char *path)
188 {
189 if (!path) return false;
190 return path[0] == '/';
191 }
192
path_concat(char * path,size_t path_size,const char * head,const char * tail)193 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 if (!path || path_size < 1) return ENOBUFS;
200
201 dst = path;
202 dst_end = path + path_size - 1; /* -1 allows for NULL terminator */
203
204 if (head && *head) {
205 /* walk to end of head */
206 for (p = head; *p; p++);
207
208 /* skip any trailing slashes in head */
209 for (p--; p > head && *p == '/'; p--);
210
211 /* If the length of head exceeds the buffer size, fail */
212 if ((p - head) > path_size-1) {
213 ret = ENOBUFS;
214 goto fail;
215 }
216
217 /* Copy head into path */
218 for (src = head; src <= p && dst < dst_end;) {
219 *dst++ = *src++;
220 }
221 }
222 if (tail && *tail) {
223 /* skip any leading slashes in tail */
224 for (p = tail; *p && *p == '/'; p++);
225
226 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 if (dst < dst_end && *(dst-1) != '/') {
234 *dst++ = '/';
235 }
236
237 /* Copy the tail into the path */
238 for (src = p; *src && dst < dst_end;) {
239 *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 if (*src) {
246 ret = ENOBUFS; /* failed to copy everything */
247 goto fail;
248 }
249 }
250 *dst = 0;
251
252 return SUCCESS;
253
254 fail:
255 /* On failure, set the buffer to the empty string for safety */
256 *path = '\0';
257 return ret;
258 }
259
make_path_absolute(char * absolute_path,size_t absolute_path_size,const char * path)260 int make_path_absolute(char *absolute_path, size_t absolute_path_size, const char *path)
261 {
262 int result = SUCCESS;
263 const char *src;
264 char *dst, *dst_end;
265
266 if (!absolute_path || absolute_path_size < 1) return ENOBUFS;
267
268 dst = absolute_path;
269 dst_end = absolute_path + absolute_path_size - 1; /* -1 allows for NULL terminator */
270
271 if (is_absolute_path(path)) {
272 for (src = path; *src && dst < dst_end;) *dst++ = *src++;
273 *dst = 0;
274 if (dst > dst_end || *src) result = ENOBUFS;
275 return result;
276 }
277
278 if ((getcwd(absolute_path, absolute_path_size) == NULL)) {
279 if (errno == ERANGE)
280 return ENOBUFS;
281 else
282 return errno;
283 }
284
285 for (dst = absolute_path; *dst && dst < dst_end; dst++);
286 if (!(path && *path)) return result;
287 if (dst > dst_end) {
288 *absolute_path = 0;
289 return ENOBUFS;
290 }
291
292 *dst++ = '/';
293 if (dst > dst_end) {
294 *absolute_path = 0;
295 return ENOBUFS;
296 }
297
298 for (src = path; *src && dst < dst_end;) *dst++ = *src++;
299 if (*src) return ENOBUFS; /* failed to copy everything */
300 *dst = 0;
301
302 return result;
303 }
304
split_path(const char * path,int * count)305 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 if (count) *count = 0;
312 if (!path) return NULL;
313
314 /* If path is absolute add in special "/" root component */
315 if (*path == '/') {
316 n_components = 1;
317 total_component_len = 2;
318 } else {
319 n_components = 0;
320 total_component_len = 0;
321 }
322
323 /* Scan for components, keep several counts */
324 for (start = end = path; *start; start = end) {
325 for (start = end; *start && *start == '/'; start++);
326 for (end = start; *end && *end != '/'; end++);
327 if ((component_len = end - start) == 0) break;
328 n_components++;
329 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 alloc_len = ((n_components+1) * sizeof(char *)) + total_component_len;
339
340 if ((mem_block = malloc(alloc_len)) == NULL) {
341 if (count) *count = -1;
342 return NULL;
343 }
344
345 /* component array */
346 array_ptr = (char **)mem_block;
347 /* components copied after end of array */
348 component_ptr = mem_block + ((n_components+1)*sizeof(char *));
349
350 /* If path is absolute add in special "/" root component */
351 if (*path == '/') {
352 *array_ptr++ = component_ptr;
353 *component_ptr++ = '/';
354 *component_ptr++ = 0;
355 }
356
357 for (start = end = path; *start; start = end) {
358 for (start = end; *start && *start == '/'; start++);
359 for (end = start; *end && *end != '/'; end++);
360 if ((end - start) == 0) break;
361
362 *array_ptr++ = component_ptr;
363 while (start < end) *component_ptr++ = *start++;
364 *component_ptr++ = 0;
365 }
366 *array_ptr = NULL;
367 if (count) *count = n_components;
368 return (char **)mem_block;
369 }
370
normalize_path(char * normalized_path,size_t normalized_path_size,const char * path)371 int normalize_path(char *normalized_path, size_t normalized_path_size, const char *path)
372 {
373 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 if (!normalized_path || normalized_path_size < 1) return ENOBUFS;
380
381 dst = normalized_path;
382 dst_end = normalized_path + normalized_path_size - 1; /* -1 allows for NULL terminator */
383 can_backup = true;
384
385 if (!path || !*path) {
386 if (dst > dst_end) {
387 *dst = 0;
388 return ENOBUFS;
389 }
390 *dst++ = '.';
391 *dst = 0;
392 return result;
393 }
394
395 if ((is_absolute = *path == '/')) {
396 if (dst < dst_end) {
397 *dst++ = '/';
398 } else {
399 *dst = 0;
400 return ENOBUFS;
401 }
402 }
403
404 for (start = end = path; *start; start = end) {
405 for (start = end; *start && *start == '/'; start++);
406 for (end = start; *end && *end != '/'; end++);
407 if ((component_len = end - start) == 0) break;
408 if (component_len == 1 && start[0] == '.') continue;
409 if (component_len == 2 && start[0] == '.' && start[1] == '.' && can_backup) {
410 /* back up one level */
411 if ((is_absolute && dst == normalized_path+1) || (!is_absolute && dst == normalized_path)) {
412 if (is_absolute) continue;
413 can_backup = false;
414 result = PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED;
415 } else {
416 if (is_absolute)
417 limit = normalized_path+1;
418 else
419 limit = normalized_path;
420 for (p = dst-1; p >= limit && *p != '/'; p--);
421 if (p < limit)
422 dst = limit;
423 else
424 dst = p;
425 continue;
426 }
427 }
428
429 if ((end-start) > (dst_end-dst)) {
430 return ENOBUFS;
431 }
432
433 if ((dst > normalized_path) && (dst < dst_end) && (dst[-1] != '/')) *dst++ = '/';
434 while ((start < end) && (dst < dst_end)) *dst++ = *start++;
435 }
436
437 if (dst == normalized_path) {
438 if (is_absolute)
439 *dst++ = '/';
440 else
441 *dst++ = '.';
442 }
443 *dst = 0;
444 return result;
445 }
446
common_path_prefix(char * common_path,size_t common_path_size,int * common_count,const char * path1,const char * path2)447 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 if (!common_path || common_path_size < 1) return ENOBUFS;
457
458 result = SUCCESS;
459 n_common = 0;
460 split1 = split_path(path1, &count1);
461 split2 = split_path(path2, &count2);
462
463 if (count1 <= count2)
464 min_count = count1;
465 else
466 min_count = count2;
467
468 if (min_count <= 0 || !split1 || !split2 ) {
469 result = SUCCESS;
470 *common_path = 0;
471 goto done;
472 }
473
474 for (n_common = 0; n_common < min_count; n_common++) {
475 if (strcmp(split1[n_common], split2[n_common]) != 0) break;
476 }
477
478 if (n_common == 0) {
479 result = SUCCESS;
480 *common_path = 0;
481 goto done;
482 }
483
484 dst = common_path;
485 dst_end = common_path + common_path_size - 1; /* -1 allows for NULL terminator */
486 for (i = 0; i < n_common; i++) {
487 for (src = split1[i]; *src && dst < dst_end;) *dst++ = *src++;
488 if (dst == dst_end && *src) {
489 *dst = 0;
490 result = ENOBUFS;
491 goto done;
492 }
493 if (dst[-1] != '/' && i < n_common-1) { /* insert path separator */
494 if (dst == dst_end) {
495 *dst = 0;
496 result = ENOBUFS;
497 goto done;
498 }
499 *dst++ = '/';
500 }
501 }
502 *dst = 0;
503
504 done:
505 free(split1);
506 free(split2);
507 if (common_count) *common_count = n_common;
508 return result;
509 }
510
make_normalized_absolute_path(char * result_path,size_t result_path_size,const char * path)511 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 if (!result_path || result_path_size < 1) return ENOBUFS;
517 *result_path = 0;
518 if ((error = make_path_absolute(absolute_path, sizeof(absolute_path), path)) != SUCCESS) return error;
519 if ((error = normalize_path(result_path, result_path_size, absolute_path)) != SUCCESS) return error;
520 return SUCCESS;
521 }
522
find_existing_directory_ancestor(char * ancestor,size_t ancestor_size,const char * path)523 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 if (!ancestor || ancestor_size < 1) return ENOBUFS;
530 *ancestor = 0;
531 strncpy(dir_path, path, sizeof(dir_path));
532 if (dir_path[sizeof(dir_path)-1] != '\0') return ENOBUFS;
533
534 while (strcmp(dir_path, "/") != 0) {
535 if (lstat(dir_path, &info) < 0) {
536 error = errno;
537 if (error != ENOENT) return error;
538 } else {
539 if (S_ISDIR(info.st_mode)) break;
540 }
541 error = get_dirname(dir_path, sizeof(dir_path), dir_path);
542 if (error != SUCCESS) {
543 return error;
544 }
545 }
546
547 strncpy(ancestor, dir_path, ancestor_size);
548 if (ancestor[ancestor_size-1] != '\0') return ENOBUFS;
549 return SUCCESS;
550 }
551
directory_list(const char * path,bool recursive,directory_list_callback_t callback,void * user_data)552 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 int error = 0;
559 char entry_path[PATH_MAX];
560 bool prune = false;
561
562 if (!(dir = opendir(path))) {
563 error = errno;
564 return error;
565 }
566
567 for (entry = readdir(dir); entry; entry = readdir(dir)) {
568
569 if (strcmp(entry->d_name, ".") == 0 ||
570 strcmp(entry->d_name, "..") == 0) {
571 continue;
572 }
573
574 error = path_concat(entry_path, sizeof(entry_path),
575 path, entry->d_name);
576 if (error != SUCCESS) {
577 closedir(dir);
578 /* Don't bother checking the return here.
579 * The path_concat error is more important
580 */
581 return error;
582 }
583
584 if (lstat(entry_path, &info) < 0) {
585 continue;
586 }
587
588 prune = !callback(path, entry->d_name, entry_path, &info, user_data);
589 if (S_ISDIR(info.st_mode)) {
590 if (recursive && !prune) {
591 error = directory_list(entry_path, recursive,
592 callback, user_data);
593 if (error != SUCCESS) {
594 closedir(dir);
595 /* Don't bother checking the return here.
596 * The directory_list error is more important
597 */
598 return error;
599 }
600 }
601 }
602 }
603 error = closedir(dir);
604 if (error) {
605 return error;
606 }
607 return SUCCESS;
608 }
609
is_ancestor_path(const char * ancestor,const char * path)610 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 bool result = false;
615
616 path_components = split_path(path, &path_count);
617 ancestor_components = split_path(ancestor, &ancestor_count);
618
619 if (!path_components || !ancestor_components) {
620 goto exit;
621 }
622
623 if (ancestor_count >= path_count) {
624 goto exit;
625 }
626
627 for (i = 0; i < ancestor_count; i++) {
628 if (strcmp(path_components[i], ancestor_components[i]) != 0) {
629 goto exit;
630 }
631 }
632
633 result = true;
634
635 exit:
636 free(path_components);
637 free(ancestor_components);
638 return result;
639 }
640
641