Merge branch 'mg/maint-send-email-lazy-editor'
[git] / compat / cygwin.c
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
93  * not be set yet. So until it is set, use cygwin lstat/stat functions.
94  * However, if core.filemode is set, we must use the Cygwin posix
95  * stat/lstat as the Windows stat functions do not determine posix filemode.
96  *
97  * Note that git_cygwin_config() does NOT call git_default_config() and this
98  * is deliberate.  Many commands read from config to establish initial
99  * values in variables and later tweak them from elsewhere (e.g. command line).
100  * init_stat() is called lazily on demand, typically much late in the program,
101  * and calling git_default_config() from here would break such variables.
102  */
103 static int native_stat = 1;
104 static int core_filemode;
105
106 static int git_cygwin_config(const char *var, const char *value, void *cb)
107 {
108         if (!strcmp(var, "core.ignorecygwinfstricks"))
109                 native_stat = git_config_bool(var, value);
110         else if (!strcmp(var, "core.filemode"))
111                 core_filemode = git_config_bool(var, value);
112         return 0;
113 }
114
115 static int init_stat(void)
116 {
117         if (have_git_dir()) {
118                 git_config(git_cygwin_config, NULL);
119                 if (!core_filemode && native_stat) {
120                         cygwin_stat_fn = cygwin_stat;
121                         cygwin_lstat_fn = cygwin_lstat;
122                 } else {
123                         cygwin_stat_fn = stat;
124                         cygwin_lstat_fn = lstat;
125                 }
126                 return 0;
127         }
128         return 1;
129 }
130
131 static int cygwin_stat_stub(const char *file_name, struct stat *buf)
132 {
133         return (init_stat() ? stat : *cygwin_stat_fn)(file_name, buf);
134 }
135
136 static int cygwin_lstat_stub(const char *file_name, struct stat *buf)
137 {
138         return (init_stat() ? lstat : *cygwin_lstat_fn)(file_name, buf);
139 }
140
141 stat_fn_t cygwin_stat_fn = cygwin_stat_stub;
142 stat_fn_t cygwin_lstat_fn = cygwin_lstat_stub;
143