]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
f4f15635 | 2 | |
11c3a366 TA |
3 | #include <errno.h> |
4 | #include <stddef.h> | |
11c3a366 | 5 | #include <stdlib.h> |
1c73b069 | 6 | #include <linux/falloc.h> |
655f2da0 | 7 | #include <linux/magic.h> |
11c3a366 TA |
8 | #include <unistd.h> |
9 | ||
b5efdb8a | 10 | #include "alloc-util.h" |
f4f15635 LP |
11 | #include "dirent-util.h" |
12 | #include "fd-util.h" | |
f4f15635 | 13 | #include "fs-util.h" |
fd74c6f3 | 14 | #include "locale-util.h" |
11c3a366 TA |
15 | #include "log.h" |
16 | #include "macro.h" | |
0499585f | 17 | #include "missing_fcntl.h" |
f5947a5e YW |
18 | #include "missing_fs.h" |
19 | #include "missing_syscall.h" | |
93cc7779 TA |
20 | #include "mkdir.h" |
21 | #include "parse-util.h" | |
22 | #include "path-util.h" | |
dccca82b | 23 | #include "process-util.h" |
34a8f081 | 24 | #include "stat-util.h" |
430fbf8e | 25 | #include "stdio-util.h" |
f4f15635 LP |
26 | #include "string-util.h" |
27 | #include "strv.h" | |
93cc7779 | 28 | #include "time-util.h" |
e4de7287 | 29 | #include "tmpfile-util.h" |
ee104e11 | 30 | #include "user-util.h" |
f4f15635 LP |
31 | #include "util.h" |
32 | ||
33 | int unlink_noerrno(const char *path) { | |
34 | PROTECT_ERRNO; | |
35 | int r; | |
36 | ||
37 | r = unlink(path); | |
38 | if (r < 0) | |
39 | return -errno; | |
40 | ||
41 | return 0; | |
42 | } | |
43 | ||
44 | int rmdir_parents(const char *path, const char *stop) { | |
45 | size_t l; | |
46 | int r = 0; | |
47 | ||
48 | assert(path); | |
49 | assert(stop); | |
50 | ||
51 | l = strlen(path); | |
52 | ||
53 | /* Skip trailing slashes */ | |
54 | while (l > 0 && path[l-1] == '/') | |
55 | l--; | |
56 | ||
57 | while (l > 0) { | |
58 | char *t; | |
59 | ||
60 | /* Skip last component */ | |
61 | while (l > 0 && path[l-1] != '/') | |
62 | l--; | |
63 | ||
64 | /* Skip trailing slashes */ | |
65 | while (l > 0 && path[l-1] == '/') | |
66 | l--; | |
67 | ||
68 | if (l <= 0) | |
69 | break; | |
70 | ||
71 | t = strndup(path, l); | |
72 | if (!t) | |
73 | return -ENOMEM; | |
74 | ||
75 | if (path_startswith(stop, t)) { | |
76 | free(t); | |
77 | return 0; | |
78 | } | |
79 | ||
80 | r = rmdir(t); | |
81 | free(t); | |
82 | ||
83 | if (r < 0) | |
84 | if (errno != ENOENT) | |
85 | return -errno; | |
86 | } | |
87 | ||
88 | return 0; | |
89 | } | |
90 | ||
f4f15635 | 91 | int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { |
2f15b625 | 92 | int r; |
f4f15635 | 93 | |
2f15b625 LP |
94 | /* Try the ideal approach first */ |
95 | if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) >= 0) | |
f4f15635 LP |
96 | return 0; |
97 | ||
2f15b625 LP |
98 | /* renameat2() exists since Linux 3.15, btrfs and FAT added support for it later. If it is not implemented, |
99 | * fall back to a different method. */ | |
100 | if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY)) | |
f4f15635 LP |
101 | return -errno; |
102 | ||
2f15b625 LP |
103 | /* Let's try to use linkat()+unlinkat() as fallback. This doesn't work on directories and on some file systems |
104 | * that do not support hard links (such as FAT, most prominently), but for files it's pretty close to what we | |
105 | * want — though not atomic (i.e. for a short period both the new and the old filename will exist). */ | |
106 | if (linkat(olddirfd, oldpath, newdirfd, newpath, 0) >= 0) { | |
107 | ||
108 | if (unlinkat(olddirfd, oldpath, 0) < 0) { | |
109 | r = -errno; /* Backup errno before the following unlinkat() alters it */ | |
110 | (void) unlinkat(newdirfd, newpath, 0); | |
111 | return r; | |
112 | } | |
113 | ||
114 | return 0; | |
f4f15635 LP |
115 | } |
116 | ||
2f15b625 | 117 | if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM)) /* FAT returns EPERM on link()… */ |
f4f15635 LP |
118 | return -errno; |
119 | ||
2f15b625 LP |
120 | /* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fallback to the racy TOCTOU |
121 | * vulnerable accessat(F_OK) check followed by classic, replacing renameat(), we have nothing better. */ | |
122 | ||
123 | if (faccessat(newdirfd, newpath, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) | |
124 | return -EEXIST; | |
125 | if (errno != ENOENT) | |
126 | return -errno; | |
127 | ||
128 | if (renameat(olddirfd, oldpath, newdirfd, newpath) < 0) | |
f4f15635 | 129 | return -errno; |
f4f15635 LP |
130 | |
131 | return 0; | |
132 | } | |
133 | ||
134 | int readlinkat_malloc(int fd, const char *p, char **ret) { | |
8e060ec2 | 135 | size_t l = FILENAME_MAX+1; |
f4f15635 LP |
136 | int r; |
137 | ||
138 | assert(p); | |
139 | assert(ret); | |
140 | ||
141 | for (;;) { | |
142 | char *c; | |
143 | ssize_t n; | |
144 | ||
145 | c = new(char, l); | |
146 | if (!c) | |
147 | return -ENOMEM; | |
148 | ||
149 | n = readlinkat(fd, p, c, l-1); | |
150 | if (n < 0) { | |
151 | r = -errno; | |
152 | free(c); | |
153 | return r; | |
154 | } | |
155 | ||
156 | if ((size_t) n < l-1) { | |
157 | c[n] = 0; | |
158 | *ret = c; | |
159 | return 0; | |
160 | } | |
161 | ||
162 | free(c); | |
163 | l *= 2; | |
164 | } | |
165 | } | |
166 | ||
167 | int readlink_malloc(const char *p, char **ret) { | |
168 | return readlinkat_malloc(AT_FDCWD, p, ret); | |
169 | } | |
170 | ||
171 | int readlink_value(const char *p, char **ret) { | |
172 | _cleanup_free_ char *link = NULL; | |
173 | char *value; | |
174 | int r; | |
175 | ||
176 | r = readlink_malloc(p, &link); | |
177 | if (r < 0) | |
178 | return r; | |
179 | ||
180 | value = basename(link); | |
181 | if (!value) | |
182 | return -ENOENT; | |
183 | ||
184 | value = strdup(value); | |
185 | if (!value) | |
186 | return -ENOMEM; | |
187 | ||
188 | *ret = value; | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | int readlink_and_make_absolute(const char *p, char **r) { | |
194 | _cleanup_free_ char *target = NULL; | |
195 | char *k; | |
196 | int j; | |
197 | ||
198 | assert(p); | |
199 | assert(r); | |
200 | ||
201 | j = readlink_malloc(p, &target); | |
202 | if (j < 0) | |
203 | return j; | |
204 | ||
205 | k = file_in_same_dir(p, target); | |
206 | if (!k) | |
207 | return -ENOMEM; | |
208 | ||
209 | *r = k; | |
210 | return 0; | |
211 | } | |
212 | ||
f4f15635 | 213 | int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { |
de321f52 | 214 | _cleanup_close_ int fd = -1; |
30ff18d8 | 215 | |
f4f15635 LP |
216 | assert(path); |
217 | ||
30ff18d8 LP |
218 | fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change |
219 | * mode/owner on the same file */ | |
de321f52 LP |
220 | if (fd < 0) |
221 | return -errno; | |
222 | ||
2dbb7e94 | 223 | return fchmod_and_chown(fd, mode, uid, gid); |
b8da477e YW |
224 | } |
225 | ||
226 | int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { | |
2dbb7e94 | 227 | bool do_chown, do_chmod; |
30ff18d8 | 228 | struct stat st; |
30ff18d8 | 229 | |
2dbb7e94 LP |
230 | /* Change ownership and access mode of the specified fd. Tries to do so safely, ensuring that at no |
231 | * point in time the access mode is above the old access mode under the old ownership or the new | |
232 | * access mode under the new ownership. Note: this call tries hard to leave the access mode | |
233 | * unaffected if the uid/gid is changed, i.e. it undoes implicit suid/sgid dropping the kernel does | |
234 | * on chown(). | |
235 | * | |
71ec74d1 | 236 | * This call is happy with O_PATH fds. */ |
b8da477e | 237 | |
71ec74d1 | 238 | if (fstat(fd, &st) < 0) |
2dbb7e94 | 239 | return -errno; |
de321f52 | 240 | |
2dbb7e94 LP |
241 | do_chown = |
242 | (uid != UID_INVALID && st.st_uid != uid) || | |
243 | (gid != GID_INVALID && st.st_gid != gid); | |
de321f52 | 244 | |
2dbb7e94 LP |
245 | do_chmod = |
246 | !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */ | |
247 | ((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) || | |
248 | do_chown); /* If we change ownership, make sure we reset the mode afterwards, since chown() | |
249 | * modifies the access mode too */ | |
30ff18d8 | 250 | |
2dbb7e94 LP |
251 | if (mode == MODE_INVALID) |
252 | mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */ | |
253 | else if ((mode & S_IFMT) != 0 && ((mode ^ st.st_mode) & S_IFMT) != 0) | |
254 | return -EINVAL; /* insist on the right file type if it was specified */ | |
de321f52 | 255 | |
2dbb7e94 LP |
256 | if (do_chown && do_chmod) { |
257 | mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */ | |
30ff18d8 | 258 | |
2dbb7e94 | 259 | if (((minimal ^ st.st_mode) & 07777) != 0) |
71ec74d1 | 260 | if (fchmod_opath(fd, minimal & 07777) < 0) |
30ff18d8 | 261 | return -errno; |
de321f52 | 262 | } |
b8da477e | 263 | |
2dbb7e94 | 264 | if (do_chown) |
71ec74d1 | 265 | if (fchownat(fd, "", uid, gid, AT_EMPTY_PATH) < 0) |
2dbb7e94 | 266 | return -errno; |
30ff18d8 | 267 | |
2dbb7e94 | 268 | if (do_chmod) |
71ec74d1 | 269 | if (fchmod_opath(fd, mode & 07777) < 0) |
2dbb7e94 | 270 | return -errno; |
30ff18d8 | 271 | |
2dbb7e94 | 272 | return do_chown || do_chmod; |
f4f15635 LP |
273 | } |
274 | ||
f4f15635 LP |
275 | int fchmod_umask(int fd, mode_t m) { |
276 | mode_t u; | |
277 | int r; | |
278 | ||
279 | u = umask(0777); | |
280 | r = fchmod(fd, m & (~u)) < 0 ? -errno : 0; | |
281 | umask(u); | |
282 | ||
283 | return r; | |
284 | } | |
285 | ||
4dfaa528 | 286 | int fchmod_opath(int fd, mode_t m) { |
22dd8d35 | 287 | char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; |
4dfaa528 FB |
288 | |
289 | /* This function operates also on fd that might have been opened with | |
290 | * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like | |
291 | * fchownat() does. */ | |
292 | ||
293 | xsprintf(procfs_path, "/proc/self/fd/%i", fd); | |
4dfaa528 FB |
294 | if (chmod(procfs_path, m) < 0) |
295 | return -errno; | |
296 | ||
297 | return 0; | |
298 | } | |
299 | ||
f4f15635 LP |
300 | int fd_warn_permissions(const char *path, int fd) { |
301 | struct stat st; | |
302 | ||
303 | if (fstat(fd, &st) < 0) | |
304 | return -errno; | |
305 | ||
b6cceaae LP |
306 | /* Don't complain if we are reading something that is not a file, for example /dev/null */ |
307 | if (!S_ISREG(st.st_mode)) | |
308 | return 0; | |
309 | ||
f4f15635 LP |
310 | if (st.st_mode & 0111) |
311 | log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); | |
312 | ||
313 | if (st.st_mode & 0002) | |
314 | log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); | |
315 | ||
df0ff127 | 316 | if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044) |
f4f15635 LP |
317 | 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); |
318 | ||
319 | return 0; | |
320 | } | |
321 | ||
322 | int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { | |
9e3fa6e8 LP |
323 | char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; |
324 | _cleanup_close_ int fd = -1; | |
325 | int r, ret = 0; | |
f4f15635 LP |
326 | |
327 | assert(path); | |
328 | ||
9e3fa6e8 LP |
329 | /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink |
330 | * itself which is updated, not its target | |
331 | * | |
332 | * Returns the first error we encounter, but tries to apply as much as possible. */ | |
f4f15635 | 333 | |
9e3fa6e8 LP |
334 | if (parents) |
335 | (void) mkdir_parents(path, 0755); | |
336 | ||
337 | /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in | |
338 | * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and | |
339 | * won't trigger any driver magic or so. */ | |
340 | fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); | |
341 | if (fd < 0) { | |
342 | if (errno != ENOENT) | |
f4f15635 | 343 | return -errno; |
f4f15635 | 344 | |
9e3fa6e8 LP |
345 | /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file |
346 | * here, and nothing else */ | |
347 | fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode); | |
348 | if (fd < 0) | |
f4f15635 LP |
349 | return -errno; |
350 | } | |
351 | ||
9e3fa6e8 LP |
352 | /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode, |
353 | * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is | |
354 | * something fchown(), fchmod(), futimensat() don't allow. */ | |
355 | xsprintf(fdpath, "/proc/self/fd/%i", fd); | |
356 | ||
4b3b5bc7 | 357 | ret = fchmod_and_chown(fd, mode, uid, gid); |
9e3fa6e8 | 358 | |
f4f15635 LP |
359 | if (stamp != USEC_INFINITY) { |
360 | struct timespec ts[2]; | |
361 | ||
362 | timespec_store(&ts[0], stamp); | |
363 | ts[1] = ts[0]; | |
9e3fa6e8 | 364 | r = utimensat(AT_FDCWD, fdpath, ts, 0); |
f4f15635 | 365 | } else |
9e3fa6e8 LP |
366 | r = utimensat(AT_FDCWD, fdpath, NULL, 0); |
367 | if (r < 0 && ret >= 0) | |
f4f15635 LP |
368 | return -errno; |
369 | ||
9e3fa6e8 | 370 | return ret; |
f4f15635 LP |
371 | } |
372 | ||
373 | int touch(const char *path) { | |
ee735086 | 374 | return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); |
f4f15635 LP |
375 | } |
376 | ||
6c9c51e5 YW |
377 | int symlink_idempotent(const char *from, const char *to, bool make_relative) { |
378 | _cleanup_free_ char *relpath = NULL; | |
f4f15635 LP |
379 | int r; |
380 | ||
381 | assert(from); | |
382 | assert(to); | |
383 | ||
6c9c51e5 YW |
384 | if (make_relative) { |
385 | _cleanup_free_ char *parent = NULL; | |
386 | ||
387 | parent = dirname_malloc(to); | |
388 | if (!parent) | |
389 | return -ENOMEM; | |
390 | ||
391 | r = path_make_relative(parent, from, &relpath); | |
392 | if (r < 0) | |
393 | return r; | |
394 | ||
395 | from = relpath; | |
396 | } | |
397 | ||
f4f15635 | 398 | if (symlink(from, to) < 0) { |
77b79723 LP |
399 | _cleanup_free_ char *p = NULL; |
400 | ||
f4f15635 LP |
401 | if (errno != EEXIST) |
402 | return -errno; | |
403 | ||
404 | r = readlink_malloc(to, &p); | |
77b79723 LP |
405 | if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */ |
406 | return -EEXIST; | |
407 | if (r < 0) /* Any other error? In that case propagate it as is */ | |
f4f15635 LP |
408 | return r; |
409 | ||
77b79723 LP |
410 | if (!streq(p, from)) /* Not the symlink we want it to be? In that case, propagate the original -EEXIST */ |
411 | return -EEXIST; | |
f4f15635 LP |
412 | } |
413 | ||
414 | return 0; | |
415 | } | |
416 | ||
417 | int symlink_atomic(const char *from, const char *to) { | |
418 | _cleanup_free_ char *t = NULL; | |
419 | int r; | |
420 | ||
421 | assert(from); | |
422 | assert(to); | |
423 | ||
424 | r = tempfn_random(to, NULL, &t); | |
425 | if (r < 0) | |
426 | return r; | |
427 | ||
428 | if (symlink(from, t) < 0) | |
429 | return -errno; | |
430 | ||
431 | if (rename(t, to) < 0) { | |
432 | unlink_noerrno(t); | |
433 | return -errno; | |
434 | } | |
435 | ||
436 | return 0; | |
437 | } | |
438 | ||
439 | int mknod_atomic(const char *path, mode_t mode, dev_t dev) { | |
440 | _cleanup_free_ char *t = NULL; | |
441 | int r; | |
442 | ||
443 | assert(path); | |
444 | ||
445 | r = tempfn_random(path, NULL, &t); | |
446 | if (r < 0) | |
447 | return r; | |
448 | ||
449 | if (mknod(t, mode, dev) < 0) | |
450 | return -errno; | |
451 | ||
452 | if (rename(t, path) < 0) { | |
453 | unlink_noerrno(t); | |
454 | return -errno; | |
455 | } | |
456 | ||
457 | return 0; | |
458 | } | |
459 | ||
460 | int mkfifo_atomic(const char *path, mode_t mode) { | |
461 | _cleanup_free_ char *t = NULL; | |
462 | int r; | |
463 | ||
464 | assert(path); | |
465 | ||
466 | r = tempfn_random(path, NULL, &t); | |
467 | if (r < 0) | |
468 | return r; | |
469 | ||
470 | if (mkfifo(t, mode) < 0) | |
471 | return -errno; | |
472 | ||
473 | if (rename(t, path) < 0) { | |
4fe3828c FB |
474 | unlink_noerrno(t); |
475 | return -errno; | |
476 | } | |
477 | ||
478 | return 0; | |
479 | } | |
480 | ||
481 | int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) { | |
482 | _cleanup_free_ char *t = NULL; | |
483 | int r; | |
484 | ||
485 | assert(path); | |
486 | ||
487 | if (path_is_absolute(path)) | |
488 | return mkfifo_atomic(path, mode); | |
489 | ||
490 | /* We're only interested in the (random) filename. */ | |
491 | r = tempfn_random_child("", NULL, &t); | |
492 | if (r < 0) | |
493 | return r; | |
494 | ||
495 | if (mkfifoat(dirfd, t, mode) < 0) | |
496 | return -errno; | |
497 | ||
498 | if (renameat(dirfd, t, dirfd, path) < 0) { | |
f4f15635 LP |
499 | unlink_noerrno(t); |
500 | return -errno; | |
501 | } | |
502 | ||
503 | return 0; | |
504 | } | |
505 | ||
506 | int get_files_in_directory(const char *path, char ***list) { | |
507 | _cleanup_closedir_ DIR *d = NULL; | |
8fb3f009 | 508 | struct dirent *de; |
f4f15635 LP |
509 | size_t bufsize = 0, n = 0; |
510 | _cleanup_strv_free_ char **l = NULL; | |
511 | ||
512 | assert(path); | |
513 | ||
514 | /* Returns all files in a directory in *list, and the number | |
515 | * of files as return value. If list is NULL returns only the | |
516 | * number. */ | |
517 | ||
518 | d = opendir(path); | |
519 | if (!d) | |
520 | return -errno; | |
521 | ||
8fb3f009 | 522 | FOREACH_DIRENT_ALL(de, d, return -errno) { |
f4f15635 LP |
523 | dirent_ensure_type(d, de); |
524 | ||
525 | if (!dirent_is_file(de)) | |
526 | continue; | |
527 | ||
528 | if (list) { | |
529 | /* one extra slot is needed for the terminating NULL */ | |
530 | if (!GREEDY_REALLOC(l, bufsize, n + 2)) | |
531 | return -ENOMEM; | |
532 | ||
533 | l[n] = strdup(de->d_name); | |
534 | if (!l[n]) | |
535 | return -ENOMEM; | |
536 | ||
537 | l[++n] = NULL; | |
538 | } else | |
539 | n++; | |
540 | } | |
541 | ||
ae2a15bc LP |
542 | if (list) |
543 | *list = TAKE_PTR(l); | |
f4f15635 LP |
544 | |
545 | return n; | |
546 | } | |
430fbf8e | 547 | |
992e8f22 LP |
548 | static int getenv_tmp_dir(const char **ret_path) { |
549 | const char *n; | |
550 | int r, ret = 0; | |
34a8f081 | 551 | |
992e8f22 | 552 | assert(ret_path); |
34a8f081 | 553 | |
992e8f22 LP |
554 | /* We use the same order of environment variables python uses in tempfile.gettempdir(): |
555 | * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */ | |
556 | FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") { | |
557 | const char *e; | |
558 | ||
559 | e = secure_getenv(n); | |
560 | if (!e) | |
561 | continue; | |
562 | if (!path_is_absolute(e)) { | |
563 | r = -ENOTDIR; | |
564 | goto next; | |
565 | } | |
99be45a4 | 566 | if (!path_is_normalized(e)) { |
992e8f22 LP |
567 | r = -EPERM; |
568 | goto next; | |
569 | } | |
570 | ||
571 | r = is_dir(e, true); | |
572 | if (r < 0) | |
573 | goto next; | |
574 | if (r == 0) { | |
575 | r = -ENOTDIR; | |
576 | goto next; | |
577 | } | |
578 | ||
579 | *ret_path = e; | |
580 | return 1; | |
581 | ||
582 | next: | |
583 | /* Remember first error, to make this more debuggable */ | |
584 | if (ret >= 0) | |
585 | ret = r; | |
34a8f081 OW |
586 | } |
587 | ||
992e8f22 LP |
588 | if (ret < 0) |
589 | return ret; | |
34a8f081 | 590 | |
992e8f22 LP |
591 | *ret_path = NULL; |
592 | return ret; | |
593 | } | |
34a8f081 | 594 | |
992e8f22 LP |
595 | static int tmp_dir_internal(const char *def, const char **ret) { |
596 | const char *e; | |
597 | int r, k; | |
598 | ||
599 | assert(def); | |
600 | assert(ret); | |
601 | ||
602 | r = getenv_tmp_dir(&e); | |
603 | if (r > 0) { | |
604 | *ret = e; | |
605 | return 0; | |
606 | } | |
607 | ||
608 | k = is_dir(def, true); | |
609 | if (k == 0) | |
610 | k = -ENOTDIR; | |
611 | if (k < 0) | |
612 | return r < 0 ? r : k; | |
613 | ||
614 | *ret = def; | |
34a8f081 OW |
615 | return 0; |
616 | } | |
617 | ||
992e8f22 LP |
618 | int var_tmp_dir(const char **ret) { |
619 | ||
620 | /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus | |
621 | * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is | |
622 | * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR, | |
623 | * making it a variable that overrides all temporary file storage locations. */ | |
624 | ||
625 | return tmp_dir_internal("/var/tmp", ret); | |
626 | } | |
627 | ||
628 | int tmp_dir(const char **ret) { | |
629 | ||
630 | /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually | |
631 | * backed by an in-memory file system: /tmp. */ | |
632 | ||
633 | return tmp_dir_internal("/tmp", ret); | |
634 | } | |
635 | ||
af229d7a ZJS |
636 | int unlink_or_warn(const char *filename) { |
637 | if (unlink(filename) < 0 && errno != ENOENT) | |
638 | /* If the file doesn't exist and the fs simply was read-only (in which | |
639 | * case unlink() returns EROFS even if the file doesn't exist), don't | |
640 | * complain */ | |
641 | if (errno != EROFS || access(filename, F_OK) >= 0) | |
642 | return log_error_errno(errno, "Failed to remove \"%s\": %m", filename); | |
643 | ||
644 | return 0; | |
645 | } | |
646 | ||
430fbf8e | 647 | int inotify_add_watch_fd(int fd, int what, uint32_t mask) { |
fbd0b64f | 648 | char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; |
430fbf8e LP |
649 | int r; |
650 | ||
651 | /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ | |
652 | xsprintf(path, "/proc/self/fd/%i", what); | |
653 | ||
654 | r = inotify_add_watch(fd, path, mask); | |
655 | if (r < 0) | |
656 | return -errno; | |
657 | ||
658 | return r; | |
659 | } | |
d944dc95 | 660 | |
27c3112d | 661 | int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) { |
27c3112d | 662 | |
fe573a79 | 663 | if (inotify_add_watch(fd, pathname, mask) < 0) { |
27c3112d | 664 | if (errno == ENOSPC) |
fe573a79 | 665 | return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname); |
27c3112d | 666 | |
fe573a79 | 667 | return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname); |
27c3112d FB |
668 | } |
669 | ||
670 | return 0; | |
671 | } | |
672 | ||
b85ee2ec | 673 | static bool unsafe_transition(const struct stat *a, const struct stat *b) { |
f14f1806 LP |
674 | /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to |
675 | * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files | |
676 | * making us believe we read something safe even though it isn't safe in the specific context we open it in. */ | |
677 | ||
678 | if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */ | |
b85ee2ec | 679 | return false; |
f14f1806 | 680 | |
b85ee2ec | 681 | return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */ |
f14f1806 LP |
682 | } |
683 | ||
fd74c6f3 FB |
684 | static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) { |
685 | _cleanup_free_ char *n1 = NULL, *n2 = NULL; | |
686 | ||
687 | if (!FLAGS_SET(flags, CHASE_WARN)) | |
36c97dec | 688 | return -ENOLINK; |
fd74c6f3 FB |
689 | |
690 | (void) fd_get_path(a, &n1); | |
691 | (void) fd_get_path(b, &n2); | |
692 | ||
36c97dec | 693 | return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK), |
fd74c6f3 | 694 | "Detected unsafe path transition %s %s %s during canonicalization of %s.", |
9a6f746f | 695 | n1, special_glyph(SPECIAL_GLYPH_ARROW), n2, path); |
fd74c6f3 FB |
696 | } |
697 | ||
145b8d0f FB |
698 | static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { |
699 | _cleanup_free_ char *n1 = NULL; | |
700 | ||
701 | if (!FLAGS_SET(flags, CHASE_WARN)) | |
702 | return -EREMOTE; | |
703 | ||
704 | (void) fd_get_path(fd, &n1); | |
705 | ||
706 | return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE), | |
707 | "Detected autofs mount point %s during canonicalization of %s.", | |
708 | n1, path); | |
f14f1806 LP |
709 | } |
710 | ||
a5648b80 | 711 | int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) { |
d944dc95 LP |
712 | _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; |
713 | _cleanup_close_ int fd = -1; | |
f10f4215 | 714 | unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */ |
f14f1806 | 715 | struct stat previous_stat; |
a9fb0867 | 716 | bool exists = true; |
d944dc95 LP |
717 | char *todo; |
718 | int r; | |
719 | ||
720 | assert(path); | |
721 | ||
1ed34d75 | 722 | /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ |
a5648b80 | 723 | if ((flags & CHASE_NONEXISTENT) && ret_fd) |
1ed34d75 LP |
724 | return -EINVAL; |
725 | ||
a5648b80 | 726 | if ((flags & CHASE_STEP) && ret_fd) |
49eb3659 LP |
727 | return -EINVAL; |
728 | ||
a49424af LP |
729 | if (isempty(path)) |
730 | return -EINVAL; | |
731 | ||
d944dc95 LP |
732 | /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following |
733 | * symlinks relative to a root directory, instead of the root of the host. | |
734 | * | |
fc4b68e5 | 735 | * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following |
c4f4fce7 LP |
736 | * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is |
737 | * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first | |
738 | * prefixed accordingly. | |
d944dc95 LP |
739 | * |
740 | * Algorithmically this operates on two path buffers: "done" are the components of the path we already | |
741 | * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to | |
742 | * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning | |
743 | * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no | |
744 | * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races | |
4293c32b | 745 | * to a minimum. |
fc4b68e5 LP |
746 | * |
747 | * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got | |
748 | * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this | |
749 | * function what to do when encountering a symlink with an absolute path as directory: prefix it by the | |
49eb3659 LP |
750 | * specified path. |
751 | * | |
a5648b80 | 752 | * There are five ways to invoke this function: |
49eb3659 | 753 | * |
a5648b80 ZJS |
754 | * 1. Without CHASE_STEP or ret_fd: in this case the path is resolved and the normalized path is |
755 | * returned in `ret_path`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set, 0 | |
756 | * is returned if the file doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set, >= 0 is | |
757 | * returned if the destination was found, -ENOENT if it wasn't. | |
49eb3659 | 758 | * |
a5648b80 | 759 | * 2. With ret_fd: in this case the destination is opened after chasing it as O_PATH and this file |
49eb3659 LP |
760 | * descriptor is returned as return value. This is useful to open files relative to some root |
761 | * directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using | |
a5648b80 | 762 | * fd_reopen() or such) before it can be used for reading/writing. ret_fd may not be combined with |
49eb3659 LP |
763 | * CHASE_NONEXISTENT. |
764 | * | |
765 | * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first | |
766 | * symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if | |
767 | * a caller wants to trace the a path through the file system verbosely. Returns < 0 on error, > 0 if the | |
768 | * path is fully normalized, and == 0 for each normalization step. This may be combined with | |
769 | * CHASE_NONEXISTENT, in which case 1 is returned when a component is not found. | |
770 | * | |
36c97dec FB |
771 | * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from |
772 | * unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If | |
4293c32b | 773 | * CHASE_WARN is also set, a warning describing the unsafe transition is emitted. |
36c97dec | 774 | * |
4293c32b ZJS |
775 | * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, path normalization |
776 | * is aborted and -EREMOTE is returned. If CHASE_WARN is also set, a warning showing the path of | |
777 | * the mount point is emitted. | |
4293c32b | 778 | */ |
d944dc95 | 779 | |
22bc57c5 | 780 | /* A root directory of "/" or "" is identical to none */ |
57ea45e1 | 781 | if (empty_or_root(original_root)) |
22bc57c5 | 782 | original_root = NULL; |
b1bfb848 | 783 | |
a5648b80 ZJS |
784 | if (!original_root && !ret_path && !(flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_STEP)) && ret_fd) { |
785 | /* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root set | |
244d2f07 | 786 | * and doesn't care about any of the other special features we provide either. */ |
1f56e4ce | 787 | r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)); |
244d2f07 LP |
788 | if (r < 0) |
789 | return -errno; | |
790 | ||
a5648b80 ZJS |
791 | *ret_fd = r; |
792 | return 0; | |
244d2f07 LP |
793 | } |
794 | ||
c4f4fce7 LP |
795 | if (original_root) { |
796 | r = path_make_absolute_cwd(original_root, &root); | |
d944dc95 LP |
797 | if (r < 0) |
798 | return r; | |
c4f4fce7 | 799 | |
382a5078 | 800 | if (flags & CHASE_PREFIX_ROOT) { |
382a5078 LP |
801 | /* We don't support relative paths in combination with a root directory */ |
802 | if (!path_is_absolute(path)) | |
803 | return -EINVAL; | |
804 | ||
c4f4fce7 | 805 | path = prefix_roota(root, path); |
382a5078 | 806 | } |
d944dc95 LP |
807 | } |
808 | ||
c4f4fce7 LP |
809 | r = path_make_absolute_cwd(path, &buffer); |
810 | if (r < 0) | |
811 | return r; | |
812 | ||
d944dc95 LP |
813 | fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH); |
814 | if (fd < 0) | |
815 | return -errno; | |
816 | ||
f14f1806 LP |
817 | if (flags & CHASE_SAFE) { |
818 | if (fstat(fd, &previous_stat) < 0) | |
819 | return -errno; | |
820 | } | |
821 | ||
d944dc95 LP |
822 | todo = buffer; |
823 | for (;;) { | |
824 | _cleanup_free_ char *first = NULL; | |
825 | _cleanup_close_ int child = -1; | |
826 | struct stat st; | |
827 | size_t n, m; | |
828 | ||
829 | /* Determine length of first component in the path */ | |
830 | n = strspn(todo, "/"); /* The slashes */ | |
831 | m = n + strcspn(todo + n, "/"); /* The entire length of the component */ | |
832 | ||
833 | /* Extract the first component. */ | |
834 | first = strndup(todo, m); | |
835 | if (!first) | |
836 | return -ENOMEM; | |
837 | ||
838 | todo += m; | |
839 | ||
b12d25a8 ZJS |
840 | /* Empty? Then we reached the end. */ |
841 | if (isempty(first)) | |
842 | break; | |
843 | ||
d944dc95 | 844 | /* Just a single slash? Then we reached the end. */ |
b12d25a8 ZJS |
845 | if (path_equal(first, "/")) { |
846 | /* Preserve the trailing slash */ | |
62570f6f LP |
847 | |
848 | if (flags & CHASE_TRAIL_SLASH) | |
849 | if (!strextend(&done, "/", NULL)) | |
850 | return -ENOMEM; | |
b12d25a8 | 851 | |
d944dc95 | 852 | break; |
b12d25a8 | 853 | } |
d944dc95 LP |
854 | |
855 | /* Just a dot? Then let's eat this up. */ | |
856 | if (path_equal(first, "/.")) | |
857 | continue; | |
858 | ||
859 | /* Two dots? Then chop off the last bit of what we already found out. */ | |
860 | if (path_equal(first, "/..")) { | |
861 | _cleanup_free_ char *parent = NULL; | |
2b6d2dda | 862 | _cleanup_close_ int fd_parent = -1; |
d944dc95 | 863 | |
a4eaf3cf LP |
864 | /* If we already are at the top, then going up will not change anything. This is in-line with |
865 | * how the kernel handles this. */ | |
57ea45e1 | 866 | if (empty_or_root(done)) |
a4eaf3cf | 867 | continue; |
d944dc95 LP |
868 | |
869 | parent = dirname_malloc(done); | |
870 | if (!parent) | |
871 | return -ENOMEM; | |
872 | ||
a4eaf3cf | 873 | /* Don't allow this to leave the root dir. */ |
d944dc95 LP |
874 | if (root && |
875 | path_startswith(done, root) && | |
876 | !path_startswith(parent, root)) | |
a4eaf3cf | 877 | continue; |
d944dc95 | 878 | |
3b319885 | 879 | free_and_replace(done, parent); |
d944dc95 | 880 | |
49eb3659 LP |
881 | if (flags & CHASE_STEP) |
882 | goto chased_one; | |
883 | ||
d944dc95 LP |
884 | fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH); |
885 | if (fd_parent < 0) | |
886 | return -errno; | |
887 | ||
f14f1806 LP |
888 | if (flags & CHASE_SAFE) { |
889 | if (fstat(fd_parent, &st) < 0) | |
890 | return -errno; | |
891 | ||
b85ee2ec | 892 | if (unsafe_transition(&previous_stat, &st)) |
fd74c6f3 | 893 | return log_unsafe_transition(fd, fd_parent, path, flags); |
f14f1806 LP |
894 | |
895 | previous_stat = st; | |
896 | } | |
897 | ||
d944dc95 | 898 | safe_close(fd); |
c10d6bdb | 899 | fd = TAKE_FD(fd_parent); |
d944dc95 LP |
900 | |
901 | continue; | |
902 | } | |
903 | ||
904 | /* Otherwise let's see what this is. */ | |
905 | child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH); | |
a9fb0867 LP |
906 | if (child < 0) { |
907 | ||
908 | if (errno == ENOENT && | |
cb638b5e | 909 | (flags & CHASE_NONEXISTENT) && |
99be45a4 | 910 | (isempty(todo) || path_is_normalized(todo))) { |
a9fb0867 | 911 | |
cb638b5e | 912 | /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return |
a9fb0867 LP |
913 | * what we got so far. But don't allow this if the remaining path contains "../ or "./" |
914 | * or something else weird. */ | |
915 | ||
a1904a46 YW |
916 | /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ |
917 | if (streq_ptr(done, "/")) | |
918 | *done = '\0'; | |
919 | ||
a9fb0867 LP |
920 | if (!strextend(&done, first, todo, NULL)) |
921 | return -ENOMEM; | |
922 | ||
923 | exists = false; | |
924 | break; | |
925 | } | |
926 | ||
d944dc95 | 927 | return -errno; |
a9fb0867 | 928 | } |
d944dc95 LP |
929 | |
930 | if (fstat(child, &st) < 0) | |
931 | return -errno; | |
f14f1806 | 932 | if ((flags & CHASE_SAFE) && |
cc14a6c0 | 933 | (empty_or_root(root) || (size_t)(todo - buffer) > strlen(root)) && |
b85ee2ec | 934 | unsafe_transition(&previous_stat, &st)) |
fd74c6f3 | 935 | return log_unsafe_transition(fd, child, path, flags); |
f14f1806 LP |
936 | |
937 | previous_stat = st; | |
938 | ||
655f2da0 | 939 | if ((flags & CHASE_NO_AUTOFS) && |
a66fee2e | 940 | fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) |
145b8d0f | 941 | return log_autofs_mount_point(child, path, flags); |
d944dc95 | 942 | |
1f56e4ce | 943 | if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { |
877777d7 | 944 | char *joined; |
d944dc95 LP |
945 | _cleanup_free_ char *destination = NULL; |
946 | ||
947 | /* This is a symlink, in this case read the destination. But let's make sure we don't follow | |
948 | * symlinks without bounds. */ | |
949 | if (--max_follow <= 0) | |
950 | return -ELOOP; | |
951 | ||
952 | r = readlinkat_malloc(fd, first + n, &destination); | |
953 | if (r < 0) | |
954 | return r; | |
955 | if (isempty(destination)) | |
956 | return -EINVAL; | |
957 | ||
958 | if (path_is_absolute(destination)) { | |
959 | ||
960 | /* An absolute destination. Start the loop from the beginning, but use the root | |
961 | * directory as base. */ | |
962 | ||
963 | safe_close(fd); | |
964 | fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH); | |
965 | if (fd < 0) | |
966 | return -errno; | |
967 | ||
f14f1806 LP |
968 | if (flags & CHASE_SAFE) { |
969 | if (fstat(fd, &st) < 0) | |
970 | return -errno; | |
971 | ||
b85ee2ec | 972 | if (unsafe_transition(&previous_stat, &st)) |
fd74c6f3 | 973 | return log_unsafe_transition(child, fd, path, flags); |
f14f1806 LP |
974 | |
975 | previous_stat = st; | |
976 | } | |
977 | ||
b539437a YW |
978 | free(done); |
979 | ||
d944dc95 LP |
980 | /* Note that we do not revalidate the root, we take it as is. */ |
981 | if (isempty(root)) | |
982 | done = NULL; | |
983 | else { | |
984 | done = strdup(root); | |
985 | if (!done) | |
986 | return -ENOMEM; | |
987 | } | |
988 | ||
8c4a8ea2 LP |
989 | /* Prefix what's left to do with what we just read, and start the loop again, but |
990 | * remain in the current directory. */ | |
2d9b74ba | 991 | joined = path_join(destination, todo); |
8c4a8ea2 | 992 | } else |
2d9b74ba | 993 | joined = path_join("/", destination, todo); |
877777d7 CCW |
994 | if (!joined) |
995 | return -ENOMEM; | |
d944dc95 | 996 | |
877777d7 CCW |
997 | free(buffer); |
998 | todo = buffer = joined; | |
d944dc95 | 999 | |
49eb3659 LP |
1000 | if (flags & CHASE_STEP) |
1001 | goto chased_one; | |
1002 | ||
d944dc95 LP |
1003 | continue; |
1004 | } | |
1005 | ||
1006 | /* If this is not a symlink, then let's just add the name we read to what we already verified. */ | |
ae2a15bc LP |
1007 | if (!done) |
1008 | done = TAKE_PTR(first); | |
1009 | else { | |
a1904a46 YW |
1010 | /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ |
1011 | if (streq(done, "/")) | |
1012 | *done = '\0'; | |
1013 | ||
d944dc95 LP |
1014 | if (!strextend(&done, first, NULL)) |
1015 | return -ENOMEM; | |
1016 | } | |
1017 | ||
1018 | /* And iterate again, but go one directory further down. */ | |
1019 | safe_close(fd); | |
c10d6bdb | 1020 | fd = TAKE_FD(child); |
d944dc95 LP |
1021 | } |
1022 | ||
1023 | if (!done) { | |
1024 | /* Special case, turn the empty string into "/", to indicate the root directory. */ | |
1025 | done = strdup("/"); | |
1026 | if (!done) | |
1027 | return -ENOMEM; | |
1028 | } | |
1029 | ||
a5648b80 ZJS |
1030 | if (ret_path) |
1031 | *ret_path = TAKE_PTR(done); | |
d944dc95 | 1032 | |
a5648b80 ZJS |
1033 | if (ret_fd) { |
1034 | /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a | |
1035 | * proper fd by opening /proc/self/fd/xyz. */ | |
1ed34d75 LP |
1036 | |
1037 | assert(fd >= 0); | |
a5648b80 | 1038 | *ret_fd = TAKE_FD(fd); |
1ed34d75 LP |
1039 | } |
1040 | ||
49eb3659 LP |
1041 | if (flags & CHASE_STEP) |
1042 | return 1; | |
1043 | ||
a9fb0867 | 1044 | return exists; |
49eb3659 LP |
1045 | |
1046 | chased_one: | |
a5648b80 | 1047 | if (ret_path) { |
49eb3659 LP |
1048 | char *c; |
1049 | ||
027cc9c9 ZJS |
1050 | c = strjoin(strempty(done), todo); |
1051 | if (!c) | |
1052 | return -ENOMEM; | |
49eb3659 | 1053 | |
a5648b80 | 1054 | *ret_path = c; |
49eb3659 LP |
1055 | } |
1056 | ||
1057 | return 0; | |
d944dc95 | 1058 | } |
57a4359e | 1059 | |
21c692e9 LP |
1060 | int chase_symlinks_and_open( |
1061 | const char *path, | |
1062 | const char *root, | |
1063 | unsigned chase_flags, | |
1064 | int open_flags, | |
1065 | char **ret_path) { | |
1066 | ||
1067 | _cleanup_close_ int path_fd = -1; | |
1068 | _cleanup_free_ char *p = NULL; | |
1069 | int r; | |
1070 | ||
1071 | if (chase_flags & CHASE_NONEXISTENT) | |
1072 | return -EINVAL; | |
1073 | ||
57ea45e1 | 1074 | if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { |
21c692e9 LP |
1075 | /* Shortcut this call if none of the special features of this call are requested */ |
1076 | r = open(path, open_flags); | |
1077 | if (r < 0) | |
1078 | return -errno; | |
1079 | ||
1080 | return r; | |
1081 | } | |
1082 | ||
a5648b80 ZJS |
1083 | r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); |
1084 | if (r < 0) | |
1085 | return r; | |
1086 | assert(path_fd >= 0); | |
21c692e9 LP |
1087 | |
1088 | r = fd_reopen(path_fd, open_flags); | |
1089 | if (r < 0) | |
1090 | return r; | |
1091 | ||
1092 | if (ret_path) | |
1093 | *ret_path = TAKE_PTR(p); | |
1094 | ||
1095 | return r; | |
1096 | } | |
1097 | ||
1098 | int chase_symlinks_and_opendir( | |
1099 | const char *path, | |
1100 | const char *root, | |
1101 | unsigned chase_flags, | |
1102 | char **ret_path, | |
1103 | DIR **ret_dir) { | |
1104 | ||
1105 | char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | |
1106 | _cleanup_close_ int path_fd = -1; | |
1107 | _cleanup_free_ char *p = NULL; | |
1108 | DIR *d; | |
a5648b80 | 1109 | int r; |
21c692e9 LP |
1110 | |
1111 | if (!ret_dir) | |
1112 | return -EINVAL; | |
1113 | if (chase_flags & CHASE_NONEXISTENT) | |
1114 | return -EINVAL; | |
1115 | ||
57ea45e1 | 1116 | if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { |
21c692e9 LP |
1117 | /* Shortcut this call if none of the special features of this call are requested */ |
1118 | d = opendir(path); | |
1119 | if (!d) | |
1120 | return -errno; | |
1121 | ||
1122 | *ret_dir = d; | |
1123 | return 0; | |
1124 | } | |
1125 | ||
a5648b80 ZJS |
1126 | r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); |
1127 | if (r < 0) | |
1128 | return r; | |
1129 | assert(path_fd >= 0); | |
21c692e9 LP |
1130 | |
1131 | xsprintf(procfs_path, "/proc/self/fd/%i", path_fd); | |
1132 | d = opendir(procfs_path); | |
1133 | if (!d) | |
1134 | return -errno; | |
1135 | ||
1136 | if (ret_path) | |
1137 | *ret_path = TAKE_PTR(p); | |
1138 | ||
1139 | *ret_dir = d; | |
1140 | return 0; | |
1141 | } | |
1142 | ||
d2bcd0ba LP |
1143 | int chase_symlinks_and_stat( |
1144 | const char *path, | |
1145 | const char *root, | |
1146 | unsigned chase_flags, | |
1147 | char **ret_path, | |
a5648b80 ZJS |
1148 | struct stat *ret_stat, |
1149 | int *ret_fd) { | |
d2bcd0ba LP |
1150 | |
1151 | _cleanup_close_ int path_fd = -1; | |
1152 | _cleanup_free_ char *p = NULL; | |
a5648b80 | 1153 | int r; |
d2bcd0ba LP |
1154 | |
1155 | assert(path); | |
1156 | assert(ret_stat); | |
1157 | ||
1158 | if (chase_flags & CHASE_NONEXISTENT) | |
1159 | return -EINVAL; | |
1160 | ||
1161 | if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { | |
1162 | /* Shortcut this call if none of the special features of this call are requested */ | |
1163 | if (stat(path, ret_stat) < 0) | |
1164 | return -errno; | |
1165 | ||
1166 | return 1; | |
1167 | } | |
1168 | ||
a5648b80 ZJS |
1169 | r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); |
1170 | if (r < 0) | |
1171 | return r; | |
1172 | assert(path_fd >= 0); | |
d2bcd0ba LP |
1173 | |
1174 | if (fstat(path_fd, ret_stat) < 0) | |
1175 | return -errno; | |
1176 | ||
1177 | if (ret_path) | |
1178 | *ret_path = TAKE_PTR(p); | |
a5648b80 ZJS |
1179 | if (ret_fd) |
1180 | *ret_fd = TAKE_FD(path_fd); | |
d2bcd0ba LP |
1181 | |
1182 | return 1; | |
1183 | } | |
1184 | ||
57a4359e | 1185 | int access_fd(int fd, int mode) { |
fbd0b64f | 1186 | char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; |
57a4359e LP |
1187 | int r; |
1188 | ||
1189 | /* Like access() but operates on an already open fd */ | |
1190 | ||
1191 | xsprintf(p, "/proc/self/fd/%i", fd); | |
57a4359e LP |
1192 | r = access(p, mode); |
1193 | if (r < 0) | |
21c692e9 | 1194 | return -errno; |
57a4359e LP |
1195 | |
1196 | return r; | |
1197 | } | |
43767d9d | 1198 | |
627d2bac ZJS |
1199 | void unlink_tempfilep(char (*p)[]) { |
1200 | /* If the file is created with mkstemp(), it will (almost always) | |
1201 | * change the suffix. Treat this as a sign that the file was | |
1202 | * successfully created. We ignore both the rare case where the | |
1203 | * original suffix is used and unlink failures. */ | |
1204 | if (!endswith(*p, ".XXXXXX")) | |
69821560 | 1205 | (void) unlink_noerrno(*p); |
627d2bac ZJS |
1206 | } |
1207 | ||
43767d9d LP |
1208 | int unlinkat_deallocate(int fd, const char *name, int flags) { |
1209 | _cleanup_close_ int truncate_fd = -1; | |
1210 | struct stat st; | |
1211 | off_t l, bs; | |
1212 | ||
1213 | /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other | |
1214 | * link to it. This is useful to ensure that other processes that might have the file open for reading won't be | |
1215 | * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up | |
1216 | * jobs ("vacuuming"), where we want to make sure the data is really gone and the disk space released and | |
1217 | * returned to the free pool. | |
1218 | * | |
1219 | * Deallocation is preferably done by FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE (👊) if supported, which means | |
1220 | * the file won't change size. That's a good thing since we shouldn't needlessly trigger SIGBUS in other | |
1221 | * programs that have mmap()ed the file. (The assumption here is that changing file contents to all zeroes | |
1222 | * underneath those programs is the better choice than simply triggering SIGBUS in them which truncation does.) | |
1223 | * However if hole punching is not implemented in the kernel or file system we'll fall back to normal file | |
1224 | * truncation (🔪), as our goal of deallocating the data space trumps our goal of being nice to readers (💐). | |
1225 | * | |
1226 | * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the | |
1227 | * primary job – to delete the file – is accomplished. */ | |
1228 | ||
1229 | if ((flags & AT_REMOVEDIR) == 0) { | |
1230 | truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK); | |
1231 | if (truncate_fd < 0) { | |
1232 | ||
1233 | /* If this failed because the file doesn't exist propagate the error right-away. Also, | |
1234 | * AT_REMOVEDIR wasn't set, and we tried to open the file for writing, which means EISDIR is | |
1235 | * returned when this is a directory but we are not supposed to delete those, hence propagate | |
1236 | * the error right-away too. */ | |
1237 | if (IN_SET(errno, ENOENT, EISDIR)) | |
1238 | return -errno; | |
1239 | ||
1240 | if (errno != ELOOP) /* don't complain if this is a symlink */ | |
1241 | log_debug_errno(errno, "Failed to open file '%s' for deallocation, ignoring: %m", name); | |
1242 | } | |
1243 | } | |
1244 | ||
1245 | if (unlinkat(fd, name, flags) < 0) | |
1246 | return -errno; | |
1247 | ||
1248 | if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */ | |
1249 | return 0; | |
1250 | ||
1251 | if (fstat(truncate_fd, &st) < 0) { | |
011723a4 | 1252 | log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name); |
43767d9d LP |
1253 | return 0; |
1254 | } | |
1255 | ||
1256 | if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0) | |
1257 | return 0; | |
1258 | ||
1259 | /* If this is a regular file, it actually took up space on disk and there are no other links it's time to | |
1260 | * punch-hole/truncate this to release the disk space. */ | |
1261 | ||
1262 | bs = MAX(st.st_blksize, 512); | |
1263 | l = DIV_ROUND_UP(st.st_size, bs) * bs; /* Round up to next block size */ | |
1264 | ||
1265 | if (fallocate(truncate_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 0, l) >= 0) | |
1266 | return 0; /* Successfully punched a hole! 😊 */ | |
1267 | ||
1268 | /* Fall back to truncation */ | |
1269 | if (ftruncate(truncate_fd, 0) < 0) { | |
1270 | log_debug_errno(errno, "Failed to truncate file to 0, ignoring: %m"); | |
1271 | return 0; | |
1272 | } | |
1273 | ||
1274 | return 0; | |
1275 | } | |
11b29a96 LP |
1276 | |
1277 | int fsync_directory_of_file(int fd) { | |
0c462ea4 | 1278 | _cleanup_free_ char *path = NULL; |
11b29a96 LP |
1279 | _cleanup_close_ int dfd = -1; |
1280 | int r; | |
1281 | ||
1282 | r = fd_verify_regular(fd); | |
1283 | if (r < 0) | |
1284 | return r; | |
1285 | ||
1286 | r = fd_get_path(fd, &path); | |
3ceae1bc | 1287 | if (r < 0) { |
b8b846d7 LP |
1288 | log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", |
1289 | fd, | |
1290 | r == -EOPNOTSUPP ? ", ignoring" : ""); | |
3ceae1bc ZJS |
1291 | |
1292 | if (r == -EOPNOTSUPP) | |
1293 | /* If /proc is not available, we're most likely running in some | |
1294 | * chroot environment, and syncing the directory is not very | |
1295 | * important in that case. Let's just silently do nothing. */ | |
1296 | return 0; | |
1297 | ||
11b29a96 | 1298 | return r; |
3ceae1bc | 1299 | } |
11b29a96 LP |
1300 | |
1301 | if (!path_is_absolute(path)) | |
1302 | return -EINVAL; | |
1303 | ||
0c462ea4 | 1304 | dfd = open_parent(path, O_CLOEXEC, 0); |
11b29a96 | 1305 | if (dfd < 0) |
0c462ea4 | 1306 | return dfd; |
11b29a96 LP |
1307 | |
1308 | if (fsync(dfd) < 0) | |
1309 | return -errno; | |
1310 | ||
1311 | return 0; | |
1312 | } | |
ef8becfa | 1313 | |
63d59b8d LP |
1314 | int fsync_full(int fd) { |
1315 | int r, q; | |
1316 | ||
1317 | /* Sync both the file and the directory */ | |
1318 | ||
1319 | r = fsync(fd) < 0 ? -errno : 0; | |
1320 | q = fsync_directory_of_file(fd); | |
1321 | ||
1322 | return r < 0 ? r : q; | |
1323 | } | |
1324 | ||
36695e88 LP |
1325 | int fsync_path_at(int at_fd, const char *path) { |
1326 | _cleanup_close_ int opened_fd = -1; | |
1327 | int fd; | |
1328 | ||
1329 | if (isempty(path)) { | |
1330 | if (at_fd == AT_FDCWD) { | |
1331 | opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); | |
1332 | if (opened_fd < 0) | |
1333 | return -errno; | |
1334 | ||
1335 | fd = opened_fd; | |
1336 | } else | |
1337 | fd = at_fd; | |
1338 | } else { | |
1339 | ||
1340 | opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC); | |
1341 | if (opened_fd < 0) | |
1342 | return -errno; | |
1343 | ||
1344 | fd = opened_fd; | |
1345 | } | |
1346 | ||
1347 | if (fsync(fd) < 0) | |
1348 | return -errno; | |
1349 | ||
1350 | return 0; | |
1351 | } | |
1352 | ||
71f51416 LP |
1353 | int syncfs_path(int atfd, const char *path) { |
1354 | _cleanup_close_ int fd = -1; | |
1355 | ||
1356 | assert(path); | |
1357 | ||
1358 | fd = openat(atfd, path, O_CLOEXEC|O_RDONLY|O_NONBLOCK); | |
1359 | if (fd < 0) | |
1360 | return -errno; | |
1361 | ||
1362 | if (syncfs(fd) < 0) | |
1363 | return -errno; | |
1364 | ||
1365 | return 0; | |
1366 | } | |
1367 | ||
ef8becfa LP |
1368 | int open_parent(const char *path, int flags, mode_t mode) { |
1369 | _cleanup_free_ char *parent = NULL; | |
1370 | int fd; | |
1371 | ||
1372 | if (isempty(path)) | |
1373 | return -EINVAL; | |
1374 | if (path_equal(path, "/")) /* requesting the parent of the root dir is fishy, let's prohibit that */ | |
1375 | return -EINVAL; | |
1376 | ||
1377 | parent = dirname_malloc(path); | |
1378 | if (!parent) | |
1379 | return -ENOMEM; | |
1380 | ||
1381 | /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an | |
1382 | * O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */ | |
1383 | ||
0c21dafb | 1384 | if (FLAGS_SET(flags, O_PATH)) |
ef8becfa | 1385 | flags |= O_DIRECTORY; |
0c21dafb | 1386 | else if (!FLAGS_SET(flags, O_TMPFILE)) |
ef8becfa LP |
1387 | flags |= O_DIRECTORY|O_RDONLY; |
1388 | ||
1389 | fd = open(parent, flags, mode); | |
1390 | if (fd < 0) | |
1391 | return -errno; | |
1392 | ||
1393 | return fd; | |
1394 | } |