chiark / gitweb /
dev-setup: allow a path prefix for use in chroots
[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
39 bool path_is_absolute(const char *p) {
40         return p[0] == '/';
41 }
42
43 bool is_path(const char *p) {
44         return !!strchr(p, '/');
45 }
46
47 char *path_get_file_name(const char *p) {
48         char *r;
49
50         assert(p);
51
52         if ((r = strrchr(p, '/')))
53                 return r + 1;
54
55         return (char*) p;
56 }
57
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;
62
63         assert(path);
64         assert(_r);
65
66         if (!*path)
67                 return -EINVAL;
68
69         for (e = path; *e; e++) {
70
71                 if (!slash && *e == '/') {
72                         a = b;
73                         b = e;
74                         slash = true;
75                 } else if (slash && *e != '/')
76                         slash = false;
77         }
78
79         if (*(e-1) == '/')
80                 p = a;
81         else
82                 p = b;
83
84         if (!p)
85                 return -EINVAL;
86
87         if (p == path)
88                 r = strdup("/");
89         else
90                 r = strndup(path, p-path);
91
92         if (!r)
93                 return -ENOMEM;
94
95         *_r = r;
96         return 0;
97 }
98
99 char **path_split_and_make_absolute(const char *p) {
100         char **l;
101         assert(p);
102
103         if (!(l = strv_split(p, ":")))
104                 return NULL;
105
106         if (!path_strv_make_absolute_cwd(l)) {
107                 strv_free(l);
108                 return NULL;
109         }
110
111         return l;
112 }
113
114 char *path_make_absolute(const char *p, const char *prefix) {
115         assert(p);
116
117         /* Makes every item in the list an absolute path by prepending
118          * the prefix, if specified and necessary */
119
120         if (path_is_absolute(p) || !prefix)
121                 return strdup(p);
122
123         return strjoin(prefix, "/", p, NULL);
124 }
125
126 char *path_make_absolute_cwd(const char *p) {
127         char *cwd, *r;
128
129         assert(p);
130
131         /* Similar to path_make_absolute(), but prefixes with the
132          * current working directory. */
133
134         if (path_is_absolute(p))
135                 return strdup(p);
136
137         if (!(cwd = get_current_dir_name()))
138                 return NULL;
139
140         r = path_make_absolute(p, cwd);
141         free(cwd);
142
143         return r;
144 }
145
146 char **path_strv_make_absolute_cwd(char **l) {
147         char **s;
148
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. */
152
153         STRV_FOREACH(s, l) {
154                 char *t;
155
156                 if (!(t = path_make_absolute_cwd(*s)))
157                         return NULL;
158
159                 free(*s);
160                 *s = t;
161         }
162
163         return l;
164 }
165
166 char **path_strv_canonicalize(char **l) {
167         char **s;
168         unsigned k = 0;
169         bool enomem = false;
170
171         if (strv_isempty(l))
172                 return l;
173
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. */
177
178         STRV_FOREACH(s, l) {
179                 char *t, *u;
180
181                 t = path_make_absolute_cwd(*s);
182                 free(*s);
183
184                 if (!t) {
185                         enomem = true;
186                         continue;
187                 }
188
189                 errno = 0;
190                 u = canonicalize_file_name(t);
191                 free(t);
192
193                 if (!u) {
194                         if (errno == ENOMEM || !errno)
195                                 enomem = true;
196
197                         continue;
198                 }
199
200                 l[k++] = u;
201         }
202
203         l[k] = NULL;
204
205         if (enomem)
206                 return NULL;
207
208         return l;
209 }
210
211 char **path_strv_remove_empty(char **l) {
212         char **f, **t;
213
214         if (!l)
215                 return NULL;
216
217         for (f = t = l; *f; f++) {
218
219                 if (dir_is_empty(*f) > 0) {
220                         free(*f);
221                         continue;
222                 }
223
224                 *(t++) = *f;
225         }
226
227         *t = NULL;
228         return l;
229 }
230
231 char *path_kill_slashes(char *path) {
232         char *f, *t;
233         bool slash = false;
234
235         /* Removes redundant inner and trailing slashes. Modifies the
236          * passed string in-place.
237          *
238          * ///foo///bar/ becomes /foo/bar
239          */
240
241         for (f = path, t = path; *f; f++) {
242
243                 if (*f == '/') {
244                         slash = true;
245                         continue;
246                 }
247
248                 if (slash) {
249                         slash = false;
250                         *(t++) = '/';
251                 }
252
253                 *(t++) = *f;
254         }
255
256         /* Special rule, if we are talking of the root directory, a
257         trailing slash is good */
258
259         if (t == path && slash)
260                 *(t++) = '/';
261
262         *t = 0;
263         return path;
264 }
265
266 bool path_startswith(const char *path, const char *prefix) {
267         assert(path);
268         assert(prefix);
269
270         if ((path[0] == '/') != (prefix[0] == '/'))
271                 return false;
272
273         for (;;) {
274                 size_t a, b;
275
276                 path += strspn(path, "/");
277                 prefix += strspn(prefix, "/");
278
279                 if (*prefix == 0)
280                         return true;
281
282                 if (*path == 0)
283                         return false;
284
285                 a = strcspn(path, "/");
286                 b = strcspn(prefix, "/");
287
288                 if (a != b)
289                         return false;
290
291                 if (memcmp(path, prefix, a) != 0)
292                         return false;
293
294                 path += a;
295                 prefix += b;
296         }
297 }
298
299 bool path_equal(const char *a, const char *b) {
300         assert(a);
301         assert(b);
302
303         if ((a[0] == '/') != (b[0] == '/'))
304                 return false;
305
306         for (;;) {
307                 size_t j, k;
308
309                 a += strspn(a, "/");
310                 b += strspn(b, "/");
311
312                 if (*a == 0 && *b == 0)
313                         return true;
314
315                 if (*a == 0 || *b == 0)
316                         return false;
317
318                 j = strcspn(a, "/");
319                 k = strcspn(b, "/");
320
321                 if (j != k)
322                         return false;
323
324                 if (memcmp(a, b, j) != 0)
325                         return false;
326
327                 a += j;
328                 b += k;
329         }
330 }
331
332 int path_is_mount_point(const char *t, bool allow_symlink) {
333         struct stat a, b;
334         char *parent;
335         int r;
336
337         if (allow_symlink)
338                 r = stat(t, &a);
339         else
340                 r = lstat(t, &a);
341
342         if (r < 0) {
343                 if (errno == ENOENT)
344                         return 0;
345
346                 return -errno;
347         }
348
349         r = path_get_parent(t, &parent);
350         if (r < 0)
351                 return r;
352
353         r = lstat(parent, &b);
354         free(parent);
355
356         if (r < 0)
357                 return -errno;
358
359         return a.st_dev != b.st_dev;
360 }
361
362 int path_is_read_only_fs(const char *path) {
363         struct statvfs st;
364
365         assert(path);
366
367         if (statvfs(path, &st) < 0)
368                 return -errno;
369
370         return !!(st.f_flag & ST_RDONLY);
371 }