chiark / gitweb /
dd12d3d63409654fb446028ba92a0860bc83e4c1
[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                 free(t);
194
195                 if (!u) {
196                         if (errno == ENOMEM || !errno)
197                                 enomem = true;
198
199                         continue;
200                 }
201
202                 l[k++] = u;
203         }
204
205         l[k] = NULL;
206
207         if (enomem)
208                 return NULL;
209
210         return l;
211 }
212
213 char **path_strv_remove_empty(char **l) {
214         char **f, **t;
215
216         if (!l)
217                 return NULL;
218
219         for (f = t = l; *f; f++) {
220
221                 if (dir_is_empty(*f) > 0) {
222                         free(*f);
223                         continue;
224                 }
225
226                 *(t++) = *f;
227         }
228
229         *t = NULL;
230         return l;
231 }
232
233 char *path_kill_slashes(char *path) {
234         char *f, *t;
235         bool slash = false;
236
237         /* Removes redundant inner and trailing slashes. Modifies the
238          * passed string in-place.
239          *
240          * ///foo///bar/ becomes /foo/bar
241          */
242
243         for (f = path, t = path; *f; f++) {
244
245                 if (*f == '/') {
246                         slash = true;
247                         continue;
248                 }
249
250                 if (slash) {
251                         slash = false;
252                         *(t++) = '/';
253                 }
254
255                 *(t++) = *f;
256         }
257
258         /* Special rule, if we are talking of the root directory, a
259         trailing slash is good */
260
261         if (t == path && slash)
262                 *(t++) = '/';
263
264         *t = 0;
265         return path;
266 }
267
268 char* path_startswith(const char *path, const char *prefix) {
269         assert(path);
270         assert(prefix);
271
272         if ((path[0] == '/') != (prefix[0] == '/'))
273                 return NULL;
274
275         for (;;) {
276                 size_t a, b;
277
278                 path += strspn(path, "/");
279                 prefix += strspn(prefix, "/");
280
281                 if (*prefix == 0)
282                         return (char*) path;
283
284                 if (*path == 0)
285                         return NULL;
286
287                 a = strcspn(path, "/");
288                 b = strcspn(prefix, "/");
289
290                 if (a != b)
291                         return NULL;
292
293                 if (memcmp(path, prefix, a) != 0)
294                         return NULL;
295
296                 path += a;
297                 prefix += b;
298         }
299 }
300
301 bool path_equal(const char *a, const char *b) {
302         assert(a);
303         assert(b);
304
305         if ((a[0] == '/') != (b[0] == '/'))
306                 return false;
307
308         for (;;) {
309                 size_t j, k;
310
311                 a += strspn(a, "/");
312                 b += strspn(b, "/");
313
314                 if (*a == 0 && *b == 0)
315                         return true;
316
317                 if (*a == 0 || *b == 0)
318                         return false;
319
320                 j = strcspn(a, "/");
321                 k = strcspn(b, "/");
322
323                 if (j != k)
324                         return false;
325
326                 if (memcmp(a, b, j) != 0)
327                         return false;
328
329                 a += j;
330                 b += k;
331         }
332 }
333
334 int path_is_mount_point(const char *t, bool allow_symlink) {
335         char *parent;
336         int r;
337         struct file_handle *h;
338         int mount_id, mount_id_parent;
339         struct stat a, b;
340
341         /* We are not actually interested in the file handles, but
342          * name_to_handle_at() also passes us the mount ID, hence use
343          * it but throw the handle away */
344
345         if (path_equal(t, "/"))
346                 return 1;
347
348         h = alloca(MAX_HANDLE_SZ);
349         h->handle_bytes = MAX_HANDLE_SZ;
350
351         r = name_to_handle_at(AT_FDCWD, t, h, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
352         if (r < 0) {
353                 if (errno == ENOSYS || errno == ENOTSUP)
354                         /* This kernel or file system does not support
355                          * name_to_handle_at(), hence fallback to the
356                          * traditional stat() logic */
357                         goto fallback;
358
359                 if (errno == ENOENT)
360                         return 0;
361
362                 return -errno;
363         }
364
365         r = path_get_parent(t, &parent);
366         if (r < 0)
367                 return r;
368
369         h->handle_bytes = MAX_HANDLE_SZ;
370         r = name_to_handle_at(AT_FDCWD, parent, h, &mount_id_parent, 0);
371         free(parent);
372
373         if (r < 0) {
374                 /* The parent can't do name_to_handle_at() but the
375                  * directory we are interested in can? If so, it must
376                  * be a mount point */
377                 if (errno == ENOTSUP)
378                         return 1;
379
380                 return -errno;
381         }
382
383         return mount_id != mount_id_parent;
384
385 fallback:
386         if (allow_symlink)
387                 r = stat(t, &a);
388         else
389                 r = lstat(t, &a);
390
391         if (r < 0) {
392                 if (errno == ENOENT)
393                         return 0;
394
395                 return -errno;
396         }
397
398         r = path_get_parent(t, &parent);
399         if (r < 0)
400                 return r;
401
402         r = lstat(parent, &b);
403         free(parent);
404
405         if (r < 0)
406                 return -errno;
407
408         return a.st_dev != b.st_dev;
409 }
410
411 int path_is_read_only_fs(const char *path) {
412         struct statvfs st;
413
414         assert(path);
415
416         if (statvfs(path, &st) < 0)
417                 return -errno;
418
419         return !!(st.f_flag & ST_RDONLY);
420 }