143 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			143 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
| #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
 | |
|  * not be set yet. So until it is set, use cygwin lstat/stat functions.
 | |
|  * However, if core.filemode is set, we must use the Cygwin posix
 | |
|  * stat/lstat as the Windows stat functions do not determine posix filemode.
 | |
|  *
 | |
|  * Note that git_cygwin_config() does NOT call git_default_config() and this
 | |
|  * is deliberate.  Many commands read from config to establish initial
 | |
|  * values in variables and later tweak them from elsewhere (e.g. command line).
 | |
|  * init_stat() is called lazily on demand, typically much late in the program,
 | |
|  * and calling git_default_config() from here would break such variables.
 | |
|  */
 | |
| static int native_stat = 1;
 | |
| static int core_filemode = 1; /* matches trust_executable_bit default */
 | |
| 
 | |
| 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);
 | |
| 	else if (!strcmp(var, "core.filemode"))
 | |
| 		core_filemode = git_config_bool(var, value);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int init_stat(void)
 | |
| {
 | |
| 	if (have_git_dir() && git_config(git_cygwin_config,NULL)) {
 | |
| 		if (!core_filemode && native_stat) {
 | |
| 			cygwin_stat_fn = cygwin_stat;
 | |
| 			cygwin_lstat_fn = cygwin_lstat;
 | |
| 		} else {
 | |
| 			cygwin_stat_fn = stat;
 | |
| 			cygwin_lstat_fn = 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;
 | |
| 
 |