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. */ |
44 | typedef unsigned int u_int32_t; |
45 | typedef 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 */ |
51 | int |
52 | winerror(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 | |
119 | DIR * |
120 | opendir(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 | |
181 | static char * |
182 | basename(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 | |
203 | struct dirent * |
204 | readdir(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 | |
224 | void |
225 | rewinddir(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 | |
251 | int |
252 | closedir(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 | |
269 | char * |
270 | dirname(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 */ |
284 | int |
285 | MT_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 ... */ |
383 | extern 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 | */ |
390 | static int |
391 | lockf(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 */ |
414 | int |
415 | MT_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 |
441 | static char _bin_path[PATH_MAX]; |
442 | char * |
443 | get_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 | |