chiark / gitweb /
util: rename ignore_file() to hidden_file()
[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 strjoin(cwd, "/", p, NULL);
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                                 free(u);
310                                 u = orig;
311                                 orig = NULL;
312                         }
313                 } else
314                         free(t);
315
316                 l[k++] = u;
317         }
318
319         l[k] = NULL;
320
321         if (enomem)
322                 return NULL;
323
324         return l;
325 }
326
327 char **path_strv_resolve_uniq(char **l, const char *prefix) {
328
329         if (strv_isempty(l))
330                 return l;
331
332         if (!path_strv_resolve(l, prefix))
333                 return NULL;
334
335         return strv_uniq(l);
336 }
337
338 char *path_kill_slashes(char *path) {
339         char *f, *t;
340         bool slash = false;
341
342         /* Removes redundant inner and trailing slashes. Modifies the
343          * passed string in-place.
344          *
345          * ///foo///bar/ becomes /foo/bar
346          */
347
348         for (f = path, t = path; *f; f++) {
349
350                 if (*f == '/') {
351                         slash = true;
352                         continue;
353                 }
354
355                 if (slash) {
356                         slash = false;
357                         *(t++) = '/';
358                 }
359
360                 *(t++) = *f;
361         }
362
363         /* Special rule, if we are talking of the root directory, a
364         trailing slash is good */
365
366         if (t == path && slash)
367                 *(t++) = '/';
368
369         *t = 0;
370         return path;
371 }
372
373 char* path_startswith(const char *path, const char *prefix) {
374         assert(path);
375         assert(prefix);
376
377         if ((path[0] == '/') != (prefix[0] == '/'))
378                 return NULL;
379
380         for (;;) {
381                 size_t a, b;
382
383                 path += strspn(path, "/");
384                 prefix += strspn(prefix, "/");
385
386                 if (*prefix == 0)
387                         return (char*) path;
388
389                 if (*path == 0)
390                         return NULL;
391
392                 a = strcspn(path, "/");
393                 b = strcspn(prefix, "/");
394
395                 if (a != b)
396                         return NULL;
397
398                 if (memcmp(path, prefix, a) != 0)
399                         return NULL;
400
401                 path += a;
402                 prefix += b;
403         }
404 }
405
406 bool path_equal(const char *a, const char *b) {
407         assert(a);
408         assert(b);
409
410         if ((a[0] == '/') != (b[0] == '/'))
411                 return false;
412
413         for (;;) {
414                 size_t j, k;
415
416                 a += strspn(a, "/");
417                 b += strspn(b, "/");
418
419                 if (*a == 0 && *b == 0)
420                         return true;
421
422                 if (*a == 0 || *b == 0)
423                         return false;
424
425                 j = strcspn(a, "/");
426                 k = strcspn(b, "/");
427
428                 if (j != k)
429                         return false;
430
431                 if (memcmp(a, b, j) != 0)
432                         return false;
433
434                 a += j;
435                 b += k;
436         }
437 }
438
439 char* path_join(const char *root, const char *path, const char *rest) {
440         assert(path);
441
442         if (!isempty(root))
443                 return strjoin(root, endswith(root, "/") ? "" : "/",
444                                path[0] == '/' ? path+1 : path,
445                                rest ? (endswith(path, "/") ? "" : "/") : NULL,
446                                rest && rest[0] == '/' ? rest+1 : rest,
447                                NULL);
448         else
449                 return strjoin(path,
450                                rest ? (endswith(path, "/") ? "" : "/") : NULL,
451                                rest && rest[0] == '/' ? rest+1 : rest,
452                                NULL);
453 }
454
455 int path_is_mount_point(const char *t, bool allow_symlink) {
456
457         union file_handle_union h = {
458                 .handle.handle_bytes = MAX_HANDLE_SZ
459         };
460
461         int mount_id = -1, mount_id_parent = -1;
462         _cleanup_free_ char *parent = NULL;
463         struct stat a, b;
464         int r;
465         bool nosupp = false;
466
467         /* We are not actually interested in the file handles, but
468          * name_to_handle_at() also passes us the mount ID, hence use
469          * it but throw the handle away */
470
471         if (path_equal(t, "/"))
472                 return 1;
473
474         r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
475         if (r < 0) {
476                 if (errno == ENOSYS)
477                         /* This kernel does not support name_to_handle_at()
478                          * fall back to the traditional stat() logic. */
479                         goto fallback;
480                 else if (errno == EOPNOTSUPP)
481                         /* This kernel or file system does not support
482                          * name_to_handle_at(), hence fallback to the
483                          * traditional stat() logic */
484                         nosupp = true;
485                 else if (errno == ENOENT)
486                         return 0;
487                 else
488                         return -errno;
489         }
490
491         r = path_get_parent(t, &parent);
492         if (r < 0)
493                 return r;
494
495         h.handle.handle_bytes = MAX_HANDLE_SZ;
496         r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, AT_SYMLINK_FOLLOW);
497         if (r < 0)
498                 if (errno == EOPNOTSUPP)
499                         if (nosupp)
500                                 /* Neither parent nor child do name_to_handle_at()?
501                                    We have no choice but to fall back. */
502                                 goto fallback;
503                         else
504                                 /* The parent can't do name_to_handle_at() but
505                                  * the directory we are interested in can?
506                                  * Or the other way around?
507                                  * If so, it must be a mount point. */
508                                 return 1;
509                 else
510                         return -errno;
511         else
512                 return mount_id != mount_id_parent;
513
514 fallback:
515         if (allow_symlink)
516                 r = stat(t, &a);
517         else
518                 r = lstat(t, &a);
519
520         if (r < 0) {
521                 if (errno == ENOENT)
522                         return 0;
523
524                 return -errno;
525         }
526
527         r = path_get_parent(t, &parent);
528         if (r < 0)
529                 return r;
530
531         r = stat(parent, &b);
532         if (r < 0)
533                 return -errno;
534
535         return a.st_dev != b.st_dev;
536 }
537
538 int path_is_read_only_fs(const char *path) {
539         struct statvfs st;
540
541         assert(path);
542
543         if (statvfs(path, &st) < 0)
544                 return -errno;
545
546         if (st.f_flag & ST_RDONLY)
547                 return true;
548
549         /* On NFS, statvfs() might not reflect whether we can actually
550          * write to the remote share. Let's try again with
551          * access(W_OK) which is more reliable, at least sometimes. */
552         if (access(path, W_OK) < 0 && errno == EROFS)
553                 return true;
554
555         return false;
556 }
557
558 int path_is_os_tree(const char *path) {
559         char *p;
560         int r;
561
562         /* We use /usr/lib/os-release as flag file if something is an OS */
563         p = strappenda(path, "/usr/lib/os-release");
564         r = access(p, F_OK);
565
566         if (r >= 0)
567                 return 1;
568
569         /* Also check for the old location in /etc, just in case. */
570         p = strappenda(path, "/etc/os-release");
571         r = access(p, F_OK);
572
573         return r >= 0;
574 }
575
576 int find_binary(const char *name, bool local, char **filename) {
577         assert(name);
578
579         if (is_path(name)) {
580                 if (local && access(name, X_OK) < 0)
581                         return -errno;
582
583                 if (filename) {
584                         char *p;
585
586                         p = path_make_absolute_cwd(name);
587                         if (!p)
588                                 return -ENOMEM;
589
590                         *filename = p;
591                 }
592
593                 return 0;
594         } else {
595                 const char *path;
596                 const char *word, *state;
597                 size_t l;
598
599                 /**
600                  * Plain getenv, not secure_getenv, because we want
601                  * to actually allow the user to pick the binary.
602                  */
603                 path = getenv("PATH");
604                 if (!path)
605                         path = DEFAULT_PATH;
606
607                 FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
608                         _cleanup_free_ char *p = NULL;
609
610                         if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
611                                 return -ENOMEM;
612
613                         if (access(p, X_OK) < 0)
614                                 continue;
615
616                         if (filename) {
617                                 *filename = path_kill_slashes(p);
618                                 p = NULL;
619                         }
620
621                         return 0;
622                 }
623
624                 return -ENOENT;
625         }
626 }
627
628 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
629         bool changed = false;
630         const char* const* i;
631
632         assert(timestamp);
633
634         if (paths == NULL)
635                 return false;
636
637         STRV_FOREACH(i, paths) {
638                 struct stat stats;
639                 usec_t u;
640
641                 if (stat(*i, &stats) < 0)
642                         continue;
643
644                 u = timespec_load(&stats.st_mtim);
645
646                 /* first check */
647                 if (*timestamp >= u)
648                         continue;
649
650                 log_debug("timestamp of '%s' changed", *i);
651
652                 /* update timestamp */
653                 if (update) {
654                         *timestamp = u;
655                         changed = true;
656                 } else
657                         return true;
658         }
659
660         return changed;
661 }
662
663 int fsck_exists(const char *fstype) {
664         _cleanup_free_ char *p = NULL, *d = NULL;
665         const char *checker;
666         int r;
667
668         checker = strappenda("fsck.", fstype);
669
670         r = find_binary(checker, true, &p);
671         if (r < 0)
672                 return r;
673
674         /* An fsck that is linked to /bin/true is a non-existent
675          * fsck */
676
677         r = readlink_malloc(p, &d);
678         if (r >= 0 &&
679             (path_equal(d, "/bin/true") ||
680              path_equal(d, "/usr/bin/true") ||
681              path_equal(d, "/dev/null")))
682                 return -ENOENT;
683
684         return 0;
685 }