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