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

systemd / systemd / 17813902210

17 Sep 2025 11:56PM UTC coverage: 72.24% (-0.04%) from 72.281%
17813902210

push

github

web-flow
sysupdate: use conf_files_list_strv_full() where possible (#38198)

37 of 44 new or added lines in 1 file covered. (84.09%)

6236 existing lines in 79 files now uncovered.

302695 of 419015 relevant lines covered (72.24%)

1046897.1 hits per line

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

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

3
#include <stdlib.h>
4
#include <unistd.h>
5

6
#include "alloc-util.h"
7
#include "errno-util.h"
8
#include "fd-util.h"
9
#include "fileio.h"
10
#include "fs-util.h"
11
#include "log.h"
12
#include "path-util.h"
13
#include "random-util.h"
14
#include "string-util.h"
15
#include "sync-util.h"
16
#include "tmpfile-util.h"
17
#include "umask-util.h"
18

19
static int fopen_temporary_internal(int dir_fd, const char *path, FILE **ret_file) {
155,783✔
20
        _cleanup_fclose_ FILE *f = NULL;
155,783✔
21
        _cleanup_close_ int fd = -EBADF;
155,783✔
22
        int r;
155,783✔
23

24
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
155,783✔
25
        assert(path);
155,783✔
26

27
        fd = openat(dir_fd, path, O_CLOEXEC|O_NOCTTY|O_RDWR|O_CREAT|O_EXCL, 0600);
155,783✔
28
        if (fd < 0)
155,783✔
29
                return -errno;
1✔
30

31
        /* This assumes that returned FILE object is short-lived and used within the same single-threaded
32
         * context and never shared externally, hence locking is not necessary. */
33

34
        r = take_fdopen_unlocked(&fd, "w", &f);
155,782✔
35
        if (r < 0) {
155,782✔
36
                (void) unlinkat(dir_fd, path, 0);
×
37
                return r;
×
38
        }
39

40
        if (ret_file)
155,782✔
41
                *ret_file = TAKE_PTR(f);
155,782✔
42

43
        return 0;
44
}
45

46
int fopen_temporary_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path) {
155,781✔
47
        _cleanup_free_ char *t = NULL;
155,781✔
48
        int r;
155,781✔
49

50
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
155,781✔
51
        assert(path);
155,781✔
52

53
        r = tempfn_random(path, NULL, &t);
155,781✔
54
        if (r < 0)
155,781✔
55
                return r;
56

57
        r = fopen_temporary_internal(dir_fd, t, ret_file);
155,781✔
58
        if (r < 0)
155,781✔
59
                return r;
60

61
        if (ret_path)
155,780✔
62
                *ret_path = TAKE_PTR(t);
155,780✔
63

64
        return 0;
65
}
66

67
int fopen_temporary_child_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path) {
2✔
68
        _cleanup_free_ char *t = NULL;
2✔
69
        int r;
2✔
70

71
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
2✔
72

73
        if (!path) {
2✔
74
                r = tmp_dir(&path);
1✔
75
                if (r < 0)
1✔
76
                        return r;
77
        }
78

79
        r = tempfn_random_child(path, NULL, &t);
2✔
80
        if (r < 0)
2✔
81
                return r;
82

83
        r = fopen_temporary_internal(dir_fd, t, ret_file);
2✔
84
        if (r < 0)
2✔
85
                return r;
86

87
        if (ret_path)
2✔
88
                *ret_path = TAKE_PTR(t);
2✔
89

90
        return 0;
91
}
92

93
/* This is much like mkostemp() but is subject to umask(). */
94
int mkostemp_safe(char *pattern) {
152✔
95
        assert(pattern);
152✔
96
        BLOCK_WITH_UMASK(0077);
152✔
97
        return RET_NERRNO(mkostemp(pattern, O_CLOEXEC));
152✔
98
}
99

100
int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) {
40✔
101
        _cleanup_close_ int fd = -EBADF;
40✔
102
        FILE *f;
40✔
103

104
        fd = mkostemp_safe(pattern);
40✔
105
        if (fd < 0)
40✔
106
                return fd;
107

108
        f = take_fdopen(&fd, mode);
40✔
109
        if (!f)
40✔
110
                return -errno;
×
111

112
        *ret_f = f;
40✔
113
        return 0;
40✔
114
}
115

116
void unlink_tempfilep(char (*p)[]) {
117✔
117
        assert(p);
117✔
118

119
        /* If the file is created with mkstemp(), it will (almost always) change the suffix.
120
         * Treat this as a sign that the file was successfully created. We ignore both the rare case
121
         * where the original suffix is used and unlink failures. */
122

123
        if (!endswith(*p, ".XXXXXX"))
117✔
124
                (void) unlink(*p);
115✔
125
}
117✔
126

127
static int tempfn_build(const char *p, const char *pre, const char *post, bool child, char **ret) {
303,789✔
128
        _cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL, *result = NULL;
303,789✔
129
        size_t len_pre, len_post, len_add;
303,789✔
130
        int r;
303,789✔
131

132
        assert(p);
303,789✔
133
        assert(ret);
303,789✔
134

135
        /*
136
         * Turns this:
137
         *         /foo/bar/waldo
138
         *
139
         * Into this :
140
         *         /foo/bar/waldo/.#<pre><post> (child == true)
141
         *         /foo/bar/.#<pre>waldo<post> (child == false)
142
         */
143

144
        if (pre && strchr(pre, '/'))
303,789✔
145
                return -EINVAL;
146

147
        if (post && strchr(post, '/'))
303,786✔
148
                return -EINVAL;
149

150
        len_pre = strlen_ptr(pre);
303,786✔
151
        len_post = strlen_ptr(post);
303,786✔
152
        /* NAME_MAX is counted *without* the trailing NUL byte. */
153
        if (len_pre > NAME_MAX - STRLEN(".#") ||
303,786✔
154
            len_post > NAME_MAX - STRLEN(".#") - len_pre)
303,786✔
155
                return -EINVAL;
156

157
        len_add = len_pre + len_post + STRLEN(".#");
303,786✔
158

159
        if (child) {
303,786✔
160
                d = strdup(p);
2,010✔
161
                if (!d)
2,010✔
162
                        return -ENOMEM;
163
        } else {
164
                r = path_extract_directory(p, &d);
301,776✔
165
                if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
301,776✔
166
                        return r;
167

168
                r = path_extract_filename(p, &fn);
301,768✔
169
                if (r < 0)
301,768✔
170
                        return r;
171

172
                if (strlen(fn) > NAME_MAX - len_add)
301,768✔
173
                        /* We cannot simply prepend and append strings to the filename. Let's truncate the filename. */
174
                        fn[NAME_MAX - len_add] = '\0';
4✔
175
        }
176

177
        nf = strjoin(".#", strempty(pre), strempty(fn), strempty(post));
607,440✔
178
        if (!nf)
303,778✔
179
                return -ENOMEM;
180

181
        if (d) {
303,778✔
182
                if (!path_extend(&d, nf))
303,183✔
183
                        return -ENOMEM;
184

185
                result = path_simplify(TAKE_PTR(d));
303,183✔
186
        } else
187
                result = TAKE_PTR(nf);
188

189
        if (!path_is_valid(result)) /* New path is not valid? (Maybe because too long?) Refuse. */
303,778✔
190
                return -EINVAL;
191

192
        *ret = TAKE_PTR(result);
303,775✔
193
        return 0;
303,775✔
194
}
195

196
int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
27✔
197
        /*
198
         * Turns this:
199
         *         /foo/bar/waldo
200
         *
201
         * Into this:
202
         *         /foo/bar/.#<extra>waldoXXXXXX
203
         */
204

205
        return tempfn_build(p, extra, "XXXXXX", /* child = */ false, ret);
27✔
206
}
207

208
int tempfn_random(const char *p, const char *extra, char **ret) {
301,751✔
209
        _cleanup_free_ char *s = NULL;
301,751✔
210

211
        assert(p);
301,751✔
212
        assert(ret);
301,751✔
213

214
        /*
215
         * Turns this:
216
         *         /foo/bar/waldo
217
         *
218
         * Into this:
219
         *         /foo/bar/.#<extra>waldobaa2a261115984a9
220
         */
221

222
        if (asprintf(&s, "%016" PRIx64, random_u64()) < 0)
301,751✔
223
                return -ENOMEM;
224

225
        return tempfn_build(p, extra, s, /* child = */ false, ret);
301,751✔
226
}
227

228
int tempfn_random_child(const char *p, const char *extra, char **ret) {
2,011✔
229
        _cleanup_free_ char *s = NULL;
2,011✔
230
        int r;
2,011✔
231

232
        assert(ret);
2,011✔
233

234
        /* Turns this:
235
         *         /foo/bar/waldo
236
         * Into this:
237
         *         /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
238
         */
239

240
        if (!p) {
2,011✔
241
                r = tmp_dir(&p);
15✔
242
                if (r < 0)
15✔
243
                        return r;
244
        }
245

246
        if (asprintf(&s, "%016" PRIx64, random_u64()) < 0)
2,011✔
247
                return -ENOMEM;
248

249
        return tempfn_build(p, extra, s, /* child = */ true, ret);
2,011✔
250
}
251

252
int open_tmpfile_unlinkable(const char *directory, int flags) {
6,371✔
253
        char *p;
6,371✔
254
        int fd, r;
6,371✔
255

256
        if (!directory) {
6,371✔
257
                r = tmp_dir(&directory);
7✔
258
                if (r < 0)
7✔
259
                        return r;
260
        } else if (isempty(directory))
6,364✔
261
                return -EINVAL;
262

263
        /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
264

265
        /* Try O_TMPFILE first, if it is supported */
266
        fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
6,371✔
267
        if (fd >= 0)
6,371✔
268
                return fd;
269

270
        /* Fall back to unguessable name + unlinking */
271
        p = strjoina(directory, "/systemd-tmp-XXXXXX");
×
272

273
        fd = mkostemp_safe(p);
×
274
        if (fd < 0)
×
275
                return fd;
276

277
        (void) unlink(p);
×
278

279
        return fd;
×
280
}
281

282
int open_tmpfile_linkable_at(int dir_fd, const char *target, int flags, char **ret_path) {
7,765✔
283
        _cleanup_free_ char *tmp = NULL;
7,765✔
284
        int r, fd;
7,765✔
285

286
        assert(target);
7,765✔
287
        assert(ret_path);
7,765✔
288

289
        /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
290
        assert((flags & O_EXCL) == 0);
7,765✔
291

292
        /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
293
         * which case "ret_path" will be returned as NULL. If not possible the temporary path name used is returned in
294
         * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
295

296
        fd = open_parent_at(dir_fd, target, O_TMPFILE|flags, 0640);
7,765✔
297
        if (fd >= 0) {
7,765✔
298
                *ret_path = NULL;
7,734✔
299
                return fd;
7,734✔
300
        }
301

302
        log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target);
31✔
303

304
        r = tempfn_random(target, NULL, &tmp);
31✔
305
        if (r < 0)
31✔
306
                return r;
307

308
        fd = openat(dir_fd, tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640);
31✔
309
        if (fd < 0)
31✔
310
                return -errno;
17✔
311

312
        *ret_path = TAKE_PTR(tmp);
14✔
313

314
        return fd;
14✔
315
}
316

317
int fopen_tmpfile_linkable_at(int dir_fd, const char *target, int flags, char **ret_path, FILE **ret_file) {
5,044✔
318
        _cleanup_free_ char *path = NULL;
5,044✔
319
        _cleanup_fclose_ FILE *f = NULL;
5,044✔
320
        _cleanup_close_ int fd = -EBADF;
5,044✔
321

322
        assert(target);
5,044✔
323
        assert(ret_file);
5,044✔
324
        assert(ret_path);
5,044✔
325

326
        fd = open_tmpfile_linkable_at(dir_fd, target, flags, &path);
5,044✔
327
        if (fd < 0)
5,044✔
328
                return fd;
329

330
        f = take_fdopen(&fd, "w");
5,044✔
331
        if (!f)
5,044✔
332
                return -ENOMEM;
333

334
        *ret_path = TAKE_PTR(path);
5,044✔
335
        *ret_file = TAKE_PTR(f);
5,044✔
336
        return 0;
5,044✔
337
}
338

339
int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, LinkTmpfileFlags flags) {
7,692✔
340
        int r;
7,692✔
341

342
        assert(fd >= 0);
7,692✔
343
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
7,692✔
344
        assert(target);
7,692✔
345

346
        /* Moves a temporary file created with open_tmpfile() above into its final place. If "path" is NULL
347
         * an fd created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE
348
         * is not supported on the directory, and renameat2() is used instead. */
349

350
        if (FLAGS_SET(flags, LINK_TMPFILE_SYNC) && fsync(fd) < 0)
7,692✔
351
                return -errno;
×
352

353
        if (path) {
7,692✔
354
                if (FLAGS_SET(flags, LINK_TMPFILE_REPLACE))
14✔
355
                        r = RET_NERRNO(renameat(dir_fd, path, dir_fd, target));
×
356
                else
357
                        r = rename_noreplace(dir_fd, path, dir_fd, target);
14✔
358
        } else {
359
                if (FLAGS_SET(flags, LINK_TMPFILE_REPLACE))
7,678✔
360
                        r = linkat_replace(fd, /* oldpath= */ NULL, dir_fd, target);
6,799✔
361
                else
362
                        r = link_fd(fd, dir_fd, target);
879✔
363
        }
364
        if (r < 0)
7,692✔
365
                return r;
366

367
        if (FLAGS_SET(flags, LINK_TMPFILE_SYNC)) {
7,689✔
368
                r = fsync_full(fd);
1,391✔
369
                if (r < 0)
1,391✔
370
                        return r;
×
371
        }
372

373
        return 0;
374
}
375

376
int flink_tmpfile_at(FILE *f, int dir_fd, const char *path, const char *target, LinkTmpfileFlags flags) {
5,044✔
377
        int fd, r;
5,044✔
378

379
        assert(f);
5,044✔
380
        assert(target);
5,044✔
381

382
        fd = fileno(f);
5,044✔
383
        if (fd < 0) /* Not all FILE* objects encapsulate fds */
5,044✔
384
                return -EBADF;
385

386
        r = fflush_and_check(f);
5,044✔
387
        if (r < 0)
5,044✔
388
                return r;
389

390
        return link_tmpfile_at(fd, dir_fd, path, target, flags);
5,044✔
391
}
392

393
int mkdtemp_malloc(const char *template, char **ret) {
3,835✔
394
        _cleanup_free_ char *p = NULL;
7,670✔
395
        int r;
3,835✔
396

397
        assert(ret);
3,835✔
398

399
        if (template)
3,835✔
400
                p = strdup(template);
3,760✔
401
        else {
402
                const char *tmp;
75✔
403

404
                r = tmp_dir(&tmp);
75✔
405
                if (r < 0)
75✔
406
                        return r;
×
407

408
                p = path_join(tmp, "XXXXXX");
75✔
409
        }
410
        if (!p)
3,835✔
411
                return -ENOMEM;
412

413
        if (!mkdtemp(p))
3,835✔
414
                return -errno;
×
415

416
        *ret = TAKE_PTR(p);
3,835✔
417
        return 0;
3,835✔
418
}
419

420
int mkdtemp_open(const char *template, int flags, char **ret) {
37✔
421
        _cleanup_free_ char *p = NULL;
37✔
422
        int fd, r;
37✔
423

424
        r = mkdtemp_malloc(template, &p);
37✔
425
        if (r < 0)
37✔
426
                return r;
427

428
        fd = RET_NERRNO(open(p, O_DIRECTORY|O_CLOEXEC|flags));
37✔
429
        if (fd < 0) {
×
430
                (void) rmdir(p);
×
431
                return fd;
×
432
        }
433

434
        if (ret)
37✔
435
                *ret = TAKE_PTR(p);
37✔
436

437
        return fd;
438
}
439

440
void cleanup_tmpfile_data_done(struct cleanup_tmpfile_data *d) {
3,950✔
441
        assert(d);
3,950✔
442

443
        if (!d->dir_fd ||
3,950✔
444
            *d->dir_fd < 0 ||
3,950✔
445
            !d->filename ||
1,474✔
446
            !*d->filename)
1,474✔
447
                return;
448

UNCOV
449
        (void) unlinkat(*d->dir_fd, *d->filename, 0);
×
UNCOV
450
        d->dir_fd = NULL;
×
UNCOV
451
        d->filename = NULL;
×
452
}
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