]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/path-util.c
core: properly validate environment data from Environment= lines in unit files
[thirdparty/systemd.git] / src / shared / path-util.c
CommitLineData
9eb977db
KS
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"
a8348796 38#include "missing.h"
9eb977db
KS
39
40bool path_is_absolute(const char *p) {
41 return p[0] == '/';
42}
43
44bool is_path(const char *p) {
45 return !!strchr(p, '/');
46}
47
48char *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
59int 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
100char **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
115char *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
b7def684 124 return strjoin(prefix, "/", p, NULL);
9eb977db
KS
125}
126
127char *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
91a6489d
LP
138 cwd = get_current_dir_name();
139 if (!cwd)
9eb977db
KS
140 return NULL;
141
142 r = path_make_absolute(p, cwd);
143 free(cwd);
144
145 return r;
146}
147
148char **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
168char **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);
c9c7aef2 185 *s = NULL;
9eb977db
KS
186
187 if (!t) {
188 enomem = true;
189 continue;
190 }
191
192 errno = 0;
193 u = canonicalize_file_name(t);
9eb977db 194 if (!u) {
874310b7
ZJS
195 if (errno == ENOENT)
196 u = t;
197 else {
198 free(t);
199 if (errno == ENOMEM || !errno)
200 enomem = true;
201
202 continue;
203 }
91a6489d
LP
204 } else
205 free(t);
9eb977db
KS
206
207 l[k++] = u;
208 }
209
210 l[k] = NULL;
211
212 if (enomem)
213 return NULL;
214
215 return l;
216}
217
9eb977db
KS
218char *path_kill_slashes(char *path) {
219 char *f, *t;
220 bool slash = false;
221
222 /* Removes redundant inner and trailing slashes. Modifies the
223 * passed string in-place.
224 *
225 * ///foo///bar/ becomes /foo/bar
226 */
227
228 for (f = path, t = path; *f; f++) {
229
230 if (*f == '/') {
231 slash = true;
232 continue;
233 }
234
235 if (slash) {
236 slash = false;
237 *(t++) = '/';
238 }
239
240 *(t++) = *f;
241 }
242
243 /* Special rule, if we are talking of the root directory, a
244 trailing slash is good */
245
246 if (t == path && slash)
247 *(t++) = '/';
248
249 *t = 0;
250 return path;
251}
252
424a19f8 253char* path_startswith(const char *path, const char *prefix) {
9eb977db
KS
254 assert(path);
255 assert(prefix);
256
257 if ((path[0] == '/') != (prefix[0] == '/'))
424a19f8 258 return NULL;
9eb977db
KS
259
260 for (;;) {
261 size_t a, b;
262
263 path += strspn(path, "/");
264 prefix += strspn(prefix, "/");
265
266 if (*prefix == 0)
424a19f8 267 return (char*) path;
9eb977db
KS
268
269 if (*path == 0)
424a19f8 270 return NULL;
9eb977db
KS
271
272 a = strcspn(path, "/");
273 b = strcspn(prefix, "/");
274
275 if (a != b)
424a19f8 276 return NULL;
9eb977db
KS
277
278 if (memcmp(path, prefix, a) != 0)
424a19f8 279 return NULL;
9eb977db
KS
280
281 path += a;
282 prefix += b;
283 }
284}
285
286bool path_equal(const char *a, const char *b) {
287 assert(a);
288 assert(b);
289
290 if ((a[0] == '/') != (b[0] == '/'))
291 return false;
292
293 for (;;) {
294 size_t j, k;
295
296 a += strspn(a, "/");
297 b += strspn(b, "/");
298
299 if (*a == 0 && *b == 0)
300 return true;
301
302 if (*a == 0 || *b == 0)
303 return false;
304
305 j = strcspn(a, "/");
306 k = strcspn(b, "/");
307
308 if (j != k)
309 return false;
310
311 if (memcmp(a, b, j) != 0)
312 return false;
313
314 a += j;
315 b += k;
316 }
317}
318
319int path_is_mount_point(const char *t, bool allow_symlink) {
9eb977db
KS
320 char *parent;
321 int r;
cde9cb34
LP
322 struct file_handle *h;
323 int mount_id, mount_id_parent;
1640a0b6 324 struct stat a, b;
9eb977db 325
cde9cb34
LP
326 /* We are not actually interested in the file handles, but
327 * name_to_handle_at() also passes us the mount ID, hence use
328 * it but throw the handle away */
329
330 if (path_equal(t, "/"))
331 return 1;
9eb977db 332
cde9cb34
LP
333 h = alloca(MAX_HANDLE_SZ);
334 h->handle_bytes = MAX_HANDLE_SZ;
335
336 r = name_to_handle_at(AT_FDCWD, t, h, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
9eb977db 337 if (r < 0) {
fa125f4e
MM
338 if (errno == ENOSYS || errno == ENOTSUP)
339 /* This kernel or file system does not support
1640a0b6
LP
340 * name_to_handle_at(), hence fallback to the
341 * traditional stat() logic */
342 goto fallback;
343
9eb977db
KS
344 if (errno == ENOENT)
345 return 0;
346
347 return -errno;
348 }
349
350 r = path_get_parent(t, &parent);
351 if (r < 0)
352 return r;
353
cde9cb34
LP
354 h->handle_bytes = MAX_HANDLE_SZ;
355 r = name_to_handle_at(AT_FDCWD, parent, h, &mount_id_parent, 0);
9eb977db
KS
356 free(parent);
357
1640a0b6
LP
358 if (r < 0) {
359 /* The parent can't do name_to_handle_at() but the
360 * directory we are interested in can? If so, it must
361 * be a mount point */
362 if (errno == ENOTSUP)
363 return 1;
364
365 return -errno;
366 }
367
368 return mount_id != mount_id_parent;
369
370fallback:
371 if (allow_symlink)
372 r = stat(t, &a);
373 else
f408b8f1 374 r = lstat(t, &a);
1640a0b6 375
8ac75493
MM
376 if (r < 0) {
377 if (errno == ENOENT)
378 return 0;
379
9eb977db 380 return -errno;
8ac75493 381 }
9eb977db 382
1640a0b6
LP
383 r = path_get_parent(t, &parent);
384 if (r < 0)
385 return r;
cde9cb34 386
1640a0b6
LP
387 r = lstat(parent, &b);
388 free(parent);
389
390 if (r < 0)
391 return -errno;
392
393 return a.st_dev != b.st_dev;
9eb977db
KS
394}
395
396int path_is_read_only_fs(const char *path) {
397 struct statvfs st;
398
399 assert(path);
400
401 if (statvfs(path, &st) < 0)
402 return -errno;
403
404 return !!(st.f_flag & ST_RDONLY);
405}