chiark / gitweb /
8bf9a3cf96c1cc809c01b192df6ed7cfd426a044
[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_absolute(char **l, const char *prefix) {
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                 _cleanup_free_ char *orig = NULL;
171
172                 if (!path_is_absolute(*s)) {
173                         free(*s);
174                         continue;
175                 }
176
177                 if (prefix) {
178                         orig = *s;
179                         t = strappend(prefix, orig);
180                         if (!t) {
181                                 enomem = true;
182                                 continue;
183                         }
184                 } else
185                         t = *s;
186
187                 errno = 0;
188                 u = canonicalize_file_name(t);
189                 if (!u) {
190                         if (errno == ENOENT) {
191                                 if (prefix) {
192                                         u = orig;
193                                         orig = NULL;
194                                         free(t);
195                                 } else
196                                         u = t;
197                         } else {
198                                 free(t);
199                                 if (errno == ENOMEM || errno == 0)
200                                         enomem = true;
201
202                                 continue;
203                         }
204                 } else if (prefix) {
205                         char *x;
206
207                         free(t);
208                         x = path_startswith(u, prefix);
209                         if (x) {
210                                 /* restore the slash if it was lost */
211                                 if (!startswith(x, "/"))
212                                         *(--x) = '/';
213
214                                 t = strdup(x);
215                                 free(u);
216                                 if (!t) {
217                                         enomem = true;
218                                         continue;
219                                 }
220                                 u = t;
221                         } else {
222                                 /* canonicalized path goes outside of
223                                  * prefix, keep the original path instead */
224                                 u = orig;
225                                 orig = NULL;
226                         }
227                 } else
228                         free(t);
229
230                 l[k++] = u;
231         }
232
233         l[k] = NULL;
234
235         if (enomem)
236                 return NULL;
237
238         return l;
239 }
240
241 char **path_strv_canonicalize_absolute_uniq(char **l, const char *prefix) {
242
243         if (strv_isempty(l))
244                 return l;
245
246         if (!path_strv_canonicalize_absolute(l, prefix))
247                 return NULL;
248
249         return strv_uniq(l);
250 }
251
252 char *path_kill_slashes(char *path) {
253         char *f, *t;
254         bool slash = false;
255
256         /* Removes redundant inner and trailing slashes. Modifies the
257          * passed string in-place.
258          *
259          * ///foo///bar/ becomes /foo/bar
260          */
261
262         for (f = path, t = path; *f; f++) {
263
264                 if (*f == '/') {
265                         slash = true;
266                         continue;
267                 }
268
269                 if (slash) {
270                         slash = false;
271                         *(t++) = '/';
272                 }
273
274                 *(t++) = *f;
275         }
276
277         /* Special rule, if we are talking of the root directory, a
278         trailing slash is good */
279
280         if (t == path && slash)
281                 *(t++) = '/';
282
283         *t = 0;
284         return path;
285 }
286
287 char* path_startswith(const char *path, const char *prefix) {
288         assert(path);
289         assert(prefix);
290
291         if ((path[0] == '/') != (prefix[0] == '/'))
292                 return NULL;
293
294         for (;;) {
295                 size_t a, b;
296
297                 path += strspn(path, "/");
298                 prefix += strspn(prefix, "/");
299
300                 if (*prefix == 0)
301                         return (char*) path;
302
303                 if (*path == 0)
304                         return NULL;
305
306                 a = strcspn(path, "/");
307                 b = strcspn(prefix, "/");
308
309                 if (a != b)
310                         return NULL;
311
312                 if (memcmp(path, prefix, a) != 0)
313                         return NULL;
314
315                 path += a;
316                 prefix += b;
317         }
318 }
319
320 bool path_equal(const char *a, const char *b) {
321         assert(a);
322         assert(b);
323
324         if ((a[0] == '/') != (b[0] == '/'))
325                 return false;
326
327         for (;;) {
328                 size_t j, k;
329
330                 a += strspn(a, "/");
331                 b += strspn(b, "/");
332
333                 if (*a == 0 && *b == 0)
334                         return true;
335
336                 if (*a == 0 || *b == 0)
337                         return false;
338
339                 j = strcspn(a, "/");
340                 k = strcspn(b, "/");
341
342                 if (j != k)
343                         return false;
344
345                 if (memcmp(a, b, j) != 0)
346                         return false;
347
348                 a += j;
349                 b += k;
350         }
351 }
352
353 int path_is_mount_point(const char *t, bool allow_symlink) {
354
355         union file_handle_union h = {
356                 .handle.handle_bytes = MAX_HANDLE_SZ
357         };
358
359         int mount_id, mount_id_parent;
360         char *parent;
361         struct stat a, b;
362         int r;
363
364         /* We are not actually interested in the file handles, but
365          * name_to_handle_at() also passes us the mount ID, hence use
366          * it but throw the handle away */
367
368         if (path_equal(t, "/"))
369                 return 1;
370
371         r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
372         if (r < 0) {
373                 if (IN_SET(errno, ENOSYS, EOPNOTSUPP))
374                         /* This kernel or file system does not support
375                          * name_to_handle_at(), hence fallback to the
376                          * traditional stat() logic */
377                         goto fallback;
378
379                 if (errno == ENOENT)
380                         return 0;
381
382                 return -errno;
383         }
384
385         r = path_get_parent(t, &parent);
386         if (r < 0)
387                 return r;
388
389         h.handle.handle_bytes = MAX_HANDLE_SZ;
390         r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, 0);
391         free(parent);
392         if (r < 0) {
393                 /* The parent can't do name_to_handle_at() but the
394                  * directory we are interested in can? If so, it must
395                  * be a mount point */
396                 if (errno == EOPNOTSUPP)
397                         return 1;
398
399                 return -errno;
400         }
401
402         return mount_id != mount_id_parent;
403
404 fallback:
405         if (allow_symlink)
406                 r = stat(t, &a);
407         else
408                 r = lstat(t, &a);
409
410         if (r < 0) {
411                 if (errno == ENOENT)
412                         return 0;
413
414                 return -errno;
415         }
416
417         r = path_get_parent(t, &parent);
418         if (r < 0)
419                 return r;
420
421         r = lstat(parent, &b);
422         free(parent);
423         if (r < 0)
424                 return -errno;
425
426         return a.st_dev != b.st_dev;
427 }
428
429 int path_is_read_only_fs(const char *path) {
430         struct statvfs st;
431
432         assert(path);
433
434         if (statvfs(path, &st) < 0)
435                 return -errno;
436
437         return !!(st.f_flag & ST_RDONLY);
438 }
439
440 int path_is_os_tree(const char *path) {
441         char *p;
442         int r;
443
444         /* We use /etc/os-release as flag file if something is an OS */
445
446         p = strappenda(path, "/etc/os-release");
447         r = access(p, F_OK);
448
449         return r < 0 ? 0 : 1;
450 }
451
452 int find_binary(const char *name, char **filename) {
453         assert(name);
454
455         if (strchr(name, '/')) {
456                 if (access(name, X_OK) < 0)
457                         return -errno;
458
459                 if (filename) {
460                         char *p;
461
462                         p = path_make_absolute_cwd(name);
463                         if (!p)
464                                 return -ENOMEM;
465
466                         *filename = p;
467                 }
468
469                 return 0;
470         } else {
471                 const char *path;
472                 char *state, *w;
473                 size_t l;
474
475                 /**
476                  * Plain getenv, not secure_getenv, because we want
477                  * to actually allow the user to pick the binary.
478                  */
479                 path = getenv("PATH");
480                 if (!path)
481                         path = DEFAULT_PATH;
482
483                 FOREACH_WORD_SEPARATOR(w, l, path, ":", state) {
484                         _cleanup_free_ char *p = NULL;
485
486                         if (asprintf(&p, "%.*s/%s", (int) l, w, name) < 0)
487                                 return -ENOMEM;
488
489                         if (access(p, X_OK) < 0)
490                                 continue;
491
492                         if (filename) {
493                                 *filename = path_kill_slashes(p);
494                                 p = NULL;
495                         }
496
497                         return 0;
498                 }
499
500                 return -ENOENT;
501         }
502 }
503
504 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
505         bool changed = false;
506         const char* const* i;
507
508         assert(timestamp);
509
510         if (paths == NULL)
511                 return false;
512
513         STRV_FOREACH(i, paths) {
514                 struct stat stats;
515                 usec_t u;
516
517                 if (stat(*i, &stats) < 0)
518                         continue;
519
520                 u = timespec_load(&stats.st_mtim);
521
522                 /* first check */
523                 if (*timestamp >= u)
524                         continue;
525
526                 log_debug("timestamp of '%s' changed", *i);
527
528                 /* update timestamp */
529                 if (update) {
530                         *timestamp = u;
531                         changed = true;
532                 } else
533                         return true;
534         }
535
536         return changed;
537 }
538
539 int fsck_exists(const char *fstype) {
540         const char *checker;
541
542         checker = strappenda("fsck.", fstype);
543         return find_binary(checker, NULL);
544 }