]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/path-util.c
hwdb: Update database of Bluetooth company identifiers
[thirdparty/systemd.git] / src / shared / path-util.c
CommitLineData
9eb977db
KS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010-2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <assert.h>
23#include <string.h>
24#include <unistd.h>
25#include <errno.h>
26#include <stdlib.h>
27#include <signal.h>
28#include <stdio.h>
29#include <fcntl.h>
30#include <dirent.h>
31#include <sys/statvfs.h>
32
33#include "macro.h"
34#include "util.h"
35#include "log.h"
36#include "strv.h"
37#include "path-util.h"
a8348796 38#include "missing.h"
9eb977db
KS
39
40bool path_is_absolute(const char *p) {
41 return p[0] == '/';
42}
43
44bool is_path(const char *p) {
45 return !!strchr(p, '/');
46}
47
9eb977db
KS
48int path_get_parent(const char *path, char **_r) {
49 const char *e, *a = NULL, *b = NULL, *p;
50 char *r;
51 bool slash = false;
52
53 assert(path);
54 assert(_r);
55
56 if (!*path)
57 return -EINVAL;
58
59 for (e = path; *e; e++) {
60
61 if (!slash && *e == '/') {
62 a = b;
63 b = e;
64 slash = true;
65 } else if (slash && *e != '/')
66 slash = false;
67 }
68
69 if (*(e-1) == '/')
70 p = a;
71 else
72 p = b;
73
74 if (!p)
75 return -EINVAL;
76
77 if (p == path)
78 r = strdup("/");
79 else
80 r = strndup(path, p-path);
81
82 if (!r)
83 return -ENOMEM;
84
85 *_r = r;
86 return 0;
87}
88
89char **path_split_and_make_absolute(const char *p) {
90 char **l;
91 assert(p);
92
116cc028
ZJS
93 l = strv_split(p, ":");
94 if (!l)
9eb977db
KS
95 return NULL;
96
97 if (!path_strv_make_absolute_cwd(l)) {
98 strv_free(l);
99 return NULL;
100 }
101
102 return l;
103}
104
105char *path_make_absolute(const char *p, const char *prefix) {
106 assert(p);
107
108 /* Makes every item in the list an absolute path by prepending
109 * the prefix, if specified and necessary */
110
111 if (path_is_absolute(p) || !prefix)
112 return strdup(p);
113
b7def684 114 return strjoin(prefix, "/", p, NULL);
9eb977db
KS
115}
116
117char *path_make_absolute_cwd(const char *p) {
116cc028 118 _cleanup_free_ char *cwd = NULL;
9eb977db
KS
119
120 assert(p);
121
122 /* Similar to path_make_absolute(), but prefixes with the
123 * current working directory. */
124
125 if (path_is_absolute(p))
126 return strdup(p);
127
91a6489d
LP
128 cwd = get_current_dir_name();
129 if (!cwd)
9eb977db
KS
130 return NULL;
131
116cc028 132 return path_make_absolute(p, cwd);
9eb977db
KS
133}
134
135char **path_strv_make_absolute_cwd(char **l) {
136 char **s;
137
138 /* Goes through every item in the string list and makes it
139 * absolute. This works in place and won't rollback any
140 * changes on failure. */
141
142 STRV_FOREACH(s, l) {
143 char *t;
144
116cc028
ZJS
145 t = path_make_absolute_cwd(*s);
146 if (!t)
9eb977db
KS
147 return NULL;
148
149 free(*s);
150 *s = t;
151 }
152
153 return l;
154}
155
112cfb18 156char **path_strv_canonicalize_absolute(char **l, const char *prefix) {
9eb977db
KS
157 char **s;
158 unsigned k = 0;
159 bool enomem = false;
160
161 if (strv_isempty(l))
162 return l;
163
164 /* Goes through every item in the string list and canonicalize
165 * the path. This works in place and won't rollback any
166 * changes on failure. */
167
168 STRV_FOREACH(s, l) {
169 char *t, *u;
170
112cfb18 171 if (!path_is_absolute(*s))
9eb977db 172 continue;
112cfb18
MM
173
174 if (prefix) {
175 t = strappend(prefix, *s);
176 free(*s);
177 *s = NULL;
178
179 if (!t) {
180 enomem = true;
181 continue;
182 }
183 } else {
184 t = *s;
185 *s = NULL;
9eb977db
KS
186 }
187
188 errno = 0;
189 u = canonicalize_file_name(t);
9eb977db 190 if (!u) {
874310b7
ZJS
191 if (errno == ENOENT)
192 u = t;
193 else {
194 free(t);
112cfb18 195 if (errno == ENOMEM || errno == 0)
874310b7
ZJS
196 enomem = true;
197
198 continue;
199 }
91a6489d
LP
200 } else
201 free(t);
9eb977db
KS
202
203 l[k++] = u;
204 }
205
206 l[k] = NULL;
207
208 if (enomem)
209 return NULL;
210
211 return l;
212}
213
112cfb18
MM
214char **path_strv_canonicalize_absolute_uniq(char **l, const char *prefix) {
215
fabe5c0e
LP
216 if (strv_isempty(l))
217 return l;
218
112cfb18 219 if (!path_strv_canonicalize_absolute(l, prefix))
fabe5c0e
LP
220 return NULL;
221
222 return strv_uniq(l);
223}
224
9eb977db
KS
225char *path_kill_slashes(char *path) {
226 char *f, *t;
227 bool slash = false;
228
229 /* Removes redundant inner and trailing slashes. Modifies the
230 * passed string in-place.
231 *
232 * ///foo///bar/ becomes /foo/bar
233 */
234
235 for (f = path, t = path; *f; f++) {
236
237 if (*f == '/') {
238 slash = true;
239 continue;
240 }
241
242 if (slash) {
243 slash = false;
244 *(t++) = '/';
245 }
246
247 *(t++) = *f;
248 }
249
250 /* Special rule, if we are talking of the root directory, a
251 trailing slash is good */
252
253 if (t == path && slash)
254 *(t++) = '/';
255
256 *t = 0;
257 return path;
258}
259
424a19f8 260char* path_startswith(const char *path, const char *prefix) {
9eb977db
KS
261 assert(path);
262 assert(prefix);
263
264 if ((path[0] == '/') != (prefix[0] == '/'))
424a19f8 265 return NULL;
9eb977db
KS
266
267 for (;;) {
268 size_t a, b;
269
270 path += strspn(path, "/");
271 prefix += strspn(prefix, "/");
272
273 if (*prefix == 0)
424a19f8 274 return (char*) path;
9eb977db
KS
275
276 if (*path == 0)
424a19f8 277 return NULL;
9eb977db
KS
278
279 a = strcspn(path, "/");
280 b = strcspn(prefix, "/");
281
282 if (a != b)
424a19f8 283 return NULL;
9eb977db
KS
284
285 if (memcmp(path, prefix, a) != 0)
424a19f8 286 return NULL;
9eb977db
KS
287
288 path += a;
289 prefix += b;
290 }
291}
292
293bool path_equal(const char *a, const char *b) {
294 assert(a);
295 assert(b);
296
297 if ((a[0] == '/') != (b[0] == '/'))
298 return false;
299
300 for (;;) {
301 size_t j, k;
302
303 a += strspn(a, "/");
304 b += strspn(b, "/");
305
306 if (*a == 0 && *b == 0)
307 return true;
308
309 if (*a == 0 || *b == 0)
310 return false;
311
312 j = strcspn(a, "/");
313 k = strcspn(b, "/");
314
315 if (j != k)
316 return false;
317
318 if (memcmp(a, b, j) != 0)
319 return false;
320
321 a += j;
322 b += k;
323 }
324}
325
326int path_is_mount_point(const char *t, bool allow_symlink) {
9eb977db
KS
327 char *parent;
328 int r;
cde9cb34
LP
329 struct file_handle *h;
330 int mount_id, mount_id_parent;
1640a0b6 331 struct stat a, b;
9eb977db 332
cde9cb34
LP
333 /* We are not actually interested in the file handles, but
334 * name_to_handle_at() also passes us the mount ID, hence use
335 * it but throw the handle away */
336
337 if (path_equal(t, "/"))
338 return 1;
9eb977db 339
cde9cb34
LP
340 h = alloca(MAX_HANDLE_SZ);
341 h->handle_bytes = MAX_HANDLE_SZ;
342
343 r = name_to_handle_at(AT_FDCWD, t, h, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
9eb977db 344 if (r < 0) {
fa125f4e
MM
345 if (errno == ENOSYS || errno == ENOTSUP)
346 /* This kernel or file system does not support
1640a0b6
LP
347 * name_to_handle_at(), hence fallback to the
348 * traditional stat() logic */
349 goto fallback;
350
9eb977db
KS
351 if (errno == ENOENT)
352 return 0;
353
354 return -errno;
355 }
356
357 r = path_get_parent(t, &parent);
358 if (r < 0)
359 return r;
360
cde9cb34
LP
361 h->handle_bytes = MAX_HANDLE_SZ;
362 r = name_to_handle_at(AT_FDCWD, parent, h, &mount_id_parent, 0);
9eb977db
KS
363 free(parent);
364
1640a0b6
LP
365 if (r < 0) {
366 /* The parent can't do name_to_handle_at() but the
367 * directory we are interested in can? If so, it must
368 * be a mount point */
369 if (errno == ENOTSUP)
370 return 1;
371
372 return -errno;
373 }
374
375 return mount_id != mount_id_parent;
376
377fallback:
378 if (allow_symlink)
379 r = stat(t, &a);
380 else
f408b8f1 381 r = lstat(t, &a);
1640a0b6 382
8ac75493
MM
383 if (r < 0) {
384 if (errno == ENOENT)
385 return 0;
386
9eb977db 387 return -errno;
8ac75493 388 }
9eb977db 389
1640a0b6
LP
390 r = path_get_parent(t, &parent);
391 if (r < 0)
392 return r;
cde9cb34 393
1640a0b6
LP
394 r = lstat(parent, &b);
395 free(parent);
396
397 if (r < 0)
398 return -errno;
399
400 return a.st_dev != b.st_dev;
9eb977db
KS
401}
402
403int path_is_read_only_fs(const char *path) {
404 struct statvfs st;
405
406 assert(path);
407
408 if (statvfs(path, &st) < 0)
409 return -errno;
410
411 return !!(st.f_flag & ST_RDONLY);
412}
66060897
LP
413
414int path_is_os_tree(const char *path) {
415 char *p;
416 int r;
417
418 /* We use /etc/os-release as flag file if something is an OS */
419
420 p = strappenda(path, "/etc/os-release");
421 r = access(p, F_OK);
422
423 return r < 0 ? 0 : 1;
424}
c9d954b2
ZJS
425
426int find_binary(const char *name, char **filename) {
427 assert(name);
4087cb9e
LP
428 assert(filename);
429
c9d954b2
ZJS
430 if (strchr(name, '/')) {
431 char *p;
432
433 if (path_is_absolute(name))
434 p = strdup(name);
435 else
436 p = path_make_absolute_cwd(name);
437 if (!p)
438 return -ENOMEM;
439
440 *filename = p;
441 return 0;
442 } else {
443 const char *path;
444 char *state, *w;
445 size_t l;
446
447 /**
448 * Plain getenv, not secure_getenv, because we want
449 * to actually allow the user to pick the binary.
450 */
451 path = getenv("PATH");
452 if (!path)
453 path = DEFAULT_PATH;
454
455 FOREACH_WORD_SEPARATOR(w, l, path, ":", state) {
456 char *p;
457
4bcc8c3c 458 if (asprintf(&p, "%.*s/%s", (int) l, w, name) < 0)
c9d954b2
ZJS
459 return -ENOMEM;
460
461 if (access(p, X_OK) < 0) {
462 free(p);
463 continue;
464 }
465
466 path_kill_slashes(p);
467 *filename = p;
468
469 return 0;
470 }
471
472 return -ENOENT;
473 }
474}
8e184852 475
2ad8416d 476bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
8e184852 477 bool changed = false;
2ad8416d 478 const char* const* i;
8e184852 479
97f2d76d
TG
480 assert(timestamp);
481
8e184852 482 if (paths == NULL)
4087cb9e 483 return false;
8e184852 484
4087cb9e 485 STRV_FOREACH(i, paths) {
8e184852 486 struct stat stats;
4087cb9e 487 usec_t u;
8e184852 488
4087cb9e 489 if (stat(*i, &stats) < 0)
8e184852
TG
490 continue;
491
4087cb9e
LP
492 u = timespec_load(&stats.st_mtim);
493
97f2d76d 494 /* first check */
4087cb9e 495 if (*timestamp >= u)
8e184852
TG
496 continue;
497
9f6445e3 498 log_debug("timestamp of '%s' changed", *i);
8e184852
TG
499
500 /* update timestamp */
4087cb9e
LP
501 if (update) {
502 *timestamp = u;
503 changed = true;
504 } else
505 return true;
8e184852 506 }
4087cb9e 507
8e184852
TG
508 return changed;
509}