| 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 | |