diff options
Diffstat (limited to 'Documentation/perf_counter/path.c')
-rw-r--r-- | Documentation/perf_counter/path.c | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/Documentation/perf_counter/path.c b/Documentation/perf_counter/path.c new file mode 100644 index 000000000000..891b612ec1a9 --- /dev/null +++ b/Documentation/perf_counter/path.c | |||
@@ -0,0 +1,392 @@ | |||
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 | static char *user_path(char *buf, char *path, int sz) | ||
165 | { | ||
166 | struct passwd *pw; | ||
167 | char *slash; | ||
168 | int len, baselen; | ||
169 | |||
170 | if (!path || path[0] != '~') | ||
171 | return NULL; | ||
172 | path++; | ||
173 | slash = strchr(path, '/'); | ||
174 | if (path[0] == '/' || !path[0]) { | ||
175 | pw = getpwuid(getuid()); | ||
176 | } | ||
177 | else { | ||
178 | if (slash) { | ||
179 | *slash = 0; | ||
180 | pw = getpwnam(path); | ||
181 | *slash = '/'; | ||
182 | } | ||
183 | else | ||
184 | pw = getpwnam(path); | ||
185 | } | ||
186 | if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir)) | ||
187 | return NULL; | ||
188 | baselen = strlen(pw->pw_dir); | ||
189 | memcpy(buf, pw->pw_dir, baselen); | ||
190 | while ((1 < baselen) && (buf[baselen-1] == '/')) { | ||
191 | buf[baselen-1] = 0; | ||
192 | baselen--; | ||
193 | } | ||
194 | if (slash && slash[1]) { | ||
195 | len = strlen(slash); | ||
196 | if (sz <= baselen + len) | ||
197 | return NULL; | ||
198 | memcpy(buf + baselen, slash, len + 1); | ||
199 | } | ||
200 | return buf; | ||
201 | } | ||
202 | |||
203 | const char *make_relative_path(const char *abs, const char *base) | ||
204 | { | ||
205 | static char buf[PATH_MAX + 1]; | ||
206 | int baselen; | ||
207 | if (!base) | ||
208 | return abs; | ||
209 | baselen = strlen(base); | ||
210 | if (prefixcmp(abs, base)) | ||
211 | return abs; | ||
212 | if (abs[baselen] == '/') | ||
213 | baselen++; | ||
214 | else if (base[baselen - 1] != '/') | ||
215 | return abs; | ||
216 | strcpy(buf, abs + baselen); | ||
217 | return buf; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * It is okay if dst == src, but they should not overlap otherwise. | ||
222 | * | ||
223 | * Performs the following normalizations on src, storing the result in dst: | ||
224 | * - Ensures that components are separated by '/' (Windows only) | ||
225 | * - Squashes sequences of '/'. | ||
226 | * - Removes "." components. | ||
227 | * - Removes ".." components, and the components the precede them. | ||
228 | * Returns failure (non-zero) if a ".." component appears as first path | ||
229 | * component anytime during the normalization. Otherwise, returns success (0). | ||
230 | * | ||
231 | * Note that this function is purely textual. It does not follow symlinks, | ||
232 | * verify the existence of the path, or make any system calls. | ||
233 | */ | ||
234 | int normalize_path_copy(char *dst, const char *src) | ||
235 | { | ||
236 | char *dst0; | ||
237 | |||
238 | if (has_dos_drive_prefix(src)) { | ||
239 | *dst++ = *src++; | ||
240 | *dst++ = *src++; | ||
241 | } | ||
242 | dst0 = dst; | ||
243 | |||
244 | if (is_dir_sep(*src)) { | ||
245 | *dst++ = '/'; | ||
246 | while (is_dir_sep(*src)) | ||
247 | src++; | ||
248 | } | ||
249 | |||
250 | for (;;) { | ||
251 | char c = *src; | ||
252 | |||
253 | /* | ||
254 | * A path component that begins with . could be | ||
255 | * special: | ||
256 | * (1) "." and ends -- ignore and terminate. | ||
257 | * (2) "./" -- ignore them, eat slash and continue. | ||
258 | * (3) ".." and ends -- strip one and terminate. | ||
259 | * (4) "../" -- strip one, eat slash and continue. | ||
260 | */ | ||
261 | if (c == '.') { | ||
262 | if (!src[1]) { | ||
263 | /* (1) */ | ||
264 | src++; | ||
265 | } else if (is_dir_sep(src[1])) { | ||
266 | /* (2) */ | ||
267 | src += 2; | ||
268 | while (is_dir_sep(*src)) | ||
269 | src++; | ||
270 | continue; | ||
271 | } else if (src[1] == '.') { | ||
272 | if (!src[2]) { | ||
273 | /* (3) */ | ||
274 | src += 2; | ||
275 | goto up_one; | ||
276 | } else if (is_dir_sep(src[2])) { | ||
277 | /* (4) */ | ||
278 | src += 3; | ||
279 | while (is_dir_sep(*src)) | ||
280 | src++; | ||
281 | goto up_one; | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
286 | /* copy up to the next '/', and eat all '/' */ | ||
287 | while ((c = *src++) != '\0' && !is_dir_sep(c)) | ||
288 | *dst++ = c; | ||
289 | if (is_dir_sep(c)) { | ||
290 | *dst++ = '/'; | ||
291 | while (is_dir_sep(c)) | ||
292 | c = *src++; | ||
293 | src--; | ||
294 | } else if (!c) | ||
295 | break; | ||
296 | continue; | ||
297 | |||
298 | up_one: | ||
299 | /* | ||
300 | * dst0..dst is prefix portion, and dst[-1] is '/'; | ||
301 | * go up one level. | ||
302 | */ | ||
303 | dst--; /* go to trailing '/' */ | ||
304 | if (dst <= dst0) | ||
305 | return -1; | ||
306 | /* Windows: dst[-1] cannot be backslash anymore */ | ||
307 | while (dst0 < dst && dst[-1] != '/') | ||
308 | dst--; | ||
309 | } | ||
310 | *dst = '\0'; | ||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | /* | ||
315 | * path = Canonical absolute path | ||
316 | * prefix_list = Colon-separated list of absolute paths | ||
317 | * | ||
318 | * Determines, for each path in prefix_list, whether the "prefix" really | ||
319 | * is an ancestor directory of path. Returns the length of the longest | ||
320 | * ancestor directory, excluding any trailing slashes, or -1 if no prefix | ||
321 | * is an ancestor. (Note that this means 0 is returned if prefix_list is | ||
322 | * "/".) "/foo" is not considered an ancestor of "/foobar". Directories | ||
323 | * are not considered to be their own ancestors. path must be in a | ||
324 | * canonical form: empty components, or "." or ".." components are not | ||
325 | * allowed. prefix_list may be null, which is like "". | ||
326 | */ | ||
327 | int longest_ancestor_length(const char *path, const char *prefix_list) | ||
328 | { | ||
329 | char buf[PATH_MAX+1]; | ||
330 | const char *ceil, *colon; | ||
331 | int len, max_len = -1; | ||
332 | |||
333 | if (prefix_list == NULL || !strcmp(path, "/")) | ||
334 | return -1; | ||
335 | |||
336 | for (colon = ceil = prefix_list; *colon; ceil = colon+1) { | ||
337 | for (colon = ceil; *colon && *colon != PATH_SEP; colon++); | ||
338 | len = colon - ceil; | ||
339 | if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil)) | ||
340 | continue; | ||
341 | strlcpy(buf, ceil, len+1); | ||
342 | if (normalize_path_copy(buf, buf) < 0) | ||
343 | continue; | ||
344 | len = strlen(buf); | ||
345 | if (len > 0 && buf[len-1] == '/') | ||
346 | buf[--len] = '\0'; | ||
347 | |||
348 | if (!strncmp(path, buf, len) && | ||
349 | path[len] == '/' && | ||
350 | len > max_len) { | ||
351 | max_len = len; | ||
352 | } | ||
353 | } | ||
354 | |||
355 | return max_len; | ||
356 | } | ||
357 | |||
358 | /* strip arbitrary amount of directory separators at end of path */ | ||
359 | static inline int chomp_trailing_dir_sep(const char *path, int len) | ||
360 | { | ||
361 | while (len && is_dir_sep(path[len - 1])) | ||
362 | len--; | ||
363 | return len; | ||
364 | } | ||
365 | |||
366 | /* | ||
367 | * If path ends with suffix (complete path components), returns the | ||
368 | * part before suffix (sans trailing directory separators). | ||
369 | * Otherwise returns NULL. | ||
370 | */ | ||
371 | char *strip_path_suffix(const char *path, const char *suffix) | ||
372 | { | ||
373 | int path_len = strlen(path), suffix_len = strlen(suffix); | ||
374 | |||
375 | while (suffix_len) { | ||
376 | if (!path_len) | ||
377 | return NULL; | ||
378 | |||
379 | if (is_dir_sep(path[path_len - 1])) { | ||
380 | if (!is_dir_sep(suffix[suffix_len - 1])) | ||
381 | return NULL; | ||
382 | path_len = chomp_trailing_dir_sep(path, path_len); | ||
383 | suffix_len = chomp_trailing_dir_sep(suffix, suffix_len); | ||
384 | } | ||
385 | else if (path[--path_len] != suffix[--suffix_len]) | ||
386 | return NULL; | ||
387 | } | ||
388 | |||
389 | if (path_len && !is_dir_sep(path[path_len - 1])) | ||
390 | return NULL; | ||
391 | return xstrndup(path, chomp_trailing_dir_sep(path, path_len)); | ||
392 | } | ||