chiark / gitweb /
systemd: do not remove empty paths from unit lookup path
[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 #include "missing.h"
39
40 bool path_is_absolute(const char *p) {
41         return p[0] == '/';
42 }
43
44 bool is_path(const char *p) {
45         return !!strchr(p, '/');
46 }
47
48 char *path_get_file_name(const char *p) {
49         char *r;
50
51         assert(p);
52
53         if ((r = strrchr(p, '/')))
54                 return r + 1;
55
56         return (char*) p;
57 }
58
59 int path_get_parent(const char *path, char **_r) {
60         const char *e, *a = NULL, *b = NULL, *p;
61         char *r;
62         bool slash = false;
63
64         assert(path);
65         assert(_r);
66
67         if (!*path)
68                 return -EINVAL;
69
70         for (e = path; *e; e++) {
71
72                 if (!slash && *e == '/') {
73                         a = b;
74                         b = e;
75                         slash = true;
76                 } else if (slash && *e != '/')
77                         slash = false;
78         }
79
80         if (*(e-1) == '/')
81                 p = a;
82         else
83                 p = b;
84
85         if (!p)
86                 return -EINVAL;
87
88         if (p == path)
89                 r = strdup("/");
90         else
91                 r = strndup(path, p-path);
92
93         if (!r)
94                 return -ENOMEM;
95
96         *_r = r;
97         return 0;
98 }
99
100 char **path_split_and_make_absolute(const char *p) {
101         char **l;
102         assert(p);
103
104         if (!(l = strv_split(p, ":")))
105                 return NULL;
106
107         if (!path_strv_make_absolute_cwd(l)) {
108                 strv_free(l);
109                 return NULL;
110         }
111
112         return l;
113 }
114
115 char *path_make_absolute(const char *p, const char *prefix) {
116         assert(p);
117
118         /* Makes every item in the list an absolute path by prepending
119          * the prefix, if specified and necessary */
120
121         if (path_is_absolute(p) || !prefix)
122                 return strdup(p);
123
124         return strjoin(prefix, "/", p, NULL);
125 }
126
127 char *path_make_absolute_cwd(const char *p) {
128         char *cwd, *r;
129
130         assert(p);
131
132         /* Similar to path_make_absolute(), but prefixes with the
133          * current working directory. */
134
135         if (path_is_absolute(p))
136                 return strdup(p);
137
138         if (!(cwd = get_current_dir_name()))
139                 return NULL;
140
141         r = path_make_absolute(p, cwd);
142         free(cwd);
143
144         return r;
145 }
146
147 char **path_strv_make_absolute_cwd(char **l) {
148         char **s;
149
150         /* Goes through every item in the string list and makes it
151          * absolute. This works in place and won't rollback any
152          * changes on failure. */
153
154         STRV_FOREACH(s, l) {
155                 char *t;
156
157                 if (!(t = path_make_absolute_cwd(*s)))
158                         return NULL;
159
160                 free(*s);
161                 *s = t;
162         }
163
164         return l;
165 }
166
167 char **path_strv_canonicalize(char **l) {
168         char **s;
169         unsigned k = 0;
170         bool enomem = false;
171
172         if (strv_isempty(l))
173                 return l;
174
175         /* Goes through every item in the string list and canonicalize
176          * the path. This works in place and won't rollback any
177          * changes on failure. */
178
179         STRV_FOREACH(s, l) {
180                 char *t, *u;
181
182                 t = path_make_absolute_cwd(*s);
183                 free(*s);
184                 *s = NULL;
185
186                 if (!t) {
187                         enomem = true;
188                         continue;
189                 }
190
191                 errno = 0;
192                 u = canonicalize_file_name(t);
193
194                 if (!u) {
195                         if (errno == ENOENT)
196                                 u = t;
197                         else {
198                                 free(t);
199                                 if (errno == ENOMEM || !errno)
200                                         enomem = true;
201
202                                 continue;
203                         }
204                 }
205
206                 l[k++] = u;
207         }
208
209         l[k] = NULL;
210
211         if (enomem)
212                 return NULL;
213
214         return l;
215 }
216
217 char **path_strv_remove_empty(char **l) {
218         char **f, **t;
219
220         if (!l)
221                 return NULL;
222
223         for (f = t = l; *f; f++) {
224
225                 if (dir_is_empty(*f) > 0) {
226                         free(*f);
227                         continue;
228                 }
229
230                 *(t++) = *f;
231         }
232
233         *t = NULL;
234         return l;
235 }
236
237 char *path_kill_slashes(char *path) {
238         char *f, *t;
239         bool slash = false;
240
241         /* Removes redundant inner and trailing slashes. Modifies the
242          * passed string in-place.
243          *
244          * ///foo///bar/ becomes /foo/bar
245          */
246
247         for (f = path, t = path; *f; f++) {
248
249                 if (*f == '/') {
250                         slash = true;
251                         continue;
252                 }
253
254                 if (slash) {
255                         slash = false;
256                         *(t++) = '/';
257                 }
258
259                 *(t++) = *f;
260         }
261
262         /* Special rule, if we are talking of the root directory, a
263         trailing slash is good */
264
265         if (t == path && slash)
266                 *(t++) = '/';
267
268         *t = 0;
269         return path;
270 }
271
272 char* path_startswith(const char *path, const char *prefix) {
273         assert(path);
274         assert(prefix);
275
276         if ((path[0] == '/') != (prefix[0] == '/'))
277                 return NULL;
278
279         for (;;) {
280                 size_t a, b;
281
282                 path += strspn(path, "/");
283                 prefix += strspn(prefix, "/");
284
285                 if (*prefix == 0)
286                         return (char*) path;
287
288                 if (*path == 0)
289                         return NULL;
290
291                 a = strcspn(path, "/");
292                 b = strcspn(prefix, "/");
293
294                 if (a != b)
295                         return NULL;
296
297                 if (memcmp(path, prefix, a) != 0)
298                         return NULL;
299
300                 path += a;
301                 prefix += b;
302         }
303 }
304
305 bool path_equal(const char *a, const char *b) {
306         assert(a);
307         assert(b);
308
309         if ((a[0] == '/') != (b[0] == '/'))
310                 return false;
311
312         for (;;) {
313                 size_t j, k;
314
315                 a += strspn(a, "/");
316                 b += strspn(b, "/");
317
318                 if (*a == 0 && *b == 0)
319                         return true;
320
321                 if (*a == 0 || *b == 0)
322                         return false;
323
324                 j = strcspn(a, "/");
325                 k = strcspn(b, "/");
326
327                 if (j != k)
328                         return false;
329
330                 if (memcmp(a, b, j) != 0)
331                         return false;
332
333                 a += j;
334                 b += k;
335         }
336 }
337
338 int path_is_mount_point(const char *t, bool allow_symlink) {
339         char *parent;
340         int r;
341         struct file_handle *h;
342         int mount_id, mount_id_parent;
343         struct stat a, b;
344
345         /* We are not actually interested in the file handles, but
346          * name_to_handle_at() also passes us the mount ID, hence use
347          * it but throw the handle away */
348
349         if (path_equal(t, "/"))
350                 return 1;
351
352         h = alloca(MAX_HANDLE_SZ);
353         h->handle_bytes = MAX_HANDLE_SZ;
354
355         r = name_to_handle_at(AT_FDCWD, t, h, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
356         if (r < 0) {
357                 if (errno == ENOSYS || errno == ENOTSUP)
358                         /* This kernel or file system does not support
359                          * name_to_handle_at(), hence fallback to the
360                          * traditional stat() logic */
361                         goto fallback;
362
363                 if (errno == ENOENT)
364                         return 0;
365
366                 return -errno;
367         }
368
369         r = path_get_parent(t, &parent);
370         if (r < 0)
371                 return r;
372
373         h->handle_bytes = MAX_HANDLE_SZ;
374         r = name_to_handle_at(AT_FDCWD, parent, h, &mount_id_parent, 0);
375         free(parent);
376
377         if (r < 0) {
378                 /* The parent can't do name_to_handle_at() but the
379                  * directory we are interested in can? If so, it must
380                  * be a mount point */
381                 if (errno == ENOTSUP)
382                         return 1;
383
384                 return -errno;
385         }
386
387         return mount_id != mount_id_parent;
388
389 fallback:
390         if (allow_symlink)
391                 r = stat(t, &a);
392         else
393                 r = lstat(t, &a);
394
395         if (r < 0) {
396                 if (errno == ENOENT)
397                         return 0;
398
399                 return -errno;
400         }
401
402         r = path_get_parent(t, &parent);
403         if (r < 0)
404                 return r;
405
406         r = lstat(parent, &b);
407         free(parent);
408
409         if (r < 0)
410                 return -errno;
411
412         return a.st_dev != b.st_dev;
413 }
414
415 int path_is_read_only_fs(const char *path) {
416         struct statvfs st;
417
418         assert(path);
419
420         if (statvfs(path, &st) < 0)
421                 return -errno;
422
423         return !!(st.f_flag & ST_RDONLY);
424 }