• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 19315930715

12 Nov 2025 11:39PM UTC coverage: 72.251% (-0.2%) from 72.412%
19315930715

push

github

bluca
mkosi: update debian commit reference to efdd7a637

* efdd7a6377 Install new file for upstream build
* 9ebdc6099e d/rules: enable 10-systemd-logind-root-ignore-inhibitors.rules.example on Ubuntu
* 1255cc7663 initramfs-tools: only skip chzdev rules if zdev_early=0
* 4675b281ee d/t/boot-and-services: skip apparmor test on armhf
* 214d6e37b2 d/t/boot-and-services: run transient unit to check syslog messages
* f4e196aa26 d/t/boot-and-services: tweak test_rsyslog regex
* dbd366a43e Install new files for upstream build
* bb7f8ef532 Install new files for upstream build
* efa7cee8a7 Install new file for upstream build
* 95aa1d1685 Install new file for upstream build
* b770f0f01b kernel-install: skip 55-initrd.install when an initrd generator is configured
* af8d1e3134 Update changelog for 258.1-2 release
* 2d0e73cd14 d/libnss-systemd.postinst: Ensure module is enabled for all four databases

306471 of 424176 relevant lines covered (72.25%)

1239443.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.41
/src/basic/fs-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <linux/falloc.h>
4
#include <stdlib.h>
5
#include <sys/file.h>
6
#include <unistd.h>
7

8
#include "alloc-util.h"
9
#include "btrfs.h"
10
#include "chattr-util.h"
11
#include "dirent-util.h"
12
#include "errno-util.h"
13
#include "fd-util.h"
14
#include "fs-util.h"
15
#include "hostname-util.h"
16
#include "label.h"
17
#include "lock-util.h"
18
#include "log.h"
19
#include "mkdir.h"
20
#include "path-util.h"
21
#include "process-util.h"
22
#include "random-util.h"
23
#include "ratelimit.h"
24
#include "stat-util.h"
25
#include "string-util.h"
26
#include "strv.h"
27
#include "time-util.h"
28
#include "tmpfile-util.h"
29
#include "umask-util.h"
30

31
int rmdir_parents(const char *path, const char *stop) {
59,304✔
32
        char *p;
59,304✔
33
        int r;
59,304✔
34

35
        assert(path);
59,304✔
36
        assert(stop);
59,304✔
37

38
        if (!path_is_safe(path))
59,304✔
39
                return -EINVAL;
40

41
        if (!path_is_safe(stop))
59,303✔
42
                return -EINVAL;
43

44
        p = strdupa_safe(path);
59,302✔
45

46
        for (;;) {
2,975✔
47
                char *slash = NULL;
62,277✔
48

49
                /* skip the last component. */
50
                r = path_find_last_component(p, /* accept_dot_dot= */ false, (const char **) &slash, NULL);
62,277✔
51
                if (r <= 0)
62,277✔
52
                        return r;
59,302✔
53
                if (slash == p)
62,277✔
54
                        return 0;
55

56
                assert(*slash == '/');
62,277✔
57
                *slash = '\0';
62,277✔
58

59
                if (path_startswith_full(stop, p, PATH_STARTSWITH_REFUSE_DOT_DOT))
62,277✔
60
                        return 0;
61

62
                if (rmdir(p) < 0 && errno != ENOENT)
62,141✔
63
                        return -errno;
59,166✔
64
        }
65
}
66

67
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
112✔
68
        int r;
112✔
69

70
        assert(olddirfd >= 0 || olddirfd == AT_FDCWD);
112✔
71
        assert(oldpath);
112✔
72
        assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
112✔
73
        assert(newpath);
112✔
74

75
        /* Try the ideal approach first */
76
        if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) >= 0)
112✔
77
                return 0;
78

79
        /* renameat2() exists since Linux 3.15, btrfs and FAT added support for it later. If it is not implemented,
80
         * fall back to a different method. */
81
        if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EINVAL)
45✔
82
                return -errno;
45✔
83

84
        /* Let's try to use linkat()+unlinkat() as fallback. This doesn't work on directories and on some file systems
85
         * that do not support hard links (such as FAT, most prominently), but for files it's pretty close to what we
86
         * want — though not atomic (i.e. for a short period both the new and the old filename will exist). */
87
        if (linkat(olddirfd, oldpath, newdirfd, newpath, 0) >= 0) {
×
88

89
                r = RET_NERRNO(unlinkat(olddirfd, oldpath, 0));
×
90
                if (r < 0) {
×
91
                        (void) unlinkat(newdirfd, newpath, 0);
×
92
                        return r;
×
93
                }
94

95
                return 0;
×
96
        }
97

98
        if (!ERRNO_IS_NOT_SUPPORTED(errno) && !IN_SET(errno, EINVAL, EPERM)) /* FAT returns EPERM on link()… */
×
99
                return -errno;
×
100

101
        /* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fall back to the racy TOCTOU
102
         * vulnerable accessat(F_OK) check followed by classic, replacing renameat(), we have nothing better. */
103

104
        if (faccessat(newdirfd, newpath, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
×
105
                return -EEXIST;
106
        if (errno != ENOENT)
×
107
                return -errno;
×
108

109
        return RET_NERRNO(renameat(olddirfd, oldpath, newdirfd, newpath));
×
110
}
111

112
int readlinkat_malloc(int fd, const char *p, char **ret) {
5,651,138✔
113
        size_t l = PATH_MAX;
5,651,138✔
114

115
        assert(fd >= 0 || fd == AT_FDCWD);
5,651,138✔
116

117
        if (fd < 0 && isempty(p))
5,651,138✔
118
                return -EISDIR; /* In this case, the fd points to the current working directory, and is
119
                                 * definitely not a symlink. Let's return earlier. */
120

121
        for (;;) {
5,651,138✔
122
                _cleanup_free_ char *c = NULL;
5,651,138✔
123
                ssize_t n;
5,651,138✔
124

125
                c = new(char, l+1);
5,651,138✔
126
                if (!c)
5,651,138✔
127
                        return -ENOMEM;
128

129
                n = readlinkat(fd, strempty(p), c, l);
5,651,140✔
130
                if (n < 0)
5,651,138✔
131
                        return -errno;
361,327✔
132

133
                if ((size_t) n < l) {
5,289,811✔
134
                        c[n] = 0;
5,289,811✔
135

136
                        if (ret)
5,289,811✔
137
                                *ret = TAKE_PTR(c);
5,289,811✔
138

139
                        return 0;
5,289,811✔
140
                }
141

142
                if (l > (SSIZE_MAX-1)/2) /* readlinkat() returns an ssize_t, and we want an extra byte for a
×
143
                                          * trailing NUL, hence do an overflow check relative to SSIZE_MAX-1
144
                                          * here */
145
                        return -EFBIG;
146

147
                l *= 2;
×
148
        }
149
}
150

151
int readlink_value(const char *p, char **ret) {
599,217✔
152
        _cleanup_free_ char *link = NULL, *name = NULL;
599,217✔
153
        int r;
599,217✔
154

155
        assert(p);
599,217✔
156
        assert(ret);
599,217✔
157

158
        r = readlink_malloc(p, &link);
599,217✔
159
        if (r < 0)
599,217✔
160
                return r;
161

162
        r = path_extract_filename(link, &name);
355,402✔
163
        if (r < 0)
355,402✔
164
                return r;
165
        if (r == O_DIRECTORY)
355,402✔
166
                return -EINVAL;
167

168
        *ret = TAKE_PTR(name);
355,402✔
169
        return 0;
355,402✔
170
}
171

172
int readlink_and_make_absolute(const char *p, char **ret) {
3,091✔
173
        _cleanup_free_ char *target = NULL;
3,091✔
174
        int r;
3,091✔
175

176
        assert(p);
3,091✔
177
        assert(ret);
3,091✔
178

179
        r = readlink_malloc(p, &target);
3,091✔
180
        if (r < 0)
3,091✔
181
                return r;
182

183
        return file_in_same_dir(p, target, ret);
28✔
184
}
185

186
int chmod_and_chown_at(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid) {
20,771✔
187
        _cleanup_close_ int fd = -EBADF;
20,771✔
188

189
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
20,771✔
190

191
        if (path) {
20,771✔
192
                /* Let's acquire an O_PATH fd, as precaution to change mode/owner on the same file */
193
                fd = openat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
20,771✔
194
                if (fd < 0)
20,771✔
195
                        return -errno;
×
196
                dir_fd = fd;
197

198
        } else if (dir_fd == AT_FDCWD) {
×
199
                /* Let's acquire an O_PATH fd of the current directory */
200
                fd = openat(dir_fd, ".", O_PATH|O_CLOEXEC|O_NOFOLLOW|O_DIRECTORY);
×
201
                if (fd < 0)
×
202
                        return -errno;
×
203
                dir_fd = fd;
204
        }
205

206
        return fchmod_and_chown(dir_fd, mode, uid, gid);
20,771✔
207
}
208

209
int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t uid, gid_t gid) {
124,747✔
210
        bool do_chown, do_chmod;
124,747✔
211
        struct stat st;
124,747✔
212
        int r;
124,747✔
213

214
        /* Change ownership and access mode of the specified fd. Tries to do so safely, ensuring that at no
215
         * point in time the access mode is above the old access mode under the old ownership or the new
216
         * access mode under the new ownership. Note: this call tries hard to leave the access mode
217
         * unaffected if the uid/gid is changed, i.e. it undoes implicit suid/sgid dropping the kernel does
218
         * on chown().
219
         *
220
         * This call is happy with O_PATH fds.
221
         *
222
         * If path is given, allow a fallback path which does not use /proc/self/fd/. On any normal system
223
         * /proc will be mounted, but in certain improperly assembled environments it might not be. This is
224
         * less secure (potential TOCTOU), so should only be used after consideration. */
225

226
        if (fstat(fd, &st) < 0)
124,747✔
227
                return -errno;
×
228

229
        do_chown =
249,494✔
230
                (uid != UID_INVALID && st.st_uid != uid) ||
124,747✔
231
                (gid != GID_INVALID && st.st_gid != gid);
29,437✔
232

233
        do_chmod =
249,494✔
234
                !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */
124,747✔
235
                ((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) ||
120,070✔
236
                 do_chown); /* If we change ownership, make sure we reset the mode afterwards, since chown()
237
                             * modifies the access mode too */
238

239
        if (mode == MODE_INVALID)
124,747✔
240
                mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */
241
        else if ((mode & S_IFMT) != 0 && ((mode ^ st.st_mode) & S_IFMT) != 0)
85,128✔
242
                return -EINVAL; /* insist on the right file type if it was specified */
243

244
        if (do_chown && do_chmod) {
124,743✔
245
                mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */
13,047✔
246

247
                if (((minimal ^ st.st_mode) & 07777) != 0) {
13,047✔
248
                        r = fchmod_opath(fd, minimal & 07777);
8✔
249
                        if (r < 0) {
8✔
250
                                if (!path || r != -ENOSYS)
×
251
                                        return r;
252

253
                                /* Fallback path which doesn't use /proc/self/fd/. */
254
                                if (chmod(path, minimal & 07777) < 0)
×
255
                                        return -errno;
×
256
                        }
257
                }
258
        }
259

260
        if (do_chown)
124,743✔
261
                if (fchownat(fd, "", uid, gid, AT_EMPTY_PATH) < 0)
13,050✔
262
                        return -errno;
1✔
263

264
        if (do_chmod) {
124,742✔
265
                r = fchmod_opath(fd, mode & 07777);
27,810✔
266
                if (r < 0) {
27,810✔
267
                        if (!path || r != -ENOSYS)
×
268
                                return r;
269

270
                        /* Fallback path which doesn't use /proc/self/fd/. */
271
                        if (chmod(path, mode & 07777) < 0)
×
272
                                return -errno;
×
273
                }
274
        }
275

276
        return do_chown || do_chmod;
124,742✔
277
}
278

279
int fchmod_umask(int fd, mode_t m) {
2,857✔
280
        _cleanup_umask_ mode_t u = umask(0777);
2,857✔
281

282
        return RET_NERRNO(fchmod(fd, m & (~u)));
2,857✔
283
}
284

285
int fchmod_opath(int fd, mode_t m) {
30,523✔
286
        /* This function operates also on fd that might have been opened with
287
         * O_PATH. The tool set we have is non-intuitive:
288
         * - fchmod(2) only operates on open files (i. e., fds with an open file description);
289
         * - fchmodat(2) does not have a flag arg like fchownat(2) does, so no way to pass AT_EMPTY_PATH;
290
         *   + it should not be confused with the libc fchmodat(3) interface, which adds 4th flag argument,
291
         *     and supports AT_EMPTY_PATH since v2.39 (previously only supported AT_SYMLINK_NOFOLLOW). So if
292
         *     the kernel has fchmodat2(2), since v2.39 glibc will call into it directly. If the kernel
293
         *     doesn't, or glibc is older than v2.39, glibc's internal fallback will return EINVAL if
294
         *     AT_EMPTY_PATH is passed.
295
         * - fchmodat2(2) supports all the AT_* flags, but is still very recent.
296
         *
297
         * We try to use fchmodat(3) first, and on EINVAL fall back to fchmodat2(), and, if that is also not
298
         * supported, resort to the /proc/self/fd dance. */
299

300
        assert(fd >= 0);
30,523✔
301

302
        if (fchmodat(fd, "", m, AT_EMPTY_PATH) >= 0)
30,523✔
303
                return 0;
304
        if (errno == EINVAL && fchmodat2(fd, "", m, AT_EMPTY_PATH) >= 0) /* glibc too old? */
2✔
305
                return 0;
306
        if (!IN_SET(errno, ENOSYS, EPERM)) /* Some container managers block unknown syscalls with EPERM */
2✔
307
                return -errno;
2✔
308

309
        if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
×
310
                if (errno != ENOENT)
×
311
                        return -errno;
×
312

313
                return proc_fd_enoent_errno();
×
314
        }
315

316
        return 0;
×
317
}
318

319
int futimens_opath(int fd, const struct timespec ts[2]) {
129,224✔
320
        /* Similar to fchmod_opath() but for futimens() */
321

322
        assert(fd >= 0);
129,224✔
323

324
        if (utimensat(fd, "", ts, AT_EMPTY_PATH) >= 0)
129,224✔
325
                return 0;
326
        if (errno != EINVAL)
×
327
                return -errno;
×
328

329
        /* Support for AT_EMPTY_PATH is added rather late (kernel 5.8), so fall back to going through /proc/
330
         * if unavailable. */
331

332
        if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, /* flags = */ 0) < 0) {
×
333
                if (errno != ENOENT)
×
334
                        return -errno;
×
335

336
                return proc_fd_enoent_errno();
×
337
        }
338

339
        return 0;
×
340
}
341

342
int stat_warn_permissions(const char *path, const struct stat *st) {
91,185✔
343
        assert(path);
91,185✔
344
        assert(st);
91,185✔
345

346
        /* Don't complain if we are reading something that is not a file, for example /dev/null */
347
        if (!S_ISREG(st->st_mode))
91,185✔
348
                return 0;
349

350
        if (st->st_mode & 0111)
91,185✔
351
                log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
×
352

353
        if (st->st_mode & 0002)
91,185✔
354
                log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
×
355

356
        if (getpid_cached() == 1 && (st->st_mode & 0044) != 0044)
91,185✔
357
                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);
13✔
358

359
        return 0;
360
}
361

362
int fd_warn_permissions(const char *path, int fd) {
×
363
        struct stat st;
×
364

365
        assert(path);
×
366
        assert(fd >= 0);
×
367

368
        if (fstat(fd, &st) < 0)
×
369
                return -errno;
×
370

371
        return stat_warn_permissions(path, &st);
×
372
}
373

374
int access_nofollow(const char *path, int mode) {
23,833✔
375
        return RET_NERRNO(faccessat(AT_FDCWD, path, mode, AT_SYMLINK_NOFOLLOW));
23,833✔
376
}
377

378
int touch_fd(int fd, usec_t stamp) {
74,135✔
379
        assert(fd >= 0);
74,135✔
380

381
        if (stamp == USEC_INFINITY)
74,135✔
382
                return futimens_opath(fd, /* ts= */ NULL);
73,564✔
383

384
        struct timespec ts[2];
571✔
385
        timespec_store(ts + 0, stamp);
571✔
386
        ts[1] = ts[0];
571✔
387
        return futimens_opath(fd, ts);
571✔
388
}
389

390
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
74,139✔
391
        _cleanup_close_ int fd = -EBADF;
74,139✔
392
        int ret;
74,139✔
393

394
        assert(path);
74,139✔
395

396
        /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink
397
         * itself which is updated, not its target
398
         *
399
         * Returns the first error we encounter, but tries to apply as much as possible. */
400

401
        if (parents)
74,139✔
402
                (void) mkdir_parents(path, 0755);
33,990✔
403

404
        /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in
405
         * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and
406
         * won't trigger any driver magic or so. */
407
        fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
74,139✔
408
        if (fd < 0) {
74,139✔
409
                if (errno != ENOENT)
48,988✔
410
                        return -errno;
×
411

412
                /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file
413
                 * here, and nothing else */
414
                fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
48,988✔
415
                if (fd < 0)
48,988✔
416
                        return -errno;
4✔
417
        }
418

419
        /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
420
         * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is
421
         * something fchown(), fchmod(), futimensat() don't allow. */
422
        ret = fchmod_and_chown(fd, mode, uid, gid);
74,135✔
423

424
        return RET_GATHER(ret, touch_fd(fd, stamp));
74,135✔
425
}
426

427
int touch(const char *path) {
39,558✔
428
        return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
39,558✔
429
}
430

431
int symlinkat_idempotent(const char *target, int atfd, const char *linkpath, bool make_relative) {
289✔
432
        _cleanup_free_ char *relpath = NULL;
289✔
433
        int r;
289✔
434

435
        assert(target);
289✔
436
        assert(linkpath);
289✔
437

438
        if (make_relative) {
289✔
439
                r = path_make_relative_parent(linkpath, target, &relpath);
185✔
440
                if (r < 0)
185✔
441
                        return r;
442

443
                target = relpath;
185✔
444
        }
445

446
        if (symlinkat(target, atfd, linkpath) < 0) {
289✔
447
                _cleanup_free_ char *p = NULL;
48✔
448

449
                if (errno != EEXIST)
48✔
450
                        return -errno;
×
451

452
                r = readlinkat_malloc(atfd, linkpath, &p);
48✔
453
                if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */
48✔
454
                        return -EEXIST;
455
                if (r < 0) /* Any other error? In that case propagate it as is */
48✔
456
                        return r;
457

458
                if (!streq(p, target)) /* Not the symlink we want it to be? In that case, propagate the original -EEXIST */
48✔
459
                        return -EEXIST;
460
        }
461

462
        return 0;
463
}
464

465
int symlinkat_atomic_full(const char *target, int atfd, const char *linkpath, SymlinkFlags flags) {
133,501✔
466
        int r;
133,501✔
467

468
        assert(target);
133,501✔
469
        assert(linkpath);
133,501✔
470

471
        _cleanup_free_ char *relpath = NULL;
133,501✔
472
        if (FLAGS_SET(flags, SYMLINK_MAKE_RELATIVE)) {
133,501✔
473
                r = path_make_relative_parent(linkpath, target, &relpath);
126,362✔
474
                if (r < 0)
126,362✔
475
                        return r;
476

477
                target = relpath;
126,362✔
478
        }
479

480
        _cleanup_free_ char *t = NULL;
133,501✔
481
        r = tempfn_random(linkpath, NULL, &t);
133,501✔
482
        if (r < 0)
133,501✔
483
                return r;
484

485
        bool call_label_ops_post = false;
133,501✔
486
        if (FLAGS_SET(flags, SYMLINK_LABEL)) {
133,501✔
487
                r = label_ops_pre(atfd, linkpath, S_IFLNK);
128,445✔
488
                if (r < 0)
128,445✔
489
                        return r;
490

491
                call_label_ops_post = true;
492
        }
493

494
        r = RET_NERRNO(symlinkat(target, atfd, t));
133,501✔
495
        if (call_label_ops_post)
133,501✔
496
                RET_GATHER(r, label_ops_post(atfd, t, /* created= */ r >= 0));
128,445✔
497
        if (r < 0)
133,501✔
498
                return r;
×
499

500
        r = RET_NERRNO(renameat(atfd, t, atfd, linkpath));
133,502✔
501
        if (r < 0) {
1✔
502
                (void) unlinkat(atfd, t, 0);
1✔
503
                return r;
1✔
504
        }
505

506
        return 0;
507
}
508

509
int mknodat_atomic(int atfd, const char *path, mode_t mode, dev_t dev) {
×
510
        _cleanup_free_ char *t = NULL;
×
511
        int r;
×
512

513
        assert(path);
×
514

515
        r = tempfn_random(path, NULL, &t);
×
516
        if (r < 0)
×
517
                return r;
518

519
        if (mknodat(atfd, t, mode, dev) < 0)
×
520
                return -errno;
×
521

522
        r = RET_NERRNO(renameat(atfd, t, atfd, path));
×
523
        if (r < 0) {
×
524
                (void) unlinkat(atfd, t, 0);
×
525
                return r;
×
526
        }
527

528
        return 0;
529
}
530

531
int mkfifoat_atomic(int atfd, const char *path, mode_t mode) {
1✔
532
        _cleanup_free_ char *t = NULL;
1✔
533
        int r;
1✔
534

535
        assert(path);
1✔
536

537
        /* We're only interested in the (random) filename.  */
538
        r = tempfn_random(path, NULL, &t);
1✔
539
        if (r < 0)
1✔
540
                return r;
541

542
        if (mkfifoat(atfd, t, mode) < 0)
1✔
543
                return -errno;
×
544

545
        r = RET_NERRNO(renameat(atfd, t, atfd, path));
1✔
546
        if (r < 0) {
×
547
                (void) unlinkat(atfd, t, 0);
×
548
                return r;
×
549
        }
550

551
        return 0;
552
}
553

554
int get_files_in_directory(const char *path, char ***ret_list) {
469✔
555
        _cleanup_strv_free_ char **l = NULL;
×
556
        _cleanup_closedir_ DIR *d = NULL;
469✔
557
        size_t n = 0;
469✔
558

559
        assert(path);
469✔
560

561
        /* Returns all files in a directory in *list, and the number
562
         * of files as return value. If list is NULL returns only the
563
         * number. */
564

565
        d = opendir(path);
469✔
566
        if (!d)
469✔
567
                return -errno;
2✔
568

569
        FOREACH_DIRENT_ALL(de, d, return -errno) {
1,941✔
570
                if (!dirent_is_file(de))
1,474✔
571
                        continue;
986✔
572

573
                if (ret_list) {
488✔
574
                        /* one extra slot is needed for the terminating NULL */
575
                        if (!GREEDY_REALLOC(l, n + 2))
475✔
576
                                return -ENOMEM;
577

578
                        l[n] = strdup(de->d_name);
475✔
579
                        if (!l[n])
475✔
580
                                return -ENOMEM;
581

582
                        l[++n] = NULL;
475✔
583
                } else
584
                        n++;
13✔
585
        }
586

587
        if (ret_list)
467✔
588
                *ret_list = TAKE_PTR(l);
464✔
589

590
        return n;
467✔
591
}
592

593
static int getenv_tmp_dir(const char **ret_path) {
2,360✔
594
        int r, ret = 0;
2,360✔
595

596
        assert(ret_path);
2,360✔
597

598
        /* We use the same order of environment variables python uses in tempfile.gettempdir():
599
         * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */
600
        FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") {
9,437✔
601
                const char *e;
7,078✔
602

603
                e = secure_getenv(n);
7,078✔
604
                if (!e)
7,078✔
605
                        continue;
7,076✔
606
                if (!path_is_absolute(e)) {
2✔
607
                        r = -ENOTDIR;
×
608
                        goto next;
×
609
                }
610
                if (!path_is_normalized(e)) {
2✔
611
                        r = -EPERM;
×
612
                        goto next;
×
613
                }
614

615
                r = is_dir(e, true);
2✔
616
                if (r < 0)
2✔
617
                        goto next;
1✔
618
                if (r == 0) {
1✔
619
                        r = -ENOTDIR;
×
620
                        goto next;
×
621
                }
622

623
                *ret_path = e;
1✔
624
                return 1;
1✔
625

626
        next:
1✔
627
                /* Remember first error, to make this more debuggable */
628
                if (ret >= 0)
1✔
629
                        ret = r;
1✔
630
        }
631

632
        if (ret < 0)
2,359✔
633
                return ret;
634

635
        *ret_path = NULL;
2,358✔
636
        return ret;
2,358✔
637
}
638

639
static int tmp_dir_internal(const char *def, const char **ret) {
2,360✔
640
        const char *e;
2,360✔
641
        int r, k;
2,360✔
642

643
        assert(def);
2,360✔
644
        assert(ret);
2,360✔
645

646
        r = getenv_tmp_dir(&e);
2,360✔
647
        if (r > 0) {
2,360✔
648
                *ret = e;
1✔
649
                return 0;
1✔
650
        }
651

652
        k = is_dir(def, /* follow = */ true);
2,359✔
653
        if (k == 0)
2,359✔
654
                k = -ENOTDIR;
655
        if (k < 0)
2,359✔
656
                return RET_GATHER(r, k);
×
657

658
        *ret = def;
2,359✔
659
        return 0;
2,359✔
660
}
661

662
int var_tmp_dir(const char **ret) {
2,220✔
663
        assert(ret);
2,220✔
664

665
        /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus
666
         * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is
667
         * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR,
668
         * making it a variable that overrides all temporary file storage locations. */
669

670
        return tmp_dir_internal("/var/tmp", ret);
2,220✔
671
}
672

673
int tmp_dir(const char **ret) {
140✔
674
        assert(ret);
140✔
675

676
        /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually
677
         * backed by an in-memory file system: /tmp. */
678

679
        return tmp_dir_internal("/tmp", ret);
140✔
680
}
681

682
int unlink_or_warn(const char *filename) {
184✔
683
        assert(filename);
184✔
684

685
        if (unlink(filename) < 0 && errno != ENOENT)
184✔
686
                /* If the file doesn't exist and the fs simply was read-only (in which
687
                 * case unlink() returns EROFS even if the file doesn't exist), don't
688
                 * complain */
689
                if (errno != EROFS || access(filename, F_OK) >= 0)
×
690
                        return log_error_errno(errno, "Failed to remove \"%s\": %m", filename);
×
691

692
        return 0;
693
}
694

695
char *rmdir_and_free(char *p) {
214,804✔
696
        PROTECT_ERRNO;
214,804✔
697

698
        if (!p)
214,804✔
699
                return NULL;
700

701
        (void) rmdir(p);
214,804✔
702
        return mfree(p);
214,804✔
703
}
704

705
char* unlink_and_free(char *p) {
6,554✔
706
        PROTECT_ERRNO;
6,554✔
707

708
        if (!p)
6,554✔
709
                return NULL;
710

711
        (void) unlink(p);
5,921✔
712
        return mfree(p);
5,921✔
713
}
714

715
int access_fd(int fd, int mode) {
27,912✔
716
        assert(fd >= 0);
27,912✔
717

718
        /* Like access() but operates on an already open fd */
719

720
        if (faccessat(fd, "", mode, AT_EMPTY_PATH) >= 0)
27,912✔
721
                return 0;
722
        if (errno != EINVAL)
1✔
723
                return -errno;
1✔
724

725
        /* Support for AT_EMPTY_PATH is added rather late (kernel 5.8), so fall back to going through /proc/
726
         * if unavailable. */
727

728
        if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) {
×
729
                if (errno != ENOENT)
×
730
                        return -errno;
×
731

732
                return proc_fd_enoent_errno();
×
733
        }
734

735
        return 0;
×
736
}
737

738
int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
83✔
739
        _cleanup_close_ int truncate_fd = -EBADF;
83✔
740
        struct stat st;
83✔
741
        off_t l, bs;
83✔
742

743
        assert(fd >= 0 || fd == AT_FDCWD);
83✔
744
        assert(name);
83✔
745
        assert((flags & ~(UNLINK_REMOVEDIR|UNLINK_ERASE)) == 0);
83✔
746

747
        /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other
748
         * link to it. This is useful to ensure that other processes that might have the file open for reading won't be
749
         * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up
750
         * jobs ("vacuuming"), where we want to make sure the data is really gone and the disk space released and
751
         * returned to the free pool.
752
         *
753
         * Deallocation is preferably done by FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE (👊) if supported, which means
754
         * the file won't change size. That's a good thing since we shouldn't needlessly trigger SIGBUS in other
755
         * programs that have mmap()ed the file. (The assumption here is that changing file contents to all zeroes
756
         * underneath those programs is the better choice than simply triggering SIGBUS in them which truncation does.)
757
         * However if hole punching is not implemented in the kernel or file system we'll fall back to normal file
758
         * truncation (🔪), as our goal of deallocating the data space trumps our goal of being nice to readers (💐).
759
         *
760
         * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the
761
         * primary job – to delete the file – is accomplished. */
762

763
        if (!FLAGS_SET(flags, UNLINK_REMOVEDIR)) {
83✔
764
                truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
83✔
765
                if (truncate_fd < 0) {
83✔
766

767
                        /* If this failed because the file doesn't exist propagate the error right-away. Also,
768
                         * AT_REMOVEDIR wasn't set, and we tried to open the file for writing, which means EISDIR is
769
                         * returned when this is a directory but we are not supposed to delete those, hence propagate
770
                         * the error right-away too. */
771
                        if (IN_SET(errno, ENOENT, EISDIR))
×
772
                                return -errno;
×
773

774
                        if (errno != ELOOP) /* don't complain if this is a symlink */
×
775
                                log_debug_errno(errno, "Failed to open file '%s' for deallocation, ignoring: %m", name);
×
776
                }
777
        }
778

779
        if (unlinkat(fd, name, FLAGS_SET(flags, UNLINK_REMOVEDIR) ? AT_REMOVEDIR : 0) < 0)
83✔
780
                return -errno;
×
781

782
        if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */
83✔
783
                return 0;
784

785
        if (fstat(truncate_fd, &st) < 0) {
83✔
786
                log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name);
×
787
                return 0;
×
788
        }
789

790
        if (!S_ISREG(st.st_mode))
83✔
791
                return 0;
792

793
        if (FLAGS_SET(flags, UNLINK_ERASE) && st.st_size > 0 && st.st_nlink == 0) {
83✔
794
                uint64_t left = st.st_size;
3✔
795
                char buffer[64 * 1024];
3✔
796

797
                /* If erasing is requested, let's overwrite the file with random data once before deleting
798
                 * it. This isn't going to give you shred(1) semantics, but hopefully should be good enough
799
                 * for stuff backed by tmpfs at least.
800
                 *
801
                 * Note that we only erase like this if the link count of the file is zero. If it is higher it
802
                 * is still linked by someone else and we'll leave it to them to remove it securely
803
                 * eventually! */
804

805
                random_bytes(buffer, sizeof(buffer));
3✔
806

807
                while (left > 0) {
6✔
808
                        ssize_t n;
3✔
809

810
                        n = write(truncate_fd, buffer, MIN(sizeof(buffer), left));
3✔
811
                        if (n < 0) {
3✔
812
                                log_debug_errno(errno, "Failed to erase data in file '%s', ignoring.", name);
×
813
                                break;
814
                        }
815

816
                        assert(left >= (size_t) n);
3✔
817
                        left -= n;
3✔
818
                }
819

820
                /* Let's refresh metadata */
821
                if (fstat(truncate_fd, &st) < 0) {
3✔
822
                        log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name);
×
823
                        return 0;
×
824
                }
825
        }
826

827
        /* Don't deallocate if there's nothing to deallocate or if the file is linked elsewhere */
828
        if (st.st_blocks == 0 || st.st_nlink > 0)
83✔
829
                return 0;
830

831
        /* If this is a regular file, it actually took up space on disk and there are no other links it's time to
832
         * punch-hole/truncate this to release the disk space. */
833

834
        bs = MAX(st.st_blksize, 512);
83✔
835
        l = ROUND_UP(st.st_size, bs); /* Round up to next block size */
83✔
836

837
        if (fallocate(truncate_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 0, l) >= 0)
83✔
838
                return 0; /* Successfully punched a hole! 😊 */
839

840
        /* Fall back to truncation */
841
        if (ftruncate(truncate_fd, 0) < 0) {
×
842
                log_debug_errno(errno, "Failed to truncate file to 0, ignoring: %m");
×
843
                return 0;
×
844
        }
845

846
        return 0;
847
}
848

849
int open_parent_at(int dir_fd, const char *path, int flags, mode_t mode) {
1,807,554✔
850
        _cleanup_free_ char *parent = NULL;
1,807,554✔
851
        int r;
1,807,554✔
852

853
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
1,807,554✔
854
        assert(path);
1,807,554✔
855

856
        r = path_extract_directory(path, &parent);
1,807,554✔
857
        if (r == -EDESTADDRREQ) {
1,807,554✔
858
                parent = strdup(".");
13,542✔
859
                if (!parent)
13,542✔
860
                        return -ENOMEM;
861
        } else if (r == -EADDRNOTAVAIL) {
1,794,012✔
862
                parent = strdup(path);
×
863
                if (!parent)
×
864
                        return -ENOMEM;
865
        } else if (r < 0)
1,794,012✔
866
                return r;
867

868
        /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an
869
         * O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */
870

871
        if (FLAGS_SET(flags, O_PATH))
1,807,554✔
872
                flags |= O_DIRECTORY;
×
873
        else if (!FLAGS_SET(flags, O_TMPFILE))
1,807,554✔
874
                flags |= O_DIRECTORY|O_RDONLY;
1,785,992✔
875

876
        return RET_NERRNO(openat(dir_fd, parent, flags, mode));
1,807,601✔
877
}
878

879
int conservative_renameat(
39,231✔
880
                int olddirfd, const char *oldpath,
881
                int newdirfd, const char *newpath) {
882

883
        _cleanup_close_ int old_fd = -EBADF, new_fd = -EBADF;
39,231✔
884
        struct stat old_stat, new_stat;
39,231✔
885

886
        /* Renames the old path to the new path, much like renameat() — except if both are regular files and
887
         * have the exact same contents and basic file attributes already. In that case remove the new file
888
         * instead. This call is useful for reducing inotify wakeups on files that are updated but don't
889
         * actually change. This function is written in a style that we rather rename too often than suppress
890
         * too much. I.e. whenever we are in doubt, we rather rename than fail. After all reducing inotify
891
         * events is an optimization only, not more. */
892

893
        old_fd = openat(olddirfd, oldpath, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_NOFOLLOW);
39,231✔
894
        if (old_fd < 0)
39,231✔
895
                goto do_rename;
×
896

897
        new_fd = openat(newdirfd, newpath, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_NOFOLLOW);
39,231✔
898
        if (new_fd < 0)
39,231✔
899
                goto do_rename;
3,404✔
900

901
        if (fstat(old_fd, &old_stat) < 0)
35,827✔
902
                goto do_rename;
×
903

904
        if (!S_ISREG(old_stat.st_mode))
35,827✔
905
                goto do_rename;
×
906

907
        if (fstat(new_fd, &new_stat) < 0)
35,827✔
908
                goto do_rename;
×
909

910
        if (stat_inode_same(&new_stat, &old_stat))
35,827✔
911
                goto is_same;
1✔
912

913
        if (old_stat.st_mode != new_stat.st_mode ||
35,826✔
914
            old_stat.st_size != new_stat.st_size ||
35,826✔
915
            old_stat.st_uid != new_stat.st_uid ||
24,935✔
916
            old_stat.st_gid != new_stat.st_gid)
24,935✔
917
                goto do_rename;
10,891✔
918

919
        for (;;) {
6✔
920
                uint8_t buf1[16*1024];
24,941✔
921
                uint8_t buf2[sizeof(buf1)];
24,941✔
922
                ssize_t l1, l2;
24,941✔
923

924
                l1 = read(old_fd, buf1, sizeof(buf1));
24,941✔
925
                if (l1 < 0)
24,941✔
926
                        goto do_rename;
320✔
927

928
                if (l1 == sizeof(buf1))
24,941✔
929
                        /* Read the full block, hence read a full block in the other file too */
930

931
                        l2 = read(new_fd, buf2, l1);
7✔
932
                else {
933
                        assert((size_t) l1 < sizeof(buf1));
24,934✔
934

935
                        /* Short read. This hence was the last block in the first file, and then came
936
                         * EOF. Read one byte more in the second file, so that we can verify we hit EOF there
937
                         * too. */
938

939
                        assert((size_t) (l1 + 1) <= sizeof(buf2));
24,934✔
940
                        l2 = read(new_fd, buf2, l1 + 1);
24,934✔
941
                }
942
                if (l2 != l1)
24,941✔
943
                        goto do_rename;
×
944

945
                if (memcmp(buf1, buf2, l1) != 0)
24,941✔
946
                        goto do_rename;
320✔
947

948
                if ((size_t) l1 < sizeof(buf1)) /* We hit EOF on the first file, and the second file too, hence exit
24,621✔
949
                                                 * now. */
950
                        break;
951
        }
952

953
is_same:
24,616✔
954
        /* Everything matches? Then don't rename, instead remove the source file, and leave the existing
955
         * destination in place */
956

957
        if (unlinkat(olddirfd, oldpath, 0) < 0)
24,616✔
958
                goto do_rename;
×
959

960
        return 0;
961

962
do_rename:
14,615✔
963
        if (renameat(olddirfd, oldpath, newdirfd, newpath) < 0)
14,615✔
964
                return -errno;
×
965

966
        return 1;
967
}
968

969
int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size) {
1,705✔
970
        RateLimit rl;
1,705✔
971
        int r;
1,705✔
972

973
        r = posix_fallocate(fd, offset, size); /* returns positive errnos on error */
1,705✔
974
        if (r != EINTR)
1,705✔
975
                return -r; /* Let's return negative errnos, like common in our codebase */
1,705✔
976

977
        /* On EINTR try a couple of times more, but protect against busy looping
978
         * (not more than 16 times per 10s) */
979
        rl = (const RateLimit) { 10 * USEC_PER_SEC, 16 };
×
980
        while (ratelimit_below(&rl)) {
×
981
                r = posix_fallocate(fd, offset, size);
×
982
                if (r != EINTR)
×
983
                        return -r;
×
984
        }
985

986
        return -EINTR;
987
}
988

989
int parse_cifs_service(
15✔
990
                const char *s,
991
                char **ret_host,
992
                char **ret_service,
993
                char **ret_path) {
994

995
        _cleanup_free_ char *h = NULL, *ss = NULL, *x = NULL;
15✔
996
        const char *p, *e, *d;
15✔
997
        char delimiter;
15✔
998

999
        /* Parses a CIFS service in form of //host/service/path… and splitting it in three parts. The last
1000
         * part is optional, in which case NULL is returned there. To maximize compatibility syntax with
1001
         * backslashes instead of slashes is accepted too. */
1002

1003
        if (!s)
15✔
1004
                return -EINVAL;
1005

1006
        p = startswith(s, "//");
14✔
1007
        if (!p) {
14✔
1008
                p = startswith(s, "\\\\");
6✔
1009
                if (!p)
6✔
1010
                        return -EINVAL;
1011
        }
1012

1013
        delimiter = s[0];
11✔
1014
        e = strchr(p, delimiter);
11✔
1015
        if (!e)
11✔
1016
                return -EINVAL;
1017

1018
        h = strndup(p, e - p);
11✔
1019
        if (!h)
11✔
1020
                return -ENOMEM;
1021

1022
        if (!hostname_is_valid(h, 0))
11✔
1023
                return -EINVAL;
1024

1025
        e++;
10✔
1026

1027
        d = strchrnul(e, delimiter);
10✔
1028

1029
        ss = strndup(e, d - e);
10✔
1030
        if (!ss)
10✔
1031
                return -ENOMEM;
1032

1033
        if (!filename_is_valid(ss))
10✔
1034
                return -EINVAL;
1035

1036
        if (!isempty(d)) {
8✔
1037
                x = strdup(skip_leading_chars(d, CHAR_TO_STR(delimiter)));
6✔
1038
                if (!x)
6✔
1039
                        return -EINVAL;
2✔
1040

1041
                /* Make sure to convert Windows-style "\" → Unix-style / */
1042
                for (char *i = x; *i; i++)
33✔
1043
                        if (*i == delimiter)
27✔
1044
                                *i = '/';
3✔
1045

1046
                if (!path_is_valid(x))
6✔
1047
                        return -EINVAL;
1048

1049
                path_simplify(x);
6✔
1050
                if (!path_is_normalized(x))
6✔
1051
                        return -EINVAL;
1052
        }
1053

1054
        if (ret_host)
6✔
1055
                *ret_host = TAKE_PTR(h);
6✔
1056
        if (ret_service)
6✔
1057
                *ret_service = TAKE_PTR(ss);
6✔
1058
        if (ret_path)
6✔
1059
                *ret_path = TAKE_PTR(x);
6✔
1060

1061
        return 0;
1062
}
1063

1064
int open_mkdir_at_full(int dirfd, const char *path, int flags, XOpenFlags xopen_flags, mode_t mode) {
220,010✔
1065
        _cleanup_close_ int fd = -EBADF, parent_fd = -EBADF;
220,010✔
1066
        _cleanup_free_ char *fname = NULL, *parent = NULL;
220,010✔
1067
        int r;
220,010✔
1068

1069
        /* Creates a directory with mkdirat() and then opens it, in the "most atomic" fashion we can
1070
         * do. Guarantees that the returned fd refers to a directory. If O_EXCL is specified will fail if the
1071
         * dir already exists. Otherwise will open an existing dir, but only if it is one.  */
1072

1073
        if (flags & ~(O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_EXCL|O_NOATIME|O_NOFOLLOW|O_PATH))
220,010✔
1074
                return -EINVAL;
1075
        if ((flags & O_ACCMODE_STRICT) != O_RDONLY)
220,010✔
1076
                return -EINVAL;
1077

1078
        /* Note that O_DIRECTORY|O_NOFOLLOW is implied, but we allow specifying it anyway. The following
1079
         * flags actually make sense to specify: O_CLOEXEC, O_EXCL, O_NOATIME, O_PATH */
1080

1081
        /* If this is not a valid filename, it's a path. Let's open the parent directory then, so
1082
         * that we can pin it, and operate below it. */
1083
        r = path_extract_directory(path, &parent);
220,010✔
1084
        if (r < 0) {
220,010✔
1085
                if (!IN_SET(r, -EDESTADDRREQ, -EADDRNOTAVAIL))
7,775✔
1086
                        return r;
1087
        } else {
1088
                r = path_extract_filename(path, &fname);
212,235✔
1089
                if (r < 0)
212,235✔
1090
                        return r;
1091

1092
                parent_fd = openat(dirfd, parent, O_PATH|O_DIRECTORY|O_CLOEXEC);
212,235✔
1093
                if (parent_fd < 0)
212,235✔
1094
                        return -errno;
×
1095

1096
                dirfd = parent_fd;
212,235✔
1097
                path = fname;
212,235✔
1098
        }
1099

1100
        fd = xopenat_full(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, xopen_flags, mode);
220,010✔
1101
        if (IN_SET(fd, -ELOOP, -ENOTDIR))
220,010✔
1102
                return -EEXIST;
1103
        if (fd < 0)
220,009✔
1104
                return fd;
4,600✔
1105

1106
        return TAKE_FD(fd);
1107
}
1108

1109
int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created) {
2,501,387✔
1110
        int fd;
2,501,387✔
1111

1112
        /* Just like openat(), but adds one thing: optionally returns whether we created the file anew or if
1113
         * it already existed before. This is only relevant if O_CREAT is set without O_EXCL, and thus will
1114
         * shortcut to openat() otherwise.
1115
         *
1116
         * Note that this routine is a bit more strict with symlinks than regular openat() is. If O_NOFOLLOW
1117
         * is not specified, then we'll follow the symlink when opening an existing file but we will *not*
1118
         * follow it when creating a new one (because that's a terrible UNIX misfeature and generally a
1119
         * security hole). */
1120

1121
        if (!FLAGS_SET(flags, O_CREAT) || FLAGS_SET(flags, O_EXCL)) {
2,501,387✔
1122
                fd = openat(dirfd, pathname, flags, mode);
490,954✔
1123
                if (fd < 0)
490,954✔
1124
                        return -errno;
41,694✔
1125

1126
                if (ret_newly_created)
449,260✔
1127
                        *ret_newly_created = FLAGS_SET(flags, O_CREAT);
449,260✔
1128
                return fd;
449,260✔
1129
        }
1130

1131
        for (unsigned attempts = 7;;) {
1132
                /* First, attempt to open without O_CREAT/O_EXCL, i.e. open existing file */
1133
                fd = openat(dirfd, pathname, flags & ~(O_CREAT | O_EXCL), mode);
2,010,716✔
1134
                if (fd >= 0) {
2,010,716✔
1135
                        if (ret_newly_created)
1,795,163✔
1136
                                *ret_newly_created = false;
1,795,163✔
1137
                        return fd;
1,795,163✔
1138
                }
1139
                if (errno != ENOENT)
215,553✔
1140
                        return -errno;
×
1141

1142
                /* So the file didn't exist yet, hence create it with O_CREAT/O_EXCL/O_NOFOLLOW. */
1143
                fd = openat(dirfd, pathname, flags | O_CREAT | O_EXCL | O_NOFOLLOW, mode);
215,553✔
1144
                if (fd >= 0) {
215,553✔
1145
                        if (ret_newly_created)
215,268✔
1146
                                *ret_newly_created = true;
215,267✔
1147
                        return fd;
215,268✔
1148
                }
1149
                if (errno != EEXIST)
285✔
1150
                        return -errno;
×
1151

1152
                /* Hmm, so now we got EEXIST? Then someone might have created the file between the first and
1153
                 * second call to openat(). Let's try again but with a limit so we don't spin forever. */
1154

1155
                if (--attempts == 0) /* Give up eventually, somebody is playing with us */
285✔
1156
                        return -EEXIST;
1157
        }
1158
}
1159

1160
int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode) {
1,063,792✔
1161
        _cleanup_close_ int fd = -EBADF;
1,063,792✔
1162
        bool made_dir = false, made_file = false;
1,063,792✔
1163
        int r;
1,063,792✔
1164

1165
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
1,063,792✔
1166

1167
        /* An inode cannot be both a directory and a regular file at the same time. */
1168
        assert(!(FLAGS_SET(open_flags, O_DIRECTORY) && FLAGS_SET(xopen_flags, XO_REGULAR)));
1,063,792✔
1169

1170
        /* This is like openat(), but has a few tricks up its sleeves, extending behaviour:
1171
         *
1172
         *   • O_DIRECTORY|O_CREAT is supported, which causes a directory to be created, and immediately
1173
         *     opened. When used with the XO_SUBVOLUME flag this will even create a btrfs subvolume.
1174
         *
1175
         *   • If O_CREAT is used with XO_LABEL, any created file will be immediately relabelled.
1176
         *
1177
         *   • If the path is specified NULL or empty, behaves like fd_reopen().
1178
         *
1179
         *   • If XO_NOCOW is specified will turn on the NOCOW btrfs flag on the file, if available.
1180
         *
1181
         *   • if XO_REGULAR is specified will return an error if inode is not a regular file.
1182
         *
1183
         *   • If mode is specified as MODE_INVALID, we'll use 0755 for dirs, and 0644 for regular files.
1184
         */
1185

1186
        if (mode == MODE_INVALID)
1,063,792✔
1187
                mode = (open_flags & O_DIRECTORY) ? 0755 : 0644;
43,512✔
1188

1189
        if (isempty(path)) {
1,063,792✔
1190
                assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL));
6,455✔
1191

1192
                if (FLAGS_SET(xopen_flags, XO_REGULAR)) {
6,455✔
1193
                        r = fd_verify_regular(dir_fd);
45✔
1194
                        if (r < 0)
45✔
1195
                                return r;
1196
                }
1197

1198
                return fd_reopen(dir_fd, open_flags & ~O_NOFOLLOW);
6,455✔
1199
        }
1200

1201
        bool call_label_ops_post = false;
1,057,337✔
1202

1203
        if (FLAGS_SET(open_flags, O_CREAT) && FLAGS_SET(xopen_flags, XO_LABEL)) {
1,057,337✔
1204
                r = label_ops_pre(dir_fd, path, FLAGS_SET(open_flags, O_DIRECTORY) ? S_IFDIR : S_IFREG);
584✔
1205
                if (r < 0)
584✔
1206
                        return r;
1207

1208
                call_label_ops_post = true;
1209
        }
1210

1211
        if (FLAGS_SET(open_flags, O_DIRECTORY|O_CREAT)) {
1,057,337✔
1212
                if (FLAGS_SET(xopen_flags, XO_SUBVOLUME))
255,503✔
1213
                        r = btrfs_subvol_make_fallback(dir_fd, path, mode);
2✔
1214
                else
1215
                        r = RET_NERRNO(mkdirat(dir_fd, path, mode));
255,501✔
1216
                if (r == -EEXIST) {
164,568✔
1217
                        if (FLAGS_SET(open_flags, O_EXCL))
164,562✔
1218
                                return -EEXIST;
1219
                } else if (r < 0)
90,941✔
1220
                        return r;
1221
                else
1222
                        made_dir = true;
1223

1224
                open_flags &= ~(O_EXCL|O_CREAT);
250,830✔
1225
        }
1226

1227
        if (FLAGS_SET(xopen_flags, XO_REGULAR)) {
1,052,664✔
1228
                /* Guarantee we return a regular fd only, and don't open the file unless we verified it
1229
                 * first */
1230

1231
                if (FLAGS_SET(open_flags, O_PATH)) {
409,546✔
1232
                        fd = openat(dir_fd, path, open_flags, mode);
1✔
1233
                        if (fd < 0) {
1✔
1234
                                r = -errno;
×
1235
                                goto error;
×
1236
                        }
1237

1238
                        r = fd_verify_regular(fd);
1✔
1239
                        if (r < 0)
1✔
1240
                                goto error;
×
1241

1242
                } else if (FLAGS_SET(open_flags, O_CREAT|O_EXCL)) {
409,545✔
1243
                        /* In O_EXCL mode we can just create the thing, everything is dealt with for us */
1244
                        fd = openat(dir_fd, path, open_flags, mode);
2✔
1245
                        if (fd < 0) {
2✔
1246
                                r = -errno;
1✔
1247
                                goto error;
1✔
1248
                        }
1249

1250
                        made_file = true;
1✔
1251
                } else {
1252
                        /* Otherwise pin the inode first via O_PATH */
1253
                        _cleanup_close_ int inode_fd = openat(dir_fd, path, O_PATH|O_CLOEXEC|(open_flags & O_NOFOLLOW));
409,543✔
1254
                        if (inode_fd < 0) {
409,543✔
1255
                                if (errno != ENOENT || !FLAGS_SET(open_flags, O_CREAT)) {
69✔
1256
                                        r = -errno;
49✔
1257
                                        goto error;
49✔
1258
                                }
1259

1260
                                /* Doesn't exist yet, then try to create it */
1261
                                fd = openat(dir_fd, path, open_flags|O_CREAT|O_EXCL, mode);
20✔
1262
                                if (fd < 0) {
20✔
1263
                                        r = -errno;
×
1264
                                        goto error;
×
1265
                                }
1266

1267
                                made_file = true;
20✔
1268
                        } else {
1269
                                /* OK, we pinned it. Now verify it's actually a regular file, and then reopen it */
1270
                                r = fd_verify_regular(inode_fd);
409,474✔
1271
                                if (r < 0)
409,474✔
1272
                                        goto error;
33✔
1273

1274
                                fd = fd_reopen(inode_fd, open_flags & ~(O_NOFOLLOW|O_CREAT));
409,441✔
1275
                                if (fd < 0) {
409,441✔
1276
                                        r = fd;
×
1277
                                        goto error;
×
1278
                                }
1279
                        }
1280
                }
1281
        } else {
1282
                fd = openat_report_new(dir_fd, path, open_flags, mode, &made_file);
643,118✔
1283
                if (fd < 0) {
643,118✔
1284
                        r = fd;
40,130✔
1285
                        goto error;
40,130✔
1286
                }
1287
        }
1288

1289
        if (call_label_ops_post) {
1,012,451✔
1290
                call_label_ops_post = false;
584✔
1291

1292
                r = label_ops_post(fd, /* path= */ NULL, made_file || made_dir);
584✔
1293
                if (r < 0)
584✔
1294
                        goto error;
×
1295
        }
1296

1297
        if (FLAGS_SET(xopen_flags, XO_NOCOW)) {
1,012,451✔
1298
                r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL);
75✔
1299
                if (r < 0 && !ERRNO_IS_IOCTL_NOT_SUPPORTED(r))
75✔
1300
                        goto error;
×
1301
        }
1302

1303
        return TAKE_FD(fd);
1304

1305
error:
40,213✔
1306
        if (call_label_ops_post)
40,213✔
1307
                (void) label_ops_post(fd >= 0 ? fd : dir_fd, fd >= 0 ? NULL : path, made_dir || made_file);
×
1308

1309
        if (made_dir || made_file)
40,213✔
1310
                (void) unlinkat(dir_fd, path, made_dir ? AT_REMOVEDIR : 0);
×
1311

1312
        return r;
1313
}
1314

1315
int xopenat_lock_full(
247,365✔
1316
                int dir_fd,
1317
                const char *path,
1318
                int open_flags,
1319
                XOpenFlags xopen_flags,
1320
                mode_t mode,
1321
                LockType locktype,
1322
                int operation) {
1323

1324
        _cleanup_close_ int fd = -EBADF;
247,365✔
1325
        int r;
247,365✔
1326

1327
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
247,365✔
1328
        assert(IN_SET(operation & ~LOCK_NB, LOCK_EX, LOCK_SH));
247,365✔
1329

1330
        /* POSIX/UNPOSIX locks don't work on directories (errno is set to -EBADF so let's return early with
1331
         * the same error here). */
1332
        if (FLAGS_SET(open_flags, O_DIRECTORY) && !IN_SET(locktype, LOCK_BSD, LOCK_NONE))
247,365✔
1333
                return -EBADF;
1334

1335
        for (;;) {
280,234✔
1336
                struct stat st;
263,799✔
1337

1338
                fd = xopenat_full(dir_fd, path, open_flags, xopen_flags, mode);
263,799✔
1339
                if (fd < 0)
263,799✔
1340
                        return fd;
8✔
1341

1342
                r = lock_generic(fd, locktype, operation);
263,799✔
1343
                if (r < 0)
263,799✔
1344
                        return r;
1345

1346
                /* If we acquired the lock, let's check if the file/directory still exists in the file
1347
                 * system. If not, then the previous exclusive owner removed it and then closed it. In such a
1348
                 * case our acquired lock is worthless, hence try again. */
1349

1350
                if (fstat(fd, &st) < 0)
263,791✔
1351
                        return -errno;
×
1352
                if (st.st_nlink > 0)
263,791✔
1353
                        break;
1354

1355
                fd = safe_close(fd);
16,435✔
1356
        }
1357

1358
        return TAKE_FD(fd);
247,356✔
1359
}
1360

1361
int link_fd(int fd, int newdirfd, const char *newpath) {
26,670✔
1362
        int r;
26,670✔
1363

1364
        assert(fd >= 0);
26,670✔
1365
        assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
26,670✔
1366
        assert(newpath);
26,670✔
1367

1368
        /* Try to link via AT_EMPTY_PATH first. This fails with ENOENT if we don't have CAP_DAC_READ_SEARCH
1369
         * on kernels < 6.10, in which case we'd then resort to /proc/self/fd/ dance.
1370
         *
1371
         * See also: https://github.com/torvalds/linux/commit/42bd2af5950456d46fdaa91c3a8fb02e680f19f5 */
1372
        r = RET_NERRNO(linkat(fd, "", newdirfd, newpath, AT_EMPTY_PATH));
26,670✔
1373
        if (r == -ENOENT) {
5,172✔
1374
                r = RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), newdirfd, newpath, AT_SYMLINK_FOLLOW));
×
1375
                if (r == -ENOENT && proc_mounted() == 0) /* No proc_fd_enoent_errno() here because we don't
×
1376
                                                            know if it's the target path that's missing. */
1377
                        return -ENOSYS;
×
1378
        }
1379

1380
        return r;
1381
}
1382

1383
int linkat_replace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
20,691✔
1384
        _cleanup_close_ int old_fd = -EBADF;
20,691✔
1385
        int r;
20,691✔
1386

1387
        assert(olddirfd >= 0 || olddirfd == AT_FDCWD);
20,691✔
1388
        assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
20,691✔
1389
        assert(!isempty(newpath)); /* source path is optional, but the target path is not */
20,691✔
1390

1391
        /* Like linkat() but replaces the target if needed. Is a NOP if source and target already share the
1392
         * same inode. */
1393

1394
        if (olddirfd == AT_FDCWD && isempty(oldpath)) /* Refuse operating on the cwd (which is a dir, and dirs can't be hardlinked) */
41,382✔
1395
                return -EISDIR;
1396

1397
        if (path_implies_directory(oldpath)) /* Refuse these definite directories early */
20,691✔
1398
                return -EISDIR;
1399

1400
        if (path_implies_directory(newpath))
20,691✔
1401
                return -EISDIR;
1402

1403
        /* First, try to link this directly */
1404
        if (oldpath)
20,691✔
1405
                r = RET_NERRNO(linkat(olddirfd, oldpath, newdirfd, newpath, 0));
123✔
1406
        else
1407
                r = link_fd(olddirfd, newdirfd, newpath);
20,568✔
1408
        if (r >= 0)
20,622✔
1409
                return 0;
15,468✔
1410
        if (r != -EEXIST)
5,223✔
1411
                return r;
1412

1413
        old_fd = xopenat(olddirfd, oldpath, O_PATH|O_CLOEXEC);
5,223✔
1414
        if (old_fd < 0)
5,223✔
1415
                return old_fd;
1416

1417
        struct stat old_st;
5,223✔
1418
        if (fstat(old_fd, &old_st) < 0)
5,223✔
1419
                return -errno;
×
1420

1421
        if (S_ISDIR(old_st.st_mode)) /* Don't bother if we are operating on a directory */
5,223✔
1422
                return -EISDIR;
1423

1424
        struct stat new_st;
5,223✔
1425
        if (fstatat(newdirfd, newpath, &new_st, AT_SYMLINK_NOFOLLOW) < 0)
5,223✔
1426
                return -errno;
×
1427

1428
        if (S_ISDIR(new_st.st_mode)) /* Refuse replacing directories */
5,223✔
1429
                return -EEXIST;
1430

1431
        if (stat_inode_same(&old_st, &new_st)) /* Already the same inode? Then shortcut this */
5,223✔
1432
                return 0;
1433

1434
        _cleanup_free_ char *tmp_path = NULL;
5,222✔
1435
        r = tempfn_random(newpath, /* extra= */ NULL, &tmp_path);
5,222✔
1436
        if (r < 0)
5,222✔
1437
                return r;
1438

1439
        r = link_fd(old_fd, newdirfd, tmp_path);
5,222✔
1440
        if (r < 0) {
5,222✔
1441
                if (!ERRNO_IS_PRIVILEGE(r))
×
1442
                        return r;
1443

1444
                /* If that didn't work due to permissions then go via the path of the dentry */
1445
                r = RET_NERRNO(linkat(olddirfd, oldpath, newdirfd, tmp_path, 0));
×
1446
                if (r < 0)
×
1447
                        return r;
1448
        }
1449

1450
        r = RET_NERRNO(renameat(newdirfd, tmp_path, newdirfd, newpath));
5,222✔
1451
        if (r < 0) {
×
1452
                (void) unlinkat(newdirfd, tmp_path, /* flags= */ 0);
×
1453
                return r;
×
1454
        }
1455

1456
        return 0;
1457
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc