'shared/strbuf.h',
'shared/util.c',
'shared/util.h',
+ 'shared/tmpfile-util.c',
+ 'shared/tmpfile-util.h',
),
gnu_symbol_visibility : 'hidden',
install : false,
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * Copyright (C) 2025 Intel Corporation.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <shared/macro.h>
+#include <shared/missing.h>
+#include <shared/tmpfile-util.h>
+#include <shared/util.h>
+
+FILE *tmpfile_openat(int dirfd, mode_t mode, struct tmpfile *file)
+{
+ const char *tmpname_tmpl = "tmpfileXXXXXX";
+ const char *tmpname;
+ char tmpfile_path[PATH_MAX];
+ int fd, n;
+ _cleanup_free_ char *targetdir;
+ FILE *fp;
+
+ targetdir = fd_lookup_path(dirfd);
+ if (targetdir == NULL)
+ goto create_fail;
+
+ n = snprintf(tmpfile_path, PATH_MAX, "%s/%s", targetdir, tmpname_tmpl);
+ if (n < 0 || n >= PATH_MAX)
+ goto create_fail;
+
+ fd = mkstemp(tmpfile_path);
+ if (fd < 0)
+ goto create_fail;
+
+ if (fchmod(fd, mode) < 0)
+ goto checkout_fail;
+
+ fp = fdopen(fd, "wb");
+ if (fp == NULL)
+ goto checkout_fail;
+
+ tmpname = basename(tmpfile_path);
+ memcpy(file->tmpname, tmpname, strlen(tmpname) + 1);
+
+ file->dirfd = dirfd;
+ file->fd = fd;
+
+ return fp;
+
+checkout_fail:
+ close(fd);
+ remove(tmpfile_path);
+
+create_fail:
+ return NULL;
+}
+
+int tmpfile_publish(struct tmpfile *file, const char *targetname)
+{
+ if (renameat(file->dirfd, file->tmpname, file->dirfd, targetname) != 0)
+ return -errno;
+
+ file->fd = -1;
+ file->dirfd = -1;
+ memset(file->tmpname, 0, PATH_MAX);
+
+ return 0;
+}
+
+void tmpfile_release(struct tmpfile *file)
+{
+ unlinkat(file->dirfd, file->tmpname, 0);
+
+ file->fd = -1;
+ file->dirfd = -1;
+ file->tmpname[0] = '\0';
+}
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * Copyright (C) 2025 Intel Corporation.
+ */
+
+#pragma once
+
+#include <stdio.h>
+#include <linux/limits.h>
+
+#include <shared/macro.h>
+
+struct tmpfile {
+ char tmpname[PATH_MAX];
+ int dirfd;
+ int fd;
+};
+
+/*
+ * Create a temporary file at the directory of `dirfd`
+*/
+_must_check_ _nonnull_(3) FILE *tmpfile_openat(int dirfd, mode_t mode,
+ struct tmpfile *file);
+
+/*
+ * Move the temporary file to `targetname`
+*/
+_nonnull_all_ int tmpfile_publish(struct tmpfile *file, const char *targetname);
+
+/*
+ * Delete the temporary file
+*/
+_nonnull_all_ void tmpfile_release(struct tmpfile *file);
return mkdir_p(path, end - path, mode);
}
+char *fd_lookup_path(int fd)
+{
+ char proc_path[PATH_MAX];
+ char fd_path[PATH_MAX];
+ ssize_t len;
+
+ len = snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd);
+ if (len < 0 || len >= (ssize_t)sizeof(proc_path))
+ return NULL;
+
+ /*
+ * We are using mkstemp to create a temporary file. We need to read the link since
+ * the mkstemp creates file with an absolute path
+ */
+ len = readlink(proc_path, fd_path, sizeof(fd_path) - 1);
+ if (len < 0 || len >= (ssize_t)sizeof(fd_path))
+ return NULL;
+
+ fd_path[len] = '\0';
+
+ return strdup(fd_path);
+}
+
static unsigned long long ts_usec(const struct timespec *ts)
{
return (unsigned long long)ts->tv_sec * USEC_PER_SEC +
int mkdir_p(const char *path, int len, mode_t mode);
int mkdir_parents(const char *path, mode_t mode);
unsigned long long stat_mstamp(const struct stat *st);
+char *fd_lookup_path(int fd);
/* time-related functions
* ************************************************************************ */
#include <shared/hash.h>
#include <shared/macro.h>
#include <shared/strbuf.h>
+#include <shared/tmpfile-util.h>
#include <shared/util.h>
#include <libkmod/libkmod-internal.h>
for (itr = depfiles; itr->name != NULL; itr++) {
FILE *fp = out;
- char tmp[NAME_MAX] = "";
+ struct tmpfile file;
int r, ferr;
if (fp == NULL) {
- int flags = O_CREAT | O_EXCL | O_WRONLY;
- int mode = 0644;
- int fd;
- int n;
-
- n = snprintf(tmp, sizeof(tmp), "%s.%i.%lli.%lli", itr->name,
- getpid(), (long long)tv.tv_usec,
- (long long)tv.tv_sec);
- if (n >= (int)sizeof(tmp)) {
- ERR("bad filename: %s.%i.%lli.%lli: path too long\n",
- itr->name, getpid(), (long long)tv.tv_usec,
- (long long)tv.tv_sec);
- continue;
- }
- fd = openat(dfd, tmp, flags, mode);
- if (fd < 0) {
- ERR("openat(%s, %s, %o, %o): %m\n", dname, tmp, flags,
- mode);
- continue;
- }
- fp = fdopen(fd, "wb");
+ mode_t mode = 0644;
+
+ fp = tmpfile_openat(dfd, mode, &file);
if (fp == NULL) {
- ERR("fdopen(%d=%s/%s): %m\n", fd, dname, tmp);
- close(fd);
+ ERR("Could not create temporary file at '%s'\n", dname);
continue;
}
}
ferr = ferror(fp) | fclose(fp);
if (r < 0) {
- if (unlinkat(dfd, tmp, 0) != 0)
- ERR("unlinkat(%s, %s): %m\n", dname, tmp);
+ tmpfile_release(&file);
ERR("Could not write index '%s': %s\n", itr->name, strerror(-r));
err = -errno;
break;
}
- if (renameat(dfd, tmp, dfd, itr->name) != 0) {
- err = -errno;
- CRIT("renameat(%s, %s, %s, %s): %m\n", dname, tmp, dname,
- itr->name);
+ err = tmpfile_publish(&file, itr->name);
+ if (err != 0) {
+ CRIT("publish temporary from %s to %s\n", file.tmpname, itr->name);
break;
}