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

systemd / systemd / 14895667988

07 May 2025 08:57PM UTC coverage: 72.225% (-0.007%) from 72.232%
14895667988

push

github

yuwata
network: log_link_message_debug_errno() automatically append %m if necessary

Follow-up for d28746ef5.
Fixes CID#1609753.

0 of 1 new or added line in 1 file covered. (0.0%)

20297 existing lines in 338 files now uncovered.

297407 of 411780 relevant lines covered (72.22%)

695716.85 hits per line

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

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

3
#include <sys/mman.h>
4

5
#include "alloc-util.h"
6
#include "errno-util.h"
7
#include "fd-util.h"
8
#include "fileio.h"
9
#include "fs-util.h"
10
#include "hexdecoct.h"
11
#include "log.h"
12
#include "macro.h"
13
#include "memfd-util.h"
14
#include "missing_fcntl.h"
15
#include "missing_syscall.h"
16
#include "path-util.h"
17
#include "process-util.h"
18
#include "random-util.h"
19
#include "stat-util.h"
20
#include "stdio-util.h"
21
#include "string-util.h"
22
#include "sync-util.h"
23
#include "tmpfile-util.h"
24
#include "umask-util.h"
25

26
static int fopen_temporary_internal(int dir_fd, const char *path, FILE **ret_file) {
160,027✔
27
        _cleanup_fclose_ FILE *f = NULL;
160,027✔
28
        _cleanup_close_ int fd = -EBADF;
160,027✔
29
        int r;
160,027✔
30

31
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
160,027✔
32
        assert(path);
160,027✔
33

34
        fd = openat(dir_fd, path, O_CLOEXEC|O_NOCTTY|O_RDWR|O_CREAT|O_EXCL, 0600);
160,027✔
35
        if (fd < 0)
160,027✔
36
                return -errno;
1✔
37

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

41
        r = take_fdopen_unlocked(&fd, "w", &f);
160,026✔
42
        if (r < 0) {
160,026✔
43
                (void) unlinkat(dir_fd, path, 0);
×
UNCOV
44
                return r;
×
45
        }
46

47
        if (ret_file)
160,026✔
48
                *ret_file = TAKE_PTR(f);
160,026✔
49

50
        return 0;
51
}
52

53
int fopen_temporary_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path) {
160,025✔
54
        _cleanup_free_ char *t = NULL;
160,025✔
55
        int r;
160,025✔
56

57
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
160,025✔
58
        assert(path);
160,025✔
59

60
        r = tempfn_random(path, NULL, &t);
160,025✔
61
        if (r < 0)
160,025✔
62
                return r;
63

64
        r = fopen_temporary_internal(dir_fd, t, ret_file);
160,025✔
65
        if (r < 0)
160,025✔
66
                return r;
67

68
        if (ret_path)
160,024✔
69
                *ret_path = TAKE_PTR(t);
160,024✔
70

71
        return 0;
72
}
73

74
int fopen_temporary_child_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path) {
2✔
75
        _cleanup_free_ char *t = NULL;
2✔
76
        int r;
2✔
77

78
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
2✔
79

80
        if (!path) {
2✔
81
                r = tmp_dir(&path);
1✔
82
                if (r < 0)
1✔
83
                        return r;
84
        }
85

86
        r = tempfn_random_child(path, NULL, &t);
2✔
87
        if (r < 0)
2✔
88
                return r;
89

90
        r = fopen_temporary_internal(dir_fd, t, ret_file);
2✔
91
        if (r < 0)
2✔
92
                return r;
93

94
        if (ret_path)
2✔
95
                *ret_path = TAKE_PTR(t);
2✔
96

97
        return 0;
98
}
99

100
/* This is much like mkostemp() but is subject to umask(). */
101
int mkostemp_safe(char *pattern) {
155✔
102
        assert(pattern);
155✔
103
        BLOCK_WITH_UMASK(0077);
155✔
104
        return RET_NERRNO(mkostemp(pattern, O_CLOEXEC));
155✔
105
}
106

107
int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) {
44✔
108
        _cleanup_close_ int fd = -EBADF;
44✔
109
        FILE *f;
44✔
110

111
        fd = mkostemp_safe(pattern);
44✔
112
        if (fd < 0)
44✔
113
                return fd;
114

115
        f = take_fdopen(&fd, mode);
44✔
116
        if (!f)
44✔
UNCOV
117
                return -errno;
×
118

119
        *ret_f = f;
44✔
120
        return 0;
44✔
121
}
122

123
void unlink_tempfilep(char (*p)[]) {
121✔
124
        assert(p);
121✔
125

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

130
        if (!endswith(*p, ".XXXXXX"))
121✔
131
                (void) unlink(*p);
119✔
132
}
121✔
133

134
static int tempfn_build(const char *p, const char *pre, const char *post, bool child, char **ret) {
320,450✔
135
        _cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL, *result = NULL;
320,450✔
136
        size_t len_pre, len_post, len_add;
320,450✔
137
        int r;
320,450✔
138

139
        assert(p);
320,450✔
140
        assert(ret);
320,450✔
141

142
        /*
143
         * Turns this:
144
         *         /foo/bar/waldo
145
         *
146
         * Into this :
147
         *         /foo/bar/waldo/.#<pre><post> (child == true)
148
         *         /foo/bar/.#<pre>waldo<post> (child == false)
149
         */
150

151
        if (pre && strchr(pre, '/'))
320,450✔
152
                return -EINVAL;
153

154
        if (post && strchr(post, '/'))
320,447✔
155
                return -EINVAL;
156

157
        len_pre = strlen_ptr(pre);
320,447✔
158
        len_post = strlen_ptr(post);
320,447✔
159
        /* NAME_MAX is counted *without* the trailing NUL byte. */
160
        if (len_pre > NAME_MAX - STRLEN(".#") ||
320,447✔
161
            len_post > NAME_MAX - STRLEN(".#") - len_pre)
320,447✔
162
                return -EINVAL;
163

164
        len_add = len_pre + len_post + STRLEN(".#");
320,447✔
165

166
        if (child) {
320,447✔
167
                d = strdup(p);
1,966✔
168
                if (!d)
1,966✔
169
                        return -ENOMEM;
170
        } else {
171
                r = path_extract_directory(p, &d);
318,481✔
172
                if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
318,481✔
173
                        return r;
174

175
                r = path_extract_filename(p, &fn);
318,473✔
176
                if (r < 0)
318,473✔
177
                        return r;
178

179
                if (strlen(fn) > NAME_MAX - len_add)
318,473✔
180
                        /* We cannot simply prepend and append strings to the filename. Let's truncate the filename. */
181
                        fn[NAME_MAX - len_add] = '\0';
4✔
182
        }
183

184
        nf = strjoin(".#", strempty(pre), strempty(fn), strempty(post));
640,764✔
185
        if (!nf)
320,439✔
186
                return -ENOMEM;
187

188
        if (d) {
320,439✔
189
                if (!path_extend(&d, nf))
319,887✔
190
                        return -ENOMEM;
191

192
                result = path_simplify(TAKE_PTR(d));
319,887✔
193
        } else
194
                result = TAKE_PTR(nf);
195

196
        if (!path_is_valid(result)) /* New path is not valid? (Maybe because too long?) Refuse. */
320,439✔
197
                return -EINVAL;
198

199
        *ret = TAKE_PTR(result);
320,436✔
200
        return 0;
320,436✔
201
}
202

203
int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
27✔
204
        /*
205
         * Turns this:
206
         *         /foo/bar/waldo
207
         *
208
         * Into this:
209
         *         /foo/bar/.#<extra>waldoXXXXXX
210
         */
211

212
        return tempfn_build(p, extra, "XXXXXX", /* child = */ false, ret);
27✔
213
}
214

215
int tempfn_random(const char *p, const char *extra, char **ret) {
318,456✔
216
        _cleanup_free_ char *s = NULL;
318,456✔
217

218
        assert(p);
318,456✔
219
        assert(ret);
318,456✔
220

221
        /*
222
         * Turns this:
223
         *         /foo/bar/waldo
224
         *
225
         * Into this:
226
         *         /foo/bar/.#<extra>waldobaa2a261115984a9
227
         */
228

229
        if (asprintf(&s, "%016" PRIx64, random_u64()) < 0)
318,456✔
230
                return -ENOMEM;
231

232
        return tempfn_build(p, extra, s, /* child = */ false, ret);
318,456✔
233
}
234

235
int tempfn_random_child(const char *p, const char *extra, char **ret) {
1,967✔
236
        _cleanup_free_ char *s = NULL;
1,967✔
237
        int r;
1,967✔
238

239
        assert(ret);
1,967✔
240

241
        /* Turns this:
242
         *         /foo/bar/waldo
243
         * Into this:
244
         *         /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
245
         */
246

247
        if (!p) {
1,967✔
248
                r = tmp_dir(&p);
15✔
249
                if (r < 0)
15✔
250
                        return r;
251
        }
252

253
        if (asprintf(&s, "%016" PRIx64, random_u64()) < 0)
1,967✔
254
                return -ENOMEM;
255

256
        return tempfn_build(p, extra, s, /* child = */ true, ret);
1,967✔
257
}
258

259
int open_tmpfile_unlinkable(const char *directory, int flags) {
6,368✔
260
        char *p;
6,368✔
261
        int fd, r;
6,368✔
262

263
        if (!directory) {
6,368✔
264
                r = tmp_dir(&directory);
7✔
265
                if (r < 0)
7✔
266
                        return r;
267
        } else if (isempty(directory))
6,361✔
268
                return -EINVAL;
269

270
        /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
271

272
        /* Try O_TMPFILE first, if it is supported */
273
        fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
6,368✔
274
        if (fd >= 0)
6,368✔
275
                return fd;
276

277
        /* Fall back to unguessable name + unlinking */
UNCOV
278
        p = strjoina(directory, "/systemd-tmp-XXXXXX");
×
279

280
        fd = mkostemp_safe(p);
×
UNCOV
281
        if (fd < 0)
×
282
                return fd;
283

UNCOV
284
        (void) unlink(p);
×
285

UNCOV
286
        return fd;
×
287
}
288

289
int open_tmpfile_linkable_at(int dir_fd, const char *target, int flags, char **ret_path) {
2,748✔
290
        _cleanup_free_ char *tmp = NULL;
2,748✔
291
        int r, fd;
2,748✔
292

293
        assert(target);
2,748✔
294
        assert(ret_path);
2,748✔
295

296
        /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
297
        assert((flags & O_EXCL) == 0);
2,748✔
298

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

303
        fd = open_parent_at(dir_fd, target, O_TMPFILE|flags, 0640);
2,748✔
304
        if (fd >= 0) {
2,748✔
305
                *ret_path = NULL;
2,715✔
306
                return fd;
2,715✔
307
        }
308

309
        log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target);
33✔
310

311
        r = tempfn_random(target, NULL, &tmp);
33✔
312
        if (r < 0)
33✔
313
                return r;
314

315
        fd = openat(dir_fd, tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640);
33✔
316
        if (fd < 0)
33✔
317
                return -errno;
17✔
318

319
        *ret_path = TAKE_PTR(tmp);
16✔
320

321
        return fd;
16✔
322
}
323

324
int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file) {
44✔
325
        _cleanup_free_ char *path = NULL;
44✔
326
        _cleanup_fclose_ FILE *f = NULL;
44✔
327
        _cleanup_close_ int fd = -EBADF;
44✔
328

329
        assert(target);
44✔
330
        assert(ret_file);
44✔
331
        assert(ret_path);
44✔
332

333
        fd = open_tmpfile_linkable(target, flags, &path);
44✔
334
        if (fd < 0)
44✔
335
                return fd;
336

337
        f = take_fdopen(&fd, "w");
44✔
338
        if (!f)
44✔
339
                return -ENOMEM;
340

341
        *ret_path = TAKE_PTR(path);
44✔
342
        *ret_file = TAKE_PTR(f);
44✔
343
        return 0;
44✔
344
}
345

346
int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, LinkTmpfileFlags flags) {
2,675✔
347
        int r;
2,675✔
348

349
        assert(fd >= 0);
2,675✔
350
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
2,675✔
351
        assert(target);
2,675✔
352

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

357
        if (FLAGS_SET(flags, LINK_TMPFILE_SYNC) && fsync(fd) < 0)
2,675✔
UNCOV
358
                return -errno;
×
359

360
        if (path) {
2,675✔
361
                if (FLAGS_SET(flags, LINK_TMPFILE_REPLACE))
16✔
UNCOV
362
                        r = RET_NERRNO(renameat(dir_fd, path, dir_fd, target));
×
363
                else
364
                        r = rename_noreplace(dir_fd, path, dir_fd, target);
16✔
365
        } else {
366
                if (FLAGS_SET(flags, LINK_TMPFILE_REPLACE))
2,659✔
367
                        r = linkat_replace(fd, /* oldpath= */ NULL, dir_fd, target);
1,783✔
368
                else
369
                        r = link_fd(fd, dir_fd, target);
876✔
370
        }
371
        if (r < 0)
2,675✔
372
                return r;
373

374
        if (FLAGS_SET(flags, LINK_TMPFILE_SYNC)) {
2,672✔
375
                r = fsync_full(fd);
1,101✔
376
                if (r < 0)
1,101✔
UNCOV
377
                        return r;
×
378
        }
379

380
        return 0;
381
}
382

383
int flink_tmpfile(FILE *f, const char *path, const char *target, LinkTmpfileFlags flags) {
44✔
384
        int fd, r;
44✔
385

386
        assert(f);
44✔
387
        assert(target);
44✔
388

389
        fd = fileno(f);
44✔
390
        if (fd < 0) /* Not all FILE* objects encapsulate fds */
44✔
391
                return -EBADF;
392

393
        r = fflush_and_check(f);
44✔
394
        if (r < 0)
44✔
395
                return r;
396

397
        return link_tmpfile(fd, path, target, flags);
44✔
398
}
399

400
int mkdtemp_malloc(const char *template, char **ret) {
3,737✔
401
        _cleanup_free_ char *p = NULL;
7,474✔
402
        int r;
3,737✔
403

404
        assert(ret);
3,737✔
405

406
        if (template)
3,737✔
407
                p = strdup(template);
3,660✔
408
        else {
409
                const char *tmp;
77✔
410

411
                r = tmp_dir(&tmp);
77✔
412
                if (r < 0)
77✔
UNCOV
413
                        return r;
×
414

415
                p = path_join(tmp, "XXXXXX");
77✔
416
        }
417
        if (!p)
3,737✔
418
                return -ENOMEM;
419

420
        if (!mkdtemp(p))
3,737✔
UNCOV
421
                return -errno;
×
422

423
        *ret = TAKE_PTR(p);
3,737✔
424
        return 0;
3,737✔
425
}
426

427
int mkdtemp_open(const char *template, int flags, char **ret) {
36✔
428
        _cleanup_free_ char *p = NULL;
36✔
429
        int fd, r;
36✔
430

431
        r = mkdtemp_malloc(template, &p);
36✔
432
        if (r < 0)
36✔
433
                return r;
434

435
        fd = RET_NERRNO(open(p, O_DIRECTORY|O_CLOEXEC|flags));
36✔
436
        if (fd < 0) {
×
437
                (void) rmdir(p);
×
UNCOV
438
                return fd;
×
439
        }
440

441
        if (ret)
36✔
442
                *ret = TAKE_PTR(p);
36✔
443

444
        return fd;
445
}
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