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