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/common.hpp>
12 #include <common/macros.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.
24 static char *utils_partial_realpath(const char *path
)
26 char *cut_path
= nullptr, *try_path
= nullptr, *try_path_prev
= nullptr;
27 const char *next
, *prev
, *end
;
28 char *resolved_path
= nullptr;
31 if (path
== nullptr) {
36 * Identify the end of the path, we don't want to treat the
37 * last char if it is a '/', we will just keep it on the side
38 * to be added at the end, and return a value coherent with
39 * the path given as argument
41 end
= path
+ strlen(path
);
42 if (*(end
- 1) == '/') {
46 /* Initiate the values of the pointers before looping */
49 /* Only to ensure try_path is not NULL to enter the while */
50 try_path
= (char *) next
;
52 /* Resolve the canonical path of the first part of the path */
53 while (try_path
!= nullptr && next
!= end
) {
54 char *try_path_buf
= nullptr;
57 * If there is not any '/' left, we want to try with
60 next
= strpbrk(next
+ 1, "/");
61 if (next
== nullptr) {
65 /* Cut the part we will be trying to resolve */
66 cut_path
= lttng_strndup(path
, next
- path
);
67 if (cut_path
== nullptr) {
68 PERROR("lttng_strndup");
72 try_path_buf
= zmalloc
<char>(LTTNG_PATH_MAX
);
78 /* Try to resolve this part */
79 try_path
= realpath((char *) cut_path
, try_path_buf
);
80 if (try_path
== nullptr) {
83 * There was an error, we just want to be assured it
84 * is linked to an unexistent directory, if it's another
85 * reason, we spawn an error
89 /* Ignore the error */
92 PERROR("realpath (partial_realpath)");
97 /* Save the place we are before trying the next step */
98 try_path_buf
= nullptr;
100 try_path_prev
= try_path
;
104 /* Free the allocated memory */
109 /* Allocate memory for the resolved path. */
110 resolved_path
= zmalloc
<char>(LTTNG_PATH_MAX
);
111 if (resolved_path
== nullptr) {
112 PERROR("zmalloc resolved path");
117 * If we were able to solve at least partially the path, we can concatenate
118 * what worked and what didn't work
120 if (try_path_prev
!= nullptr) {
121 /* If we risk to concatenate two '/', we remove one of them */
122 if (try_path_prev
[strlen(try_path_prev
) - 1] == '/' && prev
[0] == '/') {
123 try_path_prev
[strlen(try_path_prev
) - 1] = '\0';
127 * Duplicate the memory used by prev in case resolved_path and
128 * path are pointers for the same memory space
130 cut_path
= strdup(prev
);
131 if (cut_path
== nullptr) {
136 /* Concatenate the strings */
137 snprintf(resolved_path
, LTTNG_PATH_MAX
, "%s%s", try_path_prev
, cut_path
);
139 /* Free the allocated memory */
143 try_path_prev
= nullptr;
145 * Else, we just copy the path in our resolved_path to
149 strncpy(resolved_path
, path
, LTTNG_PATH_MAX
);
152 /* Then we return the 'partially' resolved path */
153 return resolved_path
;
159 if (try_path_prev
!= try_path
) {
165 static int expand_double_slashes_dot_and_dotdot(char *path
)
167 size_t expanded_path_len
, path_len
;
168 const char *curr_char
, *path_last_char
, *next_slash
, *prev_slash
;
170 path_len
= strlen(path
);
171 path_last_char
= &path
[path_len
];
177 expanded_path_len
= 0;
179 /* We iterate over the provided path to expand the "//", "../" and "./" */
180 for (curr_char
= path
; curr_char
<= path_last_char
; curr_char
= next_slash
+ 1) {
181 /* Find the next forward slash. */
182 size_t curr_token_len
;
184 if (curr_char
== path_last_char
) {
189 next_slash
= (const char *) memchr(curr_char
, '/', path_last_char
- curr_char
);
190 if (next_slash
== nullptr) {
191 /* Reached the end of the provided path. */
192 next_slash
= path_last_char
;
195 /* Compute how long is the previous token. */
196 curr_token_len
= next_slash
- curr_char
;
197 switch (curr_token_len
) {
200 * The pointer has not move meaning that curr_char is
201 * pointing to a slash. It that case there is no token
202 * to copy, so continue the iteration to find the next
208 * The pointer moved 1 character. Check if that
209 * character is a dot ('.'), if it is: omit it, else
210 * copy the token to the normalized path.
212 if (curr_char
[0] == '.') {
218 * The pointer moved 2 characters. Check if these
219 * characters are double dots ('..'). If that is the
220 * case, we need to remove the last token of the
223 if (curr_char
[0] == '.' && curr_char
[1] == '.') {
225 * Find the previous path component by
226 * using the memrchr function to find the
227 * previous forward slash and substract that
228 * len to the resulting path.
231 (const char *) lttng_memrchr(path
, '/', expanded_path_len
);
233 * If prev_slash is NULL, we reached the
234 * beginning of the path. We can't go back any
237 if (prev_slash
!= nullptr) {
238 expanded_path_len
= prev_slash
- path
;
248 * Copy the current token which is neither a '.' nor a '..'.
250 path
[expanded_path_len
++] = '/';
251 memmove(&path
[expanded_path_len
], curr_char
, curr_token_len
);
252 expanded_path_len
+= curr_token_len
;
255 if (expanded_path_len
== 0) {
256 path
[expanded_path_len
++] = '/';
259 path
[expanded_path_len
] = '\0';
266 * Make a full resolution of the given path even if it doesn't exist.
267 * This function uses the utils_partial_realpath function to resolve
268 * symlinks and relatives paths at the start of the string, and
269 * implements functionnalities to resolve the './' and '../' strings
270 * in the middle of a path. This function is only necessary because
271 * realpath(3) does not accept to resolve unexistent paths.
272 * The returned string was allocated in the function, it is thus of
273 * the responsibility of the caller to free this memory.
275 static char *_utils_expand_path(const char *path
, bool keep_symlink
)
278 char *absolute_path
= nullptr;
280 bool is_dot
, is_dotdot
;
283 if (path
== nullptr) {
287 /* Allocate memory for the absolute_path */
288 absolute_path
= zmalloc
<char>(LTTNG_PATH_MAX
);
289 if (absolute_path
== nullptr) {
290 PERROR("zmalloc expand path");
294 if (path
[0] == '/') {
295 ret
= lttng_strncpy(absolute_path
, path
, LTTNG_PATH_MAX
);
297 ERR("Path exceeds maximal size of %i bytes", LTTNG_PATH_MAX
);
302 * This is a relative path. We need to get the present working
303 * directory and start the path walk from there.
305 char current_working_dir
[LTTNG_PATH_MAX
];
308 cwd_ret
= getcwd(current_working_dir
, sizeof(current_working_dir
));
313 * Get the number of character in the CWD and allocate an array
314 * to can hold it and the path provided by the caller.
316 ret
= snprintf(absolute_path
, LTTNG_PATH_MAX
, "%s/%s", current_working_dir
, path
);
317 if (ret
>= LTTNG_PATH_MAX
) {
318 ERR("Concatenating current working directory %s and path %s exceeds maximal size of %i bytes",
327 /* Resolve partially our path */
328 char *new_absolute_path
= utils_partial_realpath(absolute_path
);
329 if (!new_absolute_path
) {
334 absolute_path
= new_absolute_path
;
337 ret
= expand_double_slashes_dot_and_dotdot(absolute_path
);
342 /* Identify the last token */
343 last_token
= strrchr(absolute_path
, '/');
345 /* Verify that this token is not a relative path */
346 is_dotdot
= (strcmp(last_token
, "/..") == 0);
347 is_dot
= (strcmp(last_token
, "/.") == 0);
349 /* If it is, take action */
350 if (is_dot
|| is_dotdot
) {
351 /* For both, remove this token */
354 /* If it was a reference to parent directory, go back one more time */
356 last_token
= strrchr(absolute_path
, '/');
358 /* If there was only one level left, we keep the first '/' */
359 if (last_token
== absolute_path
) {
367 return absolute_path
;
373 char *utils_expand_path(const char *path
)
375 return _utils_expand_path(path
, true);
378 char *utils_expand_path_keep_symlink(const char *path
)
380 return _utils_expand_path(path
, false);
This page took 0.03743 seconds and 4 git commands to generate.