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