compat / cygwin.con commit t1301-*.sh: Fix the 'forced modes' test on cygwin (452993c)
   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() && git_config(git_cygwin_config,NULL)) {
 118                if (!core_filemode && native_stat) {
 119                        cygwin_stat_fn = cygwin_stat;
 120                        cygwin_lstat_fn = cygwin_lstat;
 121                } else {
 122                        cygwin_stat_fn = stat;
 123                        cygwin_lstat_fn = lstat;
 124                }
 125                return 0;
 126        }
 127        return 1;
 128}
 129
 130static int cygwin_stat_stub(const char *file_name, struct stat *buf)
 131{
 132        return (init_stat() ? stat : *cygwin_stat_fn)(file_name, buf);
 133}
 134
 135static int cygwin_lstat_stub(const char *file_name, struct stat *buf)
 136{
 137        return (init_stat() ? lstat : *cygwin_lstat_fn)(file_name, buf);
 138}
 139
 140stat_fn_t cygwin_stat_fn = cygwin_stat_stub;
 141stat_fn_t cygwin_lstat_fn = cygwin_lstat_stub;
 142