From e1ecf0dd6897eae1594b7e9345605b8f88485b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 6 Dec 2025 14:27:39 +0100 Subject: [PATCH 1/5] wrapper: add git_mkdtemp() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend git_mkstemps_mode() to optionally call mkdir(2) instead of open(2), then use that ability to create a mkdtemp(3) replacement, git_mkdtemp(). We'll start using it in the next commit. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- wrapper.c | 21 +++++++++++++++++++-- wrapper.h | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/wrapper.c b/wrapper.c index 3d507d4204..89f6effe84 100644 --- a/wrapper.c +++ b/wrapper.c @@ -446,7 +446,11 @@ int xmkstemp(char *filename_template) #undef TMP_MAX #define TMP_MAX 16384 -int git_mkstemps_mode(char *pattern, int suffix_len, int mode) +/* + * Returns -1 on error, 0 if it created a directory, or an open file + * descriptor to the created regular file. + */ +static int git_mkdstemps_mode(char *pattern, int suffix_len, int mode, bool dir) { static const char letters[] = "abcdefghijklmnopqrstuvwxyz" @@ -488,7 +492,10 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode) v /= num_letters; } - fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode); + if (dir) + fd = mkdir(pattern, mode); + else + fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode); if (fd >= 0) return fd; /* @@ -503,6 +510,16 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode) return -1; } +char *git_mkdtemp(char *pattern) +{ + return git_mkdstemps_mode(pattern, 0, 0700, true) ? NULL : pattern; +} + +int git_mkstemps_mode(char *pattern, int suffix_len, int mode) +{ + return git_mkdstemps_mode(pattern, suffix_len, mode, false); +} + int git_mkstemp_mode(char *pattern, int mode) { /* mkstemp is just mkstemps with no suffix */ diff --git a/wrapper.h b/wrapper.h index 44a8597ac3..15ac3bab6e 100644 --- a/wrapper.h +++ b/wrapper.h @@ -37,6 +37,8 @@ int xsnprintf(char *dst, size_t max, const char *fmt, ...); int xgethostname(char *buf, size_t len); +char *git_mkdtemp(char *pattern); + /* set default permissions by passing mode arguments to open(2) */ int git_mkstemps_mode(char *pattern, int suffix_len, int mode); int git_mkstemp_mode(char *pattern, int mode); From 5ecd3590a3052820eeb3f1d6764584c537b68938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 6 Dec 2025 14:27:47 +0100 Subject: [PATCH 2/5] compat: use git_mkdtemp() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A file might appear at the path returned by mktemp(3) before we call mkdir(2). Use the more robust git_mkdtemp() instead, which retries a number of times and doesn't need to call lstat(2). Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- compat/mkdtemp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compat/mkdtemp.c b/compat/mkdtemp.c index 1136119592..fcdd4e01e1 100644 --- a/compat/mkdtemp.c +++ b/compat/mkdtemp.c @@ -2,7 +2,5 @@ char *gitmkdtemp(char *template) { - if (!*mktemp(template) || mkdir(template, 0700)) - return NULL; - return template; + return git_mkdtemp(template); } From 47bf14750eee7e43e12d20414d3698f203245a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 6 Dec 2025 14:28:26 +0100 Subject: [PATCH 3/5] compat: remove mingw_mktemp() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the mktemp(3) compatibility function now that its last caller was removed by the previous commit. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- compat/mingw-posix.h | 3 --- compat/mingw.c | 12 ------------ 2 files changed, 15 deletions(-) diff --git a/compat/mingw-posix.h b/compat/mingw-posix.h index 631a208684..0939feff27 100644 --- a/compat/mingw-posix.h +++ b/compat/mingw-posix.h @@ -241,9 +241,6 @@ int mingw_chdir(const char *dirname); int mingw_chmod(const char *filename, int mode); #define chmod mingw_chmod -char *mingw_mktemp(char *template); -#define mktemp mingw_mktemp - char *mingw_getcwd(char *pointer, int len); #define getcwd mingw_getcwd diff --git a/compat/mingw.c b/compat/mingw.c index 736a07a028..abdc968421 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1162,18 +1162,6 @@ unsigned int sleep (unsigned int seconds) return 0; } -char *mingw_mktemp(char *template) -{ - wchar_t wtemplate[MAX_PATH]; - if (xutftowcs_path(wtemplate, template) < 0) - return NULL; - if (!_wmktemp(wtemplate)) - return NULL; - if (xwcstoutf(template, wtemplate, strlen(template) + 1) < 0) - return NULL; - return template; -} - int mkstemp(char *template) { return git_mkstemp_mode(template, 0600); From 7bef658135944d26acf3e1ec9316ca11f4369cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 6 Dec 2025 14:29:43 +0100 Subject: [PATCH 4/5] banned.h: ban mktemp(3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Older versions of mktemp(3) generate easily guessable file names. The function checks if the generated name is used, which is unreliable, as a file with that name might then be created by some other process before we can do it ourselves. The function was dropped from POSIX due to its security problems. Forbid its use. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- banned.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/banned.h b/banned.h index 44e76bd90a..2b934c8c43 100644 --- a/banned.h +++ b/banned.h @@ -41,4 +41,7 @@ #undef asctime_r #define asctime_r(t, buf) BANNED(asctime_r) +#undef mktemp +#define mktemp(x) BANNED(mktemp) + #endif /* BANNED_H */ From 10bba537c4c23e713af05be700748c6a3c25bf68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 6 Dec 2025 14:35:39 +0100 Subject: [PATCH 5/5] compat: remove gitmkdtemp() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gitmkdtemp() has become a trivial wrapper around git_mkdtemp(). Remove this now unnecessary layer of indirection. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- Makefile | 1 - compat/mkdtemp.c | 6 ------ compat/posix.h | 3 +-- contrib/buildsystems/CMakeLists.txt | 4 ---- meson.build | 2 +- 5 files changed, 2 insertions(+), 14 deletions(-) delete mode 100644 compat/mkdtemp.c diff --git a/Makefile b/Makefile index 7e0f77e298..8f74b25fe7 100644 --- a/Makefile +++ b/Makefile @@ -1917,7 +1917,6 @@ ifdef NO_SETENV endif ifdef NO_MKDTEMP COMPAT_CFLAGS += -DNO_MKDTEMP - COMPAT_OBJS += compat/mkdtemp.o endif ifdef MKDIR_WO_TRAILING_SLASH COMPAT_CFLAGS += -DMKDIR_WO_TRAILING_SLASH diff --git a/compat/mkdtemp.c b/compat/mkdtemp.c deleted file mode 100644 index fcdd4e01e1..0000000000 --- a/compat/mkdtemp.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "../git-compat-util.h" - -char *gitmkdtemp(char *template) -{ - return git_mkdtemp(template); -} diff --git a/compat/posix.h b/compat/posix.h index 067a00f33b..245386fa4a 100644 --- a/compat/posix.h +++ b/compat/posix.h @@ -329,8 +329,7 @@ int gitsetenv(const char *, const char *, int); #endif #ifdef NO_MKDTEMP -#define mkdtemp gitmkdtemp -char *gitmkdtemp(char *); +#define mkdtemp git_mkdtemp #endif #ifdef NO_UNSETENV diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt index edb0fc04ad..b84d8a7c76 100644 --- a/contrib/buildsystems/CMakeLists.txt +++ b/contrib/buildsystems/CMakeLists.txt @@ -411,10 +411,6 @@ if(NOT HAVE_SETENV) list(APPEND compat_SOURCES compat/setenv.c) endif() -if(NOT HAVE_MKDTEMP) - list(APPEND compat_SOURCES compat/mkdtemp.c) -endif() - if(NOT HAVE_PREAD) list(APPEND compat_SOURCES compat/pread.c) endif() diff --git a/meson.build b/meson.build index 1f95a06edb..4a42e783b1 100644 --- a/meson.build +++ b/meson.build @@ -1401,7 +1401,7 @@ checkfuncs = { 'strlcpy' : ['strlcpy.c'], 'strtoull' : [], 'setenv' : ['setenv.c'], - 'mkdtemp' : ['mkdtemp.c'], + 'mkdtemp' : [], 'initgroups' : [], 'strtoumax' : ['strtoumax.c', 'strtoimax.c'], 'pread' : ['pread.c'],