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