Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : #include "config.h"
33 : : #endif
34 : :
35 : : #include <stdio.h>
36 : : #include <errno.h>
37 : : #include <stdlib.h>
38 : : #include <string.h>
39 : : #include <limits.h>
40 : :
41 : : #include "relative-path.h"
42 : : #include "canonpath.h"
43 : :
44 : : #define sfree(ptr) \
45 : : do { \
46 : : free(ptr); \
47 : : ptr = NULL; \
48 : : } while (0)
49 : :
50 : : /*
51 : : * count number of tokens between DELIMETER characters
52 : : */
53 : : static int
54 : : count_nodes(char *path)
55 : : {
56 : : int i;
57 : : char *tmp;
58 : :
59 [ # # ]: 0 : if (!path)
60 : : return 0;
61 : :
62 [ # # ]: 0 : for (i = 0, tmp = path; *tmp != '\0'; tmp++)
63 [ # # ]: 0 : if (*tmp == DELIMITER)
64 : 0 : i++;
65 : :
66 : : return i;
67 : : }
68 : :
69 : : /*
70 : : * return copy of next node in @path, or NULL
71 : : * @path is moved to the end of the next node
72 : : * @err is set to -errno on failure
73 : : * copy should be freed
74 : : */
75 : : static char *
76 : 0 : next_node(char **path, int *err)
77 : : {
78 : : int ret;
79 : : char *tmp, *start;
80 : :
81 [ # # ][ # # ]: 0 : if (!path || !*path) {
82 : 0 : *err = -EINVAL;
83 : 0 : return NULL;
84 : : }
85 : :
86 : 0 : *err = 0;
87 : 0 : start = *path;
88 : :
89 [ # # ]: 0 : for (tmp = *path; *tmp != '\0'; tmp++)
90 [ # # ]: 0 : if (*tmp == DELIMITER) {
91 : : int size;
92 : : char *node;
93 : :
94 : 0 : size = tmp - start + 1;
95 : 0 : node = malloc(size);
96 [ # # ]: 0 : if (!node) {
97 : 0 : *err = -ENOMEM;
98 : 0 : return NULL;
99 : : }
100 : :
101 : 0 : ret = snprintf(node, size, "%s", start);
102 [ # # ]: 0 : if (ret < 0) {
103 : 0 : free(node);
104 : 0 : *err = -EINVAL;
105 : 0 : return NULL;
106 : : }
107 : :
108 : 0 : *path = tmp;
109 : 0 : return node;
110 : : }
111 : :
112 : : return NULL;
113 : : }
114 : :
115 : : /*
116 : : * count number of nodes in common betwee @to and @from
117 : : * returns number of common nodes, or -errno on failure
118 : : */
119 : : static int
120 : 0 : count_common_nodes(char *to, char *from)
121 : : {
122 : : int err, common;
123 : : char *to_node, *from_node;
124 : :
125 [ # # ]: 0 : if (!to || !from)
126 : 0 : return -EINVAL;
127 : :
128 : 0 : err = 0;
129 : 0 : common = 0;
130 : 0 : to_node = NULL;
131 : 0 : from_node = NULL;
132 : :
133 : : do {
134 : 0 : to_node = next_node(&to, &err);
135 [ # # ][ # # ]: 0 : if (err || !to_node)
136 : : break;
137 : :
138 : 0 : from_node = next_node(&from, &err);
139 [ # # ][ # # ]: 0 : if (err || !from_node)
140 : : break;
141 : :
142 [ # # ]: 0 : if (strncmp(to_node, from_node, MAX_NAME_LEN))
143 : : break;
144 : :
145 : 0 : ++to;
146 : 0 : ++from;
147 : 0 : ++common;
148 : 0 : sfree(to_node);
149 : 0 : sfree(from_node);
150 : :
151 : 0 : } while (1);
152 : :
153 : 0 : sfree(to_node);
154 : 0 : sfree(from_node);
155 : :
156 [ # # ]: 0 : if (err)
157 : : return err;
158 : :
159 : 0 : return common;
160 : : }
161 : :
162 : : /*
163 : : * construct path of @count '../', './' if @count is zero, or NULL on error
164 : : * result should be freed
165 : : */
166 : : static char *
167 : 0 : up_nodes(int count)
168 : : {
169 : : char *path, *tmp;
170 : : int i, ret, len, size;
171 : :
172 [ # # ]: 0 : if (!count)
173 [ # # ]: 0 : return strdup("./");
174 : :
175 : 0 : len = strlen("../");
176 : 0 : size = len * count;
177 [ # # ]: 0 : if (size >= MAX_NAME_LEN)
178 : : return NULL;
179 : :
180 : 0 : path = malloc(size + 1);
181 [ # # ]: 0 : if (!path)
182 : : return NULL;
183 : :
184 : : tmp = path;
185 [ # # ]: 0 : for (i = 0; i < count; i++) {
186 : 0 : ret = sprintf(tmp, "../");
187 [ # # ]: 0 : if (ret < 0 || ret != len) {
188 : 0 : free(path);
189 : 0 : return NULL;
190 : : }
191 : 0 : tmp += ret;
192 : : }
193 : :
194 : : return path;
195 : : }
196 : :
197 : : /*
198 : : * return pointer to @offset'th node of path or NULL on error
199 : : */
200 : : static char *
201 : : node_offset(char *from, int offset)
202 : : {
203 : : char *path;
204 : :
205 [ # # ]: 0 : if (!from || !offset)
206 : : return NULL;
207 : :
208 [ # # ]: 0 : for (path = from; *path != '\0'; path++) {
209 [ # # ]: 0 : if (*path == DELIMITER)
210 [ # # ]: 0 : if (--offset == 0)
211 : 0 : return path + 1;
212 : : }
213 : :
214 : : return NULL;
215 : : }
216 : :
217 : : /*
218 : : * return a relative path from @from to @to
219 : : * result should be freed
220 : : */
221 : : char *
222 : 0 : relative_path_to(char *from, char *to, int *err)
223 : : {
224 : : int from_nodes, common;
225 : : char *to_absolute, __to_absolute[PATH_MAX];
226 : : char *from_absolute, __from_absolute[PATH_MAX];
227 : : char *up, *common_target_path, *relative_path;
228 : :
229 : 0 : *err = 0;
230 : 0 : up = NULL;
231 : 0 : to_absolute = NULL;
232 : 0 : from_absolute = NULL;
233 : 0 : relative_path = NULL;
234 : :
235 [ # # # # ]: 0 : if (strnlen(to, MAX_NAME_LEN) == MAX_NAME_LEN ||
236 : 0 : strnlen(from, MAX_NAME_LEN) == MAX_NAME_LEN) {
237 : : EPRINTF("invalid input; max path length is %d\n",
238 : : MAX_NAME_LEN);
239 : 0 : *err = -ENAMETOOLONG;
240 : 0 : return NULL;
241 : : }
242 : :
243 : 0 : to_absolute = canonpath(to, __to_absolute, sizeof(__to_absolute));
244 [ # # ]: 0 : if (!to_absolute) {
245 : : EPRINTF("failed to get absolute path of %s\n", to);
246 : 0 : *err = -errno;
247 : 0 : goto out;
248 : : }
249 : :
250 : 0 : from_absolute = canonpath(from, __from_absolute, sizeof(__from_absolute));
251 [ # # ]: 0 : if (!from_absolute) {
252 : : EPRINTF("failed to get absolute path of %s\n", from);
253 : 0 : *err = -errno;
254 : 0 : goto out;
255 : : }
256 : :
257 [ # # # # ]: 0 : if (strnlen(to_absolute, MAX_NAME_LEN) == MAX_NAME_LEN ||
258 : 0 : strnlen(from_absolute, MAX_NAME_LEN) == MAX_NAME_LEN) {
259 : : EPRINTF("invalid input; max path length is %d\n",
260 : : MAX_NAME_LEN);
261 : 0 : *err = -ENAMETOOLONG;
262 : 0 : goto out;
263 : : }
264 : :
265 : : /* count nodes in source path */
266 : 0 : from_nodes = count_nodes(from_absolute);
267 : :
268 : : /* count nodes in common */
269 : 0 : common = count_common_nodes(to_absolute + 1, from_absolute + 1);
270 [ # # ]: 0 : if (common < 0) {
271 : : EPRINTF("failed to count common nodes of %s and %s: %d\n",
272 : : to_absolute, from_absolute, common);
273 : 0 : *err = common;
274 : 0 : goto out;
275 : : }
276 : :
277 : : /* move up to common node */
278 : 0 : up = up_nodes(from_nodes - common - 1);
279 [ # # ]: 0 : if (!up) {
280 : : EPRINTF("failed to allocate relative path for %s: %d\n",
281 : : from_absolute, -ENOMEM);
282 : 0 : *err = -ENOMEM;
283 : 0 : goto out;
284 : : }
285 : :
286 : : /* get path from common node to target */
287 : 0 : common_target_path = node_offset(to_absolute, common + 1);
288 [ # # ]: 0 : if (!common_target_path) {
289 : : EPRINTF("failed to find common target path to %s: %d\n",
290 : : to_absolute, -EINVAL);
291 : 0 : *err = -EINVAL;
292 : 0 : goto out;
293 : : }
294 : :
295 : : /* get relative path */
296 [ # # ]: 0 : if (asprintf(&relative_path, "%s%s", up, common_target_path) == -1) {
297 : : EPRINTF("failed to construct final path %s%s: %d\n",
298 : : up, common_target_path, -ENOMEM);
299 : 0 : relative_path = NULL;
300 : 0 : *err = -ENOMEM;
301 : 0 : goto out;
302 : : }
303 : :
304 : : out:
305 : 0 : sfree(up);
306 : :
307 : 0 : return relative_path;
308 : : }
|