• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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