chiark / gitweb /
bus: properly shift cgroup data returned from kdbus by the container's root before...
[elogind.git] / src / shared / path-util.c
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"
38 #include "missing.h"
39
40 bool path_is_absolute(const char *p) {
41         return p[0] == '/';
42 }
43
44 bool is_path(const char *p) {
45         return !!strchr(p, '/');
46 }
47
48 int 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
89 char **path_split_and_make_absolute(const char *p) {
90         char **l;
91         assert(p);
92
93         l = strv_split(p, ":");
94         if (!l)
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
105 char *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
114         return strjoin(prefix, "/", p, NULL);
115 }
116
117 char *path_make_absolute_cwd(const char *p) {
118         _cleanup_free_ char *cwd = NULL;
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
128         cwd = get_current_dir_name();
129         if (!cwd)
130                 return NULL;
131
132         return path_make_absolute(p, cwd);
133 }
134
135 char **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
145                 t = path_make_absolute_cwd(*s);
146                 if (!t)
147                         return NULL;
148
149                 free(*s);
150                 *s = t;
151         }
152
153         return l;
154 }
155
156 char **path_strv_canonicalize(char **l) {
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
171                 t = path_make_absolute_cwd(*s);
172                 free(*s);
173                 *s = NULL;
174
175                 if (!t) {
176                         enomem = true;
177                         continue;
178                 }
179
180                 errno = 0;
181                 u = canonicalize_file_name(t);
182                 if (!u) {
183                         if (errno == ENOENT)
184                                 u = t;
185                         else {
186                                 free(t);
187                                 if (errno == ENOMEM || !errno)
188                                         enomem = true;
189
190                                 continue;
191                         }
192                 } else
193                         free(t);
194
195                 l[k++] = u;
196         }
197
198         l[k] = NULL;
199
200         if (enomem)
201                 return NULL;
202
203         return l;
204 }
205
206 char **path_strv_canonicalize_uniq(char **l) {
207         if (strv_isempty(l))
208                 return l;
209
210         if (!path_strv_canonicalize(l))
211                 return NULL;
212
213         return strv_uniq(l);
214 }
215
216 char *path_kill_slashes(char *path) {
217         char *f, *t;
218         bool slash = false;
219
220         /* Removes redundant inner and trailing slashes. Modifies the
221          * passed string in-place.
222          *
223          * ///foo///bar/ becomes /foo/bar
224          */
225
226         for (f = path, t = path; *f; f++) {
227
228                 if (*f == '/') {
229                         slash = true;
230                         continue;
231                 }
232
233                 if (slash) {
234                         slash = false;
235                         *(t++) = '/';
236                 }
237
238                 *(t++) = *f;
239         }
240
241         /* Special rule, if we are talking of the root directory, a
242         trailing slash is good */
243
244         if (t == path && slash)
245                 *(t++) = '/';
246
247         *t = 0;
248         return path;
249 }
250
251 char* path_startswith(const char *path, const char *prefix) {
252         assert(path);
253         assert(prefix);
254
255         if ((path[0] == '/') != (prefix[0] == '/'))
256                 return NULL;
257
258         for (;;) {
259                 size_t a, b;
260
261                 path += strspn(path, "/");
262                 prefix += strspn(prefix, "/");
263
264                 if (*prefix == 0)
265                         return (char*) path;
266
267                 if (*path == 0)
268                         return NULL;
269
270                 a = strcspn(path, "/");
271                 b = strcspn(prefix, "/");
272
273                 if (a != b)
274                         return NULL;
275
276                 if (memcmp(path, prefix, a) != 0)
277                         return NULL;
278
279                 path += a;
280                 prefix += b;
281         }
282 }
283
284 bool path_equal(const char *a, const char *b) {
285         assert(a);
286         assert(b);
287
288         if ((a[0] == '/') != (b[0] == '/'))
289                 return false;
290
291         for (;;) {
292                 size_t j, k;
293
294                 a += strspn(a, "/");
295                 b += strspn(b, "/");
296
297                 if (*a == 0 && *b == 0)
298                         return true;
299
300                 if (*a == 0 || *b == 0)
301                         return false;
302
303                 j = strcspn(a, "/");
304                 k = strcspn(b, "/");
305
306                 if (j != k)
307                         return false;
308
309                 if (memcmp(a, b, j) != 0)
310                         return false;
311
312                 a += j;
313                 b += k;
314         }
315 }
316
317 int path_is_mount_point(const char *t, bool allow_symlink) {
318         char *parent;
319         int r;
320         struct file_handle *h;
321         int mount_id, mount_id_parent;
322         struct stat a, b;
323
324         /* We are not actually interested in the file handles, but
325          * name_to_handle_at() also passes us the mount ID, hence use
326          * it but throw the handle away */
327
328         if (path_equal(t, "/"))
329                 return 1;
330
331         h = alloca(MAX_HANDLE_SZ);
332         h->handle_bytes = MAX_HANDLE_SZ;
333
334         r = name_to_handle_at(AT_FDCWD, t, h, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
335         if (r < 0) {
336                 if (errno == ENOSYS || errno == ENOTSUP)
337                         /* This kernel or file system does not support
338                          * name_to_handle_at(), hence fallback to the
339                          * traditional stat() logic */
340                         goto fallback;
341
342                 if (errno == ENOENT)
343                         return 0;
344
345                 return -errno;
346         }
347
348         r = path_get_parent(t, &parent);
349         if (r < 0)
350                 return r;
351
352         h->handle_bytes = MAX_HANDLE_SZ;
353         r = name_to_handle_at(AT_FDCWD, parent, h, &mount_id_parent, 0);
354         free(parent);
355
356         if (r < 0) {
357                 /* The parent can't do name_to_handle_at() but the
358                  * directory we are interested in can? If so, it must
359                  * be a mount point */
360                 if (errno == ENOTSUP)
361                         return 1;
362
363                 return -errno;
364         }
365
366         return mount_id != mount_id_parent;
367
368 fallback:
369         if (allow_symlink)
370                 r = stat(t, &a);
371         else
372                 r = lstat(t, &a);
373
374         if (r < 0) {
375                 if (errno == ENOENT)
376                         return 0;
377
378                 return -errno;
379         }
380
381         r = path_get_parent(t, &parent);
382         if (r < 0)
383                 return r;
384
385         r = lstat(parent, &b);
386         free(parent);
387
388         if (r < 0)
389                 return -errno;
390
391         return a.st_dev != b.st_dev;
392 }
393
394 int path_is_read_only_fs(const char *path) {
395         struct statvfs st;
396
397         assert(path);
398
399         if (statvfs(path, &st) < 0)
400                 return -errno;
401
402         return !!(st.f_flag & ST_RDONLY);
403 }
404
405 int path_is_os_tree(const char *path) {
406         char *p;
407         int r;
408
409         /* We use /etc/os-release as flag file if something is an OS */
410
411         p = strappenda(path, "/etc/os-release");
412         r = access(p, F_OK);
413
414         return r < 0 ? 0 : 1;
415 }
416
417 int find_binary(const char *name, char **filename) {
418         assert(name);
419         assert(filename);
420
421         if (strchr(name, '/')) {
422                 char *p;
423
424                 if (path_is_absolute(name))
425                         p = strdup(name);
426                 else
427                         p = path_make_absolute_cwd(name);
428                 if (!p)
429                         return -ENOMEM;
430
431                 *filename = p;
432                 return 0;
433         } else {
434                 const char *path;
435                 char *state, *w;
436                 size_t l;
437
438                 /**
439                  * Plain getenv, not secure_getenv, because we want
440                  * to actually allow the user to pick the binary.
441                  */
442                 path = getenv("PATH");
443                 if (!path)
444                         path = DEFAULT_PATH;
445
446                 FOREACH_WORD_SEPARATOR(w, l, path, ":", state) {
447                         char *p;
448
449                         if (asprintf(&p, "%.*s/%s", (int) l, w, name) < 0)
450                                 return -ENOMEM;
451
452                         if (access(p, X_OK) < 0) {
453                                 free(p);
454                                 continue;
455                         }
456
457                         path_kill_slashes(p);
458                         *filename = p;
459
460                         return 0;
461                 }
462
463                 return -ENOENT;
464         }
465 }
466
467 bool paths_check_timestamp(char **paths, usec_t *timestamp, bool update) {
468         bool changed = false;
469         char **i;
470
471         assert(timestamp);
472
473         if (paths == NULL)
474                 return false;
475
476         STRV_FOREACH(i, paths) {
477                 struct stat stats;
478                 usec_t u;
479
480                 if (stat(*i, &stats) < 0)
481                         continue;
482
483                 u = timespec_load(&stats.st_mtim);
484
485                 /* first check */
486                 if (*timestamp >= u)
487                         continue;
488
489                 log_debug("timestamp of '%s' changed", *i);
490
491                 /* update timestamp */
492                 if (update) {
493                         *timestamp = u;
494                         changed = true;
495                 } else
496                         return true;
497         }
498
499         return changed;
500 }