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.h>
12 #include <common/common.h>
13 #include <common/path.h>
14 #include <lttng/constant.h>
17 * Return a partial realpath(3) of the path even if the full path does not
18 * exist. For instance, with /tmp/test1/test2/test3, if test2/ does not exist
19 * but the /tmp/test1 does, the real path for /tmp/test1 is concatened with
20 * /test2/test3 then returned. In normal time, realpath(3) fails if the end
21 * point directory does not exist.
23 * Return a newly-allocated string.
26 char *utils_partial_realpath(const char *path
)
28 char *cut_path
= NULL
, *try_path
= NULL
, *try_path_prev
= NULL
;
29 const char *next
, *prev
, *end
;
30 char *resolved_path
= NULL
;
38 * Identify the end of the path, we don't want to treat the
39 * last char if it is a '/', we will just keep it on the side
40 * to be added at the end, and return a value coherent with
41 * the path given as argument
43 end
= path
+ strlen(path
);
44 if (*(end
-1) == '/') {
48 /* Initiate the values of the pointers before looping */
51 /* Only to ensure try_path is not NULL to enter the while */
52 try_path
= (char *)next
;
54 /* Resolve the canonical path of the first part of the path */
55 while (try_path
!= NULL
&& next
!= end
) {
56 char *try_path_buf
= NULL
;
59 * If there is not any '/' left, we want to try with
62 next
= strpbrk(next
+ 1, "/");
67 /* Cut the part we will be trying to resolve */
68 cut_path
= lttng_strndup(path
, next
- path
);
69 if (cut_path
== NULL
) {
70 PERROR("lttng_strndup");
74 try_path_buf
= (char *) zmalloc(LTTNG_PATH_MAX
);
80 /* Try to resolve this part */
81 try_path
= realpath((char *) cut_path
, try_path_buf
);
82 if (try_path
== NULL
) {
85 * There was an error, we just want to be assured it
86 * is linked to an unexistent directory, if it's another
87 * reason, we spawn an error
91 /* Ignore the error */
94 PERROR("realpath (partial_realpath)");
99 /* Save the place we are before trying the next step */
102 try_path_prev
= try_path
;
106 /* Free the allocated memory */
111 /* Allocate memory for the resolved path. */
112 resolved_path
= (char *) zmalloc(LTTNG_PATH_MAX
);
113 if (resolved_path
== NULL
) {
114 PERROR("zmalloc resolved path");
119 * If we were able to solve at least partially the path, we can concatenate
120 * what worked and what didn't work
122 if (try_path_prev
!= NULL
) {
123 /* If we risk to concatenate two '/', we remove one of them */
124 if (try_path_prev
[strlen(try_path_prev
) - 1] == '/' && prev
[0] == '/') {
125 try_path_prev
[strlen(try_path_prev
) - 1] = '\0';
129 * Duplicate the memory used by prev in case resolved_path and
130 * path are pointers for the same memory space
132 cut_path
= strdup(prev
);
133 if (cut_path
== NULL
) {
138 /* Concatenate the strings */
139 snprintf(resolved_path
, LTTNG_PATH_MAX
, "%s%s",
140 try_path_prev
, cut_path
);
142 /* Free the allocated memory */
146 try_path_prev
= NULL
;
148 * Else, we just copy the path in our resolved_path to
152 strncpy(resolved_path
, path
, LTTNG_PATH_MAX
);
155 /* Then we return the 'partially' resolved path */
156 return resolved_path
;
162 if (try_path_prev
!= try_path
) {
169 int expand_double_slashes_dot_and_dotdot(char *path
)
171 size_t expanded_path_len
, path_len
;
172 const char *curr_char
, *path_last_char
, *next_slash
, *prev_slash
;
174 path_len
= strlen(path
);
175 path_last_char
= &path
[path_len
];
181 expanded_path_len
= 0;
183 /* We iterate over the provided path to expand the "//", "../" and "./" */
184 for (curr_char
= path
; curr_char
<= path_last_char
; curr_char
= next_slash
+ 1) {
185 /* Find the next forward slash. */
186 size_t curr_token_len
;
188 if (curr_char
== path_last_char
) {
193 next_slash
= (const char *) memchr(curr_char
, '/', path_last_char
- curr_char
);
194 if (next_slash
== NULL
) {
195 /* Reached the end of the provided path. */
196 next_slash
= path_last_char
;
199 /* Compute how long is the previous token. */
200 curr_token_len
= next_slash
- curr_char
;
201 switch(curr_token_len
) {
204 * The pointer has not move meaning that curr_char is
205 * pointing to a slash. It that case there is no token
206 * to copy, so continue the iteration to find the next
212 * The pointer moved 1 character. Check if that
213 * character is a dot ('.'), if it is: omit it, else
214 * copy the token to the normalized path.
216 if (curr_char
[0] == '.') {
222 * The pointer moved 2 characters. Check if these
223 * characters are double dots ('..'). If that is the
224 * case, we need to remove the last token of the
227 if (curr_char
[0] == '.' && curr_char
[1] == '.') {
229 * Find the previous path component by
230 * using the memrchr function to find the
231 * previous forward slash and substract that
232 * len to the resulting path.
234 prev_slash
= (const char *) lttng_memrchr(path
, '/', expanded_path_len
);
236 * If prev_slash is NULL, we reached the
237 * beginning of the path. We can't go back any
240 if (prev_slash
!= NULL
) {
241 expanded_path_len
= prev_slash
- path
;
251 * Copy the current token which is neither a '.' nor a '..'.
253 path
[expanded_path_len
++] = '/';
254 memmove(&path
[expanded_path_len
], curr_char
, curr_token_len
);
255 expanded_path_len
+= curr_token_len
;
258 if (expanded_path_len
== 0) {
259 path
[expanded_path_len
++] = '/';
262 path
[expanded_path_len
] = '\0';
269 * Make a full resolution of the given path even if it doesn't exist.
270 * This function uses the utils_partial_realpath function to resolve
271 * symlinks and relatives paths at the start of the string, and
272 * implements functionnalities to resolve the './' and '../' strings
273 * in the middle of a path. This function is only necessary because
274 * realpath(3) does not accept to resolve unexistent paths.
275 * The returned string was allocated in the function, it is thus of
276 * the responsibility of the caller to free this memory.
279 char *_utils_expand_path(const char *path
, bool keep_symlink
)
282 char *absolute_path
= NULL
;
284 bool is_dot
, is_dotdot
;
291 /* Allocate memory for the absolute_path */
292 absolute_path
= (char *) zmalloc(LTTNG_PATH_MAX
);
293 if (absolute_path
== NULL
) {
294 PERROR("zmalloc expand path");
298 if (path
[0] == '/') {
299 ret
= lttng_strncpy(absolute_path
, path
, LTTNG_PATH_MAX
);
301 ERR("Path exceeds maximal size of %i bytes", LTTNG_PATH_MAX
);
306 * This is a relative path. We need to get the present working
307 * directory and start the path walk from there.
309 char current_working_dir
[LTTNG_PATH_MAX
];
312 cwd_ret
= getcwd(current_working_dir
, sizeof(current_working_dir
));
317 * Get the number of character in the CWD and allocate an array
318 * to can hold it and the path provided by the caller.
320 ret
= snprintf(absolute_path
, LTTNG_PATH_MAX
, "%s/%s",
321 current_working_dir
, path
);
322 if (ret
>= LTTNG_PATH_MAX
) {
323 ERR("Concatenating current working directory %s and path %s exceeds maximal size of %i bytes",
324 current_working_dir
, path
, LTTNG_PATH_MAX
);
330 /* Resolve partially our path */
331 char *new_absolute_path
= utils_partial_realpath(absolute_path
);
332 if (!new_absolute_path
) {
337 absolute_path
= new_absolute_path
;
340 ret
= expand_double_slashes_dot_and_dotdot(absolute_path
);
345 /* Identify the last token */
346 last_token
= strrchr(absolute_path
, '/');
348 /* Verify that this token is not a relative path */
349 is_dotdot
= (strcmp(last_token
, "/..") == 0);
350 is_dot
= (strcmp(last_token
, "/.") == 0);
352 /* If it is, take action */
353 if (is_dot
|| is_dotdot
) {
354 /* For both, remove this token */
357 /* If it was a reference to parent directory, go back one more time */
359 last_token
= strrchr(absolute_path
, '/');
361 /* If there was only one level left, we keep the first '/' */
362 if (last_token
== absolute_path
) {
370 return absolute_path
;
376 char *utils_expand_path(const char *path
)
378 return _utils_expand_path(path
, true);
381 char *utils_expand_path_keep_symlink(const char *path
)
383 return _utils_expand_path(path
, false);
This page took 0.042529 seconds and 4 git commands to generate.