1 /***
2 This file is part of eudev, forked from systemd.
3
4 Copyright 2010-2012 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <assert.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <signal.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <dirent.h>
29 #include <sys/statvfs.h>
30
31 #include "macro.h"
32 #include "util.h"
33 #include "log.h"
34 #include "strv.h"
35 #include "mkdir.h"
36 #include "path-util.h"
37 #include "missing.h"
38 #include "fileio.h"
39
path_is_absolute(const char * p)40 bool path_is_absolute(const char *p) {
41 return p[0] == '/';
42 }
43
is_path(const char * p)44 bool is_path(const char *p) {
45 return !!strchr(p, '/');
46 }
47
path_get_parent(const char * path,char ** _r)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
path_make_absolute(const char * p,const char * prefix)89 char *path_make_absolute(const char *p, const char *prefix) {
90 assert(p);
91
92 /* Makes every item in the list an absolute path by prepending
93 * the prefix, if specified and necessary */
94
95 if (path_is_absolute(p) || !prefix)
96 return strdup(p);
97
98 return strjoin(prefix, "/", p, NULL);
99 }
100
path_make_absolute_cwd(const char * p)101 char *path_make_absolute_cwd(const char *p) {
102 _cleanup_free_ char *cwd = NULL;
103
104 assert(p);
105
106 /* Similar to path_make_absolute(), but prefixes with the
107 * current working directory. */
108
109 if (path_is_absolute(p))
110 return strdup(p);
111
112 cwd = get_current_dir_name();
113 if (!cwd)
114 return NULL;
115
116 return strjoin(cwd, "/", p, NULL);
117 }
118
path_strv_resolve(char ** l,const char * prefix)119 char **path_strv_resolve(char **l, const char *prefix) {
120 char **s;
121 unsigned k = 0;
122 bool enomem = false;
123
124 if (strv_isempty(l))
125 return l;
126
127 /* Goes through every item in the string list and canonicalize
128 * the path. This works in place and won't rollback any
129 * changes on failure. */
130
131 STRV_FOREACH(s, l) {
132 char *t, *u;
133 _cleanup_free_ char *orig = NULL;
134
135 if (!path_is_absolute(*s)) {
136 free(*s);
137 continue;
138 }
139
140 if (prefix) {
141 orig = *s;
142 t = strappend(prefix, orig);
143 if (!t) {
144 enomem = true;
145 continue;
146 }
147 } else
148 t = *s;
149
150 errno = 0;
151 u = realpath(t, 0);
152 if (!u) {
153 if (errno == ENOENT) {
154 if (prefix) {
155 u = orig;
156 orig = NULL;
157 free(t);
158 } else
159 u = t;
160 } else {
161 free(t);
162 if (errno == ENOMEM || errno == 0)
163 enomem = true;
164
165 continue;
166 }
167 } else if (prefix) {
168 char *x;
169
170 free(t);
171 x = path_startswith(u, prefix);
172 if (x) {
173 /* restore the slash if it was lost */
174 if (!startswith(x, "/"))
175 *(--x) = '/';
176
177 t = strdup(x);
178 free(u);
179 if (!t) {
180 enomem = true;
181 continue;
182 }
183 u = t;
184 } else {
185 /* canonicalized path goes outside of
186 * prefix, keep the original path instead */
187 free(u);
188 u = orig;
189 orig = NULL;
190 }
191 } else
192 free(t);
193
194 l[k++] = u;
195 }
196
197 l[k] = NULL;
198
199 if (enomem)
200 return NULL;
201
202 return l;
203 }
204
path_strv_resolve_uniq(char ** l,const char * prefix)205 char **path_strv_resolve_uniq(char **l, const char *prefix) {
206
207 if (strv_isempty(l))
208 return l;
209
210 if (!path_strv_resolve(l, prefix))
211 return NULL;
212
213 return strv_uniq(l);
214 }
215
path_kill_slashes(char * path)216 char *path_kill_slashes(char *path) {
217 char *f, *t;
218 bool slash = false;
219
220 /* Removes redundant inner and trailing slashes. Modifies the
221 * passed string in-place.
222 *
223 * ///foo///bar/ becomes /foo/bar
224 */
225
226 for (f = path, t = path; *f; f++) {
227
228 if (*f == '/') {
229 slash = true;
230 continue;
231 }
232
233 if (slash) {
234 slash = false;
235 *(t++) = '/';
236 }
237
238 *(t++) = *f;
239 }
240
241 /* Special rule, if we are talking of the root directory, a
242 trailing slash is good */
243
244 if (t == path && slash)
245 *(t++) = '/';
246
247 *t = 0;
248 return path;
249 }
250
path_startswith(const char * path,const char * prefix)251 char* path_startswith(const char *path, const char *prefix) {
252 assert(path);
253 assert(prefix);
254
255 if ((path[0] == '/') != (prefix[0] == '/'))
256 return NULL;
257
258 for (;;) {
259 size_t a, b;
260
261 path += strspn(path, "/");
262 prefix += strspn(prefix, "/");
263
264 if (*prefix == 0)
265 return (char*) path;
266
267 if (*path == 0)
268 return NULL;
269
270 a = strcspn(path, "/");
271 b = strcspn(prefix, "/");
272
273 if (a != b)
274 return NULL;
275
276 if (memcmp(path, prefix, a) != 0)
277 return NULL;
278
279 path += a;
280 prefix += b;
281 }
282 }
283
path_compare(const char * a,const char * b)284 int path_compare(const char *a, const char *b) {
285 int d;
286
287 assert(a);
288 assert(b);
289
290 /* A relative path and an abolute path must not compare as equal.
291 * Which one is sorted before the other does not really matter.
292 * Here a relative path is ordered before an absolute path. */
293 d = (a[0] == '/') - (b[0] == '/');
294 if (d)
295 return d;
296
297 for (;;) {
298 size_t j, k;
299
300 a += strspn(a, "/");
301 b += strspn(b, "/");
302
303 if (*a == 0 && *b == 0)
304 return 0;
305
306 /* Order prefixes first: "/foo" before "/foo/bar" */
307 if (*a == 0)
308 return -1;
309 if (*b == 0)
310 return 1;
311
312 j = strcspn(a, "/");
313 k = strcspn(b, "/");
314
315 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
316 d = memcmp(a, b, MIN(j, k));
317 if (d)
318 return (d > 0) - (d < 0); /* sign of d */
319
320 /* Sort "/foo/a" before "/foo/aaa" */
321 d = (j > k) - (j < k); /* sign of (j - k) */
322 if (d)
323 return d;
324
325 a += j;
326 b += k;
327 }
328 }
329
path_equal(const char * a,const char * b)330 bool path_equal(const char *a, const char *b) {
331 return path_compare(a, b) == 0;
332 }
333
fd_fdinfo_mnt_id(int fd,const char * filename,int flags,int * mnt_id)334 static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
335 char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
336 _cleanup_free_ char *fdinfo = NULL;
337 _cleanup_close_ int subfd = -1;
338 char *p;
339 int r;
340
341 if ((flags & AT_EMPTY_PATH) && isempty(filename))
342 xsprintf(path, "/proc/self/fdinfo/%i", fd);
343 else {
344 subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
345 if (subfd < 0)
346 return -errno;
347
348 xsprintf(path, "/proc/self/fdinfo/%i", subfd);
349 }
350
351 r = read_full_file(path, &fdinfo, NULL);
352 if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
353 return -EOPNOTSUPP;
354 if (r < 0)
355 return -errno;
356
357 p = startswith(fdinfo, "mnt_id:");
358 if (!p) {
359 p = strstr(fdinfo, "\nmnt_id:");
360 if (!p) /* The mnt_id field is a relatively new addition */
361 return -EOPNOTSUPP;
362
363 p += 8;
364 }
365
366 p += strspn(p, WHITESPACE);
367 p[strcspn(p, WHITESPACE)] = 0;
368
369 return safe_atoi(p, mnt_id);
370 }
371
fd_is_mount_point(int fd)372 int fd_is_mount_point(int fd) {
373 union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
374 int mount_id = -1, mount_id_parent = -1;
375 bool nosupp = false, check_st_dev = true;
376 struct stat a, b;
377 int r;
378
379 assert(fd >= 0);
380
381 /* First we will try the name_to_handle_at() syscall, which
382 * tells us the mount id and an opaque file "handle". It is
383 * not supported everywhere though (kernel compile-time
384 * option, not all file systems are hooked up). If it works
385 * the mount id is usually good enough to tell us whether
386 * something is a mount point.
387 *
388 * If that didn't work we will try to read the mount id from
389 * /proc/self/fdinfo/<fd>. This is almost as good as
390 * name_to_handle_at(), however, does not return the the
391 * opaque file handle. The opaque file handle is pretty useful
392 * to detect the root directory, which we should always
393 * consider a mount point. Hence we use this only as
394 * fallback. Exporting the mnt_id in fdinfo is a pretty recent
395 * kernel addition.
396 *
397 * As last fallback we do traditional fstat() based st_dev
398 * comparisons. This is how things were traditionally done,
399 * but unionfs breaks breaks this since it exposes file
400 * systems with a variety of st_dev reported. Also, btrfs
401 * subvolumes have different st_dev, even though they aren't
402 * real mounts of their own. */
403
404 r = name_to_handle_at(fd, "", &h.handle, &mount_id, AT_EMPTY_PATH);
405 if (r < 0) {
406 if (errno == ENOSYS)
407 /* This kernel does not support name_to_handle_at()
408 * fall back to simpler logic. */
409 goto fallback_fdinfo;
410 else if (errno == EOPNOTSUPP)
411 /* This kernel or file system does not support
412 * name_to_handle_at(), hence let's see if the
413 * upper fs supports it (in which case it is a
414 * mount point), otherwise fallback to the
415 * traditional stat() logic */
416 nosupp = true;
417 else
418 return -errno;
419 }
420
421 r = name_to_handle_at(fd, "..", &h_parent.handle, &mount_id_parent, 0);
422 if (r < 0) {
423 if (errno == EOPNOTSUPP) {
424 if (nosupp)
425 /* Neither parent nor child do name_to_handle_at()?
426 We have no choice but to fall back. */
427 goto fallback_fdinfo;
428 else
429 /* The parent can't do name_to_handle_at() but the
430 * directory we are interested in can?
431 * If so, it must be a mount point. */
432 return 1;
433 } else
434 return -errno;
435 }
436
437 /* The parent can do name_to_handle_at() but the
438 * directory we are interested in can't? If so, it
439 * must be a mount point. */
440 if (nosupp)
441 return 1;
442
443 /* If the file handle for the directory we are
444 * interested in and its parent are identical, we
445 * assume this is the root directory, which is a mount
446 * point. */
447
448 if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
449 h.handle.handle_type == h_parent.handle.handle_type &&
450 memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
451 return 1;
452
453 return mount_id != mount_id_parent;
454
455 fallback_fdinfo:
456 r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id);
457 if (r == -EOPNOTSUPP)
458 goto fallback_fstat;
459 if (r < 0)
460 return r;
461
462 r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent);
463 if (r < 0)
464 return r;
465
466 if (mount_id != mount_id_parent)
467 return 1;
468
469 /* Hmm, so, the mount ids are the same. This leaves one
470 * special case though for the root file system. For that,
471 * let's see if the parent directory has the same inode as we
472 * are interested in. Hence, let's also do fstat() checks now,
473 * too, but avoid the st_dev comparisons, since they aren't
474 * that useful on unionfs mounts. */
475 check_st_dev = false;
476
477 fallback_fstat:
478 if (fstatat(fd, "", &a, AT_EMPTY_PATH) < 0)
479 return -errno;
480
481 if (fstatat(fd, "..", &b, 0) < 0)
482 return -errno;
483
484 /* A directory with same device and inode as its parent? Must
485 * be the root directory */
486 if (a.st_dev == b.st_dev &&
487 a.st_ino == b.st_ino)
488 return 1;
489
490 return check_st_dev && (a.st_dev != b.st_dev);
491 }
492
path_is_mount_point(const char * t,bool allow_symlink)493 int path_is_mount_point(const char *t, bool allow_symlink) {
494 _cleanup_close_ int fd = -1;
495
496 assert(t);
497
498 if (path_equal(t, "/"))
499 return 1;
500
501 fd = openat(AT_FDCWD, t, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|(allow_symlink ? 0 : O_PATH));
502 if (fd < 0)
503 return -errno;
504
505 return fd_is_mount_point(fd);
506 }
507
paths_check_timestamp(const char * const * paths,usec_t * timestamp,bool update)508 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
509 bool changed = false;
510 const char* const* i;
511
512 assert(timestamp);
513
514 if (paths == NULL)
515 return false;
516
517 STRV_FOREACH(i, paths) {
518 struct stat stats;
519 usec_t u;
520
521 if (stat(*i, &stats) < 0)
522 continue;
523
524 u = timespec_load(&stats.st_mtim);
525
526 /* first check */
527 if (*timestamp >= u)
528 continue;
529
530 log_debug("timestamp of '%s' changed", *i);
531
532 /* update timestamp */
533 if (update) {
534 *timestamp = u;
535 changed = true;
536 } else
537 return true;
538 }
539
540 return changed;
541 }
542
prefix_root(const char * root,const char * path)543 char *prefix_root(const char *root, const char *path) {
544 char *n, *p;
545 size_t l;
546
547 /* If root is passed, prefixes path with it. Otherwise returns
548 * it as is. */
549
550 assert(path);
551
552 /* First, drop duplicate prefixing slashes from the path */
553 while (path[0] == '/' && path[1] == '/')
554 path++;
555
556 if (isempty(root) || path_equal(root, "/"))
557 return strdup(path);
558
559 l = strlen(root) + 1 + strlen(path) + 1;
560
561 n = new(char, l);
562 if (!n)
563 return NULL;
564
565 p = stpcpy(n, root);
566
567 while (p > n && p[-1] == '/')
568 p--;
569
570 if (path[0] != '/')
571 *(p++) = '/';
572
573 strcpy(p, path);
574 return n;
575 }
576