2554f19765da5709b787e873da225c59f9d22bb7
[git/mingw.git] / compat / mingw.c
1 #include <stdint.h>
2 #include <stdlib.h>
3 #include <fcntl.h>
4 #include "../git-compat-util.h"
5
6 unsigned int _CRT_fmode = _O_BINARY;
7
8 int readlink(const char *path, char *buf, size_t bufsiz)
9 {
10 errno = ENOSYS;
11 return -1;
12 }
13
14 int symlink(const char *oldpath, const char *newpath)
15 {
16 errno = ENOSYS;
17 return -1;
18 }
19
20 int fchmod(int fildes, mode_t mode)
21 {
22 errno = EBADF;
23 return -1;
24 }
25
26 static inline time_t filetime_to_time_t(const FILETIME *ft)
27 {
28 long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
29 winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
30 winTime /= 10000000; /* Nano to seconds resolution */
31 return (time_t)winTime;
32 }
33
34 extern int _getdrive( void );
35 /* We keep the do_lstat code in a separate function to avoid recursion.
36 * When a path ends with a slash, the stat will fail with ENOENT. In
37 * this case, we strip the trailing slashes and stat again.
38 */
39 static int do_lstat(const char *file_name, struct stat *buf)
40 {
41 WIN32_FILE_ATTRIBUTE_DATA fdata;
42
43 if (GetFileAttributesExA(file_name, GetFileExInfoStandard, &fdata)) {
44 int fMode = S_IREAD;
45 if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
46 fMode |= S_IFDIR;
47 else
48 fMode |= S_IFREG;
49 if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
50 fMode |= S_IWRITE;
51
52 buf->st_ino = 0;
53 buf->st_gid = 0;
54 buf->st_uid = 0;
55 buf->st_nlink = 1;
56 buf->st_mode = fMode;
57 buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
58 buf->st_dev = buf->st_rdev = (_getdrive() - 1);
59 buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
60 buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
61 buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
62 errno = 0;
63 return 0;
64 }
65
66 switch (GetLastError()) {
67 case ERROR_ACCESS_DENIED:
68 case ERROR_SHARING_VIOLATION:
69 case ERROR_LOCK_VIOLATION:
70 case ERROR_SHARING_BUFFER_EXCEEDED:
71 errno = EACCES;
72 break;
73 case ERROR_BUFFER_OVERFLOW:
74 errno = ENAMETOOLONG;
75 break;
76 case ERROR_NOT_ENOUGH_MEMORY:
77 errno = ENOMEM;
78 break;
79 default:
80 errno = ENOENT;
81 break;
82 }
83 return -1;
84 }
85
86 /* We provide our own lstat/fstat functions, since the provided
87 * lstat/fstat functions are so slow. These stat functions are
88 * tailored for Git's usage (read: fast), and are not meant to be
89 * complete. Note that Git stat()s are redirected to git_lstat()
90 * too, since Windows doesn't really handle symlinks that well.
91 */
92 int git_lstat(const char *file_name, struct stat *buf)
93 {
94 int namelen;
95 static char alt_name[PATH_MAX];
96
97 if (!do_lstat(file_name, buf))
98 return 0;
99
100 /* if file_name ended in a '/', Windows returned ENOENT;
101 * try again without trailing slashes
102 */
103 if (errno != ENOENT)
104 return -1;
105
106 namelen = strlen(file_name);
107 if (namelen && file_name[namelen-1] != '/')
108 return -1;
109 while (namelen && file_name[namelen-1] == '/')
110 --namelen;
111 if (!namelen || namelen >= PATH_MAX)
112 return -1;
113
114 memcpy(alt_name, file_name, namelen);
115 alt_name[namelen] = 0;
116 return do_lstat(alt_name, buf);
117 }
118
119 #undef fstat
120 int git_fstat(int fd, struct stat *buf)
121 {
122 HANDLE fh = (HANDLE)_get_osfhandle(fd);
123 BY_HANDLE_FILE_INFORMATION fdata;
124
125 if (fh == INVALID_HANDLE_VALUE) {
126 errno = EBADF;
127 return -1;
128 }
129 /* direct non-file handles to MS's fstat() */
130 if (GetFileType(fh) != FILE_TYPE_DISK)
131 return fstat(fd, buf);
132
133 if (GetFileInformationByHandle(fh, &fdata)) {
134 int fMode = S_IREAD;
135 if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
136 fMode |= S_IFDIR;
137 else
138 fMode |= S_IFREG;
139 if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
140 fMode |= S_IWRITE;
141
142 buf->st_ino = 0;
143 buf->st_gid = 0;
144 buf->st_uid = 0;
145 buf->st_nlink = 1;
146 buf->st_mode = fMode;
147 buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
148 buf->st_dev = buf->st_rdev = (_getdrive() - 1);
149 buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
150 buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
151 buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
152 return 0;
153 }
154 errno = EBADF;
155 return -1;
156 }
157
158 /* missing: link, mkstemp, fchmod, getuid (?), gettimeofday */
159 int socketpair(int d, int type, int protocol, int sv[2])
160 {
161 return -1;
162 }
163 int syslog(int type, char *bufp, ...)
164 {
165 return -1;
166 }
167 unsigned int alarm(unsigned int seconds)
168 {
169 return 0;
170 }
171 #include <winsock2.h>
172 int fork()
173 {
174 return -1;
175 }
176
177 int kill(pid_t pid, int sig)
178 {
179 return -1;
180 }
181 unsigned int sleep (unsigned int __seconds)
182 {
183 Sleep(__seconds*1000);
184 return 0;
185 }
186 const char *inet_ntop(int af, const void *src,
187 char *dst, size_t cnt)
188 {
189 return NULL;
190 }
191 int mkstemp (char *__template)
192 {
193 char *filename = mktemp(__template);
194 if (filename == NULL)
195 return -1;
196 return open(filename, O_RDWR | O_CREAT, 0600);
197 }
198
199 int gettimeofday(struct timeval *tv, void *tz)
200 {
201 extern time_t my_mktime(struct tm *tm);
202 SYSTEMTIME st;
203 struct tm tm;
204 GetSystemTime(&st);
205 tm.tm_year = st.wYear-1900;
206 tm.tm_mon = st.wMonth-1;
207 tm.tm_mday = st.wDay;
208 tm.tm_hour = st.wHour;
209 tm.tm_min = st.wMinute;
210 tm.tm_sec = st.wSecond;
211 tv->tv_sec = my_mktime(&tm);
212 if (tv->tv_sec < 0)
213 return -1;
214 tv->tv_usec = st.wMilliseconds*1000;
215 return 0;
216 }
217
218 int pipe(int filedes[2])
219 {
220 int fd;
221 HANDLE h[2], parent;
222
223 if (_pipe(filedes, 8192, 0) < 0)
224 return -1;
225
226 parent = GetCurrentProcess();
227
228 if (!DuplicateHandle (parent, (HANDLE)_get_osfhandle(filedes[0]),
229 parent, &h[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) {
230 close(filedes[0]);
231 close(filedes[1]);
232 return -1;
233 }
234 if (!DuplicateHandle (parent, (HANDLE)_get_osfhandle(filedes[1]),
235 parent, &h[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) {
236 close(filedes[0]);
237 close(filedes[1]);
238 CloseHandle(h[0]);
239 return -1;
240 }
241 fd = _open_osfhandle(h[0], O_NOINHERIT);
242 if (fd < 0) {
243 close(filedes[0]);
244 close(filedes[1]);
245 CloseHandle(h[0]);
246 CloseHandle(h[1]);
247 return -1;
248 }
249 close(filedes[0]);
250 filedes[0] = fd;
251 fd = _open_osfhandle(h[1], O_NOINHERIT);
252 if (fd < 0) {
253 close(filedes[0]);
254 close(filedes[1]);
255 CloseHandle(h[1]);
256 return -1;
257 }
258 close(filedes[1]);
259 filedes[1] = fd;
260 return 0;
261 }
262
263 int poll(struct pollfd *ufds, unsigned int nfds, int timeout)
264 {
265 return -1;
266 }
267
268 #include <time.h>
269
270 struct tm *gmtime_r(const time_t *timep, struct tm *result)
271 {
272 memcpy(result, gmtime(timep), sizeof(struct tm));
273 return result;
274 }
275
276 struct tm *localtime_r(const time_t *timep, struct tm *result)
277 {
278 memcpy(result, localtime(timep), sizeof(struct tm));
279 return result;
280 }
281
282 #undef getcwd
283 char *mingw_getcwd(char *pointer, int len)
284 {
285 char *ret = getcwd(pointer, len);
286 if (!ret)
287 return ret;
288 if (pointer[0] != 0 && pointer[1] == ':') {
289 int i;
290 for (i = 2; pointer[i]; i++)
291 /* Thanks, Bill. You'll burn in hell for that. */
292 if (pointer[i] == '\\')
293 pointer[i] = '/';
294 }
295 return ret;
296 }
297
298 void sync(void)
299 {
300 }
301
302 void openlog(const char *ident, int option, int facility)
303 {
304 }
305
306 static const char *quote_arg(const char *arg)
307 {
308 /* count chars to quote */
309 int len = 0, n = 0;
310 int force_quotes = 0;
311 char *q, *d;
312 const char *p = arg;
313 while (*p) {
314 if (isspace(*p))
315 force_quotes = 1;
316 else if (*p == '"' || *p == '\\')
317 n++;
318 len++;
319 p++;
320 }
321 if (!force_quotes && n == 0)
322 return arg;
323
324 /* insert \ where necessary */
325 d = q = xmalloc(len+n+3);
326 *d++ = '"';
327 while (*arg) {
328 if (*arg == '"' || *arg == '\\')
329 *d++ = '\\';
330 *d++ = *arg++;
331 }
332 *d++ = '"';
333 *d++ = 0;
334 return q;
335 }
336
337 void quote_argv(const char **dst, const char **src)
338 {
339 while (*src)
340 *dst++ = quote_arg(*src++);
341 *dst = NULL;
342 }
343
344 const char *parse_interpreter(const char *cmd)
345 {
346 static char buf[100];
347 char *p, *opt;
348 int n, fd;
349
350 /* don't even try a .exe */
351 n = strlen(cmd);
352 if (n >= 4 && !strcasecmp(cmd+n-4, ".exe"))
353 return NULL;
354
355 fd = open(cmd, O_RDONLY);
356 if (fd < 0)
357 return NULL;
358 n = read(fd, buf, sizeof(buf)-1);
359 close(fd);
360 if (n < 4) /* at least '#!/x' and not error */
361 return NULL;
362
363 if (buf[0] != '#' || buf[1] != '!')
364 return NULL;
365 buf[n] = '\0';
366 p = strchr(buf, '\n');
367 if (!p)
368 return NULL;
369
370 *p = '\0';
371 if (!(p = strrchr(buf+2, '/')) && !(p = strrchr(buf+2, '\\')))
372 return NULL;
373 /* strip options */
374 if ((opt = strchr(p+1, ' ')))
375 *opt = '\0';
376 return p+1;
377 }
378
379 static int try_shell_exec(const char *cmd, const char **argv, const char **env)
380 {
381 const char **sh_argv;
382 int n;
383 const char *interpr = parse_interpreter(cmd);
384 if (!interpr)
385 return 0;
386
387 /*
388 * expand
389 * git-foo args...
390 * into
391 * sh git-foo args...
392 */
393 for (n = 0; argv[n];) n++;
394 sh_argv = xmalloc((n+2)*sizeof(char*));
395 sh_argv[0] = interpr;
396 sh_argv[1] = quote_arg(cmd);
397 quote_argv(&sh_argv[2], &argv[1]);
398 n = spawnvpe(_P_WAIT, interpr, sh_argv, env);
399 if (n == -1)
400 return 1; /* indicate that we tried but failed */
401 exit(n);
402 }
403
404 void mingw_execve(const char *cmd, const char **argv, const char **env)
405 {
406 /* check if git_command is a shell script */
407 if (!try_shell_exec(cmd, argv, env)) {
408 const char **qargv;
409 int n;
410 for (n = 0; argv[n];) n++;
411 qargv = xmalloc((n+1)*sizeof(char*));
412 quote_argv(qargv, argv);
413 int ret = spawnve(_P_WAIT, cmd, qargv, env);
414 if (ret != -1)
415 exit(ret);
416 }
417 }
418
419 int mingw_socket(int domain, int type, int protocol)
420 {
421 SOCKET s = WSASocket(domain, type, protocol, NULL, 0, 0);
422 if (s == INVALID_SOCKET) {
423 /*
424 * WSAGetLastError() values are regular BSD error codes
425 * biased by WSABASEERR.
426 * However, strerror() does not know about networking
427 * specific errors, which are values beginning at 38 or so.
428 * Therefore, we choose to leave the biased error code
429 * in errno so that _if_ someone looks up the code somewhere,
430 * then it is at least the number that are usually listed.
431 */
432 errno = WSAGetLastError();
433 return -1;
434 }
435 return s;
436 }
437
438 #undef rename
439 int mingw_rename(const char *pold, const char *pnew)
440 {
441 /*
442 * Try native rename() first to get errno right.
443 * It is based on MoveFile(), which cannot overwrite existing files.
444 */
445 if (!rename(pold, pnew))
446 return 0;
447 if (errno != EEXIST)
448 return -1;
449 if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
450 return 0;
451 /* TODO: translate more errors */
452 if (GetLastError() == ERROR_ACCESS_DENIED) {
453 struct stat st;
454 if (!stat(pnew, &st) && S_ISDIR(st.st_mode)) {
455 errno = EISDIR;
456 return -1;
457 }
458 }
459 errno = EACCES;
460 return -1;
461 }