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
+