posix_w32.c 8.86 KB
Newer Older
Vicent Marti committed
1 2 3 4 5 6
/*
 * Copyright (C) 2009-2011 the libgit2 contributors
 *
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
 */
Vicent Marti committed
7
#include "posix.h"
8
#include "path.h"
9
#include "utf-conv.h"
Vicent Marti committed
10
#include <errno.h>
11
#include <io.h>
12 13
#include <fcntl.h>

Vicent Marti committed
14 15 16

int p_unlink(const char *path)
{
17 18 19
	int ret = 0;
	wchar_t* buf;

20
	buf = gitwin_to_utf16(path);
21 22
	_wchmod(buf, 0666);
	ret = _wunlink(buf);
23
	git__free(buf);
24 25

	return ret;
Vicent Marti committed
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
}

int p_fsync(int fd)
{
	HANDLE fh = (HANDLE)_get_osfhandle(fd);

	if (fh == INVALID_HANDLE_VALUE) {
		errno = EBADF;
		return -1;
	}

	if (!FlushFileBuffers(fh)) {
		DWORD code = GetLastError();

		if (code == ERROR_INVALID_HANDLE)
			errno = EINVAL;
		else
			errno = EIO;

		return -1;
	}

	return 0;
}

GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft)
{
	long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
	winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
	winTime /= 10000000;		 /* Nano to seconds resolution */
	return (time_t)winTime;
}

static int do_lstat(const char *file_name, struct stat *buf)
{
	WIN32_FILE_ATTRIBUTE_DATA fdata;
62
	wchar_t* fbuf = gitwin_to_utf16(file_name);
Vicent Marti committed
63

64
	if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) {
Vicent Marti committed
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
		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 (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
			fMode |= S_IFLNK;

		buf->st_ino = 0;
		buf->st_gid = 0;
		buf->st_uid = 0;
		buf->st_nlink = 1;
		buf->st_mode = (mode_t)fMode;
nulltoken committed
83
		buf->st_size = ((git_off_t)fdata.nFileSizeHigh << 32) + fdata.nFileSizeLow;
Vicent Marti committed
84 85 86 87
		buf->st_dev = buf->st_rdev = (_getdrive() - 1);
		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));
88

89
		git__free(fbuf);
Vicent Marti committed
90 91 92
		return GIT_SUCCESS;
	}

93
	git__free(fbuf);
Vicent Marti committed
94

95 96 97 98 99 100 101 102 103 104 105 106 107
	switch (GetLastError()) {
		case ERROR_ACCESS_DENIED:
		case ERROR_SHARING_VIOLATION:
		case ERROR_LOCK_VIOLATION:
		case ERROR_SHARING_BUFFER_EXCEEDED:
			return GIT_EOSERR;

		case ERROR_BUFFER_OVERFLOW:
		case ERROR_NOT_ENOUGH_MEMORY:
			return GIT_ENOMEM;

		default:
			return GIT_EINVALIDPATH;
Vicent Marti committed
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
	}
}

int p_lstat(const char *file_name, struct stat *buf)
{
	int namelen, error;
	char alt_name[GIT_PATH_MAX];

	if ((error = do_lstat(file_name, buf)) == GIT_SUCCESS)
		return GIT_SUCCESS;

	/* if file_name ended in a '/', Windows returned ENOENT;
	 * try again without trailing slashes
	 */
	if (error != GIT_EINVALIDPATH)
		return git__throw(GIT_EOSERR, "Failed to lstat file");

	namelen = strlen(file_name);
	if (namelen && file_name[namelen-1] != '/')
		return git__throw(GIT_EOSERR, "Failed to lstat file");

	while (namelen && file_name[namelen-1] == '/')
		--namelen;

	if (!namelen || namelen >= GIT_PATH_MAX)
		return git__throw(GIT_ENOMEM, "Failed to lstat file");

	memcpy(alt_name, file_name, namelen);
	alt_name[namelen] = 0;
	return do_lstat(alt_name, buf);
}

int p_readlink(const char *link, char *target, size_t target_len)
{
142
	typedef DWORD (WINAPI *fpath_func)(HANDLE, LPWSTR, DWORD, DWORD);
Vicent Marti committed
143 144 145
	static fpath_func pGetFinalPath = NULL;
	HANDLE hFile;
	DWORD dwRet;
146 147
	wchar_t* link_w;
	wchar_t* target_w;
Vicent Marti committed
148 149 150 151 152 153 154 155 156

	/*
	 * Try to load the pointer to pGetFinalPath dynamically, because
	 * it is not available in platforms older than Vista
	 */
	if (pGetFinalPath == NULL) {
		HINSTANCE library = LoadLibrary("kernel32");

		if (library != NULL)
157
			pGetFinalPath = (fpath_func)GetProcAddress(library, "GetFinalPathNameByHandleW");
Vicent Marti committed
158 159 160

		if (pGetFinalPath == NULL)
			return git__throw(GIT_EOSERR,
161
				"'GetFinalPathNameByHandleW' is not available in this platform");
Vicent Marti committed
162 163
	}

164
	link_w = gitwin_to_utf16(link);
165 166 167 168 169 170 171 172 173

	hFile = CreateFileW(link_w,			// file to open
			GENERIC_READ,			// open for reading
			FILE_SHARE_READ,		// share for reading
			NULL,					// default security
			OPEN_EXISTING,			// existing file only
			FILE_FLAG_BACKUP_SEMANTICS, // normal file
			NULL);					// no attr. template

174
	git__free(link_w);
Vicent Marti committed
175 176 177 178

	if (hFile == INVALID_HANDLE_VALUE)
		return GIT_EOSERR;

179 180 181 182 183 184 185 186
	if (target_len <= 0) {
		return GIT_EINVALIDARGS;
	}

	target_w = (wchar_t*)git__malloc(target_len * sizeof(wchar_t));

	dwRet = pGetFinalPath(hFile, target_w, target_len, 0x0);
	if (dwRet >= target_len) {
187
		git__free(target_w);
188
		CloseHandle(hFile);
Vicent Marti committed
189
		return GIT_ENOMEM;
190 191 192
	}

	if (!WideCharToMultiByte(CP_UTF8, 0, target_w, -1, target, target_len * sizeof(char), NULL, NULL)) {
193
		git__free(target_w);
194 195
		return GIT_EOSERR;
	}
Vicent Marti committed
196

197
	git__free(target_w);
Vicent Marti committed
198 199 200 201
	CloseHandle(hFile);

	if (dwRet > 4) {
		/* Skip first 4 characters if they are "\\?\" */
Vicent Marti committed
202
		if (target[0] == '\\' && target[1] == '\\' && target[2] == '?' && target[3] == '\\') {
Vicent Marti committed
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
			char tmp[GIT_PATH_MAX];
			unsigned int offset = 4;
			dwRet -= 4;

			/* \??\UNC\ */
			if (dwRet > 7 && target[4] == 'U' && target[5] == 'N' && target[6] == 'C') {
				offset += 2;
				dwRet -= 2;
				target[offset] = '\\';
			}

			memcpy(tmp, target + offset, dwRet);
			memcpy(target, tmp, dwRet);
		}
	}

	target[dwRet] = '\0';
	return dwRet;
}

223 224 225
int p_open(const char *path, int flags)
{
	int fd;
226
	wchar_t* buf = gitwin_to_utf16(path);
227 228
	fd = _wopen(buf, flags | _O_BINARY);

229
	git__free(buf);
230 231 232
	return fd;
}

233
int p_creat(const char *path, mode_t mode)
234 235
{
	int fd;
236
	wchar_t* buf = gitwin_to_utf16(path);
237 238
	fd = _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode);

239
	git__free(buf);
240 241 242 243 244 245 246 247 248
	return fd;
}

int p_getcwd(char *buffer_out, size_t size)
{
	wchar_t* buf = (wchar_t*)git__malloc(sizeof(wchar_t) * (int)size);
	_wgetcwd(buf, (int)size);

	if (!WideCharToMultiByte(CP_UTF8, 0, buf, -1, buffer_out, size, NULL, NULL)) {
249
		git__free(buf);
250 251 252
		return GIT_EOSERR;
	}

253
	git__free(buf);
254 255 256 257 258 259 260 261 262 263
	return GIT_SUCCESS;
}

int p_stat(const char* path, struct stat* buf)
{
	return do_lstat(path, buf);
}

int p_chdir(const char* path)
{
264
	wchar_t* buf = gitwin_to_utf16(path);
265 266
	int ret = _wchdir(buf);

267
	git__free(buf);
268 269 270
	return ret;
}

271
int p_chmod(const char* path, mode_t mode)
272
{
273
	wchar_t* buf = gitwin_to_utf16(path);
274 275
	int ret = _wchmod(buf, mode);

276
	git__free(buf);
277 278 279 280 281
	return ret;
}

int p_rmdir(const char* path)
{
282
	wchar_t* buf = gitwin_to_utf16(path);
283 284
	int ret = _wrmdir(buf);

285
	git__free(buf);
286 287 288
	return ret;
}

Vicent Marti committed
289 290 291
int p_hide_directory__w32(const char *path)
{
	int error;
292
	wchar_t* buf = gitwin_to_utf16(path);
Vicent Marti committed
293

294
	error = SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0 ?
Vicent Marti committed
295
		GIT_SUCCESS : GIT_ERROR; /* MSDN states a "non zero" value indicates a success */
Vicent Marti committed
296

297
	git__free(buf);
298

Vicent Marti committed
299 300 301 302 303 304
	if (error < GIT_SUCCESS)
		error = git__throw(GIT_EOSERR, "Failed to hide directory '%s'", path);

	return error;
}

305
char *p_realpath(const char *orig_path, char *buffer)
306
{
307
	int ret;
308
	wchar_t* orig_path_w = gitwin_to_utf16(orig_path);
309 310 311
	wchar_t* buffer_w = (wchar_t*)git__malloc(GIT_PATH_MAX * sizeof(wchar_t));

	ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL);
312
	git__free(orig_path_w);
313

314
	if (!ret || ret > GIT_PATH_MAX) {
315 316
		buffer = NULL;
		goto done;
317
	}
318

319 320 321 322 323 324 325 326 327 328 329 330 331
	if (buffer == NULL) {
		int buffer_sz = WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, NULL, 0, NULL, NULL);

		if (!buffer_sz ||
			!(buffer = (char *)git__malloc(buffer_sz)) ||
			!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, buffer_sz, NULL, NULL))
		{
			git__free(buffer);
			buffer = NULL;
		}
	} else {
		if (!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL))
			buffer = NULL;
332
	}
333 334

done:
335
	git__free(buffer_w);
336 337
	if (buffer)
		git_path_mkposix(buffer);
338
	return buffer;
339 340
}

341 342
int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
{
343
#ifdef _MSC_VER
344 345 346 347 348 349
	int len = _vsnprintf(buffer, count, format, argptr);
	return (len < 0) ? _vscprintf(format, argptr) : len;
#else /* MinGW */
	return vsnprintf(buffer, count, format, argptr);
#endif
}
350 351 352 353 354 355 356 357 358 359 360 361

int p_snprintf(char *buffer, size_t count, const char *format, ...)
{
	va_list va;
	int r;

	va_start(va, format);
	r = p_vsnprintf(buffer, count, format, va);
	va_end(va);

	return r;
}
362

363
extern int p_creat(const char *path, mode_t mode);
364

365 366 367
int p_mkstemp(char *tmp_path)
{
#if defined(_MSC_VER)
368
	if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
Vicent Marti committed
369
		return GIT_EOSERR;
370
#else
Vicent Marti committed
371
	if (_mktemp(tmp_path) == NULL)
372
		return GIT_EOSERR;
Vicent Marti committed
373
#endif
374 375 376

	return p_creat(tmp_path, 0744);
}
377 378 379 380 381 382 383 384

int p_setenv(const char* name, const char* value, int overwrite)
{
	if (overwrite != 1)
		return EINVAL;

	return (SetEnvironmentVariableA(name, value) == 0 ? GIT_EOSERR : GIT_SUCCESS);
}
385

386
int p_access(const char* path, mode_t mode)
387
{
388
	wchar_t *buf = gitwin_to_utf16(path);
389 390 391
	int ret;

	ret = _waccess(buf, mode);
392
	git__free(buf);
393 394 395

	return ret;
}
396 397 398 399 400 401 402 403 404 405 406 407 408 409

extern int p_rename(const char *from, const char *to)
{
	wchar_t *wfrom = gitwin_to_utf16(from);
	wchar_t *wto = gitwin_to_utf16(to);
	int ret;

	ret = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR;

	git__free(wfrom);
	git__free(wto);

	return ret;
}