]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/path-util.c
binfmt,tmpfiles,modules-load,sysctl: rework the various early-boot services that...
[thirdparty/systemd.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 cwd = get_current_dir_name();
139 if (!cwd)
140 return NULL;
141
142 r = path_make_absolute(p, cwd);
143 free(cwd);
144
145 return r;
146 }
147
148 char **path_strv_make_absolute_cwd(char **l) {
149 char **s;
150
151 /* Goes through every item in the string list and makes it
152 * absolute. This works in place and won't rollback any
153 * changes on failure. */
154
155 STRV_FOREACH(s, l) {
156 char *t;
157
158 if (!(t = path_make_absolute_cwd(*s)))
159 return NULL;
160
161 free(*s);
162 *s = t;
163 }
164
165 return l;
166 }
167
168 char **path_strv_canonicalize(char **l) {
169 char **s;
170 unsigned k = 0;
171 bool enomem = false;
172
173 if (strv_isempty(l))
174 return l;
175
176 /* Goes through every item in the string list and canonicalize
177 * the path. This works in place and won't rollback any
178 * changes on failure. */
179
180 STRV_FOREACH(s, l) {
181 char *t, *u;
182
183 t = path_make_absolute_cwd(*s);
184 free(*s);
185 *s = NULL;
186
187 if (!t) {
188 enomem = true;
189 continue;
190 }
191
192 errno = 0;
193 u = canonicalize_file_name(t);
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 } else
205 free(t);
206
207 l[k++] = u;
208 }
209
210 l[k] = NULL;
211
212 if (enomem)
213 return NULL;
214
215 return l;
216 }
217
218 char **path_strv_canonicalize_uniq(char **l) {
219 if (strv_isempty(l))
220 return l;
221
222 if (!path_strv_canonicalize(l))
223 return NULL;
224
225 return strv_uniq(l);
226 }
227
228 char *path_kill_slashes(char *path) {
229 char *f, *t;
230 bool slash = false;
231
232 /* Removes redundant inner and trailing slashes. Modifies the
233 * passed string in-place.
234 *
235 * ///foo///bar/ becomes /foo/bar
236 */
237
238 for (f = path, t = path; *f; f++) {
239
240 if (*f == '/') {
241 slash = true;
242 continue;
243 }
244
245 if (slash) {
246 slash = false;
247 *(t++) = '/';
248 }
249
250 *(t++) = *f;
251 }
252
253 /* Special rule, if we are talking of the root directory, a
254 trailing slash is good */
255
256 if (t == path && slash)
257 *(t++) = '/';
258
259 *t = 0;
260 return path;
261 }
262
263 char* path_startswith(const char *path, const char *prefix) {
264 assert(path);
265 assert(prefix);
266
267 if ((path[0] == '/') != (prefix[0] == '/'))
268 return NULL;
269
270 for (;;) {
271 size_t a, b;
272
273 path += strspn(path, "/");
274 prefix += strspn(prefix, "/");
275
276 if (*prefix == 0)
277 return (char*) path;
278
279 if (*path == 0)
280 return NULL;
281
282 a = strcspn(path, "/");
283 b = strcspn(prefix, "/");
284
285 if (a != b)
286 return NULL;
287
288 if (memcmp(path, prefix, a) != 0)
289 return NULL;
290
291 path += a;
292 prefix += b;
293 }
294 }
295
296 bool path_equal(const char *a, const char *b) {
297 assert(a);
298 assert(b);
299
300 if ((a[0] == '/') != (b[0] == '/'))
301 return false;
302
303 for (;;) {
304 size_t j, k;
305
306 a += strspn(a, "/");
307 b += strspn(b, "/");
308
309 if (*a == 0 && *b == 0)
310 return true;
311
312 if (*a == 0 || *b == 0)
313 return false;
314
315 j = strcspn(a, "/");
316 k = strcspn(b, "/");
317
318 if (j != k)
319 return false;
320
321 if (memcmp(a, b, j) != 0)
322 return false;
323
324 a += j;
325 b += k;
326 }
327 }
328
329 int path_is_mount_point(const char *t, bool allow_symlink) {
330 char *parent;
331 int r;
332 struct file_handle *h;
333 int mount_id, mount_id_parent;
334 struct stat a, b;
335
336 /* We are not actually interested in the file handles, but
337 * name_to_handle_at() also passes us the mount ID, hence use
338 * it but throw the handle away */
339
340 if (path_equal(t, "/"))
341 return 1;
342
343 h = alloca(MAX_HANDLE_SZ);
344 h->handle_bytes = MAX_HANDLE_SZ;
345
346 r = name_to_handle_at(AT_FDCWD, t, h, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
347 if (r < 0) {
348 if (errno == ENOSYS || errno == ENOTSUP)
349 /* This kernel or file system does not support
350 * name_to_handle_at(), hence fallback to the
351 * traditional stat() logic */
352 goto fallback;
353
354 if (errno == ENOENT)
355 return 0;
356
357 return -errno;
358 }
359
360 r = path_get_parent(t, &parent);
361 if (r < 0)
362 return r;
363
364 h->handle_bytes = MAX_HANDLE_SZ;
365 r = name_to_handle_at(AT_FDCWD, parent, h, &mount_id_parent, 0);
366 free(parent);
367
368 if (r < 0) {
369 /* The parent can't do name_to_handle_at() but the
370 * directory we are interested in can? If so, it must
371 * be a mount point */
372 if (errno == ENOTSUP)
373 return 1;
374
375 return -errno;
376 }
377
378 return mount_id != mount_id_parent;
379
380 fallback:
381 if (allow_symlink)
382 r = stat(t, &a);
383 else
384 r = lstat(t, &a);
385
386 if (r < 0) {
387 if (errno == ENOENT)
388 return 0;
389
390 return -errno;
391 }
392
393 r = path_get_parent(t, &parent);
394 if (r < 0)
395 return r;
396
397 r = lstat(parent, &b);
398 free(parent);
399
400 if (r < 0)
401 return -errno;
402
403 return a.st_dev != b.st_dev;
404 }
405
406 int path_is_read_only_fs(const char *path) {
407 struct statvfs st;
408
409 assert(path);
410
411 if (statvfs(path, &st) < 0)
412 return -errno;
413
414 return !!(st.f_flag & ST_RDONLY);
415 }