From c2ac5819779a9b3b2dd0be3105f0d10d9878011e Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Sat, 22 Feb 2020 00:13:32 +0100 Subject: [PATCH] cgi-io: use O_TMPFILE for uploads and attempt to directly link target file Create an anonymous inode in /tmp using O_TMPFILE and attempt to link the file in place using linkat(). Only fall back to the old file copy when linking the tempfile fails. Avoids double memory use if both the temporary upload file and the destination file are located in /tmp. Ref: https://github.com/openwrt/luci/issues/3654 Signed-off-by: Jo-Philipp Wich --- Makefile | 2 +- src/main.c | 46 +++++++++++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index c8e4164..32498bc 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=cgi-io -PKG_RELEASE:=17 +PKG_RELEASE:=18 PKG_LICENSE:=GPL-2.0-or-later diff --git a/src/main.c b/src/main.c index 6e9112c..d45c67b 100644 --- a/src/main.c +++ b/src/main.c @@ -436,32 +436,44 @@ filecopy(void) return response(false, "No file data received"); } - if (lseek(st.tempfd, 0, SEEK_SET) < 0) - { - close(st.tempfd); - return response(false, "Failed to rewind temp file"); - } + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", st.tempfd); - st.filefd = open(st.filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); - - if (st.filefd < 0) + if (unlink(st.filename) < 0 && errno != ENOENT) { close(st.tempfd); - return response(false, "Failed to open target file"); + return response(false, "Failed to unlink existing file"); } - while ((len = read(st.tempfd, buf, sizeof(buf))) > 0) + if (linkat(AT_FDCWD, buf, AT_FDCWD, st.filename, AT_SYMLINK_FOLLOW) < 0) { - if (write(st.filefd, buf, len) != len) + if (lseek(st.tempfd, 0, SEEK_SET) < 0) + { + close(st.tempfd); + return response(false, "Failed to rewind temp file"); + } + + st.filefd = open(st.filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); + + if (st.filefd < 0) { close(st.tempfd); - close(st.filefd); - return response(false, "I/O failure while writing target file"); + return response(false, "Failed to open target file"); } + + while ((len = read(st.tempfd, buf, sizeof(buf))) > 0) + { + if (write(st.filefd, buf, len) != len) + { + close(st.tempfd); + close(st.filefd); + return response(false, "I/O failure while writing target file"); + } + } + + close(st.filefd); } close(st.tempfd); - close(st.filefd); if (chmod(st.filename, st.filemode)) return response(false, "Failed to chmod target file"); @@ -510,8 +522,6 @@ header_value(multipart_parser *p, const char *data, size_t len) static int data_begin_cb(multipart_parser *p) { - char tmpname[24] = "/tmp/luci-upload.XXXXXX"; - if (st.parttype == PART_FILEDATA) { if (!st.sessionid) @@ -523,12 +533,10 @@ data_begin_cb(multipart_parser *p) if (!session_access(st.sessionid, "file", st.filename, "write")) return response(false, "Access to path denied by ACL"); - st.tempfd = mkstemp(tmpname); + st.tempfd = open("/tmp", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR); if (st.tempfd < 0) return response(false, "Failed to create temporary file"); - - unlink(tmpname); } return 0; -- 2.30.2