[PATCH 28/40] Windows: Add a new lstat and fstat implementation based on Win32 API.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Marius Storm-Olsen <mstormo_git@xxxxxxxxxxxxxxx>

From: Marius Storm-Olsen <mstormo_git@xxxxxxxxxxxxxxx>

This gives us a significant speedup when adding, committing and stat'ing files.
Also, since Windows doesn't really handle symlinks, we let stat just uses lstat.
We also need to replace fstat, since our implementation and the standard stat()
functions report slightly different timestamps, possibly due to timezones.

We simply report UTC in our implementation, and do our FILETIME to time_t
conversion based on the document at http://support.microsoft.com/kb/167296.

With Moe's repo structure (100K files in 100 dirs, containing 2-4 bytes)
    mkdir bummer && cd bummer; for ((i=0;i<100;i++)); do
      mkdir $i && pushd $i;
        for ((j=0;j<1000;j++)); do echo "$j" >$j; done;
      popd;
    done

We get the following performance boost:

    With normal lstat & stat  Custom lstat/fstat
    ------------------------  ------------------------
    Command: git init         Command: git init
    ------------------------  ------------------------
    real    0m 0.047s          real   0m 0.063s
    user    0m 0.031s          user   0m 0.015s
    sys     0m 0.000s          sys    0m 0.015s
    ------------------------  ------------------------
    Command: git add .        Command: git add .
    ------------------------  ------------------------
    real    0m19.390s         real    0m12.031s       1.6x
    user    0m 0.015s         user    0m 0.031s
    sys     0m 0.030s         sys     0m 0.000s
    ------------------------  ------------------------
    Command: git commit -a..  Command: git commit -a..
    ------------------------  ------------------------
    real    0m30.812s         real    0m16.875s       1.8x
    user    0m 0.015s         user    0m 0.015s
    sys     0m 0.000s         sys     0m 0.015s
    ------------------------  ------------------------
    3x Command: git-status    3x Command: git-status
    ------------------------  ------------------------
    real    0m11.860s         real    0m 5.266s       2.2x
    user    0m 0.015s         user    0m 0.015s
    sys     0m 0.015s         sys     0m 0.015s

    real    0m11.703s         real    0m 5.234s
    user    0m 0.015s         user    0m 0.015s
    sys     0m 0.000s         sys     0m 0.000s

    real    0m11.672s         real    0m 5.250s
    user    0m 0.031s         user    0m 0.015s
    sys     0m 0.000s         sys     0m 0.000s
    ------------------------  ------------------------
    Command: git commit...    Command: git commit...
    (single file)             (single file)
    ------------------------  ------------------------
    real    0m14.234s         real    0m 7.735s       1.8x
    user    0m 0.015s         user    0m 0.031s
    sys     0m 0.000s         sys     0m 0.000s

Signed-off-by: Marius Storm-Olsen <mstormo_git@xxxxxxxxxxxxxxx>
Signed-off-by: Johannes Sixt <johannes.sixt@xxxxxxxxxx>
---
 compat/mingw.c    |  132 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 git-compat-util.h |   10 ++++-
 2 files changed, 141 insertions(+), 1 deletions(-)

diff --git a/compat/mingw.c b/compat/mingw.c
index 146c170..d44fbb3 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -23,6 +23,138 @@ int mingw_open (const char *filename, int oflags, ...)
 	return fd;
 }
 
+static inline time_t filetime_to_time_t(const FILETIME *ft)
+{
+	long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
+	winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
+	winTime /= 10000000;		 /* Nano to seconds resolution */
+	return (time_t)winTime;
+}
+
+extern int _getdrive( void );
+/* We keep the do_lstat code in a separate function to avoid recursion.
+ * When a path ends with a slash, the stat will fail with ENOENT. In
+ * this case, we strip the trailing slashes and stat again.
+ */
+static int do_lstat(const char *file_name, struct stat *buf)
+{
+	WIN32_FILE_ATTRIBUTE_DATA fdata;
+
+	if (GetFileAttributesExA(file_name, GetFileExInfoStandard, &fdata)) {
+		int fMode = S_IREAD;
+		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+			fMode |= S_IFDIR;
+		else
+			fMode |= S_IFREG;
+		if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
+			fMode |= S_IWRITE;
+
+		buf->st_ino = 0;
+		buf->st_gid = 0;
+		buf->st_uid = 0;
+		buf->st_nlink = 1;
+		buf->st_mode = fMode;
+		buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
+		buf->st_dev = buf->st_rdev = (_getdrive() - 1);
+		buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
+		buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
+		buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
+		errno = 0;
+		return 0;
+	}
+
+	switch (GetLastError()) {
+	case ERROR_ACCESS_DENIED:
+	case ERROR_SHARING_VIOLATION:
+	case ERROR_LOCK_VIOLATION:
+	case ERROR_SHARING_BUFFER_EXCEEDED:
+		errno = EACCES;
+		break;
+	case ERROR_BUFFER_OVERFLOW:
+		errno = ENAMETOOLONG;
+		break;
+	case ERROR_NOT_ENOUGH_MEMORY:
+		errno = ENOMEM;
+		break;
+	default:
+		errno = ENOENT;
+		break;
+	}
+	return -1;
+}
+
+/* We provide our own lstat/fstat functions, since the provided
+ * lstat/fstat functions are so slow. These stat functions are
+ * tailored for Git's usage (read: fast), and are not meant to be
+ * complete. Note that Git stat()s are redirected to mingw_lstat()
+ * too, since Windows doesn't really handle symlinks that well.
+ */
+int mingw_lstat(const char *file_name, struct stat *buf)
+{
+	int namelen;
+	static char alt_name[PATH_MAX];
+
+	if (!do_lstat(file_name, buf))
+		return 0;
+
+	/* if file_name ended in a '/', Windows returned ENOENT;
+	 * try again without trailing slashes
+	 */
+	if (errno != ENOENT)
+		return -1;
+
+	namelen = strlen(file_name);
+	if (namelen && file_name[namelen-1] != '/')
+		return -1;
+	while (namelen && file_name[namelen-1] == '/')
+		--namelen;
+	if (!namelen || namelen >= PATH_MAX)
+		return -1;
+
+	memcpy(alt_name, file_name, namelen);
+	alt_name[namelen] = 0;
+	return do_lstat(alt_name, buf);
+}
+
+#undef fstat
+int mingw_fstat(int fd, struct stat *buf)
+{
+	HANDLE fh = (HANDLE)_get_osfhandle(fd);
+	BY_HANDLE_FILE_INFORMATION fdata;
+
+	if (fh == INVALID_HANDLE_VALUE) {
+		errno = EBADF;
+		return -1;
+	}
+	/* direct non-file handles to MS's fstat() */
+	if (GetFileType(fh) != FILE_TYPE_DISK)
+		return fstat(fd, buf);
+
+	if (GetFileInformationByHandle(fh, &fdata)) {
+		int fMode = S_IREAD;
+		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+			fMode |= S_IFDIR;
+		else
+			fMode |= S_IFREG;
+		if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
+			fMode |= S_IWRITE;
+
+		buf->st_ino = 0;
+		buf->st_gid = 0;
+		buf->st_uid = 0;
+		buf->st_nlink = 1;
+		buf->st_mode = fMode;
+		buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
+		buf->st_dev = buf->st_rdev = (_getdrive() - 1);
+		buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
+		buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
+		buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
+		return 0;
+	}
+	errno = EBADF;
+	return -1;
+}
+
 unsigned int sleep (unsigned int seconds)
 {
 	Sleep(seconds*1000);
diff --git a/git-compat-util.h b/git-compat-util.h
index 570eb10..0583a1f 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -509,7 +509,6 @@ struct itimerval {
 #define ITIMER_REAL 0
 
 #define st_blocks st_size/512	/* will be cleaned up later */
-#define lstat stat
 
 /*
  * trivial stubs
@@ -608,6 +607,15 @@ int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
 int mingw_rename(const char*, const char*);
 #define rename mingw_rename
 
+/* Use mingw_lstat() instead of lstat()/stat() and
+ * mingw_fstat() instead of fstat() on Windows.
+ */
+int mingw_lstat(const char *file_name, struct stat *buf);
+int mingw_fstat(int fd, struct stat *buf);
+#define fstat mingw_fstat
+#define lstat mingw_lstat
+#define stat(x,y) mingw_lstat(x,y)
+
 int mingw_vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
 #define vsnprintf mingw_vsnprintf
 
-- 
1.5.4.1.126.ge5a7d

-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux