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

systemd / systemd / 19791169017

29 Nov 2025 04:02AM UTC coverage: 72.693% (-0.08%) from 72.77%
19791169017

push

github

yuwata
hwdb: add ProtoArc EM01 NL mouse configuration

309262 of 425438 relevant lines covered (72.69%)

1122947.98 hits per line

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

80.49
/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) {
60,405✔
32
        char *p;
60,405✔
33
        int r;
60,405✔
34

35
        assert(path);
60,405✔
36
        assert(stop);
60,405✔
37

38
        if (!path_is_safe(path))
60,405✔
39
                return -EINVAL;
40

41
        if (!path_is_safe(stop))
60,404✔
42
                return -EINVAL;
43

44
        p = strdupa_safe(path);
60,403✔
45

46
        for (;;) {
2,962✔
47
                char *slash = NULL;
63,365✔
48

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

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

59
                if (path_startswith_full(stop, p, PATH_STARTSWITH_REFUSE_DOT_DOT))
63,365✔
60
                        return 0;
61

62
                if (rmdir(p) < 0 && errno != ENOENT)
63,229✔
63
                        return -errno;
60,267✔
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,887,687✔
113
        size_t l = PATH_MAX;
5,887,687✔
114

115
        assert(fd >= 0 || fd == AT_FDCWD);
5,887,687✔
116

117
        if (fd < 0 && isempty(p))
5,887,687✔
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,887,687✔
122
                _cleanup_free_ char *c = NULL;
5,887,687✔
123
                ssize_t n;
5,887,687✔
124

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

129
                n = readlinkat(fd, strempty(p), c, l);
5,887,689✔
130
                if (n < 0)
5,887,687✔
131
                        return -errno;
363,623✔
132

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

136
                        if (ret)
5,524,064✔
137
                                *ret = TAKE_PTR(c);
5,524,064✔
138

139
                        return 0;
5,524,064✔
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) {
601,021✔
152
        _cleanup_free_ char *link = NULL, *name = NULL;
601,021✔
153
        int r;
601,021✔
154

155
        assert(p);
601,021✔
156
        assert(ret);
601,021✔
157

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

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

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

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

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

179
        r = readlink_malloc(p, &target);
3,111✔
180
        if (r < 0)
3,111✔
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) {
21,543✔
187
        _cleanup_close_ int fd = -EBADF;
21,543✔
188

189
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
21,543✔
190

191
        if (path) {
21,543✔
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);
21,543✔
194
                if (fd < 0)
21,543✔
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);
21,543✔
207
}
208

209
int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t uid, gid_t gid) {
126,103✔
210
        bool do_chown, do_chmod;
126,103✔
211
        struct stat st;
126,103✔
212
        int r;
126,103✔
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)
126,103✔
227
                return -errno;
×
228

229
        do_chown =
252,206✔
230
                (uid != UID_INVALID && st.st_uid != uid) ||
126,103✔
231
                (gid != GID_INVALID && st.st_gid != gid);
29,445✔
232

233
        do_chmod =
252,206✔
234
                !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */
126,103✔
235
                ((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) ||
121,506✔
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)
126,103✔
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)
86,357✔
242
                return -EINVAL; /* insist on the right file type if it was specified */
243

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

247
                if (((minimal ^ st.st_mode) & 07777) != 0) {
13,205✔
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)
126,099✔
261
                if (fchownat(fd, "", uid, gid, AT_EMPTY_PATH) < 0)
13,208✔
262
                        return -errno;
1✔
263

264
        if (do_chmod) {
126,098✔
265
                r = fchmod_opath(fd, mode & 07777);
27,937✔
266
                if (r < 0) {
27,937✔
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;
126,098✔
277
}
278

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

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

285
int fchmod_opath(int fd, mode_t m) {
30,635✔
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,635✔
301

302
        if (fchmodat(fd, "", m, AT_EMPTY_PATH) >= 0)
30,635✔
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]) {
130,081✔
320
        /* Similar to fchmod_opath() but for futimens() */
321

322
        assert(fd >= 0);
130,081✔
323

324
        if (utimensat(fd, "", ts, AT_EMPTY_PATH) >= 0)
130,081✔
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) {
94,470✔
343
        assert(path);
94,470✔
344
        assert(st);
94,470✔
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))
94,470✔
348
                return 0;
349

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

353
        if (st->st_mode & 0002)
94,470✔
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)
94,470✔
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);
14✔
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) {
24,450✔
375
        return RET_NERRNO(faccessat(AT_FDCWD, path, mode, AT_SYMLINK_NOFOLLOW));
24,450✔
376
}
377

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

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

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

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

394
        assert(path);
74,699✔
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,699✔
402
                (void) mkdir_parents(path, 0755);
34,415✔
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,699✔
408
        if (fd < 0) {
74,699✔
409
                if (errno != ENOENT)
49,310✔
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);
49,310✔
415
                if (fd < 0)
49,310✔
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,695✔
423

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

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

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

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

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

443
                target = relpath;
241✔
444
        }
445

446
        if (symlinkat(target, atfd, linkpath) < 0) {
351✔
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) {
134,366✔
466
        int r;
134,366✔
467

468
        assert(target);
134,366✔
469
        assert(linkpath);
134,366✔
470

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

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

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

485
        bool call_label_ops_post = false;
134,366✔
486
        if (FLAGS_SET(flags, SYMLINK_LABEL)) {
134,366✔
487
                r = label_ops_pre(atfd, linkpath, S_IFLNK);
129,390✔
488
                if (r < 0)
129,390✔
489
                        return r;
490

491
                call_label_ops_post = true;
492
        }
493

494
        r = RET_NERRNO(symlinkat(target, atfd, t));
134,366✔
495
        if (call_label_ops_post)
134,366✔
496
                RET_GATHER(r, label_ops_post(atfd, t, /* created= */ r >= 0));
129,390✔
497
        if (r < 0)
134,366✔
498
                return r;
×
499

500
        r = RET_NERRNO(renameat(atfd, t, atfd, linkpath));
134,367✔
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 dir_fd, 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(dir_fd, t, mode) < 0)
1✔
543
                return -errno;
×
544

545
        r = RET_NERRNO(renameat(dir_fd, t, dir_fd, path));
1✔
546
        if (r < 0) {
×
547
                (void) unlinkat(dir_fd, 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,942✔
570
                if (!dirent_is_file(de))
1,475✔
571
                        continue;
987✔
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,367✔
594
        int r, ret = 0;
2,367✔
595

596
        assert(ret_path);
2,367✔
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,465✔
601
                const char *e;
7,099✔
602

603
                e = secure_getenv(n);
7,099✔
604
                if (!e)
7,099✔
605
                        continue;
7,097✔
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,366✔
633
                return ret;
634

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

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

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

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

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

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

662
int var_tmp_dir(const char **ret) {
2,227✔
663
        assert(ret);
2,227✔
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,227✔
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) {
253✔
683
        assert(filename);
253✔
684

685
        if (unlink(filename) < 0 && errno != ENOENT)
253✔
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) {
216,108✔
696
        PROTECT_ERRNO;
216,108✔
697

698
        if (!p)
216,108✔
699
                return NULL;
700

701
        (void) rmdir(p);
216,108✔
702
        return mfree(p);
216,108✔
703
}
704

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

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

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

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

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

720
        if (faccessat(fd, "", mode, AT_EMPTY_PATH) >= 0)
28,185✔
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,896,332✔
850
        _cleanup_free_ char *parent = NULL;
1,896,332✔
851
        int r;
1,896,332✔
852

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

856
        r = path_extract_directory(path, &parent);
1,896,332✔
857
        if (r == -EDESTADDRREQ) {
1,896,332✔
858
                parent = strdup(".");
13,516✔
859
                if (!parent)
13,516✔
860
                        return -ENOMEM;
861
        } else if (r == -EADDRNOTAVAIL) {
1,882,816✔
862
                parent = strdup(path);
×
863
                if (!parent)
×
864
                        return -ENOMEM;
865
        } else if (r < 0)
1,882,816✔
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_TMPFILE))
1,896,332✔
872
                flags |= O_DIRECTORY;
1,874,730✔
873

874
        return RET_NERRNO(openat(dir_fd, parent, flags, mode));
1,896,379✔
875
}
876

877
int conservative_renameat(
39,645✔
878
                int olddirfd, const char *oldpath,
879
                int newdirfd, const char *newpath) {
880

881
        _cleanup_close_ int old_fd = -EBADF, new_fd = -EBADF;
39,645✔
882
        struct stat old_stat, new_stat;
39,645✔
883

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

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

895
        new_fd = openat(newdirfd, newpath, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_NOFOLLOW);
39,645✔
896
        if (new_fd < 0)
39,645✔
897
                goto do_rename;
3,438✔
898

899
        if (fstat(old_fd, &old_stat) < 0)
36,207✔
900
                goto do_rename;
×
901

902
        if (!S_ISREG(old_stat.st_mode))
36,207✔
903
                goto do_rename;
×
904

905
        if (fstat(new_fd, &new_stat) < 0)
36,207✔
906
                goto do_rename;
×
907

908
        if (stat_inode_same(&new_stat, &old_stat))
36,207✔
909
                goto is_same;
1✔
910

911
        if (old_stat.st_mode != new_stat.st_mode ||
36,206✔
912
            old_stat.st_size != new_stat.st_size ||
36,206✔
913
            old_stat.st_uid != new_stat.st_uid ||
25,208✔
914
            old_stat.st_gid != new_stat.st_gid)
25,208✔
915
                goto do_rename;
10,998✔
916

917
        for (;;) {
6✔
918
                uint8_t buf1[16*1024];
25,214✔
919
                uint8_t buf2[sizeof(buf1)];
25,214✔
920
                ssize_t l1, l2;
25,214✔
921

922
                l1 = read(old_fd, buf1, sizeof(buf1));
25,214✔
923
                if (l1 < 0)
25,214✔
924
                        goto do_rename;
310✔
925

926
                if (l1 == sizeof(buf1))
25,214✔
927
                        /* Read the full block, hence read a full block in the other file too */
928

929
                        l2 = read(new_fd, buf2, l1);
7✔
930
                else {
931
                        assert((size_t) l1 < sizeof(buf1));
25,207✔
932

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

937
                        assert((size_t) (l1 + 1) <= sizeof(buf2));
25,207✔
938
                        l2 = read(new_fd, buf2, l1 + 1);
25,207✔
939
                }
940
                if (l2 != l1)
25,214✔
941
                        goto do_rename;
×
942

943
                if (memcmp(buf1, buf2, l1) != 0)
25,214✔
944
                        goto do_rename;
310✔
945

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

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

955
        if (unlinkat(olddirfd, oldpath, 0) < 0)
24,899✔
956
                goto do_rename;
×
957

958
        return 0;
959

960
do_rename:
14,746✔
961
        if (renameat(olddirfd, oldpath, newdirfd, newpath) < 0)
14,746✔
962
                return -errno;
×
963

964
        return 1;
965
}
966

967
int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size) {
1,706✔
968
        RateLimit rl;
1,706✔
969
        int r;
1,706✔
970

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

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

984
        return -EINTR;
985
}
986

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

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

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

1001
        if (!s)
15✔
1002
                return -EINVAL;
1003

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

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

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

1020
        if (!hostname_is_valid(h, 0))
11✔
1021
                return -EINVAL;
1022

1023
        e++;
10✔
1024

1025
        d = strchrnul(e, delimiter);
10✔
1026

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

1031
        if (!filename_is_valid(ss))
10✔
1032
                return -EINVAL;
1033

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

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

1044
                if (!path_is_valid(x))
6✔
1045
                        return -EINVAL;
1046

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

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

1059
        return 0;
1060
}
1061

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

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

1071
        if (flags & ~(O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_EXCL|O_NOATIME|O_NOFOLLOW|O_PATH))
221,371✔
1072
                return -EINVAL;
1073
        if ((flags & O_ACCMODE_STRICT) != O_RDONLY)
221,371✔
1074
                return -EINVAL;
1075

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

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

1090
                parent_fd = openat(dirfd, parent, O_PATH|O_DIRECTORY|O_CLOEXEC);
213,527✔
1091
                if (parent_fd < 0)
213,527✔
1092
                        return -errno;
×
1093

1094
                dirfd = parent_fd;
213,527✔
1095
                path = fname;
213,527✔
1096
        }
1097

1098
        fd = xopenat_full(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, xopen_flags, mode);
221,371✔
1099
        if (IN_SET(fd, -ELOOP, -ENOTDIR))
221,371✔
1100
                return -EEXIST;
1101
        if (fd < 0)
221,370✔
1102
                return fd;
4,634✔
1103

1104
        return TAKE_FD(fd);
1105
}
1106

1107
int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created) {
2,603,269✔
1108
        int fd;
2,603,269✔
1109

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

1119
        if (!FLAGS_SET(flags, O_CREAT) || FLAGS_SET(flags, O_EXCL)) {
2,603,269✔
1120
                fd = openat(dirfd, pathname, flags, mode);
501,394✔
1121
                if (fd < 0)
501,394✔
1122
                        return -errno;
41,904✔
1123

1124
                if (ret_newly_created)
459,490✔
1125
                        *ret_newly_created = FLAGS_SET(flags, O_CREAT);
459,490✔
1126
                return fd;
459,490✔
1127
        }
1128

1129
        for (unsigned attempts = 7;;) {
1130
                /* First, attempt to open without O_CREAT/O_EXCL, i.e. open existing file */
1131
                fd = openat(dirfd, pathname, flags & ~(O_CREAT | O_EXCL), mode);
2,102,078✔
1132
                if (fd >= 0) {
2,102,078✔
1133
                        if (ret_newly_created)
1,885,185✔
1134
                                *ret_newly_created = false;
1,885,185✔
1135
                        return fd;
1,885,185✔
1136
                }
1137
                if (errno != ENOENT)
216,893✔
1138
                        return -errno;
×
1139

1140
                /* So the file didn't exist yet, hence create it with O_CREAT/O_EXCL/O_NOFOLLOW. */
1141
                fd = openat(dirfd, pathname, flags | O_CREAT | O_EXCL | O_NOFOLLOW, mode);
216,893✔
1142
                if (fd >= 0) {
216,893✔
1143
                        if (ret_newly_created)
216,688✔
1144
                                *ret_newly_created = true;
216,687✔
1145
                        return fd;
216,688✔
1146
                }
1147
                if (errno != EEXIST)
205✔
1148
                        return -errno;
×
1149

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

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

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

1163
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
1,068,263✔
1164

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

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

1184
        if (mode == MODE_INVALID)
1,068,263✔
1185
                mode = (open_flags & O_DIRECTORY) ? 0755 : 0644;
171,579✔
1186

1187
        if (isempty(path)) {
1,068,263✔
1188
                assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL));
6,536✔
1189

1190
                if (FLAGS_SET(xopen_flags, XO_REGULAR)) {
6,536✔
1191
                        r = fd_verify_regular(dir_fd);
112✔
1192
                        if (r < 0)
112✔
1193
                                return r;
1194
                }
1195

1196
                return fd_reopen(dir_fd, open_flags & ~O_NOFOLLOW);
6,536✔
1197
        }
1198

1199
        bool call_label_ops_post = false;
1,061,727✔
1200

1201
        if (FLAGS_SET(open_flags, O_CREAT) && FLAGS_SET(xopen_flags, XO_LABEL)) {
1,061,727✔
1202
                r = label_ops_pre(dir_fd, path, FLAGS_SET(open_flags, O_DIRECTORY) ? S_IFDIR : S_IFREG);
605✔
1203
                if (r < 0)
605✔
1204
                        return r;
1205

1206
                call_label_ops_post = true;
1207
        }
1208

1209
        if (FLAGS_SET(open_flags, O_DIRECTORY|O_CREAT)) {
1,061,727✔
1210
                if (FLAGS_SET(xopen_flags, XO_SUBVOLUME))
256,749✔
1211
                        r = btrfs_subvol_make_fallback(dir_fd, path, mode);
2✔
1212
                else
1213
                        r = RET_NERRNO(mkdirat(dir_fd, path, mode));
256,747✔
1214
                if (r == -EEXIST) {
165,051✔
1215
                        if (FLAGS_SET(open_flags, O_EXCL))
165,045✔
1216
                                return -EEXIST;
1217
                } else if (r < 0)
91,704✔
1218
                        return r;
1219
                else
1220
                        made_dir = true;
1221

1222
                open_flags &= ~(O_EXCL|O_CREAT);
252,042✔
1223
        }
1224

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

1229
                if (FLAGS_SET(open_flags, O_PATH)) {
408,810✔
1230
                        fd = openat(dir_fd, path, open_flags, mode);
1✔
1231
                        if (fd < 0) {
1✔
1232
                                r = -errno;
×
1233
                                goto error;
×
1234
                        }
1235

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

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

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

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

1265
                                made_file = true;
22✔
1266
                        } else {
1267
                                /* OK, we pinned it. Now verify it's actually a regular file, and then reopen it */
1268
                                r = fd_verify_regular(inode_fd);
408,734✔
1269
                                if (r < 0)
408,734✔
1270
                                        goto error;
86✔
1271

1272
                                fd = fd_reopen(inode_fd, open_flags & ~(O_NOFOLLOW|O_CREAT));
408,648✔
1273
                                if (fd < 0) {
408,648✔
1274
                                        r = fd;
×
1275
                                        goto error;
×
1276
                                }
1277
                        }
1278
                }
1279
        } else {
1280
                fd = openat_report_new(dir_fd, path, open_flags, mode, &made_file);
648,210✔
1281
                if (fd < 0) {
648,210✔
1282
                        r = fd;
40,175✔
1283
                        goto error;
40,175✔
1284
                }
1285
        }
1286

1287
        if (call_label_ops_post) {
1,016,707✔
1288
                call_label_ops_post = false;
605✔
1289

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

1295
        if (FLAGS_SET(xopen_flags, XO_NOCOW)) {
1,016,707✔
1296
                r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL);
75✔
1297
                if (r < 0 && !ERRNO_IS_IOCTL_NOT_SUPPORTED(r))
75✔
1298
                        goto error;
×
1299
        }
1300

1301
        return TAKE_FD(fd);
1302

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

1307
        if (made_dir || made_file)
40,313✔
1308
                (void) unlinkat(dir_fd, path, made_dir ? AT_REMOVEDIR : 0);
×
1309

1310
        return r;
1311
}
1312

1313
int xopenat_lock_full(
248,551✔
1314
                int dir_fd,
1315
                const char *path,
1316
                int open_flags,
1317
                XOpenFlags xopen_flags,
1318
                mode_t mode,
1319
                LockType locktype,
1320
                int operation) {
1321

1322
        _cleanup_close_ int fd = -EBADF;
248,551✔
1323
        int r;
248,551✔
1324

1325
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
248,551✔
1326
        assert(IN_SET(operation & ~LOCK_NB, LOCK_EX, LOCK_SH));
248,551✔
1327

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

1333
        for (;;) {
284,114✔
1334
                struct stat st;
266,332✔
1335

1336
                fd = xopenat_full(dir_fd, path, open_flags, xopen_flags, mode);
266,332✔
1337
                if (fd < 0)
266,332✔
1338
                        return fd;
8✔
1339

1340
                r = lock_generic(fd, locktype, operation);
266,332✔
1341
                if (r < 0)
266,332✔
1342
                        return r;
1343

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

1348
                if (fstat(fd, &st) < 0)
266,324✔
1349
                        return -errno;
×
1350
                if (st.st_nlink > 0)
266,324✔
1351
                        break;
1352

1353
                fd = safe_close(fd);
17,782✔
1354
        }
1355

1356
        return TAKE_FD(fd);
248,542✔
1357
}
1358

1359
int link_fd(int fd, int newdirfd, const char *newpath) {
26,722✔
1360
        int r;
26,722✔
1361

1362
        assert(fd >= 0);
26,722✔
1363
        assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
26,722✔
1364
        assert(newpath);
26,722✔
1365

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

1378
        return r;
1379
}
1380

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

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

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

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

1395
        if (path_implies_directory(oldpath)) /* Refuse these definite directories early */
20,747✔
1396
                return -EISDIR;
1397

1398
        if (path_implies_directory(newpath))
20,747✔
1399
                return -EISDIR;
1400

1401
        /* First, try to link this directly */
1402
        if (oldpath)
20,747✔
1403
                r = RET_NERRNO(linkat(olddirfd, oldpath, newdirfd, newpath, 0));
139✔
1404
        else
1405
                r = link_fd(olddirfd, newdirfd, newpath);
20,608✔
1406
        if (r >= 0)
20,666✔
1407
                return 0;
15,512✔
1408
        if (r != -EEXIST)
5,235✔
1409
                return r;
1410

1411
        old_fd = xopenat(olddirfd, oldpath, O_PATH|O_CLOEXEC);
5,235✔
1412
        if (old_fd < 0)
5,235✔
1413
                return old_fd;
1414

1415
        struct stat old_st;
5,235✔
1416
        if (fstat(old_fd, &old_st) < 0)
5,235✔
1417
                return -errno;
×
1418

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

1422
        struct stat new_st;
5,235✔
1423
        if (fstatat(newdirfd, newpath, &new_st, AT_SYMLINK_NOFOLLOW) < 0)
5,235✔
1424
                return -errno;
×
1425

1426
        if (S_ISDIR(new_st.st_mode)) /* Refuse replacing directories */
5,235✔
1427
                return -EEXIST;
1428

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

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

1437
        r = link_fd(old_fd, newdirfd, tmp_path);
5,234✔
1438
        if (r < 0) {
5,234✔
1439
                if (!ERRNO_IS_PRIVILEGE(r))
×
1440
                        return r;
1441

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

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

1454
        return 0;
1455
}
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