chiark / gitweb /
Prep v228: Apply more cosmetic changes found in upstream.
[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 #endif // 0
198
199 int readlink_and_make_absolute(const char *p, char **r) {
200         _cleanup_free_ char *target = NULL;
201         char *k;
202         int j;
203
204         assert(p);
205         assert(r);
206
207         j = readlink_malloc(p, &target);
208         if (j < 0)
209                 return j;
210
211         k = file_in_same_dir(p, target);
212         if (!k)
213                 return -ENOMEM;
214
215         *r = k;
216         return 0;
217 }
218
219 /// UNNEEDED by elogind
220 #if 0
221 int readlink_and_canonicalize(const char *p, char **r) {
222         char *t, *s;
223         int j;
224
225         assert(p);
226         assert(r);
227
228         j = readlink_and_make_absolute(p, &t);
229         if (j < 0)
230                 return j;
231
232         s = canonicalize_file_name(t);
233         if (s) {
234                 free(t);
235                 *r = s;
236         } else
237                 *r = t;
238
239         path_kill_slashes(*r);
240
241         return 0;
242 }
243
244 int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) {
245         _cleanup_free_ char *target = NULL, *t = NULL;
246         const char *full;
247         int r;
248
249         full = prefix_roota(root, path);
250         r = readlink_malloc(full, &target);
251         if (r < 0)
252                 return r;
253
254         t = file_in_same_dir(path, target);
255         if (!t)
256                 return -ENOMEM;
257
258         *ret = t;
259         t = NULL;
260
261         return 0;
262 }
263 #endif // 0
264
265 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
266         assert(path);
267
268         /* Under the assumption that we are running privileged we
269          * first change the access mode and only then hand out
270          * ownership to avoid a window where access is too open. */
271
272         if (mode != MODE_INVALID)
273                 if (chmod(path, mode) < 0)
274                         return -errno;
275
276         if (uid != UID_INVALID || gid != GID_INVALID)
277                 if (chown(path, uid, gid) < 0)
278                         return -errno;
279
280         return 0;
281 }
282
283 int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
284         assert(fd >= 0);
285
286         /* Under the assumption that we are running privileged we
287          * first change the access mode and only then hand out
288          * ownership to avoid a window where access is too open. */
289
290         if (mode != MODE_INVALID)
291                 if (fchmod(fd, mode) < 0)
292                         return -errno;
293
294         if (uid != UID_INVALID || gid != GID_INVALID)
295                 if (fchown(fd, uid, gid) < 0)
296                         return -errno;
297
298         return 0;
299 }
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 int symlink_idempotent(const char *from, const char *to) {
374         _cleanup_free_ char *p = NULL;
375         int r;
376
377         assert(from);
378         assert(to);
379
380         if (symlink(from, to) < 0) {
381                 if (errno != EEXIST)
382                         return -errno;
383
384                 r = readlink_malloc(to, &p);
385                 if (r < 0)
386                         return r;
387
388                 if (!streq(p, from))
389                         return -EINVAL;
390         }
391
392         return 0;
393 }
394
395 int symlink_atomic(const char *from, const char *to) {
396         _cleanup_free_ char *t = NULL;
397         int r;
398
399         assert(from);
400         assert(to);
401
402         r = tempfn_random(to, NULL, &t);
403         if (r < 0)
404                 return r;
405
406         if (symlink(from, t) < 0)
407                 return -errno;
408
409         if (rename(t, to) < 0) {
410                 unlink_noerrno(t);
411                 return -errno;
412         }
413
414         return 0;
415 }
416
417 int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
418         _cleanup_free_ char *t = NULL;
419         int r;
420
421         assert(path);
422
423         r = tempfn_random(path, NULL, &t);
424         if (r < 0)
425                 return r;
426
427         if (mknod(t, mode, dev) < 0)
428                 return -errno;
429
430         if (rename(t, path) < 0) {
431                 unlink_noerrno(t);
432                 return -errno;
433         }
434
435         return 0;
436 }
437
438 int mkfifo_atomic(const char *path, mode_t mode) {
439         _cleanup_free_ char *t = NULL;
440         int r;
441
442         assert(path);
443
444         r = tempfn_random(path, NULL, &t);
445         if (r < 0)
446                 return r;
447
448         if (mkfifo(t, mode) < 0)
449                 return -errno;
450
451         if (rename(t, path) < 0) {
452                 unlink_noerrno(t);
453                 return -errno;
454         }
455
456         return 0;
457 }
458
459 int get_files_in_directory(const char *path, char ***list) {
460         _cleanup_closedir_ DIR *d = NULL;
461         size_t bufsize = 0, n = 0;
462         _cleanup_strv_free_ char **l = NULL;
463
464         assert(path);
465
466         /* Returns all files in a directory in *list, and the number
467          * of files as return value. If list is NULL returns only the
468          * number. */
469
470         d = opendir(path);
471         if (!d)
472                 return -errno;
473
474         for (;;) {
475                 struct dirent *de;
476
477                 errno = 0;
478                 de = readdir(d);
479                 if (!de && errno != 0)
480                         return -errno;
481                 if (!de)
482                         break;
483
484                 dirent_ensure_type(d, de);
485
486                 if (!dirent_is_file(de))
487                         continue;
488
489                 if (list) {
490                         /* one extra slot is needed for the terminating NULL */
491                         if (!GREEDY_REALLOC(l, bufsize, n + 2))
492                                 return -ENOMEM;
493
494                         l[n] = strdup(de->d_name);
495                         if (!l[n])
496                                 return -ENOMEM;
497
498                         l[++n] = NULL;
499                 } else
500                         n++;
501         }
502
503         if (list) {
504                 *list = l;
505                 l = NULL; /* avoid freeing */
506         }
507
508         return n;
509 }