]>
Commit | Line | Data |
---|---|---|
e1de726c | 1 | /* |
5e0de328 | 2 | * Copyright (C) 2009-2012 the libgit2 contributors |
bb742ede VM |
3 | * |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with | |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
6 | */ | |
44ef8b1b | 7 | #include "../posix.h" |
5ad739e8 | 8 | #include "path.h" |
11d51ca6 | 9 | #include "utf-conv.h" |
f79026b4 | 10 | #include <errno.h> |
f978b748 | 11 | #include <io.h> |
7998ae5a PB |
12 | #include <fcntl.h> |
13 | ||
f79026b4 VM |
14 | |
15 | int p_unlink(const char *path) | |
16 | { | |
7998ae5a PB |
17 | int ret = 0; |
18 | wchar_t* buf; | |
19 | ||
deafee7b RB |
20 | if ((buf = gitwin_to_utf16(path)) != NULL) { |
21 | _wchmod(buf, 0666); | |
22 | ret = _wunlink(buf); | |
23 | git__free(buf); | |
24 | } | |
7998ae5a PB |
25 | |
26 | return ret; | |
f79026b4 VM |
27 | } |
28 | ||
29 | int p_fsync(int fd) | |
30 | { | |
31 | HANDLE fh = (HANDLE)_get_osfhandle(fd); | |
32 | ||
33 | if (fh == INVALID_HANDLE_VALUE) { | |
34 | errno = EBADF; | |
35 | return -1; | |
36 | } | |
37 | ||
38 | if (!FlushFileBuffers(fh)) { | |
39 | DWORD code = GetLastError(); | |
40 | ||
41 | if (code == ERROR_INVALID_HANDLE) | |
42 | errno = EINVAL; | |
43 | else | |
44 | errno = EIO; | |
45 | ||
46 | return -1; | |
47 | } | |
48 | ||
49 | return 0; | |
50 | } | |
51 | ||
52 | GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft) | |
53 | { | |
54 | long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; | |
55 | winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */ | |
56 | winTime /= 10000000; /* Nano to seconds resolution */ | |
57 | return (time_t)winTime; | |
58 | } | |
59 | ||
60 | static int do_lstat(const char *file_name, struct stat *buf) | |
61 | { | |
62 | WIN32_FILE_ATTRIBUTE_DATA fdata; | |
11d51ca6 | 63 | wchar_t* fbuf = gitwin_to_utf16(file_name); |
deafee7b RB |
64 | if (!fbuf) |
65 | return -1; | |
f79026b4 | 66 | |
7998ae5a | 67 | if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) { |
f79026b4 VM |
68 | int fMode = S_IREAD; |
69 | ||
70 | if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | |
71 | fMode |= S_IFDIR; | |
72 | else | |
73 | fMode |= S_IFREG; | |
74 | ||
75 | if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) | |
76 | fMode |= S_IWRITE; | |
77 | ||
78 | if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) | |
79 | fMode |= S_IFLNK; | |
80 | ||
81 | buf->st_ino = 0; | |
82 | buf->st_gid = 0; | |
83 | buf->st_uid = 0; | |
84 | buf->st_nlink = 1; | |
85 | buf->st_mode = (mode_t)fMode; | |
fdc8a7db | 86 | buf->st_size = ((git_off_t)fdata.nFileSizeHigh << 32) + fdata.nFileSizeLow; |
f79026b4 VM |
87 | buf->st_dev = buf->st_rdev = (_getdrive() - 1); |
88 | buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); | |
89 | buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); | |
90 | buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); | |
7998ae5a | 91 | |
3286c408 | 92 | git__free(fbuf); |
deafee7b | 93 | return 0; |
f79026b4 VM |
94 | } |
95 | ||
3286c408 | 96 | git__free(fbuf); |
deafee7b | 97 | return -1; |
f79026b4 VM |
98 | } |
99 | ||
100 | int p_lstat(const char *file_name, struct stat *buf) | |
101 | { | |
deafee7b RB |
102 | int error; |
103 | size_t namelen; | |
104 | char *alt_name; | |
f79026b4 | 105 | |
deafee7b RB |
106 | if (do_lstat(file_name, buf) == 0) |
107 | return 0; | |
f79026b4 VM |
108 | |
109 | /* if file_name ended in a '/', Windows returned ENOENT; | |
110 | * try again without trailing slashes | |
111 | */ | |
f79026b4 VM |
112 | namelen = strlen(file_name); |
113 | if (namelen && file_name[namelen-1] != '/') | |
deafee7b | 114 | return -1; |
f79026b4 VM |
115 | |
116 | while (namelen && file_name[namelen-1] == '/') | |
117 | --namelen; | |
118 | ||
deafee7b RB |
119 | if (!namelen) |
120 | return -1; | |
121 | ||
122 | alt_name = git__strndup(file_name, namelen); | |
123 | if (!alt_name) | |
124 | return -1; | |
125 | ||
126 | error = do_lstat(alt_name, buf); | |
f79026b4 | 127 | |
deafee7b RB |
128 | git__free(alt_name); |
129 | return error; | |
f79026b4 VM |
130 | } |
131 | ||
132 | int p_readlink(const char *link, char *target, size_t target_len) | |
133 | { | |
7998ae5a | 134 | typedef DWORD (WINAPI *fpath_func)(HANDLE, LPWSTR, DWORD, DWORD); |
f79026b4 VM |
135 | static fpath_func pGetFinalPath = NULL; |
136 | HANDLE hFile; | |
137 | DWORD dwRet; | |
7998ae5a PB |
138 | wchar_t* link_w; |
139 | wchar_t* target_w; | |
deafee7b RB |
140 | int error = 0; |
141 | ||
142 | assert(link && target && target_len > 0); | |
f79026b4 VM |
143 | |
144 | /* | |
145 | * Try to load the pointer to pGetFinalPath dynamically, because | |
146 | * it is not available in platforms older than Vista | |
147 | */ | |
148 | if (pGetFinalPath == NULL) { | |
149 | HINSTANCE library = LoadLibrary("kernel32"); | |
150 | ||
151 | if (library != NULL) | |
7998ae5a | 152 | pGetFinalPath = (fpath_func)GetProcAddress(library, "GetFinalPathNameByHandleW"); |
f79026b4 | 153 | |
deafee7b RB |
154 | if (pGetFinalPath == NULL) { |
155 | giterr_set(GITERR_OS, | |
7998ae5a | 156 | "'GetFinalPathNameByHandleW' is not available in this platform"); |
deafee7b RB |
157 | return -1; |
158 | } | |
f79026b4 VM |
159 | } |
160 | ||
11d51ca6 | 161 | link_w = gitwin_to_utf16(link); |
deafee7b | 162 | GITERR_CHECK_ALLOC(link_w); |
7998ae5a PB |
163 | |
164 | hFile = CreateFileW(link_w, // file to open | |
165 | GENERIC_READ, // open for reading | |
166 | FILE_SHARE_READ, // share for reading | |
167 | NULL, // default security | |
168 | OPEN_EXISTING, // existing file only | |
169 | FILE_FLAG_BACKUP_SEMANTICS, // normal file | |
170 | NULL); // no attr. template | |
171 | ||
3286c408 | 172 | git__free(link_w); |
f79026b4 | 173 | |
deafee7b RB |
174 | if (hFile == INVALID_HANDLE_VALUE) { |
175 | giterr_set(GITERR_OS, "Cannot open '%s' for reading", link); | |
176 | return -1; | |
7998ae5a PB |
177 | } |
178 | ||
179 | target_w = (wchar_t*)git__malloc(target_len * sizeof(wchar_t)); | |
deafee7b | 180 | GITERR_CHECK_ALLOC(target_w); |
7998ae5a | 181 | |
44ef8b1b | 182 | dwRet = pGetFinalPath(hFile, target_w, (DWORD)target_len, 0x0); |
deafee7b RB |
183 | if (dwRet == 0 || |
184 | dwRet >= target_len || | |
185 | !WideCharToMultiByte(CP_UTF8, 0, target_w, -1, target, | |
44ef8b1b | 186 | (int)(target_len * sizeof(char)), NULL, NULL)) |
deafee7b | 187 | error = -1; |
f79026b4 | 188 | |
3286c408 | 189 | git__free(target_w); |
f79026b4 VM |
190 | CloseHandle(hFile); |
191 | ||
deafee7b RB |
192 | if (error) |
193 | return error; | |
194 | ||
195 | /* Skip first 4 characters if they are "\\?\" */ | |
196 | if (dwRet > 4 && | |
197 | target[0] == '\\' && target[1] == '\\' && | |
198 | target[2] == '?' && target[3] == '\\') | |
199 | { | |
200 | unsigned int offset = 4; | |
201 | dwRet -= 4; | |
202 | ||
203 | /* \??\UNC\ */ | |
204 | if (dwRet > 7 && | |
205 | target[4] == 'U' && target[5] == 'N' && target[6] == 'C') | |
206 | { | |
207 | offset += 2; | |
208 | dwRet -= 2; | |
209 | target[offset] = '\\'; | |
f79026b4 | 210 | } |
deafee7b RB |
211 | |
212 | memmove(target, target + offset, dwRet); | |
f79026b4 VM |
213 | } |
214 | ||
215 | target[dwRet] = '\0'; | |
deafee7b | 216 | |
f79026b4 VM |
217 | return dwRet; |
218 | } | |
219 | ||
3191ae89 | 220 | int p_open(const char *path, int flags, ...) |
7998ae5a PB |
221 | { |
222 | int fd; | |
3191ae89 | 223 | wchar_t* buf; |
224 | mode_t mode = 0; | |
225 | ||
226 | buf = gitwin_to_utf16(path); | |
deafee7b RB |
227 | if (!buf) |
228 | return -1; | |
3191ae89 | 229 | |
230 | if (flags & O_CREAT) | |
231 | { | |
232 | va_list arg_list; | |
233 | ||
234 | va_start(arg_list, flags); | |
235 | mode = va_arg(arg_list, mode_t); | |
236 | va_end(arg_list); | |
237 | } | |
238 | ||
239 | fd = _wopen(buf, flags | _O_BINARY, mode); | |
240 | ||
3286c408 | 241 | git__free(buf); |
7998ae5a PB |
242 | return fd; |
243 | } | |
244 | ||
33127043 | 245 | int p_creat(const char *path, mode_t mode) |
7998ae5a PB |
246 | { |
247 | int fd; | |
11d51ca6 | 248 | wchar_t* buf = gitwin_to_utf16(path); |
deafee7b RB |
249 | if (!buf) |
250 | return -1; | |
7998ae5a | 251 | fd = _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode); |
3286c408 | 252 | git__free(buf); |
7998ae5a PB |
253 | return fd; |
254 | } | |
255 | ||
256 | int p_getcwd(char *buffer_out, size_t size) | |
257 | { | |
deafee7b | 258 | int ret; |
44ef8b1b RB |
259 | wchar_t* buf; |
260 | ||
261 | if ((size_t)((int)size) != size) | |
262 | return -1; | |
263 | ||
264 | buf = (wchar_t*)git__malloc(sizeof(wchar_t) * (int)size); | |
265 | GITERR_CHECK_ALLOC(buf); | |
deafee7b | 266 | |
7998ae5a PB |
267 | _wgetcwd(buf, (int)size); |
268 | ||
deafee7b | 269 | ret = WideCharToMultiByte( |
44ef8b1b | 270 | CP_UTF8, 0, buf, -1, buffer_out, (int)size, NULL, NULL); |
7998ae5a | 271 | |
3286c408 | 272 | git__free(buf); |
deafee7b | 273 | return !ret ? -1 : 0; |
7998ae5a PB |
274 | } |
275 | ||
276 | int p_stat(const char* path, struct stat* buf) | |
277 | { | |
278 | return do_lstat(path, buf); | |
279 | } | |
280 | ||
281 | int p_chdir(const char* path) | |
282 | { | |
11d51ca6 | 283 | wchar_t* buf = gitwin_to_utf16(path); |
deafee7b RB |
284 | int ret; |
285 | if (!buf) | |
286 | return -1; | |
287 | ret = _wchdir(buf); | |
3286c408 | 288 | git__free(buf); |
7998ae5a PB |
289 | return ret; |
290 | } | |
291 | ||
33127043 | 292 | int p_chmod(const char* path, mode_t mode) |
7998ae5a | 293 | { |
11d51ca6 | 294 | wchar_t* buf = gitwin_to_utf16(path); |
deafee7b RB |
295 | int ret; |
296 | if (!buf) | |
297 | return -1; | |
298 | ret = _wchmod(buf, mode); | |
3286c408 | 299 | git__free(buf); |
7998ae5a PB |
300 | return ret; |
301 | } | |
302 | ||
303 | int p_rmdir(const char* path) | |
304 | { | |
11d51ca6 | 305 | wchar_t* buf = gitwin_to_utf16(path); |
deafee7b RB |
306 | int ret; |
307 | if (!buf) | |
308 | return -1; | |
309 | ret = _wrmdir(buf); | |
3286c408 | 310 | git__free(buf); |
7998ae5a PB |
311 | return ret; |
312 | } | |
313 | ||
f79026b4 VM |
314 | int p_hide_directory__w32(const char *path) |
315 | { | |
cb8a7961 | 316 | int res; |
11d51ca6 | 317 | wchar_t* buf = gitwin_to_utf16(path); |
deafee7b RB |
318 | if (!buf) |
319 | return -1; | |
f79026b4 | 320 | |
cb8a7961 | 321 | res = SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN); |
3286c408 | 322 | git__free(buf); |
deafee7b RB |
323 | |
324 | return (res != 0) ? 0 : -1; /* MSDN states a "non zero" value indicates a success */ | |
f79026b4 VM |
325 | } |
326 | ||
19ac1ed7 | 327 | char *p_realpath(const char *orig_path, char *buffer) |
5ad739e8 | 328 | { |
9abb5bca | 329 | int ret, buffer_sz = 0; |
11d51ca6 | 330 | wchar_t* orig_path_w = gitwin_to_utf16(orig_path); |
7998ae5a PB |
331 | wchar_t* buffer_w = (wchar_t*)git__malloc(GIT_PATH_MAX * sizeof(wchar_t)); |
332 | ||
deafee7b RB |
333 | if (!orig_path_w || !buffer_w) |
334 | return NULL; | |
335 | ||
7998ae5a | 336 | ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL); |
3286c408 | 337 | git__free(orig_path_w); |
7998ae5a | 338 | |
9abb5bca | 339 | /* According to MSDN, a return value equals to zero means a failure. */ |
340 | if (ret == 0 || ret > GIT_PATH_MAX) { | |
97769280 RB |
341 | buffer = NULL; |
342 | goto done; | |
19ac1ed7 | 343 | } |
5ad739e8 | 344 | |
97769280 | 345 | if (buffer == NULL) { |
9abb5bca | 346 | buffer_sz = WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, NULL, 0, NULL, NULL); |
97769280 RB |
347 | |
348 | if (!buffer_sz || | |
349 | !(buffer = (char *)git__malloc(buffer_sz)) || | |
350 | !WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, buffer_sz, NULL, NULL)) | |
351 | { | |
352 | git__free(buffer); | |
353 | buffer = NULL; | |
9abb5bca | 354 | goto done; |
97769280 RB |
355 | } |
356 | } else { | |
9abb5bca | 357 | if (!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL)) { |
97769280 | 358 | buffer = NULL; |
9abb5bca | 359 | goto done; |
360 | } | |
361 | } | |
362 | ||
363 | if (!git_path_exists(buffer)) | |
364 | { | |
365 | if (buffer_sz > 0) | |
366 | git__free(buffer); | |
367 | ||
368 | buffer = NULL; | |
369 | errno = ENOENT; | |
7998ae5a | 370 | } |
97769280 RB |
371 | |
372 | done: | |
3286c408 | 373 | git__free(buffer_w); |
97769280 RB |
374 | if (buffer) |
375 | git_path_mkposix(buffer); | |
19ac1ed7 | 376 | return buffer; |
5ad739e8 VM |
377 | } |
378 | ||
2fc78e70 VM |
379 | int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr) |
380 | { | |
ae2e4c6a | 381 | #ifdef _MSC_VER |
60bc2d20 VM |
382 | int len; |
383 | ||
384 | if (count == 0 || (len = _vsnprintf(buffer, count, format, argptr)) < 0) | |
e1de726c | 385 | return _vscprintf(format, argptr); |
60bc2d20 VM |
386 | |
387 | return len; | |
2fc78e70 VM |
388 | #else /* MinGW */ |
389 | return vsnprintf(buffer, count, format, argptr); | |
390 | #endif | |
391 | } | |
84dd3820 VM |
392 | |
393 | int p_snprintf(char *buffer, size_t count, const char *format, ...) | |
394 | { | |
395 | va_list va; | |
396 | int r; | |
397 | ||
398 | va_start(va, format); | |
399 | r = p_vsnprintf(buffer, count, format, va); | |
400 | va_end(va); | |
401 | ||
402 | return r; | |
403 | } | |
f978b748 | 404 | |
33127043 | 405 | extern int p_creat(const char *path, mode_t mode); |
2fcf9c82 | 406 | |
f978b748 VM |
407 | int p_mkstemp(char *tmp_path) |
408 | { | |
f978b748 | 409 | #if defined(_MSC_VER) |
489c3666 | 410 | if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0) |
deafee7b | 411 | return -1; |
f978b748 | 412 | #else |
c035ede2 | 413 | if (_mktemp(tmp_path) == NULL) |
deafee7b | 414 | return -1; |
c035ede2 | 415 | #endif |
f978b748 VM |
416 | |
417 | return p_creat(tmp_path, 0744); | |
418 | } | |
222d057c PB |
419 | |
420 | int p_setenv(const char* name, const char* value, int overwrite) | |
421 | { | |
422 | if (overwrite != 1) | |
deafee7b | 423 | return -1; |
222d057c | 424 | |
deafee7b | 425 | return (SetEnvironmentVariableA(name, value) == 0 ? -1 : 0); |
222d057c | 426 | } |
dd44887a | 427 | |
33127043 | 428 | int p_access(const char* path, mode_t mode) |
dd44887a | 429 | { |
11d51ca6 | 430 | wchar_t *buf = gitwin_to_utf16(path); |
dd44887a | 431 | int ret; |
deafee7b RB |
432 | if (!buf) |
433 | return -1; | |
dd44887a CMN |
434 | |
435 | ret = _waccess(buf, mode); | |
3286c408 | 436 | git__free(buf); |
dd44887a CMN |
437 | |
438 | return ret; | |
439 | } | |
0c49ec2d | 440 | |
deafee7b | 441 | int p_rename(const char *from, const char *to) |
0c49ec2d CMN |
442 | { |
443 | wchar_t *wfrom = gitwin_to_utf16(from); | |
444 | wchar_t *wto = gitwin_to_utf16(to); | |
445 | int ret; | |
446 | ||
deafee7b RB |
447 | if (!wfrom || !wto) |
448 | return -1; | |
449 | ||
450 | ret = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1; | |
0c49ec2d CMN |
451 | |
452 | git__free(wfrom); | |
453 | git__free(wto); | |
454 | ||
455 | return ret; | |
456 | } | |
44ef8b1b RB |
457 | |
458 | int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags) | |
459 | { | |
460 | if ((size_t)((int)length) != length) | |
461 | return -1; /* giterr_set will be done by caller */ | |
462 | ||
463 | return recv(socket, buffer, (int)length, flags); | |
464 | } | |
465 | ||
466 | int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags) | |
467 | { | |
468 | if ((size_t)((int)length) != length) | |
469 | return -1; /* giterr_set will be done by caller */ | |
470 | ||
471 | return send(socket, buffer, (int)length, flags); | |
472 | } |