summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Hostetler <jeffhost@microsoft.com>2015-02-27 08:54:48 -0500
committerJeff Hostetler <jeffhost@microsoft.com>2015-03-03 08:47:44 -0500
commit7e9b21aa2a13b97b54b0083eb8f40b7f428bc829 (patch)
tree6bfb3d5344fca99a4d236e8156bb71c99510c09f
parentfb2f3a76aaf9d76b947ca7527797e0eb0b5d057f (diff)
downloadlibgit2-7e9b21aa2a13b97b54b0083eb8f40b7f428bc829.tar.gz
Fix p_ftruncate to handle big files for git_clone
-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, 70 insertions, 4 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..874cba9cc 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 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);
+}