1#define WIN32_LEAN_AND_MEAN 2#include "../git-compat-util.h" 3#include "win32.h" 4#include "../cache.h" /* to read configuration */ 5 6static 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 */ 24static 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 */ 78static int cygwin_lstat(const char *path, struct stat *buf) 79{ 80 return do_stat(path, buf, lstat); 81} 82 83static 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 */ 103static int native_stat = 1; 104static int core_filemode; 105 106static 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 115static 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 131static 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 136static 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 141stat_fn_t cygwin_stat_fn = cygwin_stat_stub; 142stat_fn_t cygwin_lstat_fn = cygwin_lstat_stub; 143