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