Commit c4d23928c3801041f392adc173ffa62a8249ee25

Edward Thomson 2016-02-11T15:41:07

fstat: use our custom `stat`

diff --git a/src/win32/posix.h b/src/win32/posix.h
index 732128b..5fab267 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -17,9 +17,10 @@
 typedef SOCKET GIT_SOCKET;
 
 #define p_lseek(f,n,w) _lseeki64(f, n, w)
-#define p_fstat(f,b) _fstat64(f, b)
+
+extern int p_fstat(int fd, struct stat *buf);
 extern int p_lstat(const char *file_name, struct stat *buf);
-extern int p_stat(const char* path, struct stat* buf);
+extern int p_stat(const char* path, struct stat *buf);
 
 extern int p_utimes(const char *filename, const struct p_timeval times[2]);
 extern int p_futimes(int fd, const struct p_timeval times[2]);
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index d743e8f..fea634b 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -398,6 +398,22 @@ static int follow_and_lstat_link(git_win32_path path, struct stat* buf)
 	return lstat_w(target_w, buf, false);
 }
 
+int p_fstat(int fd, struct stat *buf)
+{
+	BY_HANDLE_FILE_INFORMATION fhInfo;
+
+	HANDLE fh = (HANDLE)_get_osfhandle(fd);
+
+	if (fh == INVALID_HANDLE_VALUE ||
+		!GetFileInformationByHandle(fh, &fhInfo)) {
+		errno = EBADF;
+		return -1;
+	}
+
+	git_win32__file_information_to_stat(buf, &fhInfo);
+	return 0;
+}
+
 int p_stat(const char* path, struct stat* buf)
 {
 	git_win32_path path_w;
diff --git a/src/win32/w32_util.h b/src/win32/w32_util.h
index b095939..2e475e5 100644
--- a/src/win32/w32_util.h
+++ b/src/win32/w32_util.h
@@ -105,19 +105,25 @@ GIT_INLINE(void) git_win32__timeval_to_filetime(
 	ft->dwLowDateTime = (ticks & 0xffffffffLL);
 }
 
-GIT_INLINE(int) git_win32__file_attribute_to_stat(
+GIT_INLINE(void) git_win32__stat_init(
 	struct stat *st,
-	const WIN32_FILE_ATTRIBUTE_DATA *attrdata,
-	const wchar_t *path)
+	DWORD dwFileAttributes,
+	DWORD nFileSizeHigh,
+	DWORD nFileSizeLow,
+	FILETIME ftCreationTime,
+	FILETIME ftLastAccessTime,
+	FILETIME ftLastWriteTime)
 {
 	mode_t mode = S_IREAD;
 
-	if (attrdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+	memset(st, 0, sizeof(struct stat));
+
+	if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 		mode |= S_IFDIR;
 	else
 		mode |= S_IFREG;
 
-	if ((attrdata->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+	if ((dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
 		mode |= S_IWRITE;
 
 	st->st_ino = 0;
@@ -125,12 +131,39 @@ GIT_INLINE(int) git_win32__file_attribute_to_stat(
 	st->st_uid = 0;
 	st->st_nlink = 1;
 	st->st_mode = mode;
-	st->st_size = ((git_off_t)attrdata->nFileSizeHigh << 32) + attrdata->nFileSizeLow;
+	st->st_size = ((git_off_t)nFileSizeHigh << 32) + nFileSizeLow;
 	st->st_dev = _getdrive() - 1;
 	st->st_rdev = st->st_dev;
-	git_win32__filetime_to_timespec(&(attrdata->ftLastAccessTime), &(st->st_atim));
-	git_win32__filetime_to_timespec(&(attrdata->ftLastWriteTime), &(st->st_mtim));
-	git_win32__filetime_to_timespec(&(attrdata->ftCreationTime), &(st->st_ctim));
+	git_win32__filetime_to_timespec(&ftLastAccessTime, &(st->st_atim));
+	git_win32__filetime_to_timespec(&ftLastWriteTime, &(st->st_mtim));
+	git_win32__filetime_to_timespec(&ftCreationTime, &(st->st_ctim));
+}
+
+GIT_INLINE(void) git_win32__file_information_to_stat(
+	struct stat *st,
+	const BY_HANDLE_FILE_INFORMATION *fileinfo)
+{
+	git_win32__stat_init(st,
+		fileinfo->dwFileAttributes,
+		fileinfo->nFileSizeHigh,
+		fileinfo->nFileSizeLow,
+		fileinfo->ftCreationTime,
+		fileinfo->ftLastAccessTime,
+		fileinfo->ftLastWriteTime);
+}
+
+GIT_INLINE(int) git_win32__file_attribute_to_stat(
+	struct stat *st,
+	const WIN32_FILE_ATTRIBUTE_DATA *attrdata,
+	const wchar_t *path)
+{
+	git_win32__stat_init(st,
+		attrdata->dwFileAttributes,
+		attrdata->nFileSizeHigh,
+		attrdata->nFileSizeLow,
+		attrdata->ftCreationTime,
+		attrdata->ftLastAccessTime,
+		attrdata->ftLastWriteTime);
 
 	if (attrdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && path) {
 		git_win32_path target;
@@ -139,7 +172,7 @@ GIT_INLINE(int) git_win32__file_attribute_to_stat(
 			st->st_mode = (st->st_mode & ~S_IFMT) | S_IFLNK;
 
 			/* st_size gets the UTF-8 length of the target name, in bytes,
-				* not counting the NULL terminator */
+			 * not counting the NULL terminator */
 			if ((st->st_size = git__utf16_to_8(NULL, 0, target)) < 0) {
 				giterr_set(GITERR_OS, "Could not convert reparse point name for '%s'", path);
 				return -1;