Skip to content

Commit ed187bd

Browse files
committed
Merge branch 'dp/cywginstat'
* dp/cywginstat: cygwin: Use native Win32 API for stat mingw: move common functionality to win32.h add have_git_dir() function
2 parents 78a935d + adbc0b6 commit ed187bd

File tree

9 files changed

+194
-38
lines changed

9 files changed

+194
-38
lines changed

Documentation/config.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,15 @@ core.fileMode::
117117
the working copy are ignored; useful on broken filesystems like FAT.
118118
See linkgit:git-update-index[1]. True by default.
119119

120+
core.ignoreCygwinFSTricks::
121+
This option is only used by Cygwin implementation of Git. If false,
122+
the Cygwin stat() and lstat() functions are used. This may be useful
123+
if your repository consists of a few separate directories joined in
124+
one hierarchy using Cygwin mount. If true, Git uses native Win32 API
125+
whenever it is possible and falls back to Cygwin functions only to
126+
handle symbol links. The native mode is more than twice faster than
127+
normal Cygwin l/stat() functions. True by default.
128+
120129
core.trustctime::
121130
If false, the ctime differences between the index and the
122131
working copy are ignored; useful when the inode change time

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ LIB_H += cache.h
346346
LIB_H += cache-tree.h
347347
LIB_H += commit.h
348348
LIB_H += compat/mingw.h
349+
LIB_H += compat/cygwin.h
349350
LIB_H += csum-file.h
350351
LIB_H += decorate.h
351352
LIB_H += delta.h
@@ -748,6 +749,9 @@ ifeq ($(uname_S),HP-UX)
748749
NO_SYS_SELECT_H = YesPlease
749750
SNPRINTF_RETURNS_BOGUS = YesPlease
750751
endif
752+
ifneq (,$(findstring CYGWIN,$(uname_S)))
753+
COMPAT_OBJS += compat/cygwin.o
754+
endif
751755
ifneq (,$(findstring MINGW,$(uname_S)))
752756
NO_MMAP = YesPlease
753757
NO_PREAD = YesPlease

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ extern int is_bare_repository(void);
319319
extern int is_inside_git_dir(void);
320320
extern char *git_work_tree_cfg;
321321
extern int is_inside_work_tree(void);
322+
extern int have_git_dir(void);
322323
extern const char *get_git_dir(void);
323324
extern char *get_object_directory(void);
324325
extern char *get_index_file(void);

compat/cygwin.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#define WIN32_LEAN_AND_MEAN
2+
#include "../git-compat-util.h"
3+
#include "win32.h"
4+
#include "../cache.h" /* to read configuration */
5+
6+
static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts)
7+
{
8+
long long winTime = ((long long)ft->dwHighDateTime << 32) +
9+
ft->dwLowDateTime;
10+
winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
11+
/* convert 100-nsecond interval to seconds and nanoseconds */
12+
ts->tv_sec = (time_t)(winTime/10000000);
13+
ts->tv_nsec = (long)(winTime - ts->tv_sec*10000000LL) * 100;
14+
}
15+
16+
#define size_to_blocks(s) (((s)+511)/512)
17+
18+
/* do_stat is a common implementation for cygwin_lstat and cygwin_stat.
19+
*
20+
* To simplify its logic, in the case of cygwin symlinks, this implementation
21+
* falls back to the cygwin version of stat/lstat, which is provided as the
22+
* last argument.
23+
*/
24+
static int do_stat(const char *file_name, struct stat *buf, stat_fn_t cygstat)
25+
{
26+
WIN32_FILE_ATTRIBUTE_DATA fdata;
27+
28+
if (file_name[0] == '/')
29+
return cygstat (file_name, buf);
30+
31+
if (!(errno = get_file_attr(file_name, &fdata))) {
32+
/*
33+
* If the system attribute is set and it is not a directory then
34+
* it could be a symbol link created in the nowinsymlinks mode.
35+
* Normally, Cygwin works in the winsymlinks mode, so this situation
36+
* is very unlikely. For the sake of simplicity of our code, let's
37+
* Cygwin to handle it.
38+
*/
39+
if ((fdata.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) &&
40+
!(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
41+
return cygstat(file_name, buf);
42+
43+
/* fill out the stat structure */
44+
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
45+
buf->st_ino = 0;
46+
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
47+
buf->st_nlink = 1;
48+
buf->st_uid = buf->st_gid = 0;
49+
#ifdef __CYGWIN_USE_BIG_TYPES__
50+
buf->st_size = ((_off64_t)fdata.nFileSizeHigh << 32) +
51+
fdata.nFileSizeLow;
52+
#else
53+
buf->st_size = (off_t)fdata.nFileSizeLow;
54+
#endif
55+
buf->st_blocks = size_to_blocks(buf->st_size);
56+
filetime_to_timespec(&fdata.ftLastAccessTime, &buf->st_atim);
57+
filetime_to_timespec(&fdata.ftLastWriteTime, &buf->st_mtim);
58+
filetime_to_timespec(&fdata.ftCreationTime, &buf->st_ctim);
59+
return 0;
60+
} else if (errno == ENOENT) {
61+
/*
62+
* In the winsymlinks mode (which is the default), Cygwin
63+
* emulates symbol links using Windows shortcut files. These
64+
* files are formed by adding .lnk extension. So, if we have
65+
* not found the specified file name, it could be that it is
66+
* a symbol link. Let's Cygwin to deal with that.
67+
*/
68+
return cygstat(file_name, buf);
69+
}
70+
return -1;
71+
}
72+
73+
/* We provide our own lstat/stat functions, since the provided Cygwin versions
74+
* of these functions are too slow. These stat functions are tailored for Git's
75+
* usage, and therefore they are not meant to be complete and correct emulation
76+
* of lstat/stat functionality.
77+
*/
78+
static int cygwin_lstat(const char *path, struct stat *buf)
79+
{
80+
return do_stat(path, buf, lstat);
81+
}
82+
83+
static int cygwin_stat(const char *path, struct stat *buf)
84+
{
85+
return do_stat(path, buf, stat);
86+
}
87+
88+
89+
/*
90+
* At start up, we are trying to determine whether Win32 API or cygwin stat
91+
* functions should be used. The choice is determined by core.ignorecygwinfstricks.
92+
* Reading this option is not always possible immediately as git_dir may be
93+
* not be set yet. So until it is set, use cygwin lstat/stat functions.
94+
*/
95+
static int native_stat = 1;
96+
97+
static int git_cygwin_config(const char *var, const char *value, void *cb)
98+
{
99+
if (!strcmp(var, "core.ignorecygwinfstricks"))
100+
native_stat = git_config_bool(var, value);
101+
return 0;
102+
}
103+
104+
static int init_stat(void)
105+
{
106+
if (have_git_dir()) {
107+
git_config(git_cygwin_config, NULL);
108+
cygwin_stat_fn = native_stat ? cygwin_stat : stat;
109+
cygwin_lstat_fn = native_stat ? cygwin_lstat : lstat;
110+
return 0;
111+
}
112+
return 1;
113+
}
114+
115+
static int cygwin_stat_stub(const char *file_name, struct stat *buf)
116+
{
117+
return (init_stat() ? stat : *cygwin_stat_fn)(file_name, buf);
118+
}
119+
120+
static int cygwin_lstat_stub(const char *file_name, struct stat *buf)
121+
{
122+
return (init_stat() ? lstat : *cygwin_lstat_fn)(file_name, buf);
123+
}
124+
125+
stat_fn_t cygwin_stat_fn = cygwin_stat_stub;
126+
stat_fn_t cygwin_lstat_fn = cygwin_lstat_stub;
127+

compat/cygwin.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <sys/types.h>
2+
#include <sys/stat.h>
3+
4+
typedef int (*stat_fn_t)(const char*, struct stat*);
5+
extern stat_fn_t cygwin_stat_fn;
6+
extern stat_fn_t cygwin_lstat_fn;
7+
8+
#define stat(path, buf) (*cygwin_stat_fn)(path, buf)
9+
#define lstat(path, buf) (*cygwin_lstat_fn)(path, buf)

compat/mingw.c

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "../git-compat-util.h"
2+
#include "win32.h"
23
#include "../strbuf.h"
34

45
unsigned int _CRT_fmode = _O_BINARY;
@@ -39,46 +40,19 @@ static int do_lstat(const char *file_name, struct stat *buf)
3940
{
4041
WIN32_FILE_ATTRIBUTE_DATA fdata;
4142

42-
if (GetFileAttributesExA(file_name, GetFileExInfoStandard, &fdata)) {
43-
int fMode = S_IREAD;
44-
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
45-
fMode |= S_IFDIR;
46-
else
47-
fMode |= S_IFREG;
48-
if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
49-
fMode |= S_IWRITE;
50-
43+
if (!(errno = get_file_attr(file_name, &fdata))) {
5144
buf->st_ino = 0;
5245
buf->st_gid = 0;
5346
buf->st_uid = 0;
5447
buf->st_nlink = 1;
55-
buf->st_mode = fMode;
48+
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
5649
buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
5750
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
5851
buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
5952
buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
6053
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
61-
errno = 0;
6254
return 0;
6355
}
64-
65-
switch (GetLastError()) {
66-
case ERROR_ACCESS_DENIED:
67-
case ERROR_SHARING_VIOLATION:
68-
case ERROR_LOCK_VIOLATION:
69-
case ERROR_SHARING_BUFFER_EXCEEDED:
70-
errno = EACCES;
71-
break;
72-
case ERROR_BUFFER_OVERFLOW:
73-
errno = ENAMETOOLONG;
74-
break;
75-
case ERROR_NOT_ENOUGH_MEMORY:
76-
errno = ENOMEM;
77-
break;
78-
default:
79-
errno = ENOENT;
80-
break;
81-
}
8256
return -1;
8357
}
8458

@@ -130,19 +104,11 @@ int mingw_fstat(int fd, struct stat *buf)
130104
return fstat(fd, buf);
131105

132106
if (GetFileInformationByHandle(fh, &fdata)) {
133-
int fMode = S_IREAD;
134-
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
135-
fMode |= S_IFDIR;
136-
else
137-
fMode |= S_IFREG;
138-
if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
139-
fMode |= S_IWRITE;
140-
141107
buf->st_ino = 0;
142108
buf->st_gid = 0;
143109
buf->st_uid = 0;
144110
buf->st_nlink = 1;
145-
buf->st_mode = fMode;
111+
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
146112
buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
147113
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
148114
buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));

compat/win32.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/* common Win32 functions for MinGW and Cygwin */
2+
#include <windows.h>
3+
4+
static inline int file_attr_to_st_mode (DWORD attr)
5+
{
6+
int fMode = S_IREAD;
7+
if (attr & FILE_ATTRIBUTE_DIRECTORY)
8+
fMode |= S_IFDIR;
9+
else
10+
fMode |= S_IFREG;
11+
if (!(attr & FILE_ATTRIBUTE_READONLY))
12+
fMode |= S_IWRITE;
13+
return fMode;
14+
}
15+
16+
static inline int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata)
17+
{
18+
if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata))
19+
return 0;
20+
21+
switch (GetLastError()) {
22+
case ERROR_ACCESS_DENIED:
23+
case ERROR_SHARING_VIOLATION:
24+
case ERROR_LOCK_VIOLATION:
25+
case ERROR_SHARING_BUFFER_EXCEEDED:
26+
return EACCES;
27+
case ERROR_BUFFER_OVERFLOW:
28+
return ENAMETOOLONG;
29+
case ERROR_NOT_ENOUGH_MEMORY:
30+
return ENOMEM;
31+
default:
32+
return ENOENT;
33+
}
34+
}

environment.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ int is_bare_repository(void)
8080
return is_bare_repository_cfg && !get_git_work_tree();
8181
}
8282

83+
int have_git_dir(void)
84+
{
85+
return !!git_dir;
86+
}
87+
8388
const char *get_git_dir(void)
8489
{
8590
if (!git_dir)

git-compat-util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
#undef _XOPEN_SOURCE
8686
#include <grp.h>
8787
#define _XOPEN_SOURCE 600
88+
#include "compat/cygwin.h"
8889
#else
8990
#undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
9091
#include <grp.h>

0 commit comments

Comments
 (0)