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