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