chiark / gitweb /
bus: add new sd_bus_creds object to encapsulate process credentials
[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 char *path_get_file_name(const char *p) {
49         char *r;
50
51         assert(p);
52
53         r = strrchr(p, '/');
54         if (r)
55                 return r + 1;
56
57         return (char*) p;
58 }
59
60 int path_get_parent(const char *path, char **_r) {
61         const char *e, *a = NULL, *b = NULL, *p;
62         char *r;
63         bool slash = false;
64
65         assert(path);
66         assert(_r);
67
68         if (!*path)
69                 return -EINVAL;
70
71         for (e = path; *e; e++) {
72
73                 if (!slash && *e == '/') {
74                         a = b;
75                         b = e;
76                         slash = true;
77                 } else if (slash && *e != '/')
78                         slash = false;
79         }
80
81         if (*(e-1) == '/')
82                 p = a;
83         else
84                 p = b;
85
86         if (!p)
87                 return -EINVAL;
88
89         if (p == path)
90                 r = strdup("/");
91         else
92                 r = strndup(path, p-path);
93
94         if (!r)
95                 return -ENOMEM;
96
97         *_r = r;
98         return 0;
99 }
100
101 char **path_split_and_make_absolute(const char *p) {
102         char **l;
103         assert(p);
104
105         l = strv_split(p, ":");
106         if (!l)
107                 return NULL;
108
109         if (!path_strv_make_absolute_cwd(l)) {
110                 strv_free(l);
111                 return NULL;
112         }
113
114         return l;
115 }
116
117 char *path_make_absolute(const char *p, const char *prefix) {
118         assert(p);
119
120         /* Makes every item in the list an absolute path by prepending
121          * the prefix, if specified and necessary */
122
123         if (path_is_absolute(p) || !prefix)
124                 return strdup(p);
125
126         return strjoin(prefix, "/", p, NULL);
127 }
128
129 char *path_make_absolute_cwd(const char *p) {
130         _cleanup_free_ char *cwd = NULL;
131
132         assert(p);
133
134         /* Similar to path_make_absolute(), but prefixes with the
135          * current working directory. */
136
137         if (path_is_absolute(p))
138                 return strdup(p);
139
140         cwd = get_current_dir_name();
141         if (!cwd)
142                 return NULL;
143
144         return path_make_absolute(p, cwd);
145 }
146
147 char **path_strv_make_absolute_cwd(char **l) {
148         char **s;
149
150         /* Goes through every item in the string list and makes it
151          * absolute. This works in place and won't rollback any
152          * changes on failure. */
153
154         STRV_FOREACH(s, l) {
155                 char *t;
156
157                 t = path_make_absolute_cwd(*s);
158                 if (!t)
159                         return NULL;
160
161                 free(*s);
162                 *s = t;
163         }
164
165         return l;
166 }
167
168 char **path_strv_canonicalize(char **l) {
169         char **s;
170         unsigned k = 0;
171         bool enomem = false;
172
173         if (strv_isempty(l))
174                 return l;
175
176         /* Goes through every item in the string list and canonicalize
177          * the path. This works in place and won't rollback any
178          * changes on failure. */
179
180         STRV_FOREACH(s, l) {
181                 char *t, *u;
182
183                 t = path_make_absolute_cwd(*s);
184                 free(*s);
185                 *s = NULL;
186
187                 if (!t) {
188                         enomem = true;
189                         continue;
190                 }
191
192                 errno = 0;
193                 u = canonicalize_file_name(t);
194                 if (!u) {
195                         if (errno == ENOENT)
196                                 u = t;
197                         else {
198                                 free(t);
199                                 if (errno == ENOMEM || !errno)
200                                         enomem = true;
201
202                                 continue;
203                         }
204                 } else
205                         free(t);
206
207                 l[k++] = u;
208         }
209
210         l[k] = NULL;
211
212         if (enomem)
213                 return NULL;
214
215         return l;
216 }
217
218 char **path_strv_canonicalize_uniq(char **l) {
219         if (strv_isempty(l))
220                 return l;
221
222         if (!path_strv_canonicalize(l))
223                 return NULL;
224
225         return strv_uniq(l);
226 }
227
228 char *path_kill_slashes(char *path) {
229         char *f, *t;
230         bool slash = false;
231
232         /* Removes redundant inner and trailing slashes. Modifies the
233          * passed string in-place.
234          *
235          * ///foo///bar/ becomes /foo/bar
236          */
237
238         for (f = path, t = path; *f; f++) {
239
240                 if (*f == '/') {
241                         slash = true;
242                         continue;
243                 }
244
245                 if (slash) {
246                         slash = false;
247                         *(t++) = '/';
248                 }
249
250                 *(t++) = *f;
251         }
252
253         /* Special rule, if we are talking of the root directory, a
254         trailing slash is good */
255
256         if (t == path && slash)
257                 *(t++) = '/';
258
259         *t = 0;
260         return path;
261 }
262
263 char* path_startswith(const char *path, const char *prefix) {
264         assert(path);
265         assert(prefix);
266
267         if ((path[0] == '/') != (prefix[0] == '/'))
268                 return NULL;
269
270         for (;;) {
271                 size_t a, b;
272
273                 path += strspn(path, "/");
274                 prefix += strspn(prefix, "/");
275
276                 if (*prefix == 0)
277                         return (char*) path;
278
279                 if (*path == 0)
280                         return NULL;
281
282                 a = strcspn(path, "/");
283                 b = strcspn(prefix, "/");
284
285                 if (a != b)
286                         return NULL;
287
288                 if (memcmp(path, prefix, a) != 0)
289                         return NULL;
290
291                 path += a;
292                 prefix += b;
293         }
294 }
295
296 bool path_equal(const char *a, const char *b) {
297         assert(a);
298         assert(b);
299
300         if ((a[0] == '/') != (b[0] == '/'))
301                 return false;
302
303         for (;;) {
304                 size_t j, k;
305
306                 a += strspn(a, "/");
307                 b += strspn(b, "/");
308
309                 if (*a == 0 && *b == 0)
310                         return true;
311
312                 if (*a == 0 || *b == 0)
313                         return false;
314
315                 j = strcspn(a, "/");
316                 k = strcspn(b, "/");
317
318                 if (j != k)
319                         return false;
320
321                 if (memcmp(a, b, j) != 0)
322                         return false;
323
324                 a += j;
325                 b += k;
326         }
327 }
328
329 int path_is_mount_point(const char *t, bool allow_symlink) {
330         char *parent;
331         int r;
332         struct file_handle *h;
333         int mount_id, mount_id_parent;
334         struct stat a, b;
335
336         /* We are not actually interested in the file handles, but
337          * name_to_handle_at() also passes us the mount ID, hence use
338          * it but throw the handle away */
339
340         if (path_equal(t, "/"))
341                 return 1;
342
343         h = alloca(MAX_HANDLE_SZ);
344         h->handle_bytes = MAX_HANDLE_SZ;
345
346         r = name_to_handle_at(AT_FDCWD, t, h, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
347         if (r < 0) {
348                 if (errno == ENOSYS || errno == ENOTSUP)
349                         /* This kernel or file system does not support
350                          * name_to_handle_at(), hence fallback to the
351                          * traditional stat() logic */
352                         goto fallback;
353
354                 if (errno == ENOENT)
355                         return 0;
356
357                 return -errno;
358         }
359
360         r = path_get_parent(t, &parent);
361         if (r < 0)
362                 return r;
363
364         h->handle_bytes = MAX_HANDLE_SZ;
365         r = name_to_handle_at(AT_FDCWD, parent, h, &mount_id_parent, 0);
366         free(parent);
367
368         if (r < 0) {
369                 /* The parent can't do name_to_handle_at() but the
370                  * directory we are interested in can? If so, it must
371                  * be a mount point */
372                 if (errno == ENOTSUP)
373                         return 1;
374
375                 return -errno;
376         }
377
378         return mount_id != mount_id_parent;
379
380 fallback:
381         if (allow_symlink)
382                 r = stat(t, &a);
383         else
384                 r = lstat(t, &a);
385
386         if (r < 0) {
387                 if (errno == ENOENT)
388                         return 0;
389
390                 return -errno;
391         }
392
393         r = path_get_parent(t, &parent);
394         if (r < 0)
395                 return r;
396
397         r = lstat(parent, &b);
398         free(parent);
399
400         if (r < 0)
401                 return -errno;
402
403         return a.st_dev != b.st_dev;
404 }
405
406 int path_is_read_only_fs(const char *path) {
407         struct statvfs st;
408
409         assert(path);
410
411         if (statvfs(path, &st) < 0)
412                 return -errno;
413
414         return !!(st.f_flag & ST_RDONLY);
415 }
416
417 int path_is_os_tree(const char *path) {
418         char *p;
419         int r;
420
421         /* We use /etc/os-release as flag file if something is an OS */
422
423         p = strappenda(path, "/etc/os-release");
424         r = access(p, F_OK);
425
426         return r < 0 ? 0 : 1;
427 }
428
429 int find_binary(const char *name, char **filename) {
430         assert(name);
431         assert(filename);
432
433         if (strchr(name, '/')) {
434                 char *p;
435
436                 if (path_is_absolute(name))
437                         p = strdup(name);
438                 else
439                         p = path_make_absolute_cwd(name);
440                 if (!p)
441                         return -ENOMEM;
442
443                 *filename = p;
444                 return 0;
445         } else {
446                 const char *path;
447                 char *state, *w;
448                 size_t l;
449
450                 /**
451                  * Plain getenv, not secure_getenv, because we want
452                  * to actually allow the user to pick the binary.
453                  */
454                 path = getenv("PATH");
455                 if (!path)
456                         path = DEFAULT_PATH;
457
458                 FOREACH_WORD_SEPARATOR(w, l, path, ":", state) {
459                         char *p;
460
461                         if (asprintf(&p, "%.*s/%s", (int) l, w, name) < 0)
462                                 return -ENOMEM;
463
464                         if (access(p, X_OK) < 0) {
465                                 free(p);
466                                 continue;
467                         }
468
469                         path_kill_slashes(p);
470                         *filename = p;
471
472                         return 0;
473                 }
474
475                 return -ENOENT;
476         }
477 }
478
479 bool paths_check_timestamp(char **paths, usec_t *timestamp, bool update) {
480         bool changed = false;
481         char **i;
482
483         assert(timestamp);
484
485         if (paths == NULL)
486                 return false;
487
488         STRV_FOREACH(i, paths) {
489                 struct stat stats;
490                 usec_t u;
491
492                 if (stat(*i, &stats) < 0)
493                         continue;
494
495                 u = timespec_load(&stats.st_mtim);
496
497                 /* first check */
498                 if (*timestamp >= u)
499                         continue;
500
501                 log_debug("timestamp of '%s' changed\n", *i);
502
503                 /* update timestamp */
504                 if (update) {
505                         *timestamp = u;
506                         changed = true;
507                 } else
508                         return true;
509         }
510
511         return changed;
512 }