summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2015-03-04 14:55:56 +0100
committerCarlos Martín Nieto <cmn@dwim.me>2015-03-04 14:55:56 +0100
commitbdf0e734506b5b18234d48a0e7c6995aeda30b9d (patch)
tree540c17e5719bf72acc6a0b19ecf54398934de33f
parent107958d7173ab84566c9d89b1cafa08379c849fb (diff)
parentd8be5087771db34b191b13f22a2846888a477c36 (diff)
downloadlibgit2-bdf0e734506b5b18234d48a0e7c6995aeda30b9d.tar.gz
Merge pull request #2932 from jeffhostetler/jeffhostetler/big_clone_crash
Fix crash in git_clone on extremely large repos
-rw-r--r--src/win32/posix.h2
-rw-r--r--src/win32/posix_w32.c24
-rw-r--r--tests/core/ftruncate.c48
3 files changed, 69 insertions, 5 deletions
diff --git a/src/win32/posix.h b/src/win32/posix.h
index 104966edc..9ac78430b 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -41,7 +41,7 @@ extern int p_chdir(const char* path);
extern int p_chmod(const char* path, mode_t mode);
extern int p_rmdir(const char* path);
extern int p_access(const char* path, mode_t mode);
-extern int p_ftruncate(int fd, long size);
+extern int p_ftruncate(int fd, git_off_t size);
/* p_lstat is almost but not quite POSIX correct. Specifically, the use of
* ENOTDIR is wrong, in that it does not mean precisely that a non-directory
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 6e005c1cd..b8b4f43f8 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -42,12 +42,28 @@
/* GetFinalPathNameByHandleW signature */
typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
-int p_ftruncate(int fd, long size)
+/**
+ * Truncate or extend file.
+ *
+ * We now take a "git_off_t" rather than "long" because
+ * files may be longer than 2Gb.
+ */
+int p_ftruncate(int fd, git_off_t size)
{
-#if defined(_MSC_VER) && _MSC_VER >= 1500
- return _chsize_s(fd, size);
+ if (size < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+#if !defined(__MINGW32__)
+ return ((_chsize_s(fd, size) == 0) ? 0 : -1);
#else
- return _chsize(fd, size);
+ /* TODO MINGW32 Find a replacement for _chsize() that handles big files. */
+ if (size > INT32_MAX) {
+ errno = EFBIG;
+ return -1;
+ }
+ return _chsize(fd, (long)size);
#endif
}
diff --git a/tests/core/ftruncate.c b/tests/core/ftruncate.c
new file mode 100644
index 000000000..21981d677
--- /dev/null
+++ b/tests/core/ftruncate.c
@@ -0,0 +1,48 @@
+/**
+ * Some tests for p_ftruncate() to ensure that
+ * properly handles large (2Gb+) files.
+ */
+
+#include "clar_libgit2.h"
+
+static const char *filename = "core_ftruncate.txt";
+static int fd = -1;
+
+void test_core_ftruncate__initialize(void)
+{
+ if (!cl_getenv("GITTEST_INVASIVE_FS_SIZE"))
+ cl_skip();
+
+ cl_must_pass((fd = p_open(filename, O_CREAT | O_RDWR, 0644)));
+}
+
+void test_core_ftruncate__cleanup(void)
+{
+ if (fd < 0)
+ return;
+
+ p_close(fd);
+ fd = 0;
+
+ p_unlink(filename);
+}
+
+static void _extend(git_off_t i64len)
+{
+ struct stat st;
+ int error;
+
+ cl_assert((error = p_ftruncate(fd, i64len)) == 0);
+ cl_assert((error = p_fstat(fd, &st)) == 0);
+ cl_assert(st.st_size == i64len);
+}
+
+void test_core_ftruncate__2gb(void)
+{
+ _extend(0x80000001);
+}
+
+void test_core_ftruncate__4gb(void)
+{
+ _extend(0x100000001);
+}