chiark / gitweb /
path-util: fix memory leak
[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         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_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
253 char* path_startswith(const char *path, const char *prefix) {
254         assert(path);
255         assert(prefix);
256
257         if ((path[0] == '/') != (prefix[0] == '/'))
258                 return NULL;
259
260         for (;;) {
261                 size_t a, b;
262
263                 path += strspn(path, "/");
264                 prefix += strspn(prefix, "/");
265
266                 if (*prefix == 0)
267                         return (char*) path;
268
269                 if (*path == 0)
270                         return NULL;
271
272                 a = strcspn(path, "/");
273                 b = strcspn(prefix, "/");
274
275                 if (a != b)
276                         return NULL;
277
278                 if (memcmp(path, prefix, a) != 0)
279                         return NULL;
280
281                 path += a;
282                 prefix += b;
283         }
284 }
285
286 bool 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
319 int path_is_mount_point(const char *t, bool allow_symlink) {
320         char *parent;
321         int r;
322         struct file_handle *h;
323         int mount_id, mount_id_parent;
324         struct stat a, b;
325
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;
332
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);
337         if (r < 0) {
338                 if (errno == ENOSYS || errno == ENOTSUP)
339                         /* This kernel or file system does not support
340                          * name_to_handle_at(), hence fallback to the
341                          * traditional stat() logic */
342                         goto fallback;
343
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
354         h->handle_bytes = MAX_HANDLE_SZ;
355         r = name_to_handle_at(AT_FDCWD, parent, h, &mount_id_parent, 0);
356         free(parent);
357
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
370 fallback:
371         if (allow_symlink)
372                 r = stat(t, &a);
373         else
374                 r = lstat(t, &a);
375
376         if (r < 0) {
377                 if (errno == ENOENT)
378                         return 0;
379
380                 return -errno;
381         }
382
383         r = path_get_parent(t, &parent);
384         if (r < 0)
385                 return r;
386
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;
394 }
395
396 int 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 }