summaryrefslogtreecommitdiff
path: root/compat/cygwin.c
blob: 423ff20b0ef88111acbb61bd590fe39a4504a93a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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;