Merge branch 'dp/cywginstat'

* dp/cywginstat:
  cygwin: Use native Win32 API for stat
  mingw: move common functionality to win32.h
  add have_git_dir() function
This commit is contained in:
Shawn O. Pearce 2008-10-09 10:24:14 -07:00
commit ed187bd593
9 changed files with 194 additions and 38 deletions

View File

@ -117,6 +117,15 @@ core.fileMode::
the working copy are ignored; useful on broken filesystems like FAT.
See linkgit:git-update-index[1]. True by default.
core.ignoreCygwinFSTricks::
This option is only used by Cygwin implementation of Git. If false,
the Cygwin stat() and lstat() functions are used. This may be useful
if your repository consists of a few separate directories joined in
one hierarchy using Cygwin mount. If true, Git uses native Win32 API
whenever it is possible and falls back to Cygwin functions only to
handle symbol links. The native mode is more than twice faster than
normal Cygwin l/stat() functions. True by default.
core.trustctime::
If false, the ctime differences between the index and the
working copy are ignored; useful when the inode change time

View File

@ -346,6 +346,7 @@ LIB_H += cache.h
LIB_H += cache-tree.h
LIB_H += commit.h
LIB_H += compat/mingw.h
LIB_H += compat/cygwin.h
LIB_H += csum-file.h
LIB_H += decorate.h
LIB_H += delta.h
@ -748,6 +749,9 @@ ifeq ($(uname_S),HP-UX)
NO_SYS_SELECT_H = YesPlease
SNPRINTF_RETURNS_BOGUS = YesPlease
endif
ifneq (,$(findstring CYGWIN,$(uname_S)))
COMPAT_OBJS += compat/cygwin.o
endif
ifneq (,$(findstring MINGW,$(uname_S)))
NO_MMAP = YesPlease
NO_PREAD = YesPlease

View File

@ -319,6 +319,7 @@ extern int is_bare_repository(void);
extern int is_inside_git_dir(void);
extern char *git_work_tree_cfg;
extern int is_inside_work_tree(void);
extern int have_git_dir(void);
extern const char *get_git_dir(void);
extern char *get_object_directory(void);
extern char *get_index_file(void);

127
compat/cygwin.c Normal file
View File

@ -0,0 +1,127 @@
#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 be
* not be set yet. So until it is set, use cygwin lstat/stat functions.
*/
static int native_stat = 1;
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);
return 0;
}
static int init_stat(void)
{
if (have_git_dir()) {
git_config(git_cygwin_config, NULL);
cygwin_stat_fn = native_stat ? cygwin_stat : stat;
cygwin_lstat_fn = native_stat ? cygwin_lstat : 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;

9
compat/cygwin.h Normal file
View File

@ -0,0 +1,9 @@
#include <sys/types.h>
#include <sys/stat.h>
typedef int (*stat_fn_t)(const char*, struct stat*);
extern stat_fn_t cygwin_stat_fn;
extern stat_fn_t cygwin_lstat_fn;
#define stat(path, buf) (*cygwin_stat_fn)(path, buf)
#define lstat(path, buf) (*cygwin_lstat_fn)(path, buf)

View File

@ -1,4 +1,5 @@
#include "../git-compat-util.h"
#include "win32.h"
#include "../strbuf.h"
unsigned int _CRT_fmode = _O_BINARY;
@ -39,46 +40,19 @@ static int do_lstat(const char *file_name, struct stat *buf)
{
WIN32_FILE_ATTRIBUTE_DATA fdata;
if (GetFileAttributesExA(file_name, GetFileExInfoStandard, &fdata)) {
int fMode = S_IREAD;
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
fMode |= S_IFDIR;
else
fMode |= S_IFREG;
if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
fMode |= S_IWRITE;
if (!(errno = get_file_attr(file_name, &fdata))) {
buf->st_ino = 0;
buf->st_gid = 0;
buf->st_uid = 0;
buf->st_nlink = 1;
buf->st_mode = fMode;
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
errno = 0;
return 0;
}
switch (GetLastError()) {
case ERROR_ACCESS_DENIED:
case ERROR_SHARING_VIOLATION:
case ERROR_LOCK_VIOLATION:
case ERROR_SHARING_BUFFER_EXCEEDED:
errno = EACCES;
break;
case ERROR_BUFFER_OVERFLOW:
errno = ENAMETOOLONG;
break;
case ERROR_NOT_ENOUGH_MEMORY:
errno = ENOMEM;
break;
default:
errno = ENOENT;
break;
}
return -1;
}
@ -130,19 +104,11 @@ int mingw_fstat(int fd, struct stat *buf)
return fstat(fd, buf);
if (GetFileInformationByHandle(fh, &fdata)) {
int fMode = S_IREAD;
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
fMode |= S_IFDIR;
else
fMode |= S_IFREG;
if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
fMode |= S_IWRITE;
buf->st_ino = 0;
buf->st_gid = 0;
buf->st_uid = 0;
buf->st_nlink = 1;
buf->st_mode = fMode;
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));

34
compat/win32.h Normal file
View File

@ -0,0 +1,34 @@
/* common Win32 functions for MinGW and Cygwin */
#include <windows.h>
static inline int file_attr_to_st_mode (DWORD attr)
{
int fMode = S_IREAD;
if (attr & FILE_ATTRIBUTE_DIRECTORY)
fMode |= S_IFDIR;
else
fMode |= S_IFREG;
if (!(attr & FILE_ATTRIBUTE_READONLY))
fMode |= S_IWRITE;
return fMode;
}
static inline int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata)
{
if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata))
return 0;
switch (GetLastError()) {
case ERROR_ACCESS_DENIED:
case ERROR_SHARING_VIOLATION:
case ERROR_LOCK_VIOLATION:
case ERROR_SHARING_BUFFER_EXCEEDED:
return EACCES;
case ERROR_BUFFER_OVERFLOW:
return ENAMETOOLONG;
case ERROR_NOT_ENOUGH_MEMORY:
return ENOMEM;
default:
return ENOENT;
}
}

View File

@ -80,6 +80,11 @@ int is_bare_repository(void)
return is_bare_repository_cfg && !get_git_work_tree();
}
int have_git_dir(void)
{
return !!git_dir;
}
const char *get_git_dir(void)
{
if (!git_dir)

View File

@ -85,6 +85,7 @@
#undef _XOPEN_SOURCE
#include <grp.h>
#define _XOPEN_SOURCE 600
#include "compat/cygwin.h"
#else
#undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
#include <grp.h>