]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/path-util.c
util: split-out path-util.[ch]
[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"
38
39bool path_is_absolute(const char *p) {
40 return p[0] == '/';
41}
42
43bool is_path(const char *p) {
44 return !!strchr(p, '/');
45}
46
47char *path_get_file_name(const char *p) {
48 char *r;
49
50 assert(p);
51
52 if ((r = strrchr(p, '/')))
53 return r + 1;
54
55 return (char*) p;
56}
57
58int path_get_parent(const char *path, char **_r) {
59 const char *e, *a = NULL, *b = NULL, *p;
60 char *r;
61 bool slash = false;
62
63 assert(path);
64 assert(_r);
65
66 if (!*path)
67 return -EINVAL;
68
69 for (e = path; *e; e++) {
70
71 if (!slash && *e == '/') {
72 a = b;
73 b = e;
74 slash = true;
75 } else if (slash && *e != '/')
76 slash = false;
77 }
78
79 if (*(e-1) == '/')
80 p = a;
81 else
82 p = b;
83
84 if (!p)
85 return -EINVAL;
86
87 if (p == path)
88 r = strdup("/");
89 else
90 r = strndup(path, p-path);
91
92 if (!r)
93 return -ENOMEM;
94
95 *_r = r;
96 return 0;
97}
98
99char **path_split_and_make_absolute(const char *p) {
100 char **l;
101 assert(p);
102
103 if (!(l = strv_split(p, ":")))
104 return NULL;
105
106 if (!path_strv_make_absolute_cwd(l)) {
107 strv_free(l);
108 return NULL;
109 }
110
111 return l;
112}
113
114char *path_make_absolute(const char *p, const char *prefix) {
115 assert(p);
116
117 /* Makes every item in the list an absolute path by prepending
118 * the prefix, if specified and necessary */
119
120 if (path_is_absolute(p) || !prefix)
121 return strdup(p);
122
123 return join(prefix, "/", p, NULL);
124}
125
126char *path_make_absolute_cwd(const char *p) {
127 char *cwd, *r;
128
129 assert(p);
130
131 /* Similar to path_make_absolute(), but prefixes with the
132 * current working directory. */
133
134 if (path_is_absolute(p))
135 return strdup(p);
136
137 if (!(cwd = get_current_dir_name()))
138 return NULL;
139
140 r = path_make_absolute(p, cwd);
141 free(cwd);
142
143 return r;
144}
145
146char **path_strv_make_absolute_cwd(char **l) {
147 char **s;
148
149 /* Goes through every item in the string list and makes it
150 * absolute. This works in place and won't rollback any
151 * changes on failure. */
152
153 STRV_FOREACH(s, l) {
154 char *t;
155
156 if (!(t = path_make_absolute_cwd(*s)))
157 return NULL;
158
159 free(*s);
160 *s = t;
161 }
162
163 return l;
164}
165
166char **path_strv_canonicalize(char **l) {
167 char **s;
168 unsigned k = 0;
169 bool enomem = false;
170
171 if (strv_isempty(l))
172 return l;
173
174 /* Goes through every item in the string list and canonicalize
175 * the path. This works in place and won't rollback any
176 * changes on failure. */
177
178 STRV_FOREACH(s, l) {
179 char *t, *u;
180
181 t = path_make_absolute_cwd(*s);
182 free(*s);
183
184 if (!t) {
185 enomem = true;
186 continue;
187 }
188
189 errno = 0;
190 u = canonicalize_file_name(t);
191 free(t);
192
193 if (!u) {
194 if (errno == ENOMEM || !errno)
195 enomem = true;
196
197 continue;
198 }
199
200 l[k++] = u;
201 }
202
203 l[k] = NULL;
204
205 if (enomem)
206 return NULL;
207
208 return l;
209}
210
211char **path_strv_remove_empty(char **l) {
212 char **f, **t;
213
214 if (!l)
215 return NULL;
216
217 for (f = t = l; *f; f++) {
218
219 if (dir_is_empty(*f) > 0) {
220 free(*f);
221 continue;
222 }
223
224 *(t++) = *f;
225 }
226
227 *t = NULL;
228 return l;
229}
230
231char *path_kill_slashes(char *path) {
232 char *f, *t;
233 bool slash = false;
234
235 /* Removes redundant inner and trailing slashes. Modifies the
236 * passed string in-place.
237 *
238 * ///foo///bar/ becomes /foo/bar
239 */
240
241 for (f = path, t = path; *f; f++) {
242
243 if (*f == '/') {
244 slash = true;
245 continue;
246 }
247
248 if (slash) {
249 slash = false;
250 *(t++) = '/';
251 }
252
253 *(t++) = *f;
254 }
255
256 /* Special rule, if we are talking of the root directory, a
257 trailing slash is good */
258
259 if (t == path && slash)
260 *(t++) = '/';
261
262 *t = 0;
263 return path;
264}
265
266bool path_startswith(const char *path, const char *prefix) {
267 assert(path);
268 assert(prefix);
269
270 if ((path[0] == '/') != (prefix[0] == '/'))
271 return false;
272
273 for (;;) {
274 size_t a, b;
275
276 path += strspn(path, "/");
277 prefix += strspn(prefix, "/");
278
279 if (*prefix == 0)
280 return true;
281
282 if (*path == 0)
283 return false;
284
285 a = strcspn(path, "/");
286 b = strcspn(prefix, "/");
287
288 if (a != b)
289 return false;
290
291 if (memcmp(path, prefix, a) != 0)
292 return false;
293
294 path += a;
295 prefix += b;
296 }
297}
298
299bool path_equal(const char *a, const char *b) {
300 assert(a);
301 assert(b);
302
303 if ((a[0] == '/') != (b[0] == '/'))
304 return false;
305
306 for (;;) {
307 size_t j, k;
308
309 a += strspn(a, "/");
310 b += strspn(b, "/");
311
312 if (*a == 0 && *b == 0)
313 return true;
314
315 if (*a == 0 || *b == 0)
316 return false;
317
318 j = strcspn(a, "/");
319 k = strcspn(b, "/");
320
321 if (j != k)
322 return false;
323
324 if (memcmp(a, b, j) != 0)
325 return false;
326
327 a += j;
328 b += k;
329 }
330}
331
332int path_is_mount_point(const char *t, bool allow_symlink) {
333 struct stat a, b;
334 char *parent;
335 int r;
336
337 if (allow_symlink)
338 r = stat(t, &a);
339 else
340 r = lstat(t, &a);
341
342 if (r < 0) {
343 if (errno == ENOENT)
344 return 0;
345
346 return -errno;
347 }
348
349 r = path_get_parent(t, &parent);
350 if (r < 0)
351 return r;
352
353 r = lstat(parent, &b);
354 free(parent);
355
356 if (r < 0)
357 return -errno;
358
359 return a.st_dev != b.st_dev;
360}
361
362int path_is_read_only_fs(const char *path) {
363 struct statvfs st;
364
365 assert(path);
366
367 if (statvfs(path, &st) < 0)
368 return -errno;
369
370 return !!(st.f_flag & ST_RDONLY);
371}