[PATCH] Add a fast version of fstat to cygwin port

Subsystems: the rest

DORMANTno replies

3 messages, 2 authors, 2016-06-15 · open the first message on its own page

[PATCH] Add a fast version of fstat to cygwin port

From: Alex Riesen <hidden>
Date: 2016-06-15 22:46:25

Besides, the output of the fast stat and lstat is not compatible
with cygwin's fstat with regard to uid, gid and ctime fields.

Signed-off-by: Alex Riesen <redacted>
---

This is not strictly related to the other stat patches. The fstat
code is shamelessly stolen from the mingw port, sorry.

BTW, why do we have to #undef fstat, but not stat/lstat?

 compat/cygwin.c |   60 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 compat/cygwin.h |    2 +
 2 files changed, 58 insertions(+), 4 deletions(-)
diff --git a/compat/cygwin.c b/compat/cygwin.c
index ebac148..e91af4f 100644
--- a/compat/cygwin.c
+++ b/compat/cygwin.c
@@ -15,6 +15,18 @@ static inline void filetime_to_timespec(const
FILETIME *ft, struct timespec *ts)

 #define size_to_blocks(s) (((s)+511)/512)

+/*
+ * Initialize the fields of struct stat which this implementation
+ * considers constant on this platform. See also mingw.c
+ */
+static void dosify_statbuf(struct stat *buf)
+{
+	buf->st_dev = buf->st_rdev = 0; /* not used by Git */
+	buf->st_ino = 0;
+	buf->st_nlink = 1;
+	buf->st_uid = buf->st_gid = 0;
+}
+
 /* do_stat is a common implementation for cygwin_lstat and cygwin_stat.
  *
  * To simplify its logic, in the case of cygwin symlinks, this implementation
@@ -41,11 +53,8 @@ static int do_stat(const char *file_name, struct
stat *buf, stat_fn_t cygstat)
 			return cygstat(file_name, buf);

 		/* fill out the stat structure */
-		buf->st_dev = buf->st_rdev = 0; /* not used by Git */
-		buf->st_ino = 0;
+		dosify_statbuf(buf);
 		buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
-		buf->st_nlink = 1;
-		buf->st_uid = buf->st_gid = 0;
 #ifdef __CYGWIN_USE_BIG_TYPES__
 		buf->st_size = ((_off64_t)fdata.nFileSizeHigh << 32) +
 			fdata.nFileSizeLow;
@@ -85,6 +94,41 @@ static int cygwin_stat(const char *path, struct stat *buf)
 	return do_stat(path, buf, stat);
 }

+#undef fstat
+static int cygwin_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 cygwin's fstat() */
+	if (GetFileType(fh) != FILE_TYPE_DISK)
+		return fstat(fd, buf);
+
+	if (!buf) {
+		errno = EINVAL;
+		return -1;
+	}
+	if (GetFileInformationByHandle(fh, &fdata)) {
+		dosify_statbuf(buf);
+		buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
+#ifdef __CYGWIN_USE_BIG_TYPES__
+		buf->st_size = ((_off64_t)fdata.nFileSizeHigh << 32) +
+			fdata.nFileSizeLow;
+#else
+		buf->st_size = (off_t)fdata.nFileSizeLow;
+#endif
+		filetime_to_timespec(&fdata.ftLastAccessTime, &buf->st_atim);
+		filetime_to_timespec(&fdata.ftLastWriteTime, &buf->st_mtim);
+		filetime_to_timespec(&fdata.ftCreationTime, &buf->st_ctim);
+		return 0;
+	}
+	errno = EBADF;
+	return -1;
+}

 /*
  * At start up, we are trying to determine whether Win32 API or cygwin stat
@@ -119,9 +163,11 @@ static int init_stat(void)
 		if (!core_filemode && native_stat) {
 			cygwin_stat_fn = cygwin_stat;
 			cygwin_lstat_fn = cygwin_lstat;
+			cygwin_fstat_fn = cygwin_fstat;
 		} else {
 			cygwin_stat_fn = stat;
 			cygwin_lstat_fn = lstat;
+			cygwin_fstat_fn = fstat;
 		}
 		return 0;
 	}
@@ -138,6 +184,12 @@ static int cygwin_lstat_stub(const char
*file_name, struct stat *buf)
 	return (init_stat() ? lstat : *cygwin_lstat_fn)(file_name, buf);
 }

+static int cygwin_fstat_stub(int fd, struct stat *buf)
+{
+	return (init_stat() ? fstat : *cygwin_fstat_fn)(fd, buf);
+}
+
 stat_fn_t cygwin_stat_fn = cygwin_stat_stub;
 stat_fn_t cygwin_lstat_fn = cygwin_lstat_stub;
+int (*cygwin_fstat_fn)(int fd, struct stat *) = cygwin_fstat_stub;
diff --git a/compat/cygwin.h b/compat/cygwin.h
index a3229f5..fd54c82 100644
--- a/compat/cygwin.h
+++ b/compat/cygwin.h
@@ -4,6 +4,8 @@
 typedef int (*stat_fn_t)(const char*, struct stat*);
 extern stat_fn_t cygwin_stat_fn;
 extern stat_fn_t cygwin_lstat_fn;
+extern int (*cygwin_fstat_fn)(int fd, struct stat *);

 #define stat(path, buf) (*cygwin_stat_fn)(path, buf)
 #define lstat(path, buf) (*cygwin_lstat_fn)(path, buf)
+#define fstat(fd, buf) (*cygwin_fstat_fn)(fd, buf)
-- 
1.6.2.142.gaf8db

Re: [PATCH] Add a fast version of fstat to cygwin port

From: Johannes Sixt <hidden>
Date: 2016-06-15 22:46:25

Alex Riesen schrieb:
Besides, the output of the fast stat and lstat is not compatible
with cygwin's fstat with regard to uid, gid and ctime fields.
Why do you need this? I don't think that fstat() is used in a critical
path. Do you see problems with the incompatible fields?
This is not strictly related to the other stat patches. The fstat
code is shamelessly stolen from the mingw port, sorry.
I wouldn't call it "stolen", but "copied". Because if you copy it, it
becomes your responsibility and all copied bugs are yours ;)
BTW, why do we have to #undef fstat, but not stat/lstat?
Because stat and lstat are #defined with an argument list, but in those
instances where the cygwin version of stat/lstat is meant, they are used
*without* argument list (see cygwin_stat/lstat_stub), and no macro
expansion happens, and therefore we don't need to #undef the macro.

OTOH, do_fstat calls into cygwin's fstat() if the file handle is not a
file and uses an argument list that would cause a macro expansion if it
were not #undef'd:
+#undef fstat
+static int cygwin_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 cygwin's fstat() */
+	if (GetFileType(fh) != FILE_TYPE_DISK)
+		return fstat(fd, buf);
We never do that for stat/lstat.

-- Hannes

Re: [PATCH] Add a fast version of fstat to cygwin port

From: Alex Riesen <hidden>
Date: 2016-06-15 22:46:25

2009/3/19 Johannes Sixt [off-list ref]:
Alex Riesen schrieb:
quoted
BTW, why do we have to #undef fstat, but not stat/lstat?
Because stat and lstat are #defined with an argument list, but in those
instances where the cygwin version of stat/lstat is meant, they are used
*without* argument list (see cygwin_stat/lstat_stub), and no macro
expansion happens, and therefore we don't need to #undef the macro.

OTOH, do_fstat calls into cygwin's fstat() if the file handle is not a
file and uses an argument list that would cause a macro expansion if it
were not #undef'd:
...
quoted
+     /* direct non-file handles to cygwin's fstat() */
+     if (GetFileType(fh) != FILE_TYPE_DISK)
+             return fstat(fd, buf);
We never do that for stat/lstat.
I see. Not immediately obvious :)
Thanks!
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help