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 : #if defined(DUMMYSTRUCTNAME) && (defined(NONAMELESSUNION) || !defined(_MSC_EXTENSIONS)) /* Windows SDK v7.0 */
462 : ov.u.s.Offset = 4;
463 : ov.u.s.OffsetHigh = 0;
464 : #else
465 : ov.Offset = 4;
466 : ov.OffsetHigh = 0;
467 : #endif
468 :
469 : if (mode == F_ULOCK) {
470 : EnterCriticalSection(&cs);
471 : for (fpp = &lockedfiles; (fp = *fpp) != NULL; fpp = &fp->next) {
472 : if (wcscmp(fp->wfilename, wfilename) == 0) {
473 : *fpp = fp->next;
474 : LeaveCriticalSection(&cs);
475 : free(fp->wfilename);
476 : fd = fp->fildes;
477 : fh = (HANDLE) _get_osfhandle(fd);
478 : free(fp);
479 : ret = UnlockFileEx(fh, 0, 1, 0, &ov);
480 : free(wfilename);
481 : return ret ? 0 : -1;
482 : }
483 : }
484 : LeaveCriticalSection(&cs);
485 : /* didn't find the locked file, try opening the file
486 : * directly */
487 : fh = CreateFileW(wfilename,
488 : GENERIC_READ | GENERIC_WRITE, 0,
489 : NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, NULL);
490 : free(wfilename);
491 : if (fh == INVALID_HANDLE_VALUE)
492 : return -2;
493 : ret = UnlockFileEx(fh, 0, 1, 0, &ov);
494 : CloseHandle(fh);
495 : return 0;
496 : }
497 :
498 : if (_wsopen_s(&fd, wfilename, _O_CREAT | _O_RDWR | _O_TEXT, _SH_DENYNO, _S_IREAD | _S_IWRITE) != 0) {
499 : free(wfilename);
500 : return -2;
501 : }
502 : fh = (HANDLE) _get_osfhandle(fd);
503 : if (fh == INVALID_HANDLE_VALUE) {
504 : close(fd);
505 : free(wfilename);
506 : return -2;
507 : }
508 :
509 : if (mode == F_TLOCK) {
510 : ret = LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov);
511 : } else if (mode == F_LOCK) {
512 : ret = LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov);
513 : } else if (mode == F_TEST) {
514 : ret = LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov);
515 : if (ret != 0) {
516 : UnlockFileEx(fh, 0, 1, 0, &ov);
517 : close(fd);
518 : free(wfilename);
519 : return 0;
520 : }
521 : } else {
522 : close(fd);
523 : errno = EINVAL;
524 : free(wfilename);
525 : return -2;
526 : }
527 : if (ret != 0) {
528 : if ((fp = malloc(sizeof(*fp))) != NULL) {
529 : fp->wfilename = wfilename;
530 : fp->fildes = fd;
531 : EnterCriticalSection(&cs);
532 : fp->next = lockedfiles;
533 : lockedfiles = fp;
534 : LeaveCriticalSection(&cs);
535 : } else {
536 : free(wfilename);
537 : }
538 : return fd;
539 : } else {
540 : close(fd);
541 : free(wfilename);
542 : return -1;
543 : }
544 : }
545 :
546 : FILE *
547 : MT_fopen(const char *filename, const char *mode)
548 : {
549 : wchar_t *wfilename, *wmode;
550 : wfilename = utf8towchar(filename);
551 : wmode = utf8towchar(mode);
552 : FILE *f = NULL;
553 : if (wfilename != NULL && wmode != NULL && (f = _wfopen(wfilename, wmode)) != NULL && strchr(mode, 'w') != NULL)
554 : SetFileAttributesW(wfilename, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
555 : free(wfilename);
556 : free(wmode);
557 : return f;
558 : }
559 :
560 : int
561 : MT_open(const char *filename, int flags)
562 : {
563 : wchar_t *wfilename = utf8towchar(filename);
564 : if (wfilename == NULL)
565 : return -1;
566 : int fd;
567 : if (_wsopen_s(&fd, wfilename, flags, _SH_DENYNO, _S_IREAD | _S_IWRITE) != 0)
568 : fd = -1;
569 : else if (flags & O_CREAT)
570 : SetFileAttributesW(wfilename, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
571 : free(wfilename);
572 : return fd;
573 : }
574 :
575 : int
576 : MT_stat(const char *pathname, struct _stat64 *st)
577 : {
578 : wchar_t *wpathname = utf8towchar(pathname);
579 : int ret;
580 : if (wpathname == NULL)
581 : return -1;
582 :
583 : ret = _wstat64(wpathname, st);
584 : free(wpathname);
585 : return ret;
586 : }
587 :
588 : #define RETRIES 10
589 : #define SLEEPTIME 20
590 :
591 : int
592 : MT_rmdir(const char *pathname)
593 : {
594 : wchar_t *wpathname = utf8towchar(pathname);
595 : int ret;
596 : if (wpathname == NULL)
597 : return -1;
598 :
599 : for (int i = 0; i < RETRIES; i++) {
600 : ret = _wrmdir(wpathname);
601 : if (ret == 0 || errno == ENOENT)
602 : break;
603 : /* it could be the <expletive deleted> indexing
604 : * service which prevents us from doing what we have a
605 : * right to do, so try again (once) */
606 : // fprintf(stderr, "#Retry rmdir %s\n", pathname);
607 : Sleep(SLEEPTIME); /* wait a little */
608 : }
609 : free(wpathname);
610 : return ret;
611 : }
612 :
613 : static inline int
614 : WMT_remove(const wchar_t *wpathname)
615 : {
616 : int ret;
617 :
618 : SetFileAttributesW(wpathname, FILE_ATTRIBUTE_NORMAL);
619 : for (int i = 0; i < RETRIES; i++) {
620 : ret = _wunlink(wpathname);
621 : if (ret == 0 || errno == ENOENT)
622 : break;
623 : /* it could be the <expletive deleted> indexing
624 : * service which prevents us from doing what we have a
625 : * right to do, so try again (once) */
626 : // fprintf(stderr, "#Retry unlink %ls\n", wpathname);
627 : Sleep(SLEEPTIME); /* wait a little */
628 : }
629 : return ret;
630 : }
631 :
632 : int
633 : MT_remove(const char *pathname)
634 : {
635 : wchar_t *wpathname = utf8towchar(pathname);
636 : int ret;
637 : if (wpathname == NULL)
638 : return -1;
639 :
640 : ret = WMT_remove(wpathname);
641 : free(wpathname);
642 : return ret;
643 : }
644 :
645 : int
646 : MT_rename(const char *old, const char *dst)
647 : {
648 : int ret = -1;
649 : wchar_t *wold, *wdst;
650 : wold = utf8towchar(old);
651 : wdst = utf8towchar(dst);
652 :
653 : if (wold && wdst) {
654 : for (int i = 0; i < RETRIES; i++) {
655 : ret = _wrename(wold, wdst);
656 : if (ret < 0 && errno == EEXIST) {
657 : if ((ret = WMT_remove(wdst)) < 0 &&
658 : errno != ENOENT)
659 : break;
660 : ret = _wrename(wold, wdst);
661 : }
662 : if (ret == 0 || errno == ENOENT)
663 : break;
664 : /* it could be the <expletive deleted> indexing
665 : * service which prevents us from doing what we have a
666 : * right to do, so try again (once) */
667 : // fprintf(stderr, "#Retry rename %s %s\n", old, dst);
668 : Sleep(SLEEPTIME); /* wait a little */
669 : }
670 : }
671 : free(wold);
672 : free(wdst);
673 : return ret;
674 : }
675 :
676 : int
677 : MT_mkdir(const char *pathname)
678 : {
679 : wchar_t *wpathname = utf8towchar(pathname);
680 : if (wpathname == NULL)
681 : return -1;
682 : int ret = _wmkdir(wpathname);
683 : if (ret == 0)
684 : SetFileAttributesW(wpathname, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
685 : free(wpathname);
686 : return ret;
687 : }
688 :
689 : char *
690 : MT_getcwd(char *buffer, size_t size)
691 : {
692 : wchar_t *wcwd = _wgetcwd(NULL, 0);
693 : if (wcwd == NULL)
694 : return NULL;
695 : char *cwd = wchartoutf8(wcwd);
696 : free(wcwd);
697 : if (cwd == NULL)
698 : return NULL;
699 : size_t len = strcpy_len(buffer, cwd, size);
700 : free(cwd);
701 : return len < size ? buffer : NULL;
702 : }
703 :
704 : int
705 : MT_access(const char *pathname, int mode)
706 : {
707 : wchar_t *wpathname = utf8towchar(pathname);
708 : if (wpathname == NULL)
709 : return -1;
710 : int ret = _waccess(wpathname, mode);
711 : free(wpathname);
712 : return ret;
713 : }
714 :
715 : #else
716 :
717 : #if defined(HAVE_LOCKF) && defined(__MACH__)
718 : /* lockf() seems to be there, but I didn't find any header file that
719 : declares the prototype ... */
720 : extern int lockf(int fd, int cmd, off_t len);
721 : #endif
722 :
723 : #ifndef HAVE_LOCKF
724 : /* Cygwin implementation: struct flock is there, but lockf() is
725 : missing.
726 : */
727 : static int
728 : lockf(int fd, int cmd, off_t len)
729 : {
730 : struct flock l;
731 :
732 : if (cmd == F_LOCK || cmd == F_TLOCK)
733 : l.l_type = F_WRLCK;
734 : else if (cmd == F_ULOCK)
735 : l.l_type = F_UNLCK;
736 : l.l_whence = SEEK_CUR;
737 : l.l_start = 0;
738 : l.l_len = len;
739 : return fcntl(fd, cmd == F_TLOCK ? F_SETLKW : F_SETLK, &l);
740 : }
741 : #endif
742 :
743 : #include <pthread.h>
744 :
745 : #ifndef O_TEXT
746 : #define O_TEXT 0
747 : #endif
748 : /* returns -1 when locking failed,
749 : * returns -2 when the lock file could not be opened/created
750 : * returns 0 when mode is F_TEST and the lock file was not locked
751 : * returns the (open) file descriptor to the file when locking
752 : * returns 0 when unlocking */
753 : int
754 238638 : MT_lockf(const char *filename, int mode)
755 : {
756 238638 : static struct lockfile {
757 : char *filename;
758 : int fd;
759 : struct lockfile *next;
760 : } *lockfiles = NULL;
761 238638 : static pthread_mutex_t cs = PTHREAD_MUTEX_INITIALIZER;
762 238638 : struct lockfile *fp;
763 238638 : int fd;
764 238638 : off_t seek;
765 :
766 238638 : if (mode == F_ULOCK) {
767 119317 : pthread_mutex_lock(&cs);
768 119483 : for (struct lockfile **fpp = &lockfiles; (fp = *fpp) != NULL; fpp = &fp->next) {
769 119483 : if (strcmp(fp->filename, filename) == 0) {
770 119317 : *fpp = fp->next;
771 119317 : pthread_mutex_unlock(&cs);
772 119317 : free(fp->filename);
773 119317 : fd = fp->fd;
774 119317 : free(fp);
775 119317 : seek = lseek(fd, 4, SEEK_SET);
776 119317 : if (seek < 0)
777 : seek = 0; /* should never happen, just for coverity */
778 119317 : int ret = lockf(fd, mode, 1);
779 119317 : (void) lseek(fd, seek, SEEK_SET); /* move seek pointer back */
780 : /* do not close fd, it is closed by caller */
781 119317 : return ret; /* 0 if unlock successful, -1 if not */
782 : }
783 : }
784 0 : pthread_mutex_unlock(&cs);
785 : }
786 119321 : fd = open(filename, O_CREAT | O_RDWR | O_TEXT | O_CLOEXEC, MONETDB_MODE);
787 :
788 119321 : if (fd < 0)
789 : return -2;
790 :
791 238642 : if ((seek = lseek(fd, 4, SEEK_SET)) >= 0 &&
792 119321 : lockf(fd, mode, 1) == 0) {
793 119319 : if (mode == F_ULOCK || mode == F_TEST) {
794 0 : close(fd);
795 0 : return 0;
796 : }
797 119319 : if ((fp = malloc(sizeof(*fp))) != NULL) {
798 119319 : if ((fp->filename = strdup(filename)) != NULL) {
799 119319 : fp->fd = fd;
800 119319 : pthread_mutex_lock(&cs);
801 119319 : fp->next = lockfiles;
802 119319 : lockfiles = fp;
803 119319 : pthread_mutex_unlock(&cs);
804 : } else {
805 0 : free(fp);
806 : }
807 : }
808 : /* do not close else we lose the lock we want */
809 119319 : (void) lseek(fd, seek, SEEK_SET); /* move seek pointer back */
810 119319 : return fd;
811 : }
812 2 : close(fd);
813 2 : return -1;
814 : }
815 :
816 : #endif
817 :
818 : #ifndef PATH_MAX
819 : # define PATH_MAX 1024
820 : #endif
821 : static char _bin_path[PATH_MAX];
822 : char *
823 1552 : get_bin_path(void)
824 : {
825 : /* getting the path to the executable's binary, isn't all that
826 : * simple, unfortunately */
827 : #ifdef NATIVE_WIN32
828 : static wchar_t wbin_path[PATH_MAX];
829 : if (GetModuleFileNameW(NULL, wbin_path, PATH_MAX) != 0) {
830 : char *path = wchartoutf8(wbin_path);
831 : size_t len = strcpy_len(_bin_path, path, PATH_MAX);
832 : free(path);
833 : if (len < PATH_MAX)
834 : return _bin_path;
835 : }
836 : #elif defined(HAVE__NSGETEXECUTABLEPATH) /* Darwin/OSX */
837 : char buf[PATH_MAX];
838 : uint32_t size = PATH_MAX;
839 : if (_NSGetExecutablePath(buf, &size) == 0 &&
840 : realpath(buf, _bin_path) != NULL)
841 : return _bin_path;
842 : #elif defined(BSD) && defined(KERN_PROC_PATHNAME) /* BSD */
843 : int mib[4];
844 : size_t cb = sizeof(_bin_path);
845 : mib[0] = CTL_KERN;
846 : mib[1] = KERN_PROC;
847 : mib[2] = KERN_PROC_PATHNAME;
848 : mib[3] = -1;
849 : if (sysctl(mib, 4, _bin_path, &cb, NULL, 0) == 0)
850 : return _bin_path;
851 : #elif defined(HAVE_GETEXECNAME) /* Solaris */
852 : char buf[PATH_MAX];
853 : const char *execn = getexecname();
854 : /* getexecname doesn't always return an absolute path, the only
855 : * thing it seems to do is strip leading ./ from the invocation
856 : * string. */
857 : if (*execn != '/') {
858 : if (getcwd(buf, PATH_MAX) != NULL) {
859 : snprintf(buf + strlen(buf), PATH_MAX - strlen(buf), "/%s", execn);
860 : if (realpath(buf, _bin_path) != NULL)
861 : return(_bin_path);
862 : }
863 : } else {
864 : if (realpath(execn, _bin_path) != NULL)
865 : return(_bin_path);
866 : }
867 : #else /* try Linux approach, also works on Cygwin */
868 1552 : ssize_t n;
869 1552 : if ((n = readlink("/proc/self/exe", _bin_path, sizeof(_bin_path))) != -1
870 1552 : && (size_t) n < sizeof(_bin_path)) {
871 1552 : _bin_path[n] = 0;
872 1552 : return _bin_path;
873 : }
874 : #endif
875 : /* could use argv[0] (passed) to deduce location based on PATH, but
876 : * that's a lot of work and unreliable */
877 : return NULL;
878 : }
|