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