Skip to content

Commit 84e1e56

Browse files
committed
Merge branch 'boretrk/futils_mktmp'
2 parents bc74691 + 4517a48 commit 84e1e56

File tree

4 files changed

+57
-19
lines changed

4 files changed

+57
-19
lines changed

src/futils.c

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,30 +26,32 @@ int git_futils_mkpath2file(const char *file_path, const mode_t mode)
2626

2727
int git_futils_mktmp(git_str *path_out, const char *filename, mode_t mode)
2828
{
29+
const int open_flags = O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC;
30+
/* TMP_MAX is unrelated to mktemp but should provide a reasonable amount */
31+
unsigned int tries = TMP_MAX;
2932
int fd;
30-
mode_t mask;
3133

32-
p_umask(mask = p_umask(0));
34+
while (tries--) {
35+
git_str_sets(path_out, filename);
36+
git_str_puts(path_out, "_git2_XXXXXX");
3337

34-
git_str_sets(path_out, filename);
35-
git_str_puts(path_out, "_git2_XXXXXX");
36-
37-
if (git_str_oom(path_out))
38-
return -1;
38+
if (git_str_oom(path_out))
39+
return -1;
3940

40-
if ((fd = p_mkstemp(path_out->ptr)) < 0) {
41-
git_error_set(GIT_ERROR_OS,
42-
"failed to create temporary file '%s'", path_out->ptr);
43-
return -1;
44-
}
41+
/* Using mktemp is safe when we open with O_CREAT | O_EXCL */
42+
p_mktemp(path_out->ptr);
43+
/* mktemp sets template to empty string on failure */
44+
if (path_out->ptr[0] == '\0')
45+
break;
4546

46-
if (p_chmod(path_out->ptr, (mode & ~mask))) {
47-
git_error_set(GIT_ERROR_OS,
48-
"failed to set permissions on file '%s'", path_out->ptr);
49-
return -1;
47+
if ((fd = p_open(path_out->ptr, open_flags, mode)) >= 0)
48+
return fd;
5049
}
5150

52-
return fd;
51+
git_error_set(GIT_ERROR_OS,
52+
"failed to create temporary file '%s'", path_out->ptr);
53+
git_str_dispose(path_out);
54+
return -1;
5355
}
5456

5557
int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode)

src/futils.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,16 @@ typedef enum {
173173
extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags);
174174

175175
/**
176-
* Create and open a temporary file with a `_git2_` suffix.
177-
* Writes the filename into path_out.
176+
* Create and open a temporary file with a `_git2_` suffix in a
177+
* protected directory; the file created will created will honor
178+
* the current `umask`. Writes the filename into path_out.
179+
*
180+
* This function is *NOT* suitable for use in temporary directories
181+
* that are world writable. It uses `mktemp` (for portability) and
182+
* many `mktemp` implementations use weak random characters. It
183+
* should only be assumed to be suitable for atomically writing
184+
* a new file in a directory that you control.
185+
*
178186
* @return On success, an open file descriptor, else an error code < 0.
179187
*/
180188
extern int git_futils_mktmp(git_str *path_out, const char *filename, mode_t mode);

src/posix.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "common.h"
1111

12+
#include <stdlib.h>
1213
#include <fcntl.h>
1314
#include <time.h>
1415

@@ -130,6 +131,7 @@ extern ssize_t p_pwrite(int fd, const void *data, size_t size, off64_t offset);
130131

131132
#define p_close(fd) close(fd)
132133
#define p_umask(m) umask(m)
134+
#define p_mktemp(p) mktemp(p)
133135

134136
extern int p_open(const char *path, int flags, ...);
135137
extern int p_creat(const char *path, mode_t mode);

tests/core/futils.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,29 @@ void test_core_futils__recursive_rmdir_keeps_symlink_targets(void)
8787
cl_must_pass(p_rmdir("dir-target"));
8888
cl_must_pass(p_unlink("file-target"));
8989
}
90+
91+
void test_core_futils__mktmp_umask(void)
92+
{
93+
#ifdef GIT_WIN32
94+
cl_skip();
95+
#else
96+
git_str path = GIT_STR_INIT;
97+
struct stat st;
98+
int fd;
99+
100+
umask(0);
101+
cl_assert((fd = git_futils_mktmp(&path, "foo", 0777)) >= 0);
102+
cl_must_pass(p_fstat(fd, &st));
103+
cl_assert_equal_i(st.st_mode & 0777, 0777);
104+
cl_must_pass(p_unlink(path.ptr));
105+
close(fd);
106+
107+
umask(077);
108+
cl_assert((fd = git_futils_mktmp(&path, "foo", 0777)) >= 0);
109+
cl_must_pass(p_fstat(fd, &st));
110+
cl_assert_equal_i(st.st_mode & 0777, 0700);
111+
cl_must_pass(p_unlink(path.ptr));
112+
close(fd);
113+
git_str_dispose(&path);
114+
#endif
115+
}

0 commit comments

Comments
 (0)