You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
4.8 KiB
142 lines
4.8 KiB
#define WIN32_LEAN_AND_MEAN |
|
#include "../git-compat-util.h" |
|
#include "win32.h" |
|
#include "../cache.h" /* to read configuration */ |
|
|
|
static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts) |
|
{ |
|
long long winTime = ((long long)ft->dwHighDateTime << 32) + |
|
ft->dwLowDateTime; |
|
winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */ |
|
/* convert 100-nsecond interval to seconds and nanoseconds */ |
|
ts->tv_sec = (time_t)(winTime/10000000); |
|
ts->tv_nsec = (long)(winTime - ts->tv_sec*10000000LL) * 100; |
|
} |
|
|
|
#define size_to_blocks(s) (((s)+511)/512) |
|
|
|
/* do_stat is a common implementation for cygwin_lstat and cygwin_stat. |
|
* |
|
* To simplify its logic, in the case of cygwin symlinks, this implementation |
|
* falls back to the cygwin version of stat/lstat, which is provided as the |
|
* last argument. |
|
*/ |
|
static int do_stat(const char *file_name, struct stat *buf, stat_fn_t cygstat) |
|
{ |
|
WIN32_FILE_ATTRIBUTE_DATA fdata; |
|
|
|
if (file_name[0] == '/') |
|
return cygstat (file_name, buf); |
|
|
|
if (!(errno = get_file_attr(file_name, &fdata))) { |
|
/* |
|
* If the system attribute is set and it is not a directory then |
|
* it could be a symbol link created in the nowinsymlinks mode. |
|
* Normally, Cygwin works in the winsymlinks mode, so this situation |
|
* is very unlikely. For the sake of simplicity of our code, let's |
|
* Cygwin to handle it. |
|
*/ |
|
if ((fdata.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) && |
|
!(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) |
|
return cygstat(file_name, buf); |
|
|
|
/* fill out the stat structure */ |
|
buf->st_dev = buf->st_rdev = 0; /* not used by Git */ |
|
buf->st_ino = 0; |
|
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); |
|
buf->st_nlink = 1; |
|
buf->st_uid = buf->st_gid = 0; |
|
#ifdef __CYGWIN_USE_BIG_TYPES__ |
|
buf->st_size = ((_off64_t)fdata.nFileSizeHigh << 32) + |
|
fdata.nFileSizeLow; |
|
#else |
|
buf->st_size = (off_t)fdata.nFileSizeLow; |
|
#endif |
|
buf->st_blocks = size_to_blocks(buf->st_size); |
|
filetime_to_timespec(&fdata.ftLastAccessTime, &buf->st_atim); |
|
filetime_to_timespec(&fdata.ftLastWriteTime, &buf->st_mtim); |
|
filetime_to_timespec(&fdata.ftCreationTime, &buf->st_ctim); |
|
return 0; |
|
} else if (errno == ENOENT) { |
|
/* |
|
* In the winsymlinks mode (which is the default), Cygwin |
|
* emulates symbol links using Windows shortcut files. These |
|
* files are formed by adding .lnk extension. So, if we have |
|
* not found the specified file name, it could be that it is |
|
* a symbol link. Let's Cygwin to deal with that. |
|
*/ |
|
return cygstat(file_name, buf); |
|
} |
|
return -1; |
|
} |
|
|
|
/* We provide our own lstat/stat functions, since the provided Cygwin versions |
|
* of these functions are too slow. These stat functions are tailored for Git's |
|
* usage, and therefore they are not meant to be complete and correct emulation |
|
* of lstat/stat functionality. |
|
*/ |
|
static int cygwin_lstat(const char *path, struct stat *buf) |
|
{ |
|
return do_stat(path, buf, lstat); |
|
} |
|
|
|
static int cygwin_stat(const char *path, struct stat *buf) |
|
{ |
|
return do_stat(path, buf, stat); |
|
} |
|
|
|
|
|
/* |
|
* At start up, we are trying to determine whether Win32 API or cygwin stat |
|
* functions should be used. The choice is determined by core.ignorecygwinfstricks. |
|
* Reading this option is not always possible immediately as git_dir may |
|
* not be set yet. So until it is set, use cygwin lstat/stat functions. |
|
* However, if core.filemode is set, we must use the Cygwin posix |
|
* stat/lstat as the Windows stat functions do not determine posix filemode. |
|
* |
|
* Note that git_cygwin_config() does NOT call git_default_config() and this |
|
* is deliberate. Many commands read from config to establish initial |
|
* values in variables and later tweak them from elsewhere (e.g. command line). |
|
* init_stat() is called lazily on demand, typically much late in the program, |
|
* and calling git_default_config() from here would break such variables. |
|
*/ |
|
static int native_stat = 1; |
|
static int core_filemode = 1; /* matches trust_executable_bit default */ |
|
|
|
static int git_cygwin_config(const char *var, const char *value, void *cb) |
|
{ |
|
if (!strcmp(var, "core.ignorecygwinfstricks")) |
|
native_stat = git_config_bool(var, value); |
|
else if (!strcmp(var, "core.filemode")) |
|
core_filemode = git_config_bool(var, value); |
|
return 0; |
|
} |
|
|
|
static int init_stat(void) |
|
{ |
|
if (have_git_dir() && git_config(git_cygwin_config,NULL)) { |
|
if (!core_filemode && native_stat) { |
|
cygwin_stat_fn = cygwin_stat; |
|
cygwin_lstat_fn = cygwin_lstat; |
|
} else { |
|
cygwin_stat_fn = stat; |
|
cygwin_lstat_fn = lstat; |
|
} |
|
return 0; |
|
} |
|
return 1; |
|
} |
|
|
|
static int cygwin_stat_stub(const char *file_name, struct stat *buf) |
|
{ |
|
return (init_stat() ? stat : *cygwin_stat_fn)(file_name, buf); |
|
} |
|
|
|
static int cygwin_lstat_stub(const char *file_name, struct stat *buf) |
|
{ |
|
return (init_stat() ? lstat : *cygwin_lstat_fn)(file_name, buf); |
|
} |
|
|
|
stat_fn_t cygwin_stat_fn = cygwin_stat_stub; |
|
stat_fn_t cygwin_lstat_fn = cygwin_lstat_stub; |
|
|
|
|