Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 : #include <sys/types.h>
15 : #include <sys/stat.h>
16 : #include <fcntl.h>
17 : #include <unistd.h>
18 : #include <string.h>
19 : #include "mutils.h"
20 : #include "mstring.h"
21 :
22 : #ifdef HAVE_MACH_O_DYLD_H
23 : # include <mach-o/dyld.h> /* _NSGetExecutablePath on OSX >=10.5 */
24 : #endif
25 :
26 : #include <limits.h> /* PATH_MAX on Solaris */
27 :
28 : #ifdef HAVE_SYS_PARAM_H
29 : # include <sys/param.h> /* realpath on OSX */
30 : #endif
31 :
32 : #ifdef BSD /* BSD macro is defined in sys/param.h */
33 : # include <sys/sysctl.h> /* KERN_PROC_PATHNAME on BSD */
34 : #endif
35 :
36 : #ifndef O_CLOEXEC
37 : #ifdef _O_NOINHERIT
38 : #define O_CLOEXEC _O_NOINHERIT /* Windows */
39 : #else
40 : #define O_CLOEXEC 0
41 : #endif
42 : #endif
43 :
44 : #ifdef NATIVE_WIN32
45 :
46 : #include <windows.h>
47 : #include <wchar.h>
48 :
49 : /* Some definitions that we need to compile on Windows.
50 : * Note that Windows only runs on little endian architectures. */
51 : #define BIG_ENDIAN 4321
52 : #define LITTLE_ENDIAN 1234
53 : #define BYTE_ORDER LITTLE_ENDIAN
54 :
55 : wchar_t *
56 : utf8towchar(const char *src)
57 : {
58 : wchar_t *dest;
59 : size_t i = 0;
60 : size_t j = 0;
61 : uint32_t c;
62 :
63 : if (src == NULL)
64 : return NULL;
65 :
66 : /* count how many wchar_t's we need, while also checking for
67 : * correctness of the input */
68 : while (src[j]) {
69 : i++;
70 : if ((src[j+0] & 0x80) == 0) {
71 : j += 1;
72 : } else if ((src[j+0] & 0xE0) == 0xC0
73 : && (src[j+1] & 0xC0) == 0x80
74 : && (src[j+0] & 0x1E) != 0) {
75 : j += 2;
76 : } else if ((src[j+0] & 0xF0) == 0xE0
77 : && (src[j+1] & 0xC0) == 0x80
78 : && (src[j+2] & 0xC0) == 0x80
79 : && ((src[j+0] & 0x0F) != 0
80 : || (src[j+1] & 0x20) != 0)) {
81 : j += 3;
82 : } else if ((src[j+0] & 0xF8) == 0xF0
83 : && (src[j+1] & 0xC0) == 0x80
84 : && (src[j+2] & 0xC0) == 0x80
85 : && (src[j+3] & 0xC0) == 0x80) {
86 : c = (src[j+0] & 0x07) << 18
87 : | (src[j+1] & 0x3F) << 12
88 : | (src[j+2] & 0x3F) << 6
89 : | (src[j+3] & 0x3F);
90 : if (c < 0x10000
91 : || c > 0x10FFFF
92 : || (c & 0x1FF800) == 0x00D800) {
93 : return NULL;
94 : }
95 : #if SIZEOF_WCHAR_T == 2
96 : i++;
97 : #endif
98 : j += 4;
99 : } else {
100 : return NULL;
101 : }
102 : }
103 : dest = malloc((i + 1) * sizeof(wchar_t));
104 : if (dest == NULL)
105 : return NULL;
106 : /* go through the source string again, this time we can skip
107 : * the correctness tests */
108 : i = j = 0;
109 : while (src[j]) {
110 : if ((src[j+0] & 0x80) == 0) {
111 : dest[i++] = src[j+0];
112 : j += 1;
113 : } else if ((src[j+0] & 0xE0) == 0xC0) {
114 : dest[i++] = (src[j+0] & 0x1F) << 6
115 : | (src[j+1] & 0x3F);
116 : j += 2;
117 : } else if ((src[j+0] & 0xF0) == 0xE0) {
118 : dest[i++] = (src[j+0] & 0x0F) << 12
119 : | (src[j+1] & 0x3F) << 6
120 : | (src[j+2] & 0x3F);
121 : j += 3;
122 : } else if ((src[j+0] & 0xF8) == 0xF0) {
123 : c = (src[j+0] & 0x07) << 18
124 : | (src[j+1] & 0x3F) << 12
125 : | (src[j+2] & 0x3F) << 6
126 : | (src[j+3] & 0x3F);
127 : #if SIZEOF_WCHAR_T == 2
128 : dest[i++] = 0xD800 | ((c - 0x10000) >> 10);
129 : dest[i++] = 0xDE00 | (c & 0x3FF);
130 : #else
131 : dest[i++] = c;
132 : #endif
133 : j += 4;
134 : }
135 : }
136 : dest[i] = 0;
137 :
138 : /* dir manipulations fail in WIN32 if file name contains trailing
139 : * slashes; work around this */
140 : while (i > 2 && dest[i - 1] == L'\\' && dest[i - 2] != L':')
141 : dest[--i] = 0;
142 :
143 : return dest;
144 : }
145 :
146 : char *
147 : wchartoutf8(const wchar_t *ws)
148 : {
149 : size_t len = 1;
150 : for (size_t i = 0; ws[i]; i++) {
151 : if (ws[i] <= 0x7F)
152 : len += 1;
153 : else if (ws[i] <= 0x7FF)
154 : len += 2;
155 : else if (
156 : #if SIZEOF_WCHAR_T == 2
157 : (ws[i] & 0xF800) != 0xD800
158 : #else
159 : ws[i] <= 0xFFFF
160 : #endif
161 : ) {
162 : assert((ws[i] & 0xF800) != 0xD800);
163 : len += 3;
164 : } else {
165 : #if SIZEOF_WCHAR_T == 2
166 : assert((ws[i + 0] & 0xFC00) == 0xD800); /* high surrogate */
167 : assert((ws[i + 1] & 0xFC00) == 0xDC00); /* low surrogate */
168 : len += 4;
169 : i++;
170 : #else
171 : assert(ws[i] <= 0x10FFFF);
172 : len += 4;
173 : #endif
174 : }
175 : }
176 : unsigned char *us = malloc(len);
177 : if (us != NULL) {
178 : size_t j = 0;
179 : for (size_t i = 0; ws[i]; i++) {
180 : if (ws[i] <= 0x7F)
181 : us[j++] = (unsigned char) ws[i];
182 : else if (ws[i] <= 0x7FF) {
183 : us[j++] = (unsigned char) (ws[i] >> 6 | 0xC0);
184 : us[j++] = (unsigned char) ((ws[i] & 0x3F) | 0x80);
185 : } else if (
186 : #if SIZEOF_WCHAR_T == 2
187 : (ws[i] & 0xF800) != 0xD800
188 : #else
189 : ws[i] <= 0xFFFF
190 : #endif
191 : ) {
192 : us[j++] = (unsigned char) (ws[i] >> 12 | 0xE0);
193 : us[j++] = (unsigned char) (((ws[i] >> 6) & 0x3F) | 0x80);
194 : us[j++] = (unsigned char) ((ws[i] & 0x3F) | 0x80);
195 : } else {
196 : uint32_t wc;
197 : #if SIZEOF_WCHAR_T == 2
198 : wc = ((ws[i+0] & 0x03FF) + 0x40) << 10 | (ws[i+1] & 0x03FF);
199 : i++;
200 : #else
201 : wc = (uint32_t) ws[i];
202 : #endif
203 : us[j++] = (unsigned char) (wc >> 18 | 0xF0);
204 : us[j++] = (unsigned char) (((wc >> 12) & 0x3F) | 0x80);
205 : us[j++] = (unsigned char) (((wc >> 6) & 0x3F) | 0x80);
206 : us[j++] = (unsigned char) ((wc & 0x3F) | 0x80);
207 : }
208 : }
209 : us[j] = 0;
210 : }
211 : return (char *) us;
212 : }
213 :
214 : /* translate Windows error code (GetLastError()) to Unix-style error */
215 : int
216 : winerror(int e)
217 : {
218 : switch (e) {
219 : case ERROR_BAD_ENVIRONMENT:
220 : return E2BIG;
221 : case ERROR_ACCESS_DENIED:
222 : case ERROR_CANNOT_MAKE:
223 : case ERROR_CURRENT_DIRECTORY:
224 : case ERROR_DRIVE_LOCKED:
225 : case ERROR_FAIL_I24:
226 : case ERROR_LOCK_FAILED:
227 : case ERROR_LOCK_VIOLATION:
228 : case ERROR_NETWORK_ACCESS_DENIED:
229 : case ERROR_NOT_LOCKED:
230 : case ERROR_SEEK_ON_DEVICE:
231 : return EACCES;
232 : case ERROR_MAX_THRDS_REACHED:
233 : case ERROR_NESTING_NOT_ALLOWED:
234 : case ERROR_NO_PROC_SLOTS:
235 : return EAGAIN;
236 : case ERROR_DIRECT_ACCESS_HANDLE:
237 : case ERROR_INVALID_TARGET_HANDLE:
238 : return EBADF;
239 : case ERROR_CHILD_NOT_COMPLETE:
240 : case ERROR_WAIT_NO_CHILDREN:
241 : return ECHILD;
242 : case ERROR_ALREADY_EXISTS:
243 : case ERROR_FILE_EXISTS:
244 : return EEXIST;
245 : case ERROR_INVALID_ACCESS:
246 : case ERROR_INVALID_DATA:
247 : case ERROR_INVALID_FUNCTION:
248 : case ERROR_INVALID_HANDLE:
249 : case ERROR_INVALID_PARAMETER:
250 : case ERROR_NEGATIVE_SEEK:
251 : return EINVAL;
252 : case ERROR_TOO_MANY_OPEN_FILES:
253 : return EMFILE;
254 : case ERROR_BAD_NET_NAME:
255 : case ERROR_BAD_NETPATH:
256 : case ERROR_BAD_PATHNAME:
257 : case ERROR_FILE_NOT_FOUND:
258 : case ERROR_FILENAME_EXCED_RANGE:
259 : case ERROR_INVALID_DRIVE:
260 : case ERROR_NO_MORE_FILES:
261 : case ERROR_PATH_NOT_FOUND:
262 : return ENOENT;
263 : case ERROR_BAD_FORMAT:
264 : return ENOEXEC;
265 : case ERROR_ARENA_TRASHED:
266 : case ERROR_INVALID_BLOCK:
267 : case ERROR_NOT_ENOUGH_MEMORY:
268 : case ERROR_NOT_ENOUGH_QUOTA:
269 : return ENOMEM;
270 : case ERROR_DISK_FULL:
271 : return ENOSPC;
272 : case ERROR_DIR_NOT_EMPTY:
273 : return ENOTEMPTY;
274 : case ERROR_BROKEN_PIPE:
275 : return EPIPE;
276 : case ERROR_NOT_SAME_DEVICE:
277 : return EXDEV;
278 : default:
279 : return EINVAL;
280 : }
281 : }
282 :
283 : struct DIR {
284 : wchar_t *dir_name;
285 : int just_opened;
286 : HANDLE find_file_handle;
287 : void *find_file_data;
288 : struct dirent result;
289 : };
290 :
291 : DIR *
292 : opendir(const char *dirname)
293 : {
294 : DIR *result = NULL;
295 : wchar_t *mask;
296 : size_t k;
297 : DWORD e;
298 :
299 : if (dirname == NULL) {
300 : errno = EFAULT;
301 : return NULL;
302 : }
303 :
304 : result = (DIR *) malloc(sizeof(DIR));
305 : if (result == NULL) {
306 : errno = ENOMEM;
307 : return NULL;
308 : }
309 : result->find_file_data = malloc(sizeof(WIN32_FIND_DATAW));
310 : result->dir_name = utf8towchar(dirname);
311 : if (result->find_file_data == NULL || result->dir_name == NULL) {
312 : if (result->find_file_data)
313 : free(result->find_file_data);
314 : if (result->dir_name)
315 : free(result->dir_name);
316 : free(result);
317 : errno = ENOMEM;
318 : return NULL;
319 : }
320 :
321 : k = wcslen(result->dir_name);
322 : if (k && result->dir_name[k - 1] == L'\\') {
323 : result->dir_name[k - 1] = L'\0';
324 : k--;
325 : }
326 : size_t masklen = (wcslen(result->dir_name) + 3) * sizeof(wchar_t);
327 : mask = malloc(masklen);
328 : if (mask == NULL) {
329 : free(result->find_file_data);
330 : free(result->dir_name);
331 : free(result);
332 : errno = ENOMEM;
333 : return NULL;
334 : }
335 : swprintf(mask, masklen, L"%ls\\*", result->dir_name);
336 :
337 : result->find_file_handle = FindFirstFileW(mask, (LPWIN32_FIND_DATAW) result->find_file_data);
338 : if (result->find_file_handle == INVALID_HANDLE_VALUE) {
339 : e = GetLastError();
340 : free(mask);
341 : free(result->dir_name);
342 : free(result->find_file_data);
343 : free(result);
344 : SetLastError(e);
345 : errno = winerror(e);
346 : return NULL;
347 : }
348 : free(mask);
349 : result->just_opened = TRUE;
350 :
351 : return result;
352 : }
353 :
354 : static wchar_t *
355 : basename(const wchar_t *file_name)
356 : {
357 : const wchar_t *p;
358 : const wchar_t *base;
359 :
360 : if (file_name == NULL)
361 : return NULL;
362 :
363 : if (iswalpha(file_name[0]) && file_name[1] == L':')
364 : file_name += 2; /* skip over drive letter */
365 :
366 : base = NULL;
367 : for (p = file_name; *p; p++)
368 : if (*p == L'\\' || *p == L'/')
369 : base = p;
370 : if (base)
371 : return (wchar_t *) base + 1;
372 :
373 : return (wchar_t *) file_name;
374 : }
375 :
376 : struct dirent *
377 : readdir(DIR *dir)
378 : {
379 : char *base;
380 :
381 : if (dir == NULL) {
382 : errno = EFAULT;
383 : return NULL;
384 : }
385 :
386 : if (dir->just_opened)
387 : dir->just_opened = FALSE;
388 : else if (!FindNextFileW(dir->find_file_handle,
389 : (LPWIN32_FIND_DATAW) dir->find_file_data))
390 : return NULL;
391 : base = wchartoutf8(basename(((LPWIN32_FIND_DATAW) dir->find_file_data)->cFileName));
392 : if (base == NULL)
393 : return NULL;
394 : strcpy_len(dir->result.d_name, base, sizeof(dir->result.d_name));
395 : free(base);
396 : dir->result.d_namelen = (int) strlen(dir->result.d_name);
397 :
398 : return &dir->result;
399 : }
400 :
401 : void
402 : rewinddir(DIR *dir)
403 : {
404 : wchar_t *mask;
405 :
406 : if (dir == NULL) {
407 : errno = EFAULT;
408 : return;
409 : }
410 :
411 : if (!FindClose(dir->find_file_handle))
412 : fprintf(stderr, "#rewinddir(): FindClose() failed\n");
413 :
414 : size_t masklen = (wcslen(dir->dir_name) + 3) * sizeof(wchar_t);
415 : mask = malloc(masklen);
416 : if (mask == NULL) {
417 : errno = ENOMEM;
418 : dir->find_file_handle = INVALID_HANDLE_VALUE;
419 : return;
420 : }
421 : swprintf(mask, masklen, L"%ls\\*", dir->dir_name);
422 : dir->find_file_handle = FindFirstFileW(mask, (LPWIN32_FIND_DATAW) dir->find_file_data);
423 : free(mask);
424 : if (dir->find_file_handle == INVALID_HANDLE_VALUE)
425 : return;
426 : dir->just_opened = TRUE;
427 : }
428 :
429 : int
430 : closedir(DIR *dir)
431 : {
432 : if (dir == NULL) {
433 : errno = EFAULT;
434 : return -1;
435 : }
436 :
437 : if (!FindClose(dir->find_file_handle))
438 : return -1;
439 :
440 : free(dir->dir_name);
441 : free(dir->find_file_data);
442 : free(dir);
443 :
444 : return 0;
445 : }
446 :
447 : char *
448 : dirname(char *path)
449 : {
450 : char *p, *q;
451 :
452 : for (p = path, q = NULL; *p; p++)
453 : if (*p == '/' || *p == '\\')
454 : q = p;
455 : if (q == NULL)
456 : return ".";
457 : *q = '\0';
458 : return path;
459 : }
460 :
461 : /* see contract of unix MT_lockf */
462 : int
463 : MT_lockf(const char *filename, int mode)
464 : {
465 : int ret = 1, fd = -1;
466 : OVERLAPPED ov;
467 : HANDLE fh;
468 : static struct lockedfiles {
469 : struct lockedfiles *next;
470 : wchar_t *wfilename;
471 : int fildes;
472 : } *lockedfiles;
473 : static CRITICAL_SECTION cs;
474 : static bool inited = false;
475 : struct lockedfiles **fpp, *fp;
476 : wchar_t *wfilename;
477 :
478 : if (!inited) {
479 : /* here we're still running single threaded */
480 : InitializeCriticalSection(&cs);
481 : inited = true; /* only time this is changed */
482 : }
483 :
484 : if ((wfilename = utf8towchar(filename)) == NULL)
485 : return -2;
486 : ov = (OVERLAPPED) {0};
487 : #if defined(DUMMYSTRUCTNAME) && (defined(NONAMELESSUNION) || !defined(_MSC_EXTENSIONS)) /* Windows SDK v7.0 */
488 : ov.u.s.Offset = 4;
489 : ov.u.s.OffsetHigh = 0;
490 : #else
491 : ov.Offset = 4;
492 : ov.OffsetHigh = 0;
493 : #endif
494 :
495 : if (mode == F_ULOCK) {
496 : EnterCriticalSection(&cs);
497 : for (fpp = &lockedfiles; (fp = *fpp) != NULL; fpp = &fp->next) {
498 : if (wcscmp(fp->wfilename, wfilename) == 0) {
499 : *fpp = fp->next;
500 : LeaveCriticalSection(&cs);
501 : free(fp->wfilename);
502 : fd = fp->fildes;
503 : fh = (HANDLE) _get_osfhandle(fd);
504 : free(fp);
505 : ret = UnlockFileEx(fh, 0, 1, 0, &ov);
506 : free(wfilename);
507 : return ret ? 0 : -1;
508 : }
509 : }
510 : LeaveCriticalSection(&cs);
511 : /* didn't find the locked file, try opening the file
512 : * directly */
513 : fh = CreateFileW(wfilename,
514 : GENERIC_READ | GENERIC_WRITE, 0,
515 : NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, NULL);
516 : free(wfilename);
517 : if (fh == INVALID_HANDLE_VALUE)
518 : return -2;
519 : ret = UnlockFileEx(fh, 0, 1, 0, &ov);
520 : CloseHandle(fh);
521 : return 0;
522 : }
523 :
524 : if (_wsopen_s(&fd, wfilename, _O_CREAT | _O_RDWR | _O_TEXT, _SH_DENYNO, _S_IREAD | _S_IWRITE) != 0) {
525 : free(wfilename);
526 : return -2;
527 : }
528 : fh = (HANDLE) _get_osfhandle(fd);
529 : if (fh == INVALID_HANDLE_VALUE) {
530 : close(fd);
531 : free(wfilename);
532 : return -2;
533 : }
534 :
535 : if (mode == F_TLOCK) {
536 : ret = LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov);
537 : } else if (mode == F_LOCK) {
538 : ret = LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov);
539 : } else if (mode == F_TEST) {
540 : ret = LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov);
541 : if (ret != 0) {
542 : UnlockFileEx(fh, 0, 1, 0, &ov);
543 : close(fd);
544 : free(wfilename);
545 : return 0;
546 : }
547 : } else {
548 : close(fd);
549 : errno = EINVAL;
550 : free(wfilename);
551 : return -2;
552 : }
553 : if (ret != 0) {
554 : if ((fp = malloc(sizeof(*fp))) != NULL) {
555 : fp->wfilename = wfilename;
556 : fp->fildes = fd;
557 : EnterCriticalSection(&cs);
558 : fp->next = lockedfiles;
559 : lockedfiles = fp;
560 : LeaveCriticalSection(&cs);
561 : } else {
562 : free(wfilename);
563 : }
564 : return fd;
565 : } else {
566 : close(fd);
567 : free(wfilename);
568 : return -1;
569 : }
570 : }
571 :
572 : FILE *
573 : MT_fopen(const char *filename, const char *mode)
574 : {
575 : wchar_t *wfilename, *wmode;
576 : wfilename = utf8towchar(filename);
577 : wmode = utf8towchar(mode);
578 : FILE *f = NULL;
579 : if (wfilename != NULL && wmode != NULL && (f = _wfopen(wfilename, wmode)) != NULL && strchr(mode, 'w') != NULL)
580 : SetFileAttributesW(wfilename, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
581 : free(wfilename);
582 : free(wmode);
583 : return f;
584 : }
585 :
586 : int
587 : MT_open(const char *filename, int flags)
588 : {
589 : wchar_t *wfilename = utf8towchar(filename);
590 : if (wfilename == NULL)
591 : return -1;
592 : int fd;
593 : if (_wsopen_s(&fd, wfilename, flags, _SH_DENYNO, _S_IREAD | _S_IWRITE) != 0)
594 : fd = -1;
595 : else if (flags & O_CREAT)
596 : SetFileAttributesW(wfilename, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
597 : free(wfilename);
598 : return fd;
599 : }
600 :
601 : int
602 : MT_stat(const char *pathname, struct _stat64 *st)
603 : {
604 : wchar_t *wpathname = utf8towchar(pathname);
605 : int ret;
606 : if (wpathname == NULL)
607 : return -1;
608 :
609 : ret = _wstat64(wpathname, st);
610 : free(wpathname);
611 : return ret;
612 : }
613 :
614 : #define RETRIES 10
615 : #define SLEEPTIME 20
616 :
617 : int
618 : MT_rmdir(const char *pathname)
619 : {
620 : wchar_t *wpathname = utf8towchar(pathname);
621 : int ret;
622 : if (wpathname == NULL)
623 : return -1;
624 :
625 : for (int i = 0; i < RETRIES; i++) {
626 : ret = _wrmdir(wpathname);
627 : if (ret == 0 || errno == ENOENT)
628 : break;
629 : /* it could be the <expletive deleted> indexing
630 : * service which prevents us from doing what we have a
631 : * right to do, so try again (once) */
632 : // fprintf(stderr, "#Retry rmdir %s\n", pathname);
633 : Sleep(SLEEPTIME); /* wait a little */
634 : }
635 : free(wpathname);
636 : return ret;
637 : }
638 :
639 : static inline int
640 : WMT_remove(const wchar_t *wpathname)
641 : {
642 : int ret;
643 :
644 : SetFileAttributesW(wpathname, FILE_ATTRIBUTE_NORMAL);
645 : for (int i = 0; i < RETRIES; i++) {
646 : ret = _wunlink(wpathname);
647 : if (ret == 0 || errno == ENOENT)
648 : break;
649 : /* it could be the <expletive deleted> indexing
650 : * service which prevents us from doing what we have a
651 : * right to do, so try again (once) */
652 : // fprintf(stderr, "#Retry unlink %ls\n", wpathname);
653 : Sleep(SLEEPTIME); /* wait a little */
654 : }
655 : return ret;
656 : }
657 :
658 : int
659 : MT_remove(const char *pathname)
660 : {
661 : wchar_t *wpathname = utf8towchar(pathname);
662 : int ret;
663 : if (wpathname == NULL)
664 : return -1;
665 :
666 : ret = WMT_remove(wpathname);
667 : free(wpathname);
668 : return ret;
669 : }
670 :
671 : int
672 : MT_rename(const char *old, const char *dst)
673 : {
674 : int ret = -1;
675 : wchar_t *wold, *wdst;
676 : wold = utf8towchar(old);
677 : wdst = utf8towchar(dst);
678 :
679 : if (wold && wdst) {
680 : for (int i = 0; i < RETRIES; i++) {
681 : ret = _wrename(wold, wdst);
682 : if (ret < 0 && errno == EEXIST) {
683 : if ((ret = WMT_remove(wdst)) < 0 &&
684 : errno != ENOENT)
685 : break;
686 : ret = _wrename(wold, wdst);
687 : }
688 : if (ret == 0 || errno == ENOENT)
689 : break;
690 : /* it could be the <expletive deleted> indexing
691 : * service which prevents us from doing what we have a
692 : * right to do, so try again (once) */
693 : // fprintf(stderr, "#Retry rename %s %s\n", old, dst);
694 : Sleep(SLEEPTIME); /* wait a little */
695 : }
696 : }
697 : free(wold);
698 : free(wdst);
699 : return ret;
700 : }
701 :
702 : int
703 : MT_mkdir(const char *pathname)
704 : {
705 : wchar_t *wpathname = utf8towchar(pathname);
706 : if (wpathname == NULL)
707 : return -1;
708 : int ret = _wmkdir(wpathname);
709 : if (ret == 0)
710 : SetFileAttributesW(wpathname, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
711 : free(wpathname);
712 : return ret;
713 : }
714 :
715 : char *
716 : MT_getcwd(char *buffer, size_t size)
717 : {
718 : wchar_t *wcwd = _wgetcwd(NULL, 0);
719 : if (wcwd == NULL)
720 : return NULL;
721 : char *cwd = wchartoutf8(wcwd);
722 : free(wcwd);
723 : if (cwd == NULL)
724 : return NULL;
725 : size_t len = strcpy_len(buffer, cwd, size);
726 : free(cwd);
727 : return len < size ? buffer : NULL;
728 : }
729 :
730 : int
731 : MT_access(const char *pathname, int mode)
732 : {
733 : wchar_t *wpathname = utf8towchar(pathname);
734 : if (wpathname == NULL)
735 : return -1;
736 : int ret = _waccess(wpathname, mode);
737 : free(wpathname);
738 : return ret;
739 : }
740 :
741 : #else
742 :
743 : #if defined(HAVE_LOCKF) && defined(__MACH__)
744 : /* lockf() seems to be there, but I didn't find any header file that
745 : declares the prototype ... */
746 : extern int lockf(int fd, int cmd, off_t len);
747 : #endif
748 :
749 : #ifndef HAVE_LOCKF
750 : /* Cygwin implementation: struct flock is there, but lockf() is
751 : missing.
752 : */
753 : static int
754 : lockf(int fd, int cmd, off_t len)
755 : {
756 : struct flock l;
757 :
758 : if (cmd == F_LOCK || cmd == F_TLOCK)
759 : l.l_type = F_WRLCK;
760 : else if (cmd == F_ULOCK)
761 : l.l_type = F_UNLCK;
762 : l.l_whence = SEEK_CUR;
763 : l.l_start = 0;
764 : l.l_len = len;
765 : return fcntl(fd, cmd == F_TLOCK ? F_SETLKW : F_SETLK, &l);
766 : }
767 : #endif
768 :
769 : #include <pthread.h>
770 :
771 : #ifndef O_TEXT
772 : #define O_TEXT 0
773 : #endif
774 : /* returns -1 when locking failed,
775 : * returns -2 when the lock file could not be opened/created
776 : * returns 0 when mode is F_TEST and the lock file was not locked
777 : * returns the (open) file descriptor to the file when locking
778 : * returns 0 when unlocking */
779 : int
780 90292 : MT_lockf(const char *filename, int mode)
781 : {
782 90292 : static struct lockfile {
783 : char *filename;
784 : int fd;
785 : struct lockfile *next;
786 : } *lockfiles = NULL;
787 90292 : static pthread_mutex_t cs = PTHREAD_MUTEX_INITIALIZER;
788 90292 : struct lockfile *fp;
789 90292 : int fd;
790 90292 : off_t seek;
791 :
792 90292 : if (mode == F_ULOCK) {
793 45144 : pthread_mutex_lock(&cs);
794 45320 : for (struct lockfile **fpp = &lockfiles; (fp = *fpp) != NULL; fpp = &fp->next) {
795 45320 : if (strcmp(fp->filename, filename) == 0) {
796 45144 : *fpp = fp->next;
797 45144 : pthread_mutex_unlock(&cs);
798 45144 : free(fp->filename);
799 45144 : fd = fp->fd;
800 45144 : free(fp);
801 45144 : seek = lseek(fd, 4, SEEK_SET);
802 45144 : if (seek < 0)
803 : seek = 0; /* should never happen, just for coverity */
804 45144 : int ret = lockf(fd, mode, 1);
805 45144 : (void) lseek(fd, seek, SEEK_SET); /* move seek pointer back */
806 : /* do not close fd, it is closed by caller */
807 45144 : return ret; /* 0 if unlock successful, -1 if not */
808 : }
809 : }
810 0 : pthread_mutex_unlock(&cs);
811 : }
812 45148 : fd = open(filename, O_CREAT | O_RDWR | O_TEXT | O_CLOEXEC, MONETDB_MODE);
813 :
814 45148 : if (fd < 0)
815 : return -2;
816 :
817 90296 : if ((seek = lseek(fd, 4, SEEK_SET)) >= 0 &&
818 45148 : lockf(fd, mode, 1) == 0) {
819 45146 : if (mode == F_ULOCK || mode == F_TEST) {
820 0 : close(fd);
821 0 : return 0;
822 : }
823 45146 : if ((fp = malloc(sizeof(*fp))) != NULL) {
824 45146 : if ((fp->filename = strdup(filename)) != NULL) {
825 45146 : fp->fd = fd;
826 45146 : pthread_mutex_lock(&cs);
827 45146 : fp->next = lockfiles;
828 45146 : lockfiles = fp;
829 45146 : pthread_mutex_unlock(&cs);
830 : } else {
831 0 : free(fp);
832 : }
833 : }
834 : /* do not close else we lose the lock we want */
835 45146 : (void) lseek(fd, seek, SEEK_SET); /* move seek pointer back */
836 45146 : return fd;
837 : }
838 2 : close(fd);
839 2 : return -1;
840 : }
841 :
842 : #endif
843 :
844 : #ifndef PATH_MAX
845 : # define PATH_MAX 1024
846 : #endif
847 : static char _bin_path[PATH_MAX];
848 : char *
849 343 : get_bin_path(void)
850 : {
851 : /* getting the path to the executable's binary, isn't all that
852 : * simple, unfortunately */
853 : #ifdef NATIVE_WIN32
854 : static wchar_t wbin_path[PATH_MAX];
855 : if (GetModuleFileNameW(NULL, wbin_path, PATH_MAX) != 0) {
856 : char *path = wchartoutf8(wbin_path);
857 : size_t len = strcpy_len(_bin_path, path, PATH_MAX);
858 : free(path);
859 : if (len < PATH_MAX)
860 : return _bin_path;
861 : }
862 : #elif defined(HAVE__NSGETEXECUTABLEPATH) /* Darwin/OSX */
863 : char buf[PATH_MAX];
864 : uint32_t size = PATH_MAX;
865 : if (_NSGetExecutablePath(buf, &size) == 0 &&
866 : realpath(buf, _bin_path) != NULL)
867 : return _bin_path;
868 : #elif defined(BSD) && defined(KERN_PROC_PATHNAME) /* BSD */
869 : int mib[4];
870 : size_t cb = sizeof(_bin_path);
871 : mib[0] = CTL_KERN;
872 : mib[1] = KERN_PROC;
873 : mib[2] = KERN_PROC_PATHNAME;
874 : mib[3] = -1;
875 : if (sysctl(mib, 4, _bin_path, &cb, NULL, 0) == 0)
876 : return _bin_path;
877 : #elif defined(HAVE_GETEXECNAME) /* Solaris */
878 : char buf[PATH_MAX];
879 : const char *execn = getexecname();
880 : /* getexecname doesn't always return an absolute path, the only
881 : * thing it seems to do is strip leading ./ from the invocation
882 : * string. */
883 : if (*execn != '/') {
884 : if (getcwd(buf, PATH_MAX) != NULL) {
885 : snprintf(buf + strlen(buf), PATH_MAX - strlen(buf), "/%s", execn);
886 : if (realpath(buf, _bin_path) != NULL)
887 : return(_bin_path);
888 : }
889 : } else {
890 : if (realpath(execn, _bin_path) != NULL)
891 : return(_bin_path);
892 : }
893 : #else /* try Linux approach, also works on Cygwin */
894 343 : if (readlink("/proc/self/exe",
895 : _bin_path, sizeof(_bin_path)) != -1)
896 343 : return _bin_path;
897 : #endif
898 : /* could use argv[0] (passed) to deduce location based on PATH, but
899 : * that's a lot of work and unreliable */
900 : return NULL;
901 : }
|