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

systemd / systemd / 28272947092

26 Jun 2026 08:38PM UTC coverage: 72.893% (+0.2%) from 72.703%
28272947092

push

github

poettering
sysupdate: Address review feedback on CheckNew varlink scaffolding

Follow-up to #42422:

 - Rename process_image() to context_process_image(), since it now
   operates on a Context object.
 - Use IN_SET() in image_type_can_sysupdate() instead of a switch.
 - Name the return parameters of context_list_components() ret_xyz, per
   our coding style.
 - Drop a redundant "else" after a return in vl_method_check_new().

9 of 11 new or added lines in 1 file covered. (81.82%)

12567 existing lines in 144 files now uncovered.

341026 of 467845 relevant lines covered (72.89%)

1339355.33 hits per line

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

92.63
/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) {
166,899✔
20
        _cleanup_fclose_ FILE *f = NULL;
166,899✔
21
        _cleanup_close_ int fd = -EBADF;
166,899✔
22
        int r;
166,899✔
23

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

27
        fd = openat(dir_fd, path, O_CLOEXEC|O_NOCTTY|O_RDWR|O_CREAT|O_EXCL, 0600);
166,899✔
28
        if (fd < 0)
166,899✔
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);
166,898✔
35
        if (r < 0) {
166,898✔
36
                (void) unlinkat(dir_fd, path, 0);
×
37
                return r;
×
38
        }
39

40
        if (ret_file)
166,898✔
41
                *ret_file = TAKE_PTR(f);
166,898✔
42

43
        return 0;
44
}
45

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

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

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

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

61
        if (ret_path)
166,893✔
62
                *ret_path = TAKE_PTR(t);
166,893✔
63

64
        return 0;
65
}
66

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

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

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

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

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

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

90
        return 0;
91
}
92

93
/* This is much like mkostemp() but is subject to umask(). */
94
int mkostemp_safe(char *pattern) {
321✔
95
        assert(pattern);
321✔
96
        BLOCK_WITH_UMASK(0077);
321✔
97
        return RET_NERRNO(mkostemp(pattern, O_CLOEXEC));
321✔
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
        assert(ret_f);
40✔
105

106
        fd = mkostemp_safe(pattern);
40✔
107
        if (fd < 0)
40✔
108
                return fd;
109

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

114
        *ret_f = f;
40✔
115
        return 0;
40✔
116
}
117

118
void unlink_tempfilep(char (*p)[]) {
178✔
119
        assert(p);
178✔
120

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

125
        if (!endswith(*p, ".XXXXXX"))
178✔
126
                (void) unlink(*p);
176✔
127
}
178✔
128

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

134
        assert(p);
300,690✔
135
        assert(ret);
300,690✔
136

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

146
        if (pre && strchr(pre, '/'))
300,690✔
147
                return -EINVAL;
148

149
        if (post && strchr(post, '/'))
300,687✔
150
                return -EINVAL;
151

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

159
        len_add = len_pre + len_post + STRLEN(".#");
300,687✔
160

161
        if (child) {
300,687✔
162
                d = strdup(p);
15,330✔
163
                if (!d)
15,330✔
164
                        return -ENOMEM;
165
        } else {
166
                r = path_split_prefix_filename(p, &d, &fn);
285,357✔
167
                if (r < 0)
285,357✔
168
                        return r;
169

170
                /* Truncate the filename if it would become too long after mangling. */
171
                strshorten(fn, NAME_MAX - len_add);
285,349✔
172
        }
173

174
        nf = strjoin(".#", strempty(pre), strempty(fn), strempty(post));
601,283✔
175
        if (!nf)
300,679✔
176
                return -ENOMEM;
177

178
        if (d) {
300,679✔
179
                if (!path_extend(&d, nf))
294,482✔
180
                        return -ENOMEM;
181

182
                result = path_simplify(TAKE_PTR(d));
294,482✔
183
        } else
184
                result = TAKE_PTR(nf);
185

186
        if (!path_is_valid(result)) /* New path is not valid? (Maybe because too long?) Refuse. */
300,679✔
187
                return -EINVAL;
188

189
        *ret = TAKE_PTR(result);
300,676✔
190
        return 0;
300,676✔
191
}
192

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

202
        return tempfn_build(p, extra, "XXXXXX", /* child= */ false, ret);
27✔
203
}
204

205
int tempfn_random(const char *p, const char *extra, char **ret) {
285,332✔
206
        _cleanup_free_ char *s = NULL;
285,332✔
207

208
        assert(p);
285,332✔
209
        assert(ret);
285,332✔
210

211
        /*
212
         * Turns this:
213
         *         /foo/bar/waldo
214
         *
215
         * Into this:
216
         *         /foo/bar/.#<extra>waldobaa2a261115984a9
217
         */
218

219
        if (asprintf(&s, "%016" PRIx64, random_u64()) < 0)
285,332✔
220
                return -ENOMEM;
221

222
        return tempfn_build(p, extra, s, /* child= */ false, ret);
285,332✔
223
}
224

225
int tempfn_random_child(const char *p, const char *extra, char **ret) {
15,331✔
226
        _cleanup_free_ char *s = NULL;
15,331✔
227
        int r;
15,331✔
228

229
        assert(ret);
15,331✔
230

231
        /* Turns this:
232
         *         /foo/bar/waldo
233
         * Into this:
234
         *         /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
235
         */
236

237
        if (!p) {
15,331✔
238
                r = tmp_dir(&p);
16✔
239
                if (r < 0)
16✔
240
                        return r;
241
        }
242

243
        if (asprintf(&s, "%016" PRIx64, random_u64()) < 0)
15,331✔
244
                return -ENOMEM;
245

246
        return tempfn_build(p, extra, s, /* child= */ true, ret);
15,331✔
247
}
248

249
int open_tmpfile_unlinkable(const char *directory, int flags) {
6,380✔
250
        int r;
6,380✔
251

252
        if (!directory) {
6,380✔
253
                r = tmp_dir(&directory);
9✔
254
                if (r < 0)
9✔
255
                        return r;
6,380✔
256
        } else if (isempty(directory))
12,751✔
257
                return -EINVAL;
258

259
        /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
260

261
        /* Try O_TMPFILE first, if it is supported */
262
        int fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
6,380✔
263
        if (fd >= 0)
6,380✔
264
                return fd;
265

266
        /* Fall back to unguessable name + unlinking */
UNCOV
267
        _cleanup_free_ char *p = path_join(directory, "/systemd-tmp-XXXXXX");
×
UNCOV
268
        if (!p)
×
269
                return -ENOMEM;
270

UNCOV
271
        fd = mkostemp_safe(p);
×
UNCOV
272
        if (fd < 0)
×
273
                return fd;
274

275
        (void) unlink(p);
×
276

UNCOV
277
        return fd;
×
278
}
279

280
int open_tmpfile_linkable_at(int dir_fd, const char *target, int flags, char **ret_path) {
24,790✔
281
        int r;
24,790✔
282

283
        assert(target);
24,790✔
284
        assert(ret_path);
24,790✔
285

286
        /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
287
        assert((flags & O_EXCL) == 0);
24,790✔
288

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

293
        int fd = open_parent_at(dir_fd, target, O_TMPFILE|flags, 0640);
24,790✔
294
        if (fd >= 0) {
24,790✔
295
                *ret_path = NULL;
24,660✔
296
                return fd;
24,790✔
297
        }
298

299
        if (!ERRNO_IS_NEG_NOT_SUPPORTED(fd))
130✔
300
                log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target);
33✔
301

302
        _cleanup_free_ char *tmp = NULL;
130✔
303
        r = tempfn_random(target, NULL, &tmp);
130✔
304
        if (r < 0)
130✔
305
                return r;
306

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

311
        *ret_path = TAKE_PTR(tmp);
97✔
312

313
        return fd;
97✔
314
}
315

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

321
        assert(target);
6,098✔
322
        assert(ret_file);
6,098✔
323
        assert(ret_path);
6,098✔
324

325
        fd = open_tmpfile_linkable_at(dir_fd, target, flags, &path);
6,098✔
326
        if (fd < 0)
6,098✔
327
                return fd;
328

329
        f = take_fdopen(&fd, "w");
6,098✔
330
        if (!f)
6,098✔
331
                return -ENOMEM;
332

333
        *ret_path = TAKE_PTR(path);
6,098✔
334
        *ret_file = TAKE_PTR(f);
6,098✔
335
        return 0;
6,098✔
336
}
337

338
int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, LinkTmpfileFlags flags) {
24,567✔
339
        int r;
24,567✔
340

341
        assert(fd >= 0);
24,567✔
342
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
24,567✔
343
        assert(target);
24,567✔
344

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

349
        if (FLAGS_SET(flags, LINK_TMPFILE_SYNC) && fsync(fd) < 0)
24,567✔
UNCOV
350
                return -errno;
×
351

352
        if (path) {
24,567✔
353
                if (FLAGS_SET(flags, LINK_TMPFILE_REPLACE))
97✔
354
                        r = RET_NERRNO(renameat(dir_fd, path, dir_fd, target));
70✔
355
                else
356
                        r = rename_noreplace(dir_fd, path, dir_fd, target);
27✔
357
        } else {
358
                if (FLAGS_SET(flags, LINK_TMPFILE_REPLACE))
24,470✔
359
                        r = linkat_replace(fd, /* oldpath= */ NULL, dir_fd, target);
23,402✔
360
                else
361
                        r = link_fd(fd, dir_fd, target);
1,068✔
362
        }
363
        if (r < 0)
24,497✔
364
                return r;
365

366
        if (FLAGS_SET(flags, LINK_TMPFILE_SYNC)) {
24,563✔
367
                r = fsync_full(fd);
1,754✔
368
                if (r < 0)
1,754✔
UNCOV
369
                        return r;
×
370
        }
371

372
        return 0;
373
}
374

375
int flink_tmpfile_at(FILE *f, int dir_fd, const char *path, const char *target, LinkTmpfileFlags flags) {
6,098✔
376
        int fd, r;
6,098✔
377

378
        assert(f);
6,098✔
379
        assert(target);
6,098✔
380

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

385
        r = fflush_and_check(f);
6,098✔
386
        if (r < 0)
6,098✔
387
                return r;
388

389
        return link_tmpfile_at(fd, dir_fd, path, target, flags);
6,098✔
390
}
391

392
int mkdtemp_malloc(const char *template, char **ret) {
4,693✔
393
        _cleanup_free_ char *p = NULL;
9,386✔
394
        int r;
4,693✔
395

396
        assert(ret);
4,693✔
397

398
        if (template)
4,693✔
399
                p = strdup(template);
4,549✔
400
        else {
401
                const char *tmp;
144✔
402

403
                r = tmp_dir(&tmp);
144✔
404
                if (r < 0)
144✔
UNCOV
405
                        return r;
×
406

407
                p = path_join(tmp, "XXXXXX");
144✔
408
        }
409
        if (!p)
4,693✔
410
                return -ENOMEM;
411

412
        if (!mkdtemp(p))
4,693✔
UNCOV
413
                return -errno;
×
414

415
        *ret = TAKE_PTR(p);
4,693✔
416
        return 0;
4,693✔
417
}
418

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

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

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

433
        if (ret)
60✔
434
                *ret = TAKE_PTR(p);
60✔
435

436
        return fd;
437
}
438

439
void cleanup_tmpfile_data_done(struct cleanup_tmpfile_data *d) {
19,073✔
440
        assert(d);
19,073✔
441

442
        if (!d->dir_fd ||
19,073✔
443
            (*d->dir_fd < 0 && *d->dir_fd != AT_FDCWD) ||
19,073✔
444
            !d->filename ||
19,073✔
445
            !*d->filename)
19,073✔
446
                return;
19,070✔
447

448
        PROTECT_ERRNO;
6✔
449

450
        (void) unlinkat(*d->dir_fd, *d->filename, /* flags= */ 0);
3✔
451
        d->dir_fd = NULL;
3✔
452
        d->filename = NULL;
3✔
453
}
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