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