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