]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/tmpfile-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
11 #include "memfd-util.h"
12 #include "missing_fcntl.h"
13 #include "missing_syscall.h"
14 #include "path-util.h"
15 #include "process-util.h"
16 #include "random-util.h"
17 #include "stdio-util.h"
18 #include "string-util.h"
19 #include "tmpfile-util.h"
20 #include "umask-util.h"
22 static int fopen_temporary_internal(int dir_fd
, const char *path
, FILE **ret_file
) {
23 _cleanup_fclose_
FILE *f
= NULL
;
24 _cleanup_close_
int fd
= -EBADF
;
27 assert(dir_fd
>= 0 || dir_fd
== AT_FDCWD
);
30 fd
= openat(dir_fd
, path
, O_CLOEXEC
|O_NOCTTY
|O_RDWR
|O_CREAT
|O_EXCL
, 0600);
34 /* This assumes that returned FILE object is short-lived and used within the same single-threaded
35 * context and never shared externally, hence locking is not necessary. */
37 r
= take_fdopen_unlocked(&fd
, "w", &f
);
39 (void) unlinkat(dir_fd
, path
, 0);
44 *ret_file
= TAKE_PTR(f
);
49 int fopen_temporary_at(int dir_fd
, const char *path
, FILE **ret_file
, char **ret_path
) {
50 _cleanup_free_
char *t
= NULL
;
53 assert(dir_fd
>= 0 || dir_fd
== AT_FDCWD
);
56 r
= tempfn_random(path
, NULL
, &t
);
60 r
= fopen_temporary_internal(dir_fd
, t
, ret_file
);
65 *ret_path
= TAKE_PTR(t
);
70 int fopen_temporary_child_at(int dir_fd
, const char *path
, FILE **ret_file
, char **ret_path
) {
71 _cleanup_free_
char *t
= NULL
;
74 assert(dir_fd
>= 0 || dir_fd
== AT_FDCWD
);
82 r
= tempfn_random_child(path
, NULL
, &t
);
86 r
= fopen_temporary_internal(dir_fd
, t
, ret_file
);
91 *ret_path
= TAKE_PTR(t
);
96 /* This is much like mkostemp() but is subject to umask(). */
97 int mkostemp_safe(char *pattern
) {
99 BLOCK_WITH_UMASK(0077);
100 return RET_NERRNO(mkostemp(pattern
, O_CLOEXEC
));
103 int fmkostemp_safe(char *pattern
, const char *mode
, FILE **ret_f
) {
104 _cleanup_close_
int fd
= -EBADF
;
107 fd
= mkostemp_safe(pattern
);
111 f
= take_fdopen(&fd
, mode
);
119 static int tempfn_build(const char *p
, const char *pre
, const char *post
, bool child
, char **ret
) {
120 _cleanup_free_
char *d
= NULL
, *fn
= NULL
, *nf
= NULL
, *result
= NULL
;
121 size_t len_pre
, len_post
, len_add
;
132 * /foo/bar/waldo/.#<pre><post> (child == true)
133 * /foo/bar/.#<pre>waldo<post> (child == false)
136 if (pre
&& strchr(pre
, '/'))
139 if (post
&& strchr(post
, '/'))
142 len_pre
= strlen_ptr(pre
);
143 len_post
= strlen_ptr(post
);
144 /* NAME_MAX is counted *without* the trailing NUL byte. */
145 if (len_pre
> NAME_MAX
- STRLEN(".#") ||
146 len_post
> NAME_MAX
- STRLEN(".#") - len_pre
)
149 len_add
= len_pre
+ len_post
+ STRLEN(".#");
156 r
= path_extract_directory(p
, &d
);
157 if (r
< 0 && r
!= -EDESTADDRREQ
) /* EDESTADDRREQ → No directory specified, just a filename */
160 r
= path_extract_filename(p
, &fn
);
164 if (strlen(fn
) > NAME_MAX
- len_add
)
165 /* We cannot simply prepend and append strings to the filename. Let's truncate the filename. */
166 fn
[NAME_MAX
- len_add
] = '\0';
169 nf
= strjoin(".#", strempty(pre
), strempty(fn
), strempty(post
));
174 if (!path_extend(&d
, nf
))
177 result
= path_simplify(TAKE_PTR(d
));
179 result
= TAKE_PTR(nf
);
181 if (!path_is_valid(result
)) /* New path is not valid? (Maybe because too long?) Refuse. */
184 *ret
= TAKE_PTR(result
);
188 int tempfn_xxxxxx(const char *p
, const char *extra
, char **ret
) {
194 * /foo/bar/.#<extra>waldoXXXXXX
197 return tempfn_build(p
, extra
, "XXXXXX", /* child = */ false, ret
);
200 int tempfn_random(const char *p
, const char *extra
, char **ret
) {
201 _cleanup_free_
char *s
= NULL
;
211 * /foo/bar/.#<extra>waldobaa2a261115984a9
214 if (asprintf(&s
, "%016" PRIx64
, random_u64()) < 0)
217 return tempfn_build(p
, extra
, s
, /* child = */ false, ret
);
220 int tempfn_random_child(const char *p
, const char *extra
, char **ret
) {
221 _cleanup_free_
char *s
= NULL
;
229 * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
238 if (asprintf(&s
, "%016" PRIx64
, random_u64()) < 0)
241 return tempfn_build(p
, extra
, s
, /* child = */ true, ret
);
244 int open_tmpfile_unlinkable(const char *directory
, int flags
) {
249 r
= tmp_dir(&directory
);
252 } else if (isempty(directory
))
255 /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
257 /* Try O_TMPFILE first, if it is supported */
258 fd
= open(directory
, flags
|O_TMPFILE
|O_EXCL
, S_IRUSR
|S_IWUSR
);
262 /* Fall back to unguessable name + unlinking */
263 p
= strjoina(directory
, "/systemd-tmp-XXXXXX");
265 fd
= mkostemp_safe(p
);
274 int open_tmpfile_linkable(const char *target
, int flags
, char **ret_path
) {
275 _cleanup_free_
char *tmp
= NULL
;
281 /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
282 assert((flags
& O_EXCL
) == 0);
284 /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
285 * which case "ret_path" will be returned as NULL. If not possible the temporary path name used is returned in
286 * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
288 fd
= open_parent(target
, O_TMPFILE
|flags
, 0640);
294 log_debug_errno(fd
, "Failed to use O_TMPFILE for %s: %m", target
);
296 r
= tempfn_random(target
, NULL
, &tmp
);
300 fd
= open(tmp
, O_CREAT
|O_EXCL
|O_NOFOLLOW
|O_NOCTTY
|flags
, 0640);
304 *ret_path
= TAKE_PTR(tmp
);
309 int fopen_tmpfile_linkable(const char *target
, int flags
, char **ret_path
, FILE **ret_file
) {
310 _cleanup_free_
char *path
= NULL
;
311 _cleanup_fclose_
FILE *f
= NULL
;
312 _cleanup_close_
int fd
= -EBADF
;
318 fd
= open_tmpfile_linkable(target
, flags
, &path
);
322 f
= take_fdopen(&fd
, "w");
326 *ret_path
= TAKE_PTR(path
);
327 *ret_file
= TAKE_PTR(f
);
331 int link_tmpfile(int fd
, const char *path
, const char *target
) {
335 /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd
336 * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported
337 * on the directory, and renameat2() is used instead.
339 * Note that in both cases we will not replace existing files. This is because linkat() does not support this
340 * operation currently (renameat2() does), and there is no nice way to emulate this. */
343 return rename_noreplace(AT_FDCWD
, path
, AT_FDCWD
, target
);
345 return RET_NERRNO(linkat(AT_FDCWD
, FORMAT_PROC_FD_PATH(fd
), AT_FDCWD
, target
, AT_SYMLINK_FOLLOW
));
348 int flink_tmpfile(FILE *f
, const char *path
, const char *target
) {
355 if (fd
< 0) /* Not all FILE* objects encapsulate fds */
358 r
= fflush_sync_and_check(f
);
362 return link_tmpfile(fd
, path
, target
);
365 int mkdtemp_malloc(const char *template, char **ret
) {
366 _cleanup_free_
char *p
= NULL
;
372 p
= strdup(template);
380 p
= path_join(tmp
, "XXXXXX");
392 int mkdtemp_open(const char *template, int flags
, char **ret
) {
393 _cleanup_free_
char *p
= NULL
;
396 r
= mkdtemp_malloc(template, &p
);
400 fd
= RET_NERRNO(open(p
, O_DIRECTORY
|O_CLOEXEC
|flags
));