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) {
Event leaked_storage: Variable "dir" going out of scope leaks the storage it points to.
Also see events: [alloc_fn][var_assign][leaked_storage][noescape]
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) {
Event leaked_storage: Variable "dir" going out of scope leaks the storage it points to.
Also see events: [alloc_fn][var_assign][leaked_storage][noescape]
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