chiark / gitweb /
Fix typo
[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 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
136         char *r, *p;
137         unsigned n_parents;
138
139         assert(from_dir);
140         assert(to_path);
141         assert(_r);
142
143         /* Strips the common part, and adds ".." elements as necessary. */
144
145         if (!path_is_absolute(from_dir))
146                 return -EINVAL;
147
148         if (!path_is_absolute(to_path))
149                 return -EINVAL;
150
151         /* Skip the common part. */
152         for (;;) {
153                 size_t a;
154                 size_t b;
155
156                 from_dir += strspn(from_dir, "/");
157                 to_path += strspn(to_path, "/");
158
159                 if (!*from_dir) {
160                         if (!*to_path)
161                                 /* from_dir equals to_path. */
162                                 r = strdup(".");
163                         else
164                                 /* from_dir is a parent directory of to_path. */
165                                 r = strdup(to_path);
166
167                         if (!r)
168                                 return -ENOMEM;
169
170                         path_kill_slashes(r);
171
172                         *_r = r;
173                         return 0;
174                 }
175
176                 if (!*to_path)
177                         break;
178
179                 a = strcspn(from_dir, "/");
180                 b = strcspn(to_path, "/");
181
182                 if (a != b)
183                         break;
184
185                 if (memcmp(from_dir, to_path, a) != 0)
186                         break;
187
188                 from_dir += a;
189                 to_path += b;
190         }
191
192         /* If we're here, then "from_dir" has one or more elements that need to
193          * be replaced with "..". */
194
195         /* Count the number of necessary ".." elements. */
196         for (n_parents = 0;;) {
197                 from_dir += strspn(from_dir, "/");
198
199                 if (!*from_dir)
200                         break;
201
202                 from_dir += strcspn(from_dir, "/");
203                 n_parents++;
204         }
205
206         r = malloc(n_parents * 3 + strlen(to_path) + 1);
207         if (!r)
208                 return -ENOMEM;
209
210         for (p = r; n_parents > 0; n_parents--, p += 3)
211                 memcpy(p, "../", 3);
212
213         strcpy(p, to_path);
214         path_kill_slashes(r);
215
216         *_r = r;
217         return 0;
218 }
219
220 char **path_strv_make_absolute_cwd(char **l) {
221         char **s;
222
223         /* Goes through every item in the string list and makes it
224          * absolute. This works in place and won't rollback any
225          * changes on failure. */
226
227         STRV_FOREACH(s, l) {
228                 char *t;
229
230                 t = path_make_absolute_cwd(*s);
231                 if (!t)
232                         return NULL;
233
234                 free(*s);
235                 *s = t;
236         }
237
238         return l;
239 }
240
241 char **path_strv_resolve(char **l, const char *prefix) {
242         char **s;
243         unsigned k = 0;
244         bool enomem = false;
245
246         if (strv_isempty(l))
247                 return l;
248
249         /* Goes through every item in the string list and canonicalize
250          * the path. This works in place and won't rollback any
251          * changes on failure. */
252
253         STRV_FOREACH(s, l) {
254                 char *t, *u;
255                 _cleanup_free_ char *orig = NULL;
256
257                 if (!path_is_absolute(*s)) {
258                         free(*s);
259                         continue;
260                 }
261
262                 if (prefix) {
263                         orig = *s;
264                         t = strappend(prefix, orig);
265                         if (!t) {
266                                 enomem = true;
267                                 continue;
268                         }
269                 } else
270                         t = *s;
271
272                 errno = 0;
273                 u = canonicalize_file_name(t);
274                 if (!u) {
275                         if (errno == ENOENT) {
276                                 if (prefix) {
277                                         u = orig;
278                                         orig = NULL;
279                                         free(t);
280                                 } else
281                                         u = t;
282                         } else {
283                                 free(t);
284                                 if (errno == ENOMEM || errno == 0)
285                                         enomem = true;
286
287                                 continue;
288                         }
289                 } else if (prefix) {
290                         char *x;
291
292                         free(t);
293                         x = path_startswith(u, prefix);
294                         if (x) {
295                                 /* restore the slash if it was lost */
296                                 if (!startswith(x, "/"))
297                                         *(--x) = '/';
298
299                                 t = strdup(x);
300                                 free(u);
301                                 if (!t) {
302                                         enomem = true;
303                                         continue;
304                                 }
305                                 u = t;
306                         } else {
307                                 /* canonicalized path goes outside of
308                                  * prefix, keep the original path instead */
309                                 u = orig;
310                                 orig = NULL;
311                         }
312                 } else
313                         free(t);
314
315                 l[k++] = u;
316         }
317
318         l[k] = NULL;
319
320         if (enomem)
321                 return NULL;
322
323         return l;
324 }
325
326 char **path_strv_resolve_uniq(char **l, const char *prefix) {
327
328         if (strv_isempty(l))
329                 return l;
330
331         if (!path_strv_resolve(l, prefix))
332                 return NULL;
333
334         return strv_uniq(l);
335 }
336
337 char *path_kill_slashes(char *path) {
338         char *f, *t;
339         bool slash = false;
340
341         /* Removes redundant inner and trailing slashes. Modifies the
342          * passed string in-place.
343          *
344          * ///foo///bar/ becomes /foo/bar
345          */
346
347         for (f = path, t = path; *f; f++) {
348
349                 if (*f == '/') {
350                         slash = true;
351                         continue;
352                 }
353
354                 if (slash) {
355                         slash = false;
356                         *(t++) = '/';
357                 }
358
359                 *(t++) = *f;
360         }
361
362         /* Special rule, if we are talking of the root directory, a
363         trailing slash is good */
364
365         if (t == path && slash)
366                 *(t++) = '/';
367
368         *t = 0;
369         return path;
370 }
371
372 char* path_startswith(const char *path, const char *prefix) {
373         assert(path);
374         assert(prefix);
375
376         if ((path[0] == '/') != (prefix[0] == '/'))
377                 return NULL;
378
379         for (;;) {
380                 size_t a, b;
381
382                 path += strspn(path, "/");
383                 prefix += strspn(prefix, "/");
384
385                 if (*prefix == 0)
386                         return (char*) path;
387
388                 if (*path == 0)
389                         return NULL;
390
391                 a = strcspn(path, "/");
392                 b = strcspn(prefix, "/");
393
394                 if (a != b)
395                         return NULL;
396
397                 if (memcmp(path, prefix, a) != 0)
398                         return NULL;
399
400                 path += a;
401                 prefix += b;
402         }
403 }
404
405 bool path_equal(const char *a, const char *b) {
406         assert(a);
407         assert(b);
408
409         if ((a[0] == '/') != (b[0] == '/'))
410                 return false;
411
412         for (;;) {
413                 size_t j, k;
414
415                 a += strspn(a, "/");
416                 b += strspn(b, "/");
417
418                 if (*a == 0 && *b == 0)
419                         return true;
420
421                 if (*a == 0 || *b == 0)
422                         return false;
423
424                 j = strcspn(a, "/");
425                 k = strcspn(b, "/");
426
427                 if (j != k)
428                         return false;
429
430                 if (memcmp(a, b, j) != 0)
431                         return false;
432
433                 a += j;
434                 b += k;
435         }
436 }
437
438 int path_is_mount_point(const char *t, bool allow_symlink) {
439
440         union file_handle_union h = {
441                 .handle.handle_bytes = MAX_HANDLE_SZ
442         };
443
444         int mount_id, mount_id_parent;
445         _cleanup_free_ char *parent = NULL;
446         struct stat a, b;
447         int r;
448
449         /* We are not actually interested in the file handles, but
450          * name_to_handle_at() also passes us the mount ID, hence use
451          * it but throw the handle away */
452
453         if (path_equal(t, "/"))
454                 return 1;
455
456         r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
457         if (r < 0) {
458                 if (IN_SET(errno, ENOSYS, EOPNOTSUPP))
459                         /* This kernel or file system does not support
460                          * name_to_handle_at(), hence fallback to the
461                          * traditional stat() logic */
462                         goto fallback;
463
464                 if (errno == ENOENT)
465                         return 0;
466
467                 return -errno;
468         }
469
470         r = path_get_parent(t, &parent);
471         if (r < 0)
472                 return r;
473
474         h.handle.handle_bytes = MAX_HANDLE_SZ;
475         r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, 0);
476         if (r < 0) {
477                 /* The parent can't do name_to_handle_at() but the
478                  * directory we are interested in can? If so, it must
479                  * be a mount point */
480                 if (errno == EOPNOTSUPP)
481                         return 1;
482
483                 return -errno;
484         }
485
486         return mount_id != mount_id_parent;
487
488 fallback:
489         if (allow_symlink)
490                 r = stat(t, &a);
491         else
492                 r = lstat(t, &a);
493
494         if (r < 0) {
495                 if (errno == ENOENT)
496                         return 0;
497
498                 return -errno;
499         }
500
501         r = path_get_parent(t, &parent);
502         if (r < 0)
503                 return r;
504
505         r = lstat(parent, &b);
506         if (r < 0)
507                 return -errno;
508
509         return a.st_dev != b.st_dev;
510 }
511
512 int path_is_read_only_fs(const char *path) {
513         struct statvfs st;
514
515         assert(path);
516
517         if (statvfs(path, &st) < 0)
518                 return -errno;
519
520         return !!(st.f_flag & ST_RDONLY);
521 }
522
523 int path_is_os_tree(const char *path) {
524         char *p;
525         int r;
526
527         /* We use /usr/lib/os-release as flag file if something is an OS */
528         p = strappenda(path, "/usr/lib/os-release");
529         r = access(p, F_OK);
530
531         if (r >= 0)
532                 return 1;
533
534         /* Also check for the old location in /etc, just in case. */
535         p = strappenda(path, "/etc/os-release");
536         r = access(p, F_OK);
537
538         return r >= 0;
539 }
540
541 int find_binary(const char *name, char **filename) {
542         assert(name);
543
544         if (is_path(name)) {
545                 if (access(name, X_OK) < 0)
546                         return -errno;
547
548                 if (filename) {
549                         char *p;
550
551                         p = path_make_absolute_cwd(name);
552                         if (!p)
553                                 return -ENOMEM;
554
555                         *filename = p;
556                 }
557
558                 return 0;
559         } else {
560                 const char *path;
561                 char *state, *w;
562                 size_t l;
563
564                 /**
565                  * Plain getenv, not secure_getenv, because we want
566                  * to actually allow the user to pick the binary.
567                  */
568                 path = getenv("PATH");
569                 if (!path)
570                         path = DEFAULT_PATH;
571
572                 FOREACH_WORD_SEPARATOR(w, l, path, ":", state) {
573                         _cleanup_free_ char *p = NULL;
574
575                         if (asprintf(&p, "%.*s/%s", (int) l, w, name) < 0)
576                                 return -ENOMEM;
577
578                         if (access(p, X_OK) < 0)
579                                 continue;
580
581                         if (filename) {
582                                 *filename = path_kill_slashes(p);
583                                 p = NULL;
584                         }
585
586                         return 0;
587                 }
588
589                 return -ENOENT;
590         }
591 }
592
593 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
594         bool changed = false;
595         const char* const* i;
596
597         assert(timestamp);
598
599         if (paths == NULL)
600                 return false;
601
602         STRV_FOREACH(i, paths) {
603                 struct stat stats;
604                 usec_t u;
605
606                 if (stat(*i, &stats) < 0)
607                         continue;
608
609                 u = timespec_load(&stats.st_mtim);
610
611                 /* first check */
612                 if (*timestamp >= u)
613                         continue;
614
615                 log_debug("timestamp of '%s' changed", *i);
616
617                 /* update timestamp */
618                 if (update) {
619                         *timestamp = u;
620                         changed = true;
621                 } else
622                         return true;
623         }
624
625         return changed;
626 }
627
628 int fsck_exists(const char *fstype) {
629         _cleanup_free_ char *p = NULL, *d = NULL;
630         const char *checker;
631         int r;
632
633         checker = strappenda("fsck.", fstype);
634
635         r = find_binary(checker, &p);
636         if (r < 0)
637                 return r;
638
639         /* An fsck that is linked to /bin/true is a non-existent
640          * fsck */
641
642         r = readlink_malloc(p, &d);
643         if (r >= 0 &&
644             (path_equal(d, "/bin/true") ||
645              path_equal(d, "/usr/bin/true") ||
646              path_equal(d, "/dev/null")))
647                 return -ENOENT;
648
649         return 0;
650 }