]>
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 int fopen_temporary(const char *path
, FILE **ret_f
, char **ret_temp_path
) {
23 _cleanup_fclose_
FILE *f
= NULL
;
24 _cleanup_free_
char *t
= NULL
;
25 _cleanup_close_
int fd
= -1;
29 r
= tempfn_xxxxxx(path
, NULL
, &t
);
39 t
= path_join(d
, "XXXXXX");
44 fd
= mkostemp_safe(t
);
48 /* This assumes that returned FILE object is short-lived and used within the same single-threaded
49 * context and never shared externally, hence locking is not necessary. */
51 r
= take_fdopen_unlocked(&fd
, "w", &f
);
61 *ret_temp_path
= TAKE_PTR(t
);
66 /* This is much like mkostemp() but is subject to umask(). */
67 int mkostemp_safe(char *pattern
) {
69 BLOCK_WITH_UMASK(0077);
70 return RET_NERRNO(mkostemp(pattern
, O_CLOEXEC
));
73 int fmkostemp_safe(char *pattern
, const char *mode
, FILE **ret_f
) {
74 _cleanup_close_
int fd
= -1;
77 fd
= mkostemp_safe(pattern
);
81 f
= take_fdopen(&fd
, mode
);
89 int tempfn_xxxxxx(const char *p
, const char *extra
, char **ret
) {
90 _cleanup_free_
char *d
= NULL
, *fn
= NULL
, *nf
= NULL
;
100 * /foo/bar/.#<extra>waldoXXXXXX
103 r
= path_extract_directory(p
, &d
);
104 if (r
< 0 && r
!= -EDESTADDRREQ
) /* EDESTADDRREQ → No directory specified, just a filename */
107 r
= path_extract_filename(p
, &fn
);
111 nf
= strjoin(".#", strempty(extra
), fn
, "XXXXXX");
115 if (!filename_is_valid(nf
)) /* New name is not valid? (Maybe because too long?) Refuse. */
119 if (!path_extend(&d
, nf
))
122 *ret
= path_simplify(TAKE_PTR(d
));
129 int tempfn_random(const char *p
, const char *extra
, char **ret
) {
130 _cleanup_free_
char *d
= NULL
, *fn
= NULL
, *nf
= NULL
;
140 * /foo/bar/.#<extra>waldobaa2a261115984a9
143 r
= path_extract_directory(p
, &d
);
144 if (r
< 0 && r
!= -EDESTADDRREQ
) /* EDESTADDRREQ → No directory specified, just a filename */
147 r
= path_extract_filename(p
, &fn
);
151 if (asprintf(&nf
, ".#%s%s%016" PRIx64
,
157 if (!filename_is_valid(nf
)) /* Not valid? (maybe because too long now?) — refuse early */
161 if (!path_extend(&d
, nf
))
164 *ret
= path_simplify(TAKE_PTR(d
));
171 int tempfn_random_child(const char *p
, const char *extra
, char **ret
) {
181 * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
190 extra
= strempty(extra
);
192 t
= new(char, strlen(p
) + 3 + strlen(extra
) + 16 + 1);
197 x
= stpcpy(stpcpy(t
, ".#"), extra
);
199 x
= stpcpy(stpcpy(stpcpy(t
, p
), "/.#"), extra
);
202 for (unsigned i
= 0; i
< 16; i
++) {
203 *(x
++) = hexchar(u
& 0xF);
209 *ret
= path_simplify(t
);
213 int open_tmpfile_unlinkable(const char *directory
, int flags
) {
218 r
= tmp_dir(&directory
);
221 } else if (isempty(directory
))
224 /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
226 /* Try O_TMPFILE first, if it is supported */
227 fd
= open(directory
, flags
|O_TMPFILE
|O_EXCL
, S_IRUSR
|S_IWUSR
);
231 /* Fall back to unguessable name + unlinking */
232 p
= strjoina(directory
, "/systemd-tmp-XXXXXX");
234 fd
= mkostemp_safe(p
);
243 int open_tmpfile_linkable(const char *target
, int flags
, char **ret_path
) {
244 _cleanup_free_
char *tmp
= NULL
;
250 /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
251 assert((flags
& O_EXCL
) == 0);
253 /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
254 * which case "ret_path" will be returned as NULL. If not possible the temporary path name used is returned in
255 * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
257 fd
= open_parent(target
, O_TMPFILE
|flags
, 0640);
263 log_debug_errno(fd
, "Failed to use O_TMPFILE for %s: %m", target
);
265 r
= tempfn_random(target
, NULL
, &tmp
);
269 fd
= open(tmp
, O_CREAT
|O_EXCL
|O_NOFOLLOW
|O_NOCTTY
|flags
, 0640);
273 *ret_path
= TAKE_PTR(tmp
);
278 int link_tmpfile(int fd
, const char *path
, const char *target
) {
282 /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd
283 * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported
284 * on the directory, and renameat2() is used instead.
286 * Note that in both cases we will not replace existing files. This is because linkat() does not support this
287 * operation currently (renameat2() does), and there is no nice way to emulate this. */
290 return rename_noreplace(AT_FDCWD
, path
, AT_FDCWD
, target
);
292 return RET_NERRNO(linkat(AT_FDCWD
, FORMAT_PROC_FD_PATH(fd
), AT_FDCWD
, target
, AT_SYMLINK_FOLLOW
));
295 int mkdtemp_malloc(const char *template, char **ret
) {
296 _cleanup_free_
char *p
= NULL
;
302 p
= strdup(template);
310 p
= path_join(tmp
, "XXXXXX");