1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9#include "monetdb_config.h"
10#include <sys/types.h>
11#include <sys/stat.h>
12#include <fcntl.h>
13#include <unistd.h>
14#include <string.h>
15#include "mutils.h"
16#include "mstring.h"
17
18#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
19#include <execinfo.h>
20#endif
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#define O_CLOEXEC 0
38#endif
39
40#ifdef NATIVE_WIN32
41
42/* Some definitions that we need to compile on Windows.
43 * Note that Windows only runs on little endian architectures. */
44typedef unsigned int u_int32_t;
45typedef int int32_t;
46#define BIG_ENDIAN 4321
47#define LITTLE_ENDIAN 1234
48#define BYTE_ORDER LITTLE_ENDIAN
49
50/* translate Windows error code (GetLastError()) to Unix-style error */
51int
52winerror(int e)
53{
54 switch (e) {
55 case ERROR_BAD_ENVIRONMENT:
56 return E2BIG;
57 case ERROR_ACCESS_DENIED:
58 case ERROR_CANNOT_MAKE:
59 case ERROR_CURRENT_DIRECTORY:
60 case ERROR_DRIVE_LOCKED:
61 case ERROR_FAIL_I24:
62 case ERROR_LOCK_FAILED:
63 case ERROR_LOCK_VIOLATION:
64 case ERROR_NETWORK_ACCESS_DENIED:
65 case ERROR_NOT_LOCKED:
66 case ERROR_SEEK_ON_DEVICE:
67 return EACCES;
68 case ERROR_MAX_THRDS_REACHED:
69 case ERROR_NESTING_NOT_ALLOWED:
70 case ERROR_NO_PROC_SLOTS:
71 return EAGAIN;
72 case ERROR_DIRECT_ACCESS_HANDLE:
73 case ERROR_INVALID_TARGET_HANDLE:
74 return EBADF;
75 case ERROR_CHILD_NOT_COMPLETE:
76 case ERROR_WAIT_NO_CHILDREN:
77 return ECHILD;
78 case ERROR_ALREADY_EXISTS:
79 case ERROR_FILE_EXISTS:
80 return EEXIST;
81 case ERROR_INVALID_ACCESS:
82 case ERROR_INVALID_DATA:
83 case ERROR_INVALID_FUNCTION:
84 case ERROR_INVALID_HANDLE:
85 case ERROR_INVALID_PARAMETER:
86 case ERROR_NEGATIVE_SEEK:
87 return EINVAL;
88 case ERROR_TOO_MANY_OPEN_FILES:
89 return EMFILE;
90 case ERROR_BAD_NET_NAME:
91 case ERROR_BAD_NETPATH:
92 case ERROR_BAD_PATHNAME:
93 case ERROR_FILE_NOT_FOUND:
94 case ERROR_FILENAME_EXCED_RANGE:
95 case ERROR_INVALID_DRIVE:
96 case ERROR_NO_MORE_FILES:
97 case ERROR_PATH_NOT_FOUND:
98 return ENOENT;
99 case ERROR_BAD_FORMAT:
100 return ENOEXEC;
101 case ERROR_ARENA_TRASHED:
102 case ERROR_INVALID_BLOCK:
103 case ERROR_NOT_ENOUGH_MEMORY:
104 case ERROR_NOT_ENOUGH_QUOTA:
105 return ENOMEM;
106 case ERROR_DISK_FULL:
107 return ENOSPC;
108 case ERROR_DIR_NOT_EMPTY:
109 return ENOTEMPTY;
110 case ERROR_BROKEN_PIPE:
111 return EPIPE;
112 case ERROR_NOT_SAME_DEVICE:
113 return EXDEV;
114 default:
115 return EINVAL;
116 }
117}
118
119DIR *
120opendir(const char *dirname)
121{
122 DIR *result = NULL;
123 char *mask;
124 size_t k;
125 DWORD e;
126
127 if (dirname == NULL) {
128 errno = EFAULT;
129 return NULL;
130 }
131
132 result = (DIR *) malloc(sizeof(DIR));
133 if (result == NULL) {
134 errno = ENOMEM;
135 return NULL;
136 }
137 result->find_file_data = malloc(sizeof(WIN32_FIND_DATA));
138 result->dir_name = strdup(dirname);
139 if (result->find_file_data == NULL || result->dir_name == NULL) {
140 if (result->find_file_data)
141 free(result->find_file_data);
142 if (result->dir_name)
143 free(result->dir_name);
144 free(result);
145 errno = ENOMEM;
146 return NULL;
147 }
148
149 k = strlen(result->dir_name);
150 if (k && result->dir_name[k - 1] == '\\') {
151 result->dir_name[k - 1] = '\0';
152 k--;
153 }
154 mask = malloc(strlen(result->dir_name) + 3);
155 if (mask == NULL) {
156 free(result->find_file_data);
157 free(result->dir_name);
158 free(result);
159 errno = ENOMEM;
160 return NULL;
161 }
162 sprintf(mask, "%s\\*", result->dir_name);
163
164 result->find_file_handle = FindFirstFile(mask, (LPWIN32_FIND_DATA) result->find_file_data);
165 if (result->find_file_handle == INVALID_HANDLE_VALUE) {
166 e = GetLastError();
167 free(mask);
168 free(result->dir_name);
169 free(result->find_file_data);
170 free(result);
171 SetLastError(e);
172 errno = winerror(e);
173 return NULL;
174 }
175 free(mask);
176 result->just_opened = TRUE;
177
178 return result;
179}
180
181static char *
182basename(const char *file_name)
183{
184 const char *p;
185 const char *base;
186
187 if (file_name == NULL)
188 return NULL;
189
190 if (isalpha((unsigned char) file_name[0]) && file_name[1] == ':')
191 file_name += 2; /* skip over drive letter */
192
193 base = NULL;
194 for (p = file_name; *p; p++)
195 if (*p == '\\' || *p == '/')
196 base = p;
197 if (base)
198 return (char *) base + 1;
199
200 return (char *) file_name;
201}
202
203struct dirent *
204readdir(DIR *dir)
205{
206 static struct dirent result;
207
208 if (dir == NULL) {
209 errno = EFAULT;
210 return NULL;
211 }
212
213 if (dir->just_opened)
214 dir->just_opened = FALSE;
215 else if (!FindNextFile(dir->find_file_handle,
216 (LPWIN32_FIND_DATA) dir->find_file_data))
217 return NULL;
218 strcpy_len(result.d_name, basename(((LPWIN32_FIND_DATA) dir->find_file_data)->cFileName), sizeof(result.d_name));
219 result.d_namelen = (int) strlen(result.d_name);
220
221 return &result;
222}
223
224void
225rewinddir(DIR *dir)
226{
227 char *mask;
228
229 if (dir == NULL) {
230 errno = EFAULT;
231 return;
232 }
233
234 if (!FindClose(dir->find_file_handle))
235 fprintf(stderr, "#rewinddir(): FindClose() failed\n");
236
237 mask = malloc(strlen(dir->dir_name) + 3);
238 if (mask == NULL) {
239 errno = ENOMEM;
240 dir->find_file_handle = INVALID_HANDLE_VALUE;
241 return;
242 }
243 sprintf(mask, "%s\\*", dir->dir_name);
244 dir->find_file_handle = FindFirstFile(mask, (LPWIN32_FIND_DATA) dir->find_file_data);
245 free(mask);
246 if (dir->find_file_handle == INVALID_HANDLE_VALUE)
247 return;
248 dir->just_opened = TRUE;
249}
250
251int
252closedir(DIR *dir)
253{
254 if (dir == NULL) {
255 errno = EFAULT;
256 return -1;
257 }
258
259 if (!FindClose(dir->find_file_handle))
260 return -1;
261
262 free(dir->dir_name);
263 free(dir->find_file_data);
264 free(dir);
265
266 return 0;
267}
268
269char *
270dirname(char *path)
271{
272 char *p, *q;
273
274 for (p = path, q = NULL; *p; p++)
275 if (*p == '/' || *p == '\\')
276 q = p;
277 if (q == NULL)
278 return ".";
279 *q = '\0';
280 return path;
281}
282
283/* see contract of unix MT_lockf */
284int
285MT_lockf(char *filename, int mode, off_t off, off_t len)
286{
287 int ret = 1, fd = -1;
288 OVERLAPPED ov;
289 HANDLE fh;
290 static struct lockedfiles {
291 struct lockedfiles *next;
292 char *filename;
293 int fildes;
294 } *lockedfiles;
295 struct lockedfiles **fpp, *fp;
296
297 ov = (OVERLAPPED) {0};
298#if defined(DUMMYSTRUCTNAME) && (defined(NONAMELESSUNION) || !defined(_MSC_EXTENSIONS)) /* Windows SDK v7.0 */
299 ov.u.s.Offset = (unsigned int) off;
300#if 0
301 ov.u.s.OffsetHigh = off >> 32;
302#else
303 ov.u.s.OffsetHigh = 0; /* sizeof(off) == 4, i.e. off >> 32 is not possible */
304#endif
305#else
306 ov.Offset = (unsigned int) off;
307#if 0
308 ov.OffsetHigh = off >> 32;
309#else
310 ov.OffsetHigh = 0; /* sizeof(off) == 4, i.e. off >> 32 is not possible */
311#endif
312#endif
313
314 if (mode == F_ULOCK) {
315 for (fpp = &lockedfiles; (fp = *fpp) != NULL; fpp = &fp->next) {
316 if (strcmp(fp->filename, filename) == 0) {
317 free(fp->filename);
318 fd = fp->fildes;
319 fh = (HANDLE) _get_osfhandle(fd);
320 fp = *fpp;
321 *fpp = fp->next;
322 free(fp);
323 ret = UnlockFileEx(fh, 0, len, 0, &ov);
324 return ret ? 0 : -1;
325 }
326 }
327 /* didn't find the locked file, try opening the file
328 * directly */
329 fh = CreateFile(filename,
330 GENERIC_READ | GENERIC_WRITE, 0,
331 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
332 if (fh == INVALID_HANDLE_VALUE)
333 return -2;
334 ret = UnlockFileEx(fh, 0, len, 0, &ov);
335 CloseHandle(fh);
336 return 0;
337 }
338
339 fd = open(filename, O_CREAT | O_RDWR | O_TEXT | O_CLOEXEC, MONETDB_MODE);
340 if (fd < 0)
341 return -2;
342 fh = (HANDLE) _get_osfhandle(fd);
343 if (fh == INVALID_HANDLE_VALUE) {
344 close(fd);
345 return -2;
346 }
347
348 if (mode == F_TLOCK) {
349 ret = LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, len, 0, &ov);
350 } else if (mode == F_LOCK) {
351 ret = LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, len, 0, &ov);
352 } else if (mode == F_TEST) {
353 ret = LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, len, 0, &ov);
354 if (ret != 0) {
355 UnlockFileEx(fh, 0, len, 0, &ov);
356 close(fd);
357 return 0;
358 }
359 } else {
360 close(fd);
361 errno = EINVAL;
362 return -2;
363 }
364 if (ret != 0) {
365 if ((fp = malloc(sizeof(*fp))) != NULL &&
366 (fp->filename = strdup(filename)) != NULL) {
367 fp->fildes = fd;
368 fp->next = lockedfiles;
369 lockedfiles = fp;
370 }
371 return fd;
372 } else {
373 close(fd);
374 return -1;
375 }
376}
377
378#else
379
380#if defined(HAVE_LOCKF) && defined(__MACH__)
381/* lockf() seems to be there, but I didn't find any header file that
382 declares the prototype ... */
383extern int lockf(int fd, int cmd, off_t len);
384#endif
385
386#ifndef HAVE_LOCKF
387/* Cygwin implementation: struct flock is there, but lockf() is
388 missing.
389 */
390static int
391lockf(int fd, int cmd, off_t len)
392{
393 struct flock l;
394
395 if (cmd == F_LOCK || cmd == F_TLOCK)
396 l.l_type = F_WRLCK;
397 else if (cmd == F_ULOCK)
398 l.l_type = F_UNLCK;
399 l.l_whence = SEEK_CUR;
400 l.l_start = 0;
401 l.l_len = len;
402 return fcntl(fd, cmd == F_TLOCK ? F_SETLKW : F_SETLK, &l);
403}
404#endif
405
406#ifndef O_TEXT
407#define O_TEXT 0
408#endif
409/* returns -1 when locking failed,
410 * returns -2 when the lock file could not be opened/created
411 * returns 0 when mode is F_TEST and the lock file was not locked
412 * returns the (open) file descriptor to the file when locking
413 * returns 0 when unlocking */
414int
415MT_lockf(char *filename, int mode, off_t off, off_t len)
416{
417 int fd = open(filename, O_CREAT | O_RDWR | O_TEXT | O_CLOEXEC, MONETDB_MODE);
418
419 if (fd < 0)
420 return -2;
421
422 if (lseek(fd, off, SEEK_SET) >= 0 &&
423 lockf(fd, mode, len) == 0) {
424 if (mode == F_ULOCK || mode == F_TEST) {
425 close(fd);
426 return 0;
427 }
428 /* do not close else we lose the lock we want */
429 (void) lseek(fd, 0, SEEK_SET); /* move seek pointer back */
430 return fd;
431 }
432 close(fd);
433 return -1;
434}
435
436#endif
437
438#ifndef PATH_MAX
439# define PATH_MAX 1024
440#endif
441static char _bin_path[PATH_MAX];
442char *
443get_bin_path(void)
444{
445 /* getting the path to the executable's binary, isn't all that
446 * simple, unfortunately */
447#ifdef NATIVE_WIN32
448 if (GetModuleFileName(NULL, _bin_path,
449 (DWORD) sizeof(_bin_path)) != 0)
450 return _bin_path;
451#elif defined(HAVE__NSGETEXECUTABLEPATH) /* Darwin/OSX */
452 char buf[PATH_MAX];
453 uint32_t size = PATH_MAX;
454 if (_NSGetExecutablePath(buf, &size) == 0 &&
455 realpath(buf, _bin_path) != NULL)
456 return _bin_path;
457#elif defined(BSD) && defined(KERN_PROC_PATHNAME) /* BSD */
458 int mib[4];
459 size_t cb = sizeof(_bin_path);
460 mib[0] = CTL_KERN;
461 mib[1] = KERN_PROC;
462 mib[2] = KERN_PROC_PATHNAME;
463 mib[3] = -1;
464 if (sysctl(mib, 4, _bin_path, &cb, NULL, 0) == 0)
465 return _bin_path;
466#elif defined(HAVE_GETEXECNAME) /* Solaris */
467 char buf[PATH_MAX];
468 const char *execn = getexecname();
469 /* getexecname doesn't always return an absolute path, the only
470 * thing it seems to do is strip leading ./ from the invocation
471 * string. */
472 if (*execn != '/') {
473 if (getcwd(buf, PATH_MAX) != NULL) {
474 snprintf(buf + strlen(buf), PATH_MAX - strlen(buf), "/%s", execn);
475 if (realpath(buf, _bin_path) != NULL)
476 return(_bin_path);
477 }
478 } else {
479 if (realpath(execn, _bin_path) != NULL)
480 return(_bin_path);
481 }
482#else /* try Linux approach, also works on Cygwin */
483 if (readlink("/proc/self/exe",
484 _bin_path, sizeof(_bin_path)) != -1)
485 return _bin_path;
486#endif
487 /* could use argv[0] (passed) to deduce location based on PATH, but
488 * that's a lot of work and unreliable */
489 return NULL;
490}
491