|
| 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 be |
| 93 | + * not be set yet. So until it is set, use cygwin lstat/stat functions. |
| 94 | + */ |
| 95 | +static int native_stat = 1; |
| 96 | + |
| 97 | +static int git_cygwin_config(const char *var, const char *value, void *cb) |
| 98 | +{ |
| 99 | + if (!strcmp(var, "core.ignorecygwinfstricks")) |
| 100 | + native_stat = git_config_bool(var, value); |
| 101 | + return 0; |
| 102 | +} |
| 103 | + |
| 104 | +static int init_stat(void) |
| 105 | +{ |
| 106 | + if (have_git_dir()) { |
| 107 | + git_config(git_cygwin_config, NULL); |
| 108 | + cygwin_stat_fn = native_stat ? cygwin_stat : stat; |
| 109 | + cygwin_lstat_fn = native_stat ? cygwin_lstat : lstat; |
| 110 | + return 0; |
| 111 | + } |
| 112 | + return 1; |
| 113 | +} |
| 114 | + |
| 115 | +static int cygwin_stat_stub(const char *file_name, struct stat *buf) |
| 116 | +{ |
| 117 | + return (init_stat() ? stat : *cygwin_stat_fn)(file_name, buf); |
| 118 | +} |
| 119 | + |
| 120 | +static int cygwin_lstat_stub(const char *file_name, struct stat *buf) |
| 121 | +{ |
| 122 | + return (init_stat() ? lstat : *cygwin_lstat_fn)(file_name, buf); |
| 123 | +} |
| 124 | + |
| 125 | +stat_fn_t cygwin_stat_fn = cygwin_stat_stub; |
| 126 | +stat_fn_t cygwin_lstat_fn = cygwin_lstat_stub; |
| 127 | + |
0 commit comments