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