Fix p_ftruncate to handle big files for git_clone
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
diff --git a/src/win32/posix.h b/src/win32/posix.h
index 104966e..9ac7843 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 6e005c1..874cba9 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -42,12 +42,30 @@
/* 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 (size < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
#if defined(_MSC_VER) && _MSC_VER >= 1500
- return _chsize_s(fd, size);
+ return ((_chsize_s(fd, size) == 0) ? 0 : -1);
#else
- return _chsize(fd, size);
+ /* TODO Find a replacement for _chsize() that handles big files.
+ * This comment is probably __MINGW32__ specific.
+ */
+ 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 0000000..21981d6
--- /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);
+}