199ca880aSopenharmony_ci/*** 299ca880aSopenharmony_ci This file is part of eudev, forked from systemd. 399ca880aSopenharmony_ci 499ca880aSopenharmony_ci Copyright 2010-2012 Lennart Poettering 599ca880aSopenharmony_ci 699ca880aSopenharmony_ci systemd is free software; you can redistribute it and/or modify it 799ca880aSopenharmony_ci under the terms of the GNU Lesser General Public License as published by 899ca880aSopenharmony_ci the Free Software Foundation; either version 2.1 of the License, or 999ca880aSopenharmony_ci (at your option) any later version. 1099ca880aSopenharmony_ci 1199ca880aSopenharmony_ci systemd is distributed in the hope that it will be useful, but 1299ca880aSopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1399ca880aSopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1499ca880aSopenharmony_ci Lesser General Public License for more details. 1599ca880aSopenharmony_ci 1699ca880aSopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1799ca880aSopenharmony_ci along with systemd; If not, see <http://www.gnu.org/licenses/>. 1899ca880aSopenharmony_ci***/ 1999ca880aSopenharmony_ci 2099ca880aSopenharmony_ci#include <assert.h> 2199ca880aSopenharmony_ci#include <string.h> 2299ca880aSopenharmony_ci#include <unistd.h> 2399ca880aSopenharmony_ci#include <errno.h> 2499ca880aSopenharmony_ci#include <stdlib.h> 2599ca880aSopenharmony_ci#include <signal.h> 2699ca880aSopenharmony_ci#include <stdio.h> 2799ca880aSopenharmony_ci#include <fcntl.h> 2899ca880aSopenharmony_ci#include <dirent.h> 2999ca880aSopenharmony_ci#include <sys/statvfs.h> 3099ca880aSopenharmony_ci 3199ca880aSopenharmony_ci#include "macro.h" 3299ca880aSopenharmony_ci#include "util.h" 3399ca880aSopenharmony_ci#include "log.h" 3499ca880aSopenharmony_ci#include "strv.h" 3599ca880aSopenharmony_ci#include "mkdir.h" 3699ca880aSopenharmony_ci#include "path-util.h" 3799ca880aSopenharmony_ci#include "missing.h" 3899ca880aSopenharmony_ci#include "fileio.h" 3999ca880aSopenharmony_ci 4099ca880aSopenharmony_cibool path_is_absolute(const char *p) { 4199ca880aSopenharmony_ci return p[0] == '/'; 4299ca880aSopenharmony_ci} 4399ca880aSopenharmony_ci 4499ca880aSopenharmony_cibool is_path(const char *p) { 4599ca880aSopenharmony_ci return !!strchr(p, '/'); 4699ca880aSopenharmony_ci} 4799ca880aSopenharmony_ci 4899ca880aSopenharmony_ciint path_get_parent(const char *path, char **_r) { 4999ca880aSopenharmony_ci const char *e, *a = NULL, *b = NULL, *p; 5099ca880aSopenharmony_ci char *r; 5199ca880aSopenharmony_ci bool slash = false; 5299ca880aSopenharmony_ci 5399ca880aSopenharmony_ci assert(path); 5499ca880aSopenharmony_ci assert(_r); 5599ca880aSopenharmony_ci 5699ca880aSopenharmony_ci if (!*path) 5799ca880aSopenharmony_ci return -EINVAL; 5899ca880aSopenharmony_ci 5999ca880aSopenharmony_ci for (e = path; *e; e++) { 6099ca880aSopenharmony_ci 6199ca880aSopenharmony_ci if (!slash && *e == '/') { 6299ca880aSopenharmony_ci a = b; 6399ca880aSopenharmony_ci b = e; 6499ca880aSopenharmony_ci slash = true; 6599ca880aSopenharmony_ci } else if (slash && *e != '/') 6699ca880aSopenharmony_ci slash = false; 6799ca880aSopenharmony_ci } 6899ca880aSopenharmony_ci 6999ca880aSopenharmony_ci if (*(e-1) == '/') 7099ca880aSopenharmony_ci p = a; 7199ca880aSopenharmony_ci else 7299ca880aSopenharmony_ci p = b; 7399ca880aSopenharmony_ci 7499ca880aSopenharmony_ci if (!p) 7599ca880aSopenharmony_ci return -EINVAL; 7699ca880aSopenharmony_ci 7799ca880aSopenharmony_ci if (p == path) 7899ca880aSopenharmony_ci r = strdup("/"); 7999ca880aSopenharmony_ci else 8099ca880aSopenharmony_ci r = strndup(path, p-path); 8199ca880aSopenharmony_ci 8299ca880aSopenharmony_ci if (!r) 8399ca880aSopenharmony_ci return -ENOMEM; 8499ca880aSopenharmony_ci 8599ca880aSopenharmony_ci *_r = r; 8699ca880aSopenharmony_ci return 0; 8799ca880aSopenharmony_ci} 8899ca880aSopenharmony_ci 8999ca880aSopenharmony_cichar *path_make_absolute(const char *p, const char *prefix) { 9099ca880aSopenharmony_ci assert(p); 9199ca880aSopenharmony_ci 9299ca880aSopenharmony_ci /* Makes every item in the list an absolute path by prepending 9399ca880aSopenharmony_ci * the prefix, if specified and necessary */ 9499ca880aSopenharmony_ci 9599ca880aSopenharmony_ci if (path_is_absolute(p) || !prefix) 9699ca880aSopenharmony_ci return strdup(p); 9799ca880aSopenharmony_ci 9899ca880aSopenharmony_ci return strjoin(prefix, "/", p, NULL); 9999ca880aSopenharmony_ci} 10099ca880aSopenharmony_ci 10199ca880aSopenharmony_cichar *path_make_absolute_cwd(const char *p) { 10299ca880aSopenharmony_ci _cleanup_free_ char *cwd = NULL; 10399ca880aSopenharmony_ci 10499ca880aSopenharmony_ci assert(p); 10599ca880aSopenharmony_ci 10699ca880aSopenharmony_ci /* Similar to path_make_absolute(), but prefixes with the 10799ca880aSopenharmony_ci * current working directory. */ 10899ca880aSopenharmony_ci 10999ca880aSopenharmony_ci if (path_is_absolute(p)) 11099ca880aSopenharmony_ci return strdup(p); 11199ca880aSopenharmony_ci 11299ca880aSopenharmony_ci cwd = get_current_dir_name(); 11399ca880aSopenharmony_ci if (!cwd) 11499ca880aSopenharmony_ci return NULL; 11599ca880aSopenharmony_ci 11699ca880aSopenharmony_ci return strjoin(cwd, "/", p, NULL); 11799ca880aSopenharmony_ci} 11899ca880aSopenharmony_ci 11999ca880aSopenharmony_cichar **path_strv_resolve(char **l, const char *prefix) { 12099ca880aSopenharmony_ci char **s; 12199ca880aSopenharmony_ci unsigned k = 0; 12299ca880aSopenharmony_ci bool enomem = false; 12399ca880aSopenharmony_ci 12499ca880aSopenharmony_ci if (strv_isempty(l)) 12599ca880aSopenharmony_ci return l; 12699ca880aSopenharmony_ci 12799ca880aSopenharmony_ci /* Goes through every item in the string list and canonicalize 12899ca880aSopenharmony_ci * the path. This works in place and won't rollback any 12999ca880aSopenharmony_ci * changes on failure. */ 13099ca880aSopenharmony_ci 13199ca880aSopenharmony_ci STRV_FOREACH(s, l) { 13299ca880aSopenharmony_ci char *t, *u; 13399ca880aSopenharmony_ci _cleanup_free_ char *orig = NULL; 13499ca880aSopenharmony_ci 13599ca880aSopenharmony_ci if (!path_is_absolute(*s)) { 13699ca880aSopenharmony_ci free(*s); 13799ca880aSopenharmony_ci continue; 13899ca880aSopenharmony_ci } 13999ca880aSopenharmony_ci 14099ca880aSopenharmony_ci if (prefix) { 14199ca880aSopenharmony_ci orig = *s; 14299ca880aSopenharmony_ci t = strappend(prefix, orig); 14399ca880aSopenharmony_ci if (!t) { 14499ca880aSopenharmony_ci enomem = true; 14599ca880aSopenharmony_ci continue; 14699ca880aSopenharmony_ci } 14799ca880aSopenharmony_ci } else 14899ca880aSopenharmony_ci t = *s; 14999ca880aSopenharmony_ci 15099ca880aSopenharmony_ci errno = 0; 15199ca880aSopenharmony_ci u = realpath(t, 0); 15299ca880aSopenharmony_ci if (!u) { 15399ca880aSopenharmony_ci if (errno == ENOENT) { 15499ca880aSopenharmony_ci if (prefix) { 15599ca880aSopenharmony_ci u = orig; 15699ca880aSopenharmony_ci orig = NULL; 15799ca880aSopenharmony_ci free(t); 15899ca880aSopenharmony_ci } else 15999ca880aSopenharmony_ci u = t; 16099ca880aSopenharmony_ci } else { 16199ca880aSopenharmony_ci free(t); 16299ca880aSopenharmony_ci if (errno == ENOMEM || errno == 0) 16399ca880aSopenharmony_ci enomem = true; 16499ca880aSopenharmony_ci 16599ca880aSopenharmony_ci continue; 16699ca880aSopenharmony_ci } 16799ca880aSopenharmony_ci } else if (prefix) { 16899ca880aSopenharmony_ci char *x; 16999ca880aSopenharmony_ci 17099ca880aSopenharmony_ci free(t); 17199ca880aSopenharmony_ci x = path_startswith(u, prefix); 17299ca880aSopenharmony_ci if (x) { 17399ca880aSopenharmony_ci /* restore the slash if it was lost */ 17499ca880aSopenharmony_ci if (!startswith(x, "/")) 17599ca880aSopenharmony_ci *(--x) = '/'; 17699ca880aSopenharmony_ci 17799ca880aSopenharmony_ci t = strdup(x); 17899ca880aSopenharmony_ci free(u); 17999ca880aSopenharmony_ci if (!t) { 18099ca880aSopenharmony_ci enomem = true; 18199ca880aSopenharmony_ci continue; 18299ca880aSopenharmony_ci } 18399ca880aSopenharmony_ci u = t; 18499ca880aSopenharmony_ci } else { 18599ca880aSopenharmony_ci /* canonicalized path goes outside of 18699ca880aSopenharmony_ci * prefix, keep the original path instead */ 18799ca880aSopenharmony_ci free(u); 18899ca880aSopenharmony_ci u = orig; 18999ca880aSopenharmony_ci orig = NULL; 19099ca880aSopenharmony_ci } 19199ca880aSopenharmony_ci } else 19299ca880aSopenharmony_ci free(t); 19399ca880aSopenharmony_ci 19499ca880aSopenharmony_ci l[k++] = u; 19599ca880aSopenharmony_ci } 19699ca880aSopenharmony_ci 19799ca880aSopenharmony_ci l[k] = NULL; 19899ca880aSopenharmony_ci 19999ca880aSopenharmony_ci if (enomem) 20099ca880aSopenharmony_ci return NULL; 20199ca880aSopenharmony_ci 20299ca880aSopenharmony_ci return l; 20399ca880aSopenharmony_ci} 20499ca880aSopenharmony_ci 20599ca880aSopenharmony_cichar **path_strv_resolve_uniq(char **l, const char *prefix) { 20699ca880aSopenharmony_ci 20799ca880aSopenharmony_ci if (strv_isempty(l)) 20899ca880aSopenharmony_ci return l; 20999ca880aSopenharmony_ci 21099ca880aSopenharmony_ci if (!path_strv_resolve(l, prefix)) 21199ca880aSopenharmony_ci return NULL; 21299ca880aSopenharmony_ci 21399ca880aSopenharmony_ci return strv_uniq(l); 21499ca880aSopenharmony_ci} 21599ca880aSopenharmony_ci 21699ca880aSopenharmony_cichar *path_kill_slashes(char *path) { 21799ca880aSopenharmony_ci char *f, *t; 21899ca880aSopenharmony_ci bool slash = false; 21999ca880aSopenharmony_ci 22099ca880aSopenharmony_ci /* Removes redundant inner and trailing slashes. Modifies the 22199ca880aSopenharmony_ci * passed string in-place. 22299ca880aSopenharmony_ci * 22399ca880aSopenharmony_ci * ///foo///bar/ becomes /foo/bar 22499ca880aSopenharmony_ci */ 22599ca880aSopenharmony_ci 22699ca880aSopenharmony_ci for (f = path, t = path; *f; f++) { 22799ca880aSopenharmony_ci 22899ca880aSopenharmony_ci if (*f == '/') { 22999ca880aSopenharmony_ci slash = true; 23099ca880aSopenharmony_ci continue; 23199ca880aSopenharmony_ci } 23299ca880aSopenharmony_ci 23399ca880aSopenharmony_ci if (slash) { 23499ca880aSopenharmony_ci slash = false; 23599ca880aSopenharmony_ci *(t++) = '/'; 23699ca880aSopenharmony_ci } 23799ca880aSopenharmony_ci 23899ca880aSopenharmony_ci *(t++) = *f; 23999ca880aSopenharmony_ci } 24099ca880aSopenharmony_ci 24199ca880aSopenharmony_ci /* Special rule, if we are talking of the root directory, a 24299ca880aSopenharmony_ci trailing slash is good */ 24399ca880aSopenharmony_ci 24499ca880aSopenharmony_ci if (t == path && slash) 24599ca880aSopenharmony_ci *(t++) = '/'; 24699ca880aSopenharmony_ci 24799ca880aSopenharmony_ci *t = 0; 24899ca880aSopenharmony_ci return path; 24999ca880aSopenharmony_ci} 25099ca880aSopenharmony_ci 25199ca880aSopenharmony_cichar* path_startswith(const char *path, const char *prefix) { 25299ca880aSopenharmony_ci assert(path); 25399ca880aSopenharmony_ci assert(prefix); 25499ca880aSopenharmony_ci 25599ca880aSopenharmony_ci if ((path[0] == '/') != (prefix[0] == '/')) 25699ca880aSopenharmony_ci return NULL; 25799ca880aSopenharmony_ci 25899ca880aSopenharmony_ci for (;;) { 25999ca880aSopenharmony_ci size_t a, b; 26099ca880aSopenharmony_ci 26199ca880aSopenharmony_ci path += strspn(path, "/"); 26299ca880aSopenharmony_ci prefix += strspn(prefix, "/"); 26399ca880aSopenharmony_ci 26499ca880aSopenharmony_ci if (*prefix == 0) 26599ca880aSopenharmony_ci return (char*) path; 26699ca880aSopenharmony_ci 26799ca880aSopenharmony_ci if (*path == 0) 26899ca880aSopenharmony_ci return NULL; 26999ca880aSopenharmony_ci 27099ca880aSopenharmony_ci a = strcspn(path, "/"); 27199ca880aSopenharmony_ci b = strcspn(prefix, "/"); 27299ca880aSopenharmony_ci 27399ca880aSopenharmony_ci if (a != b) 27499ca880aSopenharmony_ci return NULL; 27599ca880aSopenharmony_ci 27699ca880aSopenharmony_ci if (memcmp(path, prefix, a) != 0) 27799ca880aSopenharmony_ci return NULL; 27899ca880aSopenharmony_ci 27999ca880aSopenharmony_ci path += a; 28099ca880aSopenharmony_ci prefix += b; 28199ca880aSopenharmony_ci } 28299ca880aSopenharmony_ci} 28399ca880aSopenharmony_ci 28499ca880aSopenharmony_ciint path_compare(const char *a, const char *b) { 28599ca880aSopenharmony_ci int d; 28699ca880aSopenharmony_ci 28799ca880aSopenharmony_ci assert(a); 28899ca880aSopenharmony_ci assert(b); 28999ca880aSopenharmony_ci 29099ca880aSopenharmony_ci /* A relative path and an abolute path must not compare as equal. 29199ca880aSopenharmony_ci * Which one is sorted before the other does not really matter. 29299ca880aSopenharmony_ci * Here a relative path is ordered before an absolute path. */ 29399ca880aSopenharmony_ci d = (a[0] == '/') - (b[0] == '/'); 29499ca880aSopenharmony_ci if (d) 29599ca880aSopenharmony_ci return d; 29699ca880aSopenharmony_ci 29799ca880aSopenharmony_ci for (;;) { 29899ca880aSopenharmony_ci size_t j, k; 29999ca880aSopenharmony_ci 30099ca880aSopenharmony_ci a += strspn(a, "/"); 30199ca880aSopenharmony_ci b += strspn(b, "/"); 30299ca880aSopenharmony_ci 30399ca880aSopenharmony_ci if (*a == 0 && *b == 0) 30499ca880aSopenharmony_ci return 0; 30599ca880aSopenharmony_ci 30699ca880aSopenharmony_ci /* Order prefixes first: "/foo" before "/foo/bar" */ 30799ca880aSopenharmony_ci if (*a == 0) 30899ca880aSopenharmony_ci return -1; 30999ca880aSopenharmony_ci if (*b == 0) 31099ca880aSopenharmony_ci return 1; 31199ca880aSopenharmony_ci 31299ca880aSopenharmony_ci j = strcspn(a, "/"); 31399ca880aSopenharmony_ci k = strcspn(b, "/"); 31499ca880aSopenharmony_ci 31599ca880aSopenharmony_ci /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ 31699ca880aSopenharmony_ci d = memcmp(a, b, MIN(j, k)); 31799ca880aSopenharmony_ci if (d) 31899ca880aSopenharmony_ci return (d > 0) - (d < 0); /* sign of d */ 31999ca880aSopenharmony_ci 32099ca880aSopenharmony_ci /* Sort "/foo/a" before "/foo/aaa" */ 32199ca880aSopenharmony_ci d = (j > k) - (j < k); /* sign of (j - k) */ 32299ca880aSopenharmony_ci if (d) 32399ca880aSopenharmony_ci return d; 32499ca880aSopenharmony_ci 32599ca880aSopenharmony_ci a += j; 32699ca880aSopenharmony_ci b += k; 32799ca880aSopenharmony_ci } 32899ca880aSopenharmony_ci} 32999ca880aSopenharmony_ci 33099ca880aSopenharmony_cibool path_equal(const char *a, const char *b) { 33199ca880aSopenharmony_ci return path_compare(a, b) == 0; 33299ca880aSopenharmony_ci} 33399ca880aSopenharmony_ci 33499ca880aSopenharmony_cistatic int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) { 33599ca880aSopenharmony_ci char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)]; 33699ca880aSopenharmony_ci _cleanup_free_ char *fdinfo = NULL; 33799ca880aSopenharmony_ci _cleanup_close_ int subfd = -1; 33899ca880aSopenharmony_ci char *p; 33999ca880aSopenharmony_ci int r; 34099ca880aSopenharmony_ci 34199ca880aSopenharmony_ci if ((flags & AT_EMPTY_PATH) && isempty(filename)) 34299ca880aSopenharmony_ci xsprintf(path, "/proc/self/fdinfo/%i", fd); 34399ca880aSopenharmony_ci else { 34499ca880aSopenharmony_ci subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); 34599ca880aSopenharmony_ci if (subfd < 0) 34699ca880aSopenharmony_ci return -errno; 34799ca880aSopenharmony_ci 34899ca880aSopenharmony_ci xsprintf(path, "/proc/self/fdinfo/%i", subfd); 34999ca880aSopenharmony_ci } 35099ca880aSopenharmony_ci 35199ca880aSopenharmony_ci r = read_full_file(path, &fdinfo, NULL); 35299ca880aSopenharmony_ci if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */ 35399ca880aSopenharmony_ci return -EOPNOTSUPP; 35499ca880aSopenharmony_ci if (r < 0) 35599ca880aSopenharmony_ci return -errno; 35699ca880aSopenharmony_ci 35799ca880aSopenharmony_ci p = startswith(fdinfo, "mnt_id:"); 35899ca880aSopenharmony_ci if (!p) { 35999ca880aSopenharmony_ci p = strstr(fdinfo, "\nmnt_id:"); 36099ca880aSopenharmony_ci if (!p) /* The mnt_id field is a relatively new addition */ 36199ca880aSopenharmony_ci return -EOPNOTSUPP; 36299ca880aSopenharmony_ci 36399ca880aSopenharmony_ci p += 8; 36499ca880aSopenharmony_ci } 36599ca880aSopenharmony_ci 36699ca880aSopenharmony_ci p += strspn(p, WHITESPACE); 36799ca880aSopenharmony_ci p[strcspn(p, WHITESPACE)] = 0; 36899ca880aSopenharmony_ci 36999ca880aSopenharmony_ci return safe_atoi(p, mnt_id); 37099ca880aSopenharmony_ci} 37199ca880aSopenharmony_ci 37299ca880aSopenharmony_ciint fd_is_mount_point(int fd) { 37399ca880aSopenharmony_ci union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT; 37499ca880aSopenharmony_ci int mount_id = -1, mount_id_parent = -1; 37599ca880aSopenharmony_ci bool nosupp = false, check_st_dev = true; 37699ca880aSopenharmony_ci struct stat a, b; 37799ca880aSopenharmony_ci int r; 37899ca880aSopenharmony_ci 37999ca880aSopenharmony_ci assert(fd >= 0); 38099ca880aSopenharmony_ci 38199ca880aSopenharmony_ci /* First we will try the name_to_handle_at() syscall, which 38299ca880aSopenharmony_ci * tells us the mount id and an opaque file "handle". It is 38399ca880aSopenharmony_ci * not supported everywhere though (kernel compile-time 38499ca880aSopenharmony_ci * option, not all file systems are hooked up). If it works 38599ca880aSopenharmony_ci * the mount id is usually good enough to tell us whether 38699ca880aSopenharmony_ci * something is a mount point. 38799ca880aSopenharmony_ci * 38899ca880aSopenharmony_ci * If that didn't work we will try to read the mount id from 38999ca880aSopenharmony_ci * /proc/self/fdinfo/<fd>. This is almost as good as 39099ca880aSopenharmony_ci * name_to_handle_at(), however, does not return the the 39199ca880aSopenharmony_ci * opaque file handle. The opaque file handle is pretty useful 39299ca880aSopenharmony_ci * to detect the root directory, which we should always 39399ca880aSopenharmony_ci * consider a mount point. Hence we use this only as 39499ca880aSopenharmony_ci * fallback. Exporting the mnt_id in fdinfo is a pretty recent 39599ca880aSopenharmony_ci * kernel addition. 39699ca880aSopenharmony_ci * 39799ca880aSopenharmony_ci * As last fallback we do traditional fstat() based st_dev 39899ca880aSopenharmony_ci * comparisons. This is how things were traditionally done, 39999ca880aSopenharmony_ci * but unionfs breaks breaks this since it exposes file 40099ca880aSopenharmony_ci * systems with a variety of st_dev reported. Also, btrfs 40199ca880aSopenharmony_ci * subvolumes have different st_dev, even though they aren't 40299ca880aSopenharmony_ci * real mounts of their own. */ 40399ca880aSopenharmony_ci 40499ca880aSopenharmony_ci r = name_to_handle_at(fd, "", &h.handle, &mount_id, AT_EMPTY_PATH); 40599ca880aSopenharmony_ci if (r < 0) { 40699ca880aSopenharmony_ci if (errno == ENOSYS) 40799ca880aSopenharmony_ci /* This kernel does not support name_to_handle_at() 40899ca880aSopenharmony_ci * fall back to simpler logic. */ 40999ca880aSopenharmony_ci goto fallback_fdinfo; 41099ca880aSopenharmony_ci else if (errno == EOPNOTSUPP) 41199ca880aSopenharmony_ci /* This kernel or file system does not support 41299ca880aSopenharmony_ci * name_to_handle_at(), hence let's see if the 41399ca880aSopenharmony_ci * upper fs supports it (in which case it is a 41499ca880aSopenharmony_ci * mount point), otherwise fallback to the 41599ca880aSopenharmony_ci * traditional stat() logic */ 41699ca880aSopenharmony_ci nosupp = true; 41799ca880aSopenharmony_ci else 41899ca880aSopenharmony_ci return -errno; 41999ca880aSopenharmony_ci } 42099ca880aSopenharmony_ci 42199ca880aSopenharmony_ci r = name_to_handle_at(fd, "..", &h_parent.handle, &mount_id_parent, 0); 42299ca880aSopenharmony_ci if (r < 0) { 42399ca880aSopenharmony_ci if (errno == EOPNOTSUPP) { 42499ca880aSopenharmony_ci if (nosupp) 42599ca880aSopenharmony_ci /* Neither parent nor child do name_to_handle_at()? 42699ca880aSopenharmony_ci We have no choice but to fall back. */ 42799ca880aSopenharmony_ci goto fallback_fdinfo; 42899ca880aSopenharmony_ci else 42999ca880aSopenharmony_ci /* The parent can't do name_to_handle_at() but the 43099ca880aSopenharmony_ci * directory we are interested in can? 43199ca880aSopenharmony_ci * If so, it must be a mount point. */ 43299ca880aSopenharmony_ci return 1; 43399ca880aSopenharmony_ci } else 43499ca880aSopenharmony_ci return -errno; 43599ca880aSopenharmony_ci } 43699ca880aSopenharmony_ci 43799ca880aSopenharmony_ci /* The parent can do name_to_handle_at() but the 43899ca880aSopenharmony_ci * directory we are interested in can't? If so, it 43999ca880aSopenharmony_ci * must be a mount point. */ 44099ca880aSopenharmony_ci if (nosupp) 44199ca880aSopenharmony_ci return 1; 44299ca880aSopenharmony_ci 44399ca880aSopenharmony_ci /* If the file handle for the directory we are 44499ca880aSopenharmony_ci * interested in and its parent are identical, we 44599ca880aSopenharmony_ci * assume this is the root directory, which is a mount 44699ca880aSopenharmony_ci * point. */ 44799ca880aSopenharmony_ci 44899ca880aSopenharmony_ci if (h.handle.handle_bytes == h_parent.handle.handle_bytes && 44999ca880aSopenharmony_ci h.handle.handle_type == h_parent.handle.handle_type && 45099ca880aSopenharmony_ci memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0) 45199ca880aSopenharmony_ci return 1; 45299ca880aSopenharmony_ci 45399ca880aSopenharmony_ci return mount_id != mount_id_parent; 45499ca880aSopenharmony_ci 45599ca880aSopenharmony_cifallback_fdinfo: 45699ca880aSopenharmony_ci r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id); 45799ca880aSopenharmony_ci if (r == -EOPNOTSUPP) 45899ca880aSopenharmony_ci goto fallback_fstat; 45999ca880aSopenharmony_ci if (r < 0) 46099ca880aSopenharmony_ci return r; 46199ca880aSopenharmony_ci 46299ca880aSopenharmony_ci r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent); 46399ca880aSopenharmony_ci if (r < 0) 46499ca880aSopenharmony_ci return r; 46599ca880aSopenharmony_ci 46699ca880aSopenharmony_ci if (mount_id != mount_id_parent) 46799ca880aSopenharmony_ci return 1; 46899ca880aSopenharmony_ci 46999ca880aSopenharmony_ci /* Hmm, so, the mount ids are the same. This leaves one 47099ca880aSopenharmony_ci * special case though for the root file system. For that, 47199ca880aSopenharmony_ci * let's see if the parent directory has the same inode as we 47299ca880aSopenharmony_ci * are interested in. Hence, let's also do fstat() checks now, 47399ca880aSopenharmony_ci * too, but avoid the st_dev comparisons, since they aren't 47499ca880aSopenharmony_ci * that useful on unionfs mounts. */ 47599ca880aSopenharmony_ci check_st_dev = false; 47699ca880aSopenharmony_ci 47799ca880aSopenharmony_cifallback_fstat: 47899ca880aSopenharmony_ci if (fstatat(fd, "", &a, AT_EMPTY_PATH) < 0) 47999ca880aSopenharmony_ci return -errno; 48099ca880aSopenharmony_ci 48199ca880aSopenharmony_ci if (fstatat(fd, "..", &b, 0) < 0) 48299ca880aSopenharmony_ci return -errno; 48399ca880aSopenharmony_ci 48499ca880aSopenharmony_ci /* A directory with same device and inode as its parent? Must 48599ca880aSopenharmony_ci * be the root directory */ 48699ca880aSopenharmony_ci if (a.st_dev == b.st_dev && 48799ca880aSopenharmony_ci a.st_ino == b.st_ino) 48899ca880aSopenharmony_ci return 1; 48999ca880aSopenharmony_ci 49099ca880aSopenharmony_ci return check_st_dev && (a.st_dev != b.st_dev); 49199ca880aSopenharmony_ci} 49299ca880aSopenharmony_ci 49399ca880aSopenharmony_ciint path_is_mount_point(const char *t, bool allow_symlink) { 49499ca880aSopenharmony_ci _cleanup_close_ int fd = -1; 49599ca880aSopenharmony_ci 49699ca880aSopenharmony_ci assert(t); 49799ca880aSopenharmony_ci 49899ca880aSopenharmony_ci if (path_equal(t, "/")) 49999ca880aSopenharmony_ci return 1; 50099ca880aSopenharmony_ci 50199ca880aSopenharmony_ci fd = openat(AT_FDCWD, t, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|(allow_symlink ? 0 : O_PATH)); 50299ca880aSopenharmony_ci if (fd < 0) 50399ca880aSopenharmony_ci return -errno; 50499ca880aSopenharmony_ci 50599ca880aSopenharmony_ci return fd_is_mount_point(fd); 50699ca880aSopenharmony_ci} 50799ca880aSopenharmony_ci 50899ca880aSopenharmony_cibool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) { 50999ca880aSopenharmony_ci bool changed = false; 51099ca880aSopenharmony_ci const char* const* i; 51199ca880aSopenharmony_ci 51299ca880aSopenharmony_ci assert(timestamp); 51399ca880aSopenharmony_ci 51499ca880aSopenharmony_ci if (paths == NULL) 51599ca880aSopenharmony_ci return false; 51699ca880aSopenharmony_ci 51799ca880aSopenharmony_ci STRV_FOREACH(i, paths) { 51899ca880aSopenharmony_ci struct stat stats; 51999ca880aSopenharmony_ci usec_t u; 52099ca880aSopenharmony_ci 52199ca880aSopenharmony_ci if (stat(*i, &stats) < 0) 52299ca880aSopenharmony_ci continue; 52399ca880aSopenharmony_ci 52499ca880aSopenharmony_ci u = timespec_load(&stats.st_mtim); 52599ca880aSopenharmony_ci 52699ca880aSopenharmony_ci /* first check */ 52799ca880aSopenharmony_ci if (*timestamp >= u) 52899ca880aSopenharmony_ci continue; 52999ca880aSopenharmony_ci 53099ca880aSopenharmony_ci log_debug("timestamp of '%s' changed", *i); 53199ca880aSopenharmony_ci 53299ca880aSopenharmony_ci /* update timestamp */ 53399ca880aSopenharmony_ci if (update) { 53499ca880aSopenharmony_ci *timestamp = u; 53599ca880aSopenharmony_ci changed = true; 53699ca880aSopenharmony_ci } else 53799ca880aSopenharmony_ci return true; 53899ca880aSopenharmony_ci } 53999ca880aSopenharmony_ci 54099ca880aSopenharmony_ci return changed; 54199ca880aSopenharmony_ci} 54299ca880aSopenharmony_ci 54399ca880aSopenharmony_cichar *prefix_root(const char *root, const char *path) { 54499ca880aSopenharmony_ci char *n, *p; 54599ca880aSopenharmony_ci size_t l; 54699ca880aSopenharmony_ci 54799ca880aSopenharmony_ci /* If root is passed, prefixes path with it. Otherwise returns 54899ca880aSopenharmony_ci * it as is. */ 54999ca880aSopenharmony_ci 55099ca880aSopenharmony_ci assert(path); 55199ca880aSopenharmony_ci 55299ca880aSopenharmony_ci /* First, drop duplicate prefixing slashes from the path */ 55399ca880aSopenharmony_ci while (path[0] == '/' && path[1] == '/') 55499ca880aSopenharmony_ci path++; 55599ca880aSopenharmony_ci 55699ca880aSopenharmony_ci if (isempty(root) || path_equal(root, "/")) 55799ca880aSopenharmony_ci return strdup(path); 55899ca880aSopenharmony_ci 55999ca880aSopenharmony_ci l = strlen(root) + 1 + strlen(path) + 1; 56099ca880aSopenharmony_ci 56199ca880aSopenharmony_ci n = new(char, l); 56299ca880aSopenharmony_ci if (!n) 56399ca880aSopenharmony_ci return NULL; 56499ca880aSopenharmony_ci 56599ca880aSopenharmony_ci p = stpcpy(n, root); 56699ca880aSopenharmony_ci 56799ca880aSopenharmony_ci while (p > n && p[-1] == '/') 56899ca880aSopenharmony_ci p--; 56999ca880aSopenharmony_ci 57099ca880aSopenharmony_ci if (path[0] != '/') 57199ca880aSopenharmony_ci *(p++) = '/'; 57299ca880aSopenharmony_ci 57399ca880aSopenharmony_ci strcpy(p, path); 57499ca880aSopenharmony_ci return n; 57599ca880aSopenharmony_ci} 576