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

systemd / systemd / 14554080340

19 Apr 2025 11:46AM UTC coverage: 72.101% (-0.03%) from 72.13%
14554080340

push

github

web-flow
Add two new paragraphs to coding style about header files (#37188)

296880 of 411754 relevant lines covered (72.1%)

687547.52 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 "fd-util.h"
7
#include "fileio.h"
8
#include "fs-util.h"
9
#include "hexdecoct.h"
10
#include "log.h"
11
#include "macro.h"
12
#include "memfd-util.h"
13
#include "missing_fcntl.h"
14
#include "missing_syscall.h"
15
#include "path-util.h"
16
#include "process-util.h"
17
#include "random-util.h"
18
#include "stat-util.h"
19
#include "stdio-util.h"
20
#include "string-util.h"
21
#include "sync-util.h"
22
#include "tmpfile-util.h"
23
#include "umask-util.h"
24

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

30
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
162,552✔
31
        assert(path);
162,552✔
32

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

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

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

46
        if (ret_file)
162,551✔
47
                *ret_file = TAKE_PTR(f);
162,551✔
48

49
        return 0;
50
}
51

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

56
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
162,550✔
57
        assert(path);
162,550✔
58

59
        r = tempfn_random(path, NULL, &t);
162,550✔
60
        if (r < 0)
162,550✔
61
                return r;
62

63
        r = fopen_temporary_internal(dir_fd, t, ret_file);
162,550✔
64
        if (r < 0)
162,550✔
65
                return r;
66

67
        if (ret_path)
162,549✔
68
                *ret_path = TAKE_PTR(t);
162,549✔
69

70
        return 0;
71
}
72

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

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

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

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

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

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

96
        return 0;
97
}
98

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

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

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

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

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

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

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

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

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

138
        assert(p);
303,414✔
139
        assert(ret);
303,414✔
140

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

150
        if (pre && strchr(pre, '/'))
303,414✔
151
                return -EINVAL;
152

153
        if (post && strchr(post, '/'))
303,411✔
154
                return -EINVAL;
155

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

163
        len_add = len_pre + len_post + STRLEN(".#");
303,411✔
164

165
        if (child) {
303,411✔
166
                d = strdup(p);
1,956✔
167
                if (!d)
1,956✔
168
                        return -ENOMEM;
169
        } else {
170
                r = path_extract_directory(p, &d);
301,455✔
171
                if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
301,455✔
172
                        return r;
173

174
                r = path_extract_filename(p, &fn);
301,447✔
175
                if (r < 0)
301,447✔
176
                        return r;
177

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

183
        nf = strjoin(".#", strempty(pre), strempty(fn), strempty(post));
606,692✔
184
        if (!nf)
303,403✔
185
                return -ENOMEM;
186

187
        if (d) {
303,403✔
188
                if (!path_extend(&d, nf))
302,866✔
189
                        return -ENOMEM;
190

191
                result = path_simplify(TAKE_PTR(d));
302,866✔
192
        } else
193
                result = TAKE_PTR(nf);
194

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

198
        *ret = TAKE_PTR(result);
303,400✔
199
        return 0;
303,400✔
200
}
201

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

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

214
int tempfn_random(const char *p, const char *extra, char **ret) {
301,430✔
215
        _cleanup_free_ char *s = NULL;
301,430✔
216

217
        assert(p);
301,430✔
218
        assert(ret);
301,430✔
219

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

228
        if (asprintf(&s, "%016" PRIx64, random_u64()) < 0)
301,430✔
229
                return -ENOMEM;
230

231
        return tempfn_build(p, extra, s, /* child = */ false, ret);
301,430✔
232
}
233

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

238
        assert(ret);
1,957✔
239

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

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

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

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

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

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

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

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

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

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

283
        (void) unlink(p);
×
284

285
        return fd;
×
286
}
287

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

292
        assert(target);
2,743✔
293
        assert(ret_path);
2,743✔
294

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

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

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

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

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

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

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

320
        return fd;
16✔
321
}
322

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

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

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

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

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

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

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

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

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

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

373
        if (FLAGS_SET(flags, LINK_TMPFILE_SYNC)) {
2,669✔
374
                r = fsync_full(fd);
1,099✔
375
                if (r < 0)
1,099✔
376
                        return r;
×
377
        }
378

379
        return 0;
380
}
381

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

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

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

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

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

399
int mkdtemp_malloc(const char *template, char **ret) {
3,733✔
400
        _cleanup_free_ char *p = NULL;
7,466✔
401
        int r;
3,733✔
402

403
        assert(ret);
3,733✔
404

405
        if (template)
3,733✔
406
                p = strdup(template);
3,656✔
407
        else {
408
                const char *tmp;
77✔
409

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

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

419
        if (!mkdtemp(p))
3,733✔
420
                return -errno;
×
421

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

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

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

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

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

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