chiark / gitweb /
9449e02a3f8130019553042811c6f08b065d3837
[elogind.git] / src / basic / fs-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 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 "alloc-util.h"
23 #include "dirent-util.h"
24 #include "fd-util.h"
25 #include "fileio.h"
26 #include "fs-util.h"
27 #include "mkdir.h"
28 #include "parse-util.h"
29 #include "path-util.h"
30 #include "string-util.h"
31 #include "strv.h"
32 #include "user-util.h"
33 #include "util.h"
34
35 int unlink_noerrno(const char *path) {
36         PROTECT_ERRNO;
37         int r;
38
39         r = unlink(path);
40         if (r < 0)
41                 return -errno;
42
43         return 0;
44 }
45
46 /// UNNEEDED by elogind
47 #if 0
48 int rmdir_parents(const char *path, const char *stop) {
49         size_t l;
50         int r = 0;
51
52         assert(path);
53         assert(stop);
54
55         l = strlen(path);
56
57         /* Skip trailing slashes */
58         while (l > 0 && path[l-1] == '/')
59                 l--;
60
61         while (l > 0) {
62                 char *t;
63
64                 /* Skip last component */
65                 while (l > 0 && path[l-1] != '/')
66                         l--;
67
68                 /* Skip trailing slashes */
69                 while (l > 0 && path[l-1] == '/')
70                         l--;
71
72                 if (l <= 0)
73                         break;
74
75                 t = strndup(path, l);
76                 if (!t)
77                         return -ENOMEM;
78
79                 if (path_startswith(stop, t)) {
80                         free(t);
81                         return 0;
82                 }
83
84                 r = rmdir(t);
85                 free(t);
86
87                 if (r < 0)
88                         if (errno != ENOENT)
89                                 return -errno;
90         }
91
92         return 0;
93 }
94
95
96 int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
97         struct stat buf;
98         int ret;
99
100         ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
101         if (ret >= 0)
102                 return 0;
103
104         /* renameat2() exists since Linux 3.15, btrfs added support for it later.
105          * If it is not implemented, fallback to another method. */
106         if (!IN_SET(errno, EINVAL, ENOSYS))
107                 return -errno;
108
109         /* The link()/unlink() fallback does not work on directories. But
110          * renameat() without RENAME_NOREPLACE gives the same semantics on
111          * directories, except when newpath is an *empty* directory. This is
112          * good enough. */
113         ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
114         if (ret >= 0 && S_ISDIR(buf.st_mode)) {
115                 ret = renameat(olddirfd, oldpath, newdirfd, newpath);
116                 return ret >= 0 ? 0 : -errno;
117         }
118
119         /* If it is not a directory, use the link()/unlink() fallback. */
120         ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
121         if (ret < 0)
122                 return -errno;
123
124         ret = unlinkat(olddirfd, oldpath, 0);
125         if (ret < 0) {
126                 /* backup errno before the following unlinkat() alters it */
127                 ret = errno;
128                 (void) unlinkat(newdirfd, newpath, 0);
129                 errno = ret;
130                 return -errno;
131         }
132
133         return 0;
134 }
135 #endif // 0
136
137 int readlinkat_malloc(int fd, const char *p, char **ret) {
138         size_t l = 100;
139         int r;
140
141         assert(p);
142         assert(ret);
143
144         for (;;) {
145                 char *c;
146                 ssize_t n;
147
148                 c = new(char, l);
149                 if (!c)
150                         return -ENOMEM;
151
152                 n = readlinkat(fd, p, c, l-1);
153                 if (n < 0) {
154                         r = -errno;
155                         free(c);
156                         return r;
157                 }
158
159                 if ((size_t) n < l-1) {
160                         c[n] = 0;
161                         *ret = c;
162                         return 0;
163                 }
164
165                 free(c);
166                 l *= 2;
167         }
168 }
169
170 int readlink_malloc(const char *p, char **ret) {
171         return readlinkat_malloc(AT_FDCWD, p, ret);
172 }
173
174 /// UNNEEDED by elogind
175 #if 0
176 int readlink_value(const char *p, char **ret) {
177         _cleanup_free_ char *link = NULL;
178         char *value;
179         int r;
180
181         r = readlink_malloc(p, &link);
182         if (r < 0)
183                 return r;
184
185         value = basename(link);
186         if (!value)
187                 return -ENOENT;
188
189         value = strdup(value);
190         if (!value)
191                 return -ENOMEM;
192
193         *ret = value;
194
195         return 0;
196 }
197
198 int readlink_and_make_absolute(const char *p, char **r) {
199         _cleanup_free_ char *target = NULL;
200         char *k;
201         int j;
202
203         assert(p);
204         assert(r);
205
206         j = readlink_malloc(p, &target);
207         if (j < 0)
208                 return j;
209
210         k = file_in_same_dir(p, target);
211         if (!k)
212                 return -ENOMEM;
213
214         *r = k;
215         return 0;
216 }
217
218 int readlink_and_canonicalize(const char *p, char **r) {
219         char *t, *s;
220         int j;
221
222         assert(p);
223         assert(r);
224
225         j = readlink_and_make_absolute(p, &t);
226         if (j < 0)
227                 return j;
228
229         s = canonicalize_file_name(t);
230         if (s) {
231                 free(t);
232                 *r = s;
233         } else
234                 *r = t;
235
236         path_kill_slashes(*r);
237
238         return 0;
239 }
240
241 int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) {
242         _cleanup_free_ char *target = NULL, *t = NULL;
243         const char *full;
244         int r;
245
246         full = prefix_roota(root, path);
247         r = readlink_malloc(full, &target);
248         if (r < 0)
249                 return r;
250
251         t = file_in_same_dir(path, target);
252         if (!t)
253                 return -ENOMEM;
254
255         *ret = t;
256         t = NULL;
257
258         return 0;
259 }
260 #endif // 0
261
262 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
263         assert(path);
264
265         /* Under the assumption that we are running privileged we
266          * first change the access mode and only then hand out
267          * ownership to avoid a window where access is too open. */
268
269         if (mode != MODE_INVALID)
270                 if (chmod(path, mode) < 0)
271                         return -errno;
272
273         if (uid != UID_INVALID || gid != GID_INVALID)
274                 if (chown(path, uid, gid) < 0)
275                         return -errno;
276
277         return 0;
278 }
279
280 /// UNNEEDED by elogind
281 #if 0
282 int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
283         assert(fd >= 0);
284
285         /* Under the assumption that we are running privileged we
286          * first change the access mode and only then hand out
287          * ownership to avoid a window where access is too open. */
288
289         if (mode != MODE_INVALID)
290                 if (fchmod(fd, mode) < 0)
291                         return -errno;
292
293         if (uid != UID_INVALID || gid != GID_INVALID)
294                 if (fchown(fd, uid, gid) < 0)
295                         return -errno;
296
297         return 0;
298 }
299 #endif // 0
300
301 int fchmod_umask(int fd, mode_t m) {
302         mode_t u;
303         int r;
304
305         u = umask(0777);
306         r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
307         umask(u);
308
309         return r;
310 }
311
312 int fd_warn_permissions(const char *path, int fd) {
313         struct stat st;
314
315         if (fstat(fd, &st) < 0)
316                 return -errno;
317
318         if (st.st_mode & 0111)
319                 log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
320
321         if (st.st_mode & 0002)
322                 log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
323
324         if (getpid() == 1 && (st.st_mode & 0044) != 0044)
325                 log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
326
327         return 0;
328 }
329
330 int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
331         _cleanup_close_ int fd;
332         int r;
333
334         assert(path);
335
336         if (parents)
337                 mkdir_parents(path, 0755);
338
339         fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
340         if (fd < 0)
341                 return -errno;
342
343         if (mode != MODE_INVALID) {
344                 r = fchmod(fd, mode);
345                 if (r < 0)
346                         return -errno;
347         }
348
349         if (uid != UID_INVALID || gid != GID_INVALID) {
350                 r = fchown(fd, uid, gid);
351                 if (r < 0)
352                         return -errno;
353         }
354
355         if (stamp != USEC_INFINITY) {
356                 struct timespec ts[2];
357
358                 timespec_store(&ts[0], stamp);
359                 ts[1] = ts[0];
360                 r = futimens(fd, ts);
361         } else
362                 r = futimens(fd, NULL);
363         if (r < 0)
364                 return -errno;
365
366         return 0;
367 }
368
369 int touch(const char *path) {
370         return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
371 }
372
373 /// UNNEEDED by elogind
374 #if 0
375 int symlink_idempotent(const char *from, const char *to) {
376         _cleanup_free_ char *p = NULL;
377         int r;
378
379         assert(from);
380         assert(to);
381
382         if (symlink(from, to) < 0) {
383                 if (errno != EEXIST)
384                         return -errno;
385
386                 r = readlink_malloc(to, &p);
387                 if (r < 0)
388                         return r;
389
390                 if (!streq(p, from))
391                         return -EINVAL;
392         }
393
394         return 0;
395 }
396
397 int symlink_atomic(const char *from, const char *to) {
398         _cleanup_free_ char *t = NULL;
399         int r;
400
401         assert(from);
402         assert(to);
403
404         r = tempfn_random(to, NULL, &t);
405         if (r < 0)
406                 return r;
407
408         if (symlink(from, t) < 0)
409                 return -errno;
410
411         if (rename(t, to) < 0) {
412                 unlink_noerrno(t);
413                 return -errno;
414         }
415
416         return 0;
417 }
418
419 int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
420         _cleanup_free_ char *t = NULL;
421         int r;
422
423         assert(path);
424
425         r = tempfn_random(path, NULL, &t);
426         if (r < 0)
427                 return r;
428
429         if (mknod(t, mode, dev) < 0)
430                 return -errno;
431
432         if (rename(t, path) < 0) {
433                 unlink_noerrno(t);
434                 return -errno;
435         }
436
437         return 0;
438 }
439
440 int mkfifo_atomic(const char *path, mode_t mode) {
441         _cleanup_free_ char *t = NULL;
442         int r;
443
444         assert(path);
445
446         r = tempfn_random(path, NULL, &t);
447         if (r < 0)
448                 return r;
449
450         if (mkfifo(t, mode) < 0)
451                 return -errno;
452
453         if (rename(t, path) < 0) {
454                 unlink_noerrno(t);
455                 return -errno;
456         }
457
458         return 0;
459 }
460 #endif // 0
461
462 int get_files_in_directory(const char *path, char ***list) {
463         _cleanup_closedir_ DIR *d = NULL;
464         size_t bufsize = 0, n = 0;
465         _cleanup_strv_free_ char **l = NULL;
466
467         assert(path);
468
469         /* Returns all files in a directory in *list, and the number
470          * of files as return value. If list is NULL returns only the
471          * number. */
472
473         d = opendir(path);
474         if (!d)
475                 return -errno;
476
477         for (;;) {
478                 struct dirent *de;
479
480                 errno = 0;
481                 de = readdir(d);
482                 if (!de && errno != 0)
483                         return -errno;
484                 if (!de)
485                         break;
486
487                 dirent_ensure_type(d, de);
488
489                 if (!dirent_is_file(de))
490                         continue;
491
492                 if (list) {
493                         /* one extra slot is needed for the terminating NULL */
494                         if (!GREEDY_REALLOC(l, bufsize, n + 2))
495                                 return -ENOMEM;
496
497                         l[n] = strdup(de->d_name);
498                         if (!l[n])
499                                 return -ENOMEM;
500
501                         l[++n] = NULL;
502                 } else
503                         n++;
504         }
505
506         if (list) {
507                 *list = l;
508                 l = NULL; /* avoid freeing */
509         }
510
511         return n;
512 }