util: make path_is_mount_point() recognize bind mounts, too
[systemd_ALT/systemd_imz.git] / src / shared / path-util.c
blob2bdbd22dc71becce8484dec92fb81dc8326e68ef
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
3 /***
4 This file is part of systemd.
6 Copyright 2010-2012 Lennart Poettering
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.
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.
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 ***/
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>
33 #include "macro.h"
34 #include "util.h"
35 #include "log.h"
36 #include "strv.h"
37 #include "path-util.h"
39 bool path_is_absolute(const char *p) {
40 return p[0] == '/';
43 bool is_path(const char *p) {
44 return !!strchr(p, '/');
47 char *path_get_file_name(const char *p) {
48 char *r;
50 assert(p);
52 if ((r = strrchr(p, '/')))
53 return r + 1;
55 return (char*) p;
58 int path_get_parent(const char *path, char **_r) {
59 const char *e, *a = NULL, *b = NULL, *p;
60 char *r;
61 bool slash = false;
63 assert(path);
64 assert(_r);
66 if (!*path)
67 return -EINVAL;
69 for (e = path; *e; e++) {
71 if (!slash && *e == '/') {
72 a = b;
73 b = e;
74 slash = true;
75 } else if (slash && *e != '/')
76 slash = false;
79 if (*(e-1) == '/')
80 p = a;
81 else
82 p = b;
84 if (!p)
85 return -EINVAL;
87 if (p == path)
88 r = strdup("/");
89 else
90 r = strndup(path, p-path);
92 if (!r)
93 return -ENOMEM;
95 *_r = r;
96 return 0;
99 char **path_split_and_make_absolute(const char *p) {
100 char **l;
101 assert(p);
103 if (!(l = strv_split(p, ":")))
104 return NULL;
106 if (!path_strv_make_absolute_cwd(l)) {
107 strv_free(l);
108 return NULL;
111 return l;
114 char *path_make_absolute(const char *p, const char *prefix) {
115 assert(p);
117 /* Makes every item in the list an absolute path by prepending
118 * the prefix, if specified and necessary */
120 if (path_is_absolute(p) || !prefix)
121 return strdup(p);
123 return strjoin(prefix, "/", p, NULL);
126 char *path_make_absolute_cwd(const char *p) {
127 char *cwd, *r;
129 assert(p);
131 /* Similar to path_make_absolute(), but prefixes with the
132 * current working directory. */
134 if (path_is_absolute(p))
135 return strdup(p);
137 if (!(cwd = get_current_dir_name()))
138 return NULL;
140 r = path_make_absolute(p, cwd);
141 free(cwd);
143 return r;
146 char **path_strv_make_absolute_cwd(char **l) {
147 char **s;
149 /* Goes through every item in the string list and makes it
150 * absolute. This works in place and won't rollback any
151 * changes on failure. */
153 STRV_FOREACH(s, l) {
154 char *t;
156 if (!(t = path_make_absolute_cwd(*s)))
157 return NULL;
159 free(*s);
160 *s = t;
163 return l;
166 char **path_strv_canonicalize(char **l) {
167 char **s;
168 unsigned k = 0;
169 bool enomem = false;
171 if (strv_isempty(l))
172 return l;
174 /* Goes through every item in the string list and canonicalize
175 * the path. This works in place and won't rollback any
176 * changes on failure. */
178 STRV_FOREACH(s, l) {
179 char *t, *u;
181 t = path_make_absolute_cwd(*s);
182 free(*s);
184 if (!t) {
185 enomem = true;
186 continue;
189 errno = 0;
190 u = canonicalize_file_name(t);
191 free(t);
193 if (!u) {
194 if (errno == ENOMEM || !errno)
195 enomem = true;
197 continue;
200 l[k++] = u;
203 l[k] = NULL;
205 if (enomem)
206 return NULL;
208 return l;
211 char **path_strv_remove_empty(char **l) {
212 char **f, **t;
214 if (!l)
215 return NULL;
217 for (f = t = l; *f; f++) {
219 if (dir_is_empty(*f) > 0) {
220 free(*f);
221 continue;
224 *(t++) = *f;
227 *t = NULL;
228 return l;
231 char *path_kill_slashes(char *path) {
232 char *f, *t;
233 bool slash = false;
235 /* Removes redundant inner and trailing slashes. Modifies the
236 * passed string in-place.
238 * ///foo///bar/ becomes /foo/bar
241 for (f = path, t = path; *f; f++) {
243 if (*f == '/') {
244 slash = true;
245 continue;
248 if (slash) {
249 slash = false;
250 *(t++) = '/';
253 *(t++) = *f;
256 /* Special rule, if we are talking of the root directory, a
257 trailing slash is good */
259 if (t == path && slash)
260 *(t++) = '/';
262 *t = 0;
263 return path;
266 bool path_startswith(const char *path, const char *prefix) {
267 assert(path);
268 assert(prefix);
270 if ((path[0] == '/') != (prefix[0] == '/'))
271 return false;
273 for (;;) {
274 size_t a, b;
276 path += strspn(path, "/");
277 prefix += strspn(prefix, "/");
279 if (*prefix == 0)
280 return true;
282 if (*path == 0)
283 return false;
285 a = strcspn(path, "/");
286 b = strcspn(prefix, "/");
288 if (a != b)
289 return false;
291 if (memcmp(path, prefix, a) != 0)
292 return false;
294 path += a;
295 prefix += b;
299 bool path_equal(const char *a, const char *b) {
300 assert(a);
301 assert(b);
303 if ((a[0] == '/') != (b[0] == '/'))
304 return false;
306 for (;;) {
307 size_t j, k;
309 a += strspn(a, "/");
310 b += strspn(b, "/");
312 if (*a == 0 && *b == 0)
313 return true;
315 if (*a == 0 || *b == 0)
316 return false;
318 j = strcspn(a, "/");
319 k = strcspn(b, "/");
321 if (j != k)
322 return false;
324 if (memcmp(a, b, j) != 0)
325 return false;
327 a += j;
328 b += k;
332 int path_is_mount_point(const char *t, bool allow_symlink) {
333 char *parent;
334 int r;
335 struct file_handle *h;
336 int mount_id, mount_id_parent;
338 /* We are not actually interested in the file handles, but
339 * name_to_handle_at() also passes us the mount ID, hence use
340 * it but throw the handle away */
342 if (path_equal(t, "/"))
343 return 1;
345 h = alloca(MAX_HANDLE_SZ);
346 h->handle_bytes = MAX_HANDLE_SZ;
348 r = name_to_handle_at(AT_FDCWD, t, h, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
349 if (r < 0) {
350 if (errno == ENOENT)
351 return 0;
353 return -errno;
356 r = path_get_parent(t, &parent);
357 if (r < 0)
358 return r;
360 h->handle_bytes = MAX_HANDLE_SZ;
361 r = name_to_handle_at(AT_FDCWD, parent, h, &mount_id_parent, 0);
362 free(parent);
364 if (r < 0)
365 return -errno;
368 return mount_id != mount_id_parent;
371 int path_is_read_only_fs(const char *path) {
372 struct statvfs st;
374 assert(path);
376 if (statvfs(path, &st) < 0)
377 return -errno;
379 return !!(st.f_flag & ST_RDONLY);