xref: /openbmc/linux/tools/perf/util/path.c (revision 78c99ba1)
1 /*
2  * I'm tired of doing "vsnprintf()" etc just to open a
3  * file, so here's a "return static buffer with printf"
4  * interface for paths.
5  *
6  * It's obviously not thread-safe. Sue me. But it's quite
7  * useful for doing things like
8  *
9  *   f = open(mkpath("%s/%s.perf", base, name), O_RDONLY);
10  *
11  * which is what it's designed for.
12  */
13 #include "cache.h"
14 
15 static char bad_path[] = "/bad-path/";
16 /*
17  * Two hacks:
18  */
19 
20 static char *get_perf_dir(void)
21 {
22 	return ".";
23 }
24 
25 size_t strlcpy(char *dest, const char *src, size_t size)
26 {
27 	size_t ret = strlen(src);
28 
29 	if (size) {
30 		size_t len = (ret >= size) ? size - 1 : ret;
31 		memcpy(dest, src, len);
32 		dest[len] = '\0';
33 	}
34 	return ret;
35 }
36 
37 
38 static char *get_pathname(void)
39 {
40 	static char pathname_array[4][PATH_MAX];
41 	static int index;
42 	return pathname_array[3 & ++index];
43 }
44 
45 static char *cleanup_path(char *path)
46 {
47 	/* Clean it up */
48 	if (!memcmp(path, "./", 2)) {
49 		path += 2;
50 		while (*path == '/')
51 			path++;
52 	}
53 	return path;
54 }
55 
56 char *mksnpath(char *buf, size_t n, const char *fmt, ...)
57 {
58 	va_list args;
59 	unsigned len;
60 
61 	va_start(args, fmt);
62 	len = vsnprintf(buf, n, fmt, args);
63 	va_end(args);
64 	if (len >= n) {
65 		strlcpy(buf, bad_path, n);
66 		return buf;
67 	}
68 	return cleanup_path(buf);
69 }
70 
71 static char *perf_vsnpath(char *buf, size_t n, const char *fmt, va_list args)
72 {
73 	const char *perf_dir = get_perf_dir();
74 	size_t len;
75 
76 	len = strlen(perf_dir);
77 	if (n < len + 1)
78 		goto bad;
79 	memcpy(buf, perf_dir, len);
80 	if (len && !is_dir_sep(perf_dir[len-1]))
81 		buf[len++] = '/';
82 	len += vsnprintf(buf + len, n - len, fmt, args);
83 	if (len >= n)
84 		goto bad;
85 	return cleanup_path(buf);
86 bad:
87 	strlcpy(buf, bad_path, n);
88 	return buf;
89 }
90 
91 char *perf_snpath(char *buf, size_t n, const char *fmt, ...)
92 {
93 	va_list args;
94 	va_start(args, fmt);
95 	(void)perf_vsnpath(buf, n, fmt, args);
96 	va_end(args);
97 	return buf;
98 }
99 
100 char *perf_pathdup(const char *fmt, ...)
101 {
102 	char path[PATH_MAX];
103 	va_list args;
104 	va_start(args, fmt);
105 	(void)perf_vsnpath(path, sizeof(path), fmt, args);
106 	va_end(args);
107 	return xstrdup(path);
108 }
109 
110 char *mkpath(const char *fmt, ...)
111 {
112 	va_list args;
113 	unsigned len;
114 	char *pathname = get_pathname();
115 
116 	va_start(args, fmt);
117 	len = vsnprintf(pathname, PATH_MAX, fmt, args);
118 	va_end(args);
119 	if (len >= PATH_MAX)
120 		return bad_path;
121 	return cleanup_path(pathname);
122 }
123 
124 char *perf_path(const char *fmt, ...)
125 {
126 	const char *perf_dir = get_perf_dir();
127 	char *pathname = get_pathname();
128 	va_list args;
129 	unsigned len;
130 
131 	len = strlen(perf_dir);
132 	if (len > PATH_MAX-100)
133 		return bad_path;
134 	memcpy(pathname, perf_dir, len);
135 	if (len && perf_dir[len-1] != '/')
136 		pathname[len++] = '/';
137 	va_start(args, fmt);
138 	len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
139 	va_end(args);
140 	if (len >= PATH_MAX)
141 		return bad_path;
142 	return cleanup_path(pathname);
143 }
144 
145 
146 /* perf_mkstemp() - create tmp file honoring TMPDIR variable */
147 int perf_mkstemp(char *path, size_t len, const char *template)
148 {
149 	const char *tmp;
150 	size_t n;
151 
152 	tmp = getenv("TMPDIR");
153 	if (!tmp)
154 		tmp = "/tmp";
155 	n = snprintf(path, len, "%s/%s", tmp, template);
156 	if (len <= n) {
157 		errno = ENAMETOOLONG;
158 		return -1;
159 	}
160 	return mkstemp(path);
161 }
162 
163 
164 const char *make_relative_path(const char *abs, const char *base)
165 {
166 	static char buf[PATH_MAX + 1];
167 	int baselen;
168 	if (!base)
169 		return abs;
170 	baselen = strlen(base);
171 	if (prefixcmp(abs, base))
172 		return abs;
173 	if (abs[baselen] == '/')
174 		baselen++;
175 	else if (base[baselen - 1] != '/')
176 		return abs;
177 	strcpy(buf, abs + baselen);
178 	return buf;
179 }
180 
181 /*
182  * It is okay if dst == src, but they should not overlap otherwise.
183  *
184  * Performs the following normalizations on src, storing the result in dst:
185  * - Ensures that components are separated by '/' (Windows only)
186  * - Squashes sequences of '/'.
187  * - Removes "." components.
188  * - Removes ".." components, and the components the precede them.
189  * Returns failure (non-zero) if a ".." component appears as first path
190  * component anytime during the normalization. Otherwise, returns success (0).
191  *
192  * Note that this function is purely textual.  It does not follow symlinks,
193  * verify the existence of the path, or make any system calls.
194  */
195 int normalize_path_copy(char *dst, const char *src)
196 {
197 	char *dst0;
198 
199 	if (has_dos_drive_prefix(src)) {
200 		*dst++ = *src++;
201 		*dst++ = *src++;
202 	}
203 	dst0 = dst;
204 
205 	if (is_dir_sep(*src)) {
206 		*dst++ = '/';
207 		while (is_dir_sep(*src))
208 			src++;
209 	}
210 
211 	for (;;) {
212 		char c = *src;
213 
214 		/*
215 		 * A path component that begins with . could be
216 		 * special:
217 		 * (1) "." and ends   -- ignore and terminate.
218 		 * (2) "./"           -- ignore them, eat slash and continue.
219 		 * (3) ".." and ends  -- strip one and terminate.
220 		 * (4) "../"          -- strip one, eat slash and continue.
221 		 */
222 		if (c == '.') {
223 			if (!src[1]) {
224 				/* (1) */
225 				src++;
226 			} else if (is_dir_sep(src[1])) {
227 				/* (2) */
228 				src += 2;
229 				while (is_dir_sep(*src))
230 					src++;
231 				continue;
232 			} else if (src[1] == '.') {
233 				if (!src[2]) {
234 					/* (3) */
235 					src += 2;
236 					goto up_one;
237 				} else if (is_dir_sep(src[2])) {
238 					/* (4) */
239 					src += 3;
240 					while (is_dir_sep(*src))
241 						src++;
242 					goto up_one;
243 				}
244 			}
245 		}
246 
247 		/* copy up to the next '/', and eat all '/' */
248 		while ((c = *src++) != '\0' && !is_dir_sep(c))
249 			*dst++ = c;
250 		if (is_dir_sep(c)) {
251 			*dst++ = '/';
252 			while (is_dir_sep(c))
253 				c = *src++;
254 			src--;
255 		} else if (!c)
256 			break;
257 		continue;
258 
259 	up_one:
260 		/*
261 		 * dst0..dst is prefix portion, and dst[-1] is '/';
262 		 * go up one level.
263 		 */
264 		dst--;	/* go to trailing '/' */
265 		if (dst <= dst0)
266 			return -1;
267 		/* Windows: dst[-1] cannot be backslash anymore */
268 		while (dst0 < dst && dst[-1] != '/')
269 			dst--;
270 	}
271 	*dst = '\0';
272 	return 0;
273 }
274 
275 /*
276  * path = Canonical absolute path
277  * prefix_list = Colon-separated list of absolute paths
278  *
279  * Determines, for each path in prefix_list, whether the "prefix" really
280  * is an ancestor directory of path.  Returns the length of the longest
281  * ancestor directory, excluding any trailing slashes, or -1 if no prefix
282  * is an ancestor.  (Note that this means 0 is returned if prefix_list is
283  * "/".) "/foo" is not considered an ancestor of "/foobar".  Directories
284  * are not considered to be their own ancestors.  path must be in a
285  * canonical form: empty components, or "." or ".." components are not
286  * allowed.  prefix_list may be null, which is like "".
287  */
288 int longest_ancestor_length(const char *path, const char *prefix_list)
289 {
290 	char buf[PATH_MAX+1];
291 	const char *ceil, *colon;
292 	int len, max_len = -1;
293 
294 	if (prefix_list == NULL || !strcmp(path, "/"))
295 		return -1;
296 
297 	for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
298 		for (colon = ceil; *colon && *colon != PATH_SEP; colon++);
299 		len = colon - ceil;
300 		if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
301 			continue;
302 		strlcpy(buf, ceil, len+1);
303 		if (normalize_path_copy(buf, buf) < 0)
304 			continue;
305 		len = strlen(buf);
306 		if (len > 0 && buf[len-1] == '/')
307 			buf[--len] = '\0';
308 
309 		if (!strncmp(path, buf, len) &&
310 		    path[len] == '/' &&
311 		    len > max_len) {
312 			max_len = len;
313 		}
314 	}
315 
316 	return max_len;
317 }
318 
319 /* strip arbitrary amount of directory separators at end of path */
320 static inline int chomp_trailing_dir_sep(const char *path, int len)
321 {
322 	while (len && is_dir_sep(path[len - 1]))
323 		len--;
324 	return len;
325 }
326 
327 /*
328  * If path ends with suffix (complete path components), returns the
329  * part before suffix (sans trailing directory separators).
330  * Otherwise returns NULL.
331  */
332 char *strip_path_suffix(const char *path, const char *suffix)
333 {
334 	int path_len = strlen(path), suffix_len = strlen(suffix);
335 
336 	while (suffix_len) {
337 		if (!path_len)
338 			return NULL;
339 
340 		if (is_dir_sep(path[path_len - 1])) {
341 			if (!is_dir_sep(suffix[suffix_len - 1]))
342 				return NULL;
343 			path_len = chomp_trailing_dir_sep(path, path_len);
344 			suffix_len = chomp_trailing_dir_sep(suffix, suffix_len);
345 		}
346 		else if (path[--path_len] != suffix[--suffix_len])
347 			return NULL;
348 	}
349 
350 	if (path_len && !is_dir_sep(path[path_len - 1]))
351 		return NULL;
352 	return xstrndup(path, chomp_trailing_dir_sep(path, path_len));
353 }
354