2 * Copyright (C) 2012 David Goulet <dgoulet@efficios.com>
3 * Copyright (C) 2013 Raphaël Beamonte <raphael.beamonte@gmail.com>
4 * Copyright (C) 2013 Jérémie Galarneau <jeremie.galarneau@efficios.com>
5 * Copyright (C) 2021 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
7 * SPDX-License-Identifier: GPL-2.0-only
11 #include <common/macros.hpp>
12 #include <common/common.hpp>
13 #include <common/path.hpp>
16 * Return a partial realpath(3) of the path even if the full path does not
17 * exist. For instance, with /tmp/test1/test2/test3, if test2/ does not exist
18 * but the /tmp/test1 does, the real path for /tmp/test1 is concatened with
19 * /test2/test3 then returned. In normal time, realpath(3) fails if the end
20 * point directory does not exist.
22 * Return a newly-allocated string.
25 char *utils_partial_realpath(const char *path
)
27 char *cut_path
= NULL
, *try_path
= NULL
, *try_path_prev
= NULL
;
28 const char *next
, *prev
, *end
;
29 char *resolved_path
= NULL
;
37 * Identify the end of the path, we don't want to treat the
38 * last char if it is a '/', we will just keep it on the side
39 * to be added at the end, and return a value coherent with
40 * the path given as argument
42 end
= path
+ strlen(path
);
43 if (*(end
-1) == '/') {
47 /* Initiate the values of the pointers before looping */
50 /* Only to ensure try_path is not NULL to enter the while */
51 try_path
= (char *)next
;
53 /* Resolve the canonical path of the first part of the path */
54 while (try_path
!= NULL
&& next
!= end
) {
55 char *try_path_buf
= NULL
;
58 * If there is not any '/' left, we want to try with
61 next
= strpbrk(next
+ 1, "/");
66 /* Cut the part we will be trying to resolve */
67 cut_path
= lttng_strndup(path
, next
- path
);
68 if (cut_path
== NULL
) {
69 PERROR("lttng_strndup");
73 try_path_buf
= zmalloc
<char>(LTTNG_PATH_MAX
);
79 /* Try to resolve this part */
80 try_path
= realpath((char *) cut_path
, try_path_buf
);
81 if (try_path
== NULL
) {
84 * There was an error, we just want to be assured it
85 * is linked to an unexistent directory, if it's another
86 * reason, we spawn an error
90 /* Ignore the error */
93 PERROR("realpath (partial_realpath)");
98 /* Save the place we are before trying the next step */
101 try_path_prev
= try_path
;
105 /* Free the allocated memory */
110 /* Allocate memory for the resolved path. */
111 resolved_path
= zmalloc
<char>(LTTNG_PATH_MAX
);
112 if (resolved_path
== NULL
) {
113 PERROR("zmalloc resolved path");
118 * If we were able to solve at least partially the path, we can concatenate
119 * what worked and what didn't work
121 if (try_path_prev
!= NULL
) {
122 /* If we risk to concatenate two '/', we remove one of them */
123 if (try_path_prev
[strlen(try_path_prev
) - 1] == '/' && prev
[0] == '/') {
124 try_path_prev
[strlen(try_path_prev
) - 1] = '\0';
128 * Duplicate the memory used by prev in case resolved_path and
129 * path are pointers for the same memory space
131 cut_path
= strdup(prev
);
132 if (cut_path
== NULL
) {
137 /* Concatenate the strings */
138 snprintf(resolved_path
, LTTNG_PATH_MAX
, "%s%s",
139 try_path_prev
, cut_path
);
141 /* Free the allocated memory */
145 try_path_prev
= NULL
;
147 * Else, we just copy the path in our resolved_path to
151 strncpy(resolved_path
, path
, LTTNG_PATH_MAX
);
154 /* Then we return the 'partially' resolved path */
155 return resolved_path
;
161 if (try_path_prev
!= try_path
) {
168 int expand_double_slashes_dot_and_dotdot(char *path
)
170 size_t expanded_path_len
, path_len
;
171 const char *curr_char
, *path_last_char
, *next_slash
, *prev_slash
;
173 path_len
= strlen(path
);
174 path_last_char
= &path
[path_len
];
180 expanded_path_len
= 0;
182 /* We iterate over the provided path to expand the "//", "../" and "./" */
183 for (curr_char
= path
; curr_char
<= path_last_char
; curr_char
= next_slash
+ 1) {
184 /* Find the next forward slash. */
185 size_t curr_token_len
;
187 if (curr_char
== path_last_char
) {
192 next_slash
= (const char *) memchr(curr_char
, '/', path_last_char
- curr_char
);
193 if (next_slash
== NULL
) {
194 /* Reached the end of the provided path. */
195 next_slash
= path_last_char
;
198 /* Compute how long is the previous token. */
199 curr_token_len
= next_slash
- curr_char
;
200 switch(curr_token_len
) {
203 * The pointer has not move meaning that curr_char is
204 * pointing to a slash. It that case there is no token
205 * to copy, so continue the iteration to find the next
211 * The pointer moved 1 character. Check if that
212 * character is a dot ('.'), if it is: omit it, else
213 * copy the token to the normalized path.
215 if (curr_char
[0] == '.') {
221 * The pointer moved 2 characters. Check if these
222 * characters are double dots ('..'). If that is the
223 * case, we need to remove the last token of the
226 if (curr_char
[0] == '.' && curr_char
[1] == '.') {
228 * Find the previous path component by
229 * using the memrchr function to find the
230 * previous forward slash and substract that
231 * len to the resulting path.
233 prev_slash
= (const char *) lttng_memrchr(path
, '/', expanded_path_len
);
235 * If prev_slash is NULL, we reached the
236 * beginning of the path. We can't go back any
239 if (prev_slash
!= NULL
) {
240 expanded_path_len
= prev_slash
- path
;
250 * Copy the current token which is neither a '.' nor a '..'.
252 path
[expanded_path_len
++] = '/';
253 memmove(&path
[expanded_path_len
], curr_char
, curr_token_len
);
254 expanded_path_len
+= curr_token_len
;
257 if (expanded_path_len
== 0) {
258 path
[expanded_path_len
++] = '/';
261 path
[expanded_path_len
] = '\0';
268 * Make a full resolution of the given path even if it doesn't exist.
269 * This function uses the utils_partial_realpath function to resolve
270 * symlinks and relatives paths at the start of the string, and
271 * implements functionnalities to resolve the './' and '../' strings
272 * in the middle of a path. This function is only necessary because
273 * realpath(3) does not accept to resolve unexistent paths.
274 * The returned string was allocated in the function, it is thus of
275 * the responsibility of the caller to free this memory.
278 char *_utils_expand_path(const char *path
, bool keep_symlink
)
281 char *absolute_path
= NULL
;
283 bool is_dot
, is_dotdot
;
290 /* Allocate memory for the absolute_path */
291 absolute_path
= zmalloc
<char>(LTTNG_PATH_MAX
);
292 if (absolute_path
== NULL
) {
293 PERROR("zmalloc expand path");
297 if (path
[0] == '/') {
298 ret
= lttng_strncpy(absolute_path
, path
, LTTNG_PATH_MAX
);
300 ERR("Path exceeds maximal size of %i bytes", LTTNG_PATH_MAX
);
305 * This is a relative path. We need to get the present working
306 * directory and start the path walk from there.
308 char current_working_dir
[LTTNG_PATH_MAX
];
311 cwd_ret
= getcwd(current_working_dir
, sizeof(current_working_dir
));
316 * Get the number of character in the CWD and allocate an array
317 * to can hold it and the path provided by the caller.
319 ret
= snprintf(absolute_path
, LTTNG_PATH_MAX
, "%s/%s",
320 current_working_dir
, path
);
321 if (ret
>= LTTNG_PATH_MAX
) {
322 ERR("Concatenating current working directory %s and path %s exceeds maximal size of %i bytes",
323 current_working_dir
, path
, LTTNG_PATH_MAX
);
329 /* Resolve partially our path */
330 char *new_absolute_path
= utils_partial_realpath(absolute_path
);
331 if (!new_absolute_path
) {
336 absolute_path
= new_absolute_path
;
339 ret
= expand_double_slashes_dot_and_dotdot(absolute_path
);
344 /* Identify the last token */
345 last_token
= strrchr(absolute_path
, '/');
347 /* Verify that this token is not a relative path */
348 is_dotdot
= (strcmp(last_token
, "/..") == 0);
349 is_dot
= (strcmp(last_token
, "/.") == 0);
351 /* If it is, take action */
352 if (is_dot
|| is_dotdot
) {
353 /* For both, remove this token */
356 /* If it was a reference to parent directory, go back one more time */
358 last_token
= strrchr(absolute_path
, '/');
360 /* If there was only one level left, we keep the first '/' */
361 if (last_token
== absolute_path
) {
369 return absolute_path
;
375 char *utils_expand_path(const char *path
)
377 return _utils_expand_path(path
, true);
380 char *utils_expand_path_keep_symlink(const char *path
)
382 return _utils_expand_path(path
, false);
This page took 0.03852 seconds and 4 git commands to generate.