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