1 //===-- File.cpp ------------------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "lldb/Host/File.h" 10 11 #include <errno.h> 12 #include <fcntl.h> 13 #include <limits.h> 14 #include <stdarg.h> 15 #include <stdio.h> 16 17 #ifdef _WIN32 18 #include "lldb/Host/windows/windows.h" 19 #else 20 #include <sys/ioctl.h> 21 #include <sys/stat.h> 22 #include <termios.h> 23 #include <unistd.h> 24 #endif 25 26 #include "llvm/Support/ConvertUTF.h" 27 #include "llvm/Support/Errno.h" 28 #include "llvm/Support/FileSystem.h" 29 #include "llvm/Support/Process.h" 30 31 #include "lldb/Host/Config.h" 32 #include "lldb/Host/FileSystem.h" 33 #include "lldb/Host/Host.h" 34 #include "lldb/Utility/DataBufferHeap.h" 35 #include "lldb/Utility/FileSpec.h" 36 #include "lldb/Utility/Log.h" 37 38 using namespace lldb; 39 using namespace lldb_private; 40 using llvm::Expected; 41 42 Expected<const char *> 43 File::GetStreamOpenModeFromOptions(File::OpenOptions options) { 44 if (options & File::eOpenOptionAppend) { 45 if (options & File::eOpenOptionRead) { 46 if (options & File::eOpenOptionCanCreateNewOnly) 47 return "a+x"; 48 else 49 return "a+"; 50 } else if (options & File::eOpenOptionWrite) { 51 if (options & File::eOpenOptionCanCreateNewOnly) 52 return "ax"; 53 else 54 return "a"; 55 } 56 } else if (options & File::eOpenOptionRead && 57 options & File::eOpenOptionWrite) { 58 if (options & File::eOpenOptionCanCreate) { 59 if (options & File::eOpenOptionCanCreateNewOnly) 60 return "w+x"; 61 else 62 return "w+"; 63 } else 64 return "r+"; 65 } else if (options & File::eOpenOptionRead) { 66 return "r"; 67 } else if (options & File::eOpenOptionWrite) { 68 return "w"; 69 } 70 return llvm::createStringError( 71 llvm::inconvertibleErrorCode(), 72 "invalid options, cannot convert to mode string"); 73 } 74 75 Expected<File::OpenOptions> File::GetOptionsFromMode(llvm::StringRef mode) { 76 OpenOptions opts = 77 llvm::StringSwitch<OpenOptions>(mode) 78 .Cases("r", "rb", eOpenOptionRead) 79 .Cases("w", "wb", eOpenOptionWrite) 80 .Cases("a", "ab", 81 eOpenOptionWrite | eOpenOptionAppend | eOpenOptionCanCreate) 82 .Cases("r+", "rb+", "r+b", eOpenOptionRead | eOpenOptionWrite) 83 .Cases("w+", "wb+", "w+b", 84 eOpenOptionRead | eOpenOptionWrite | eOpenOptionCanCreate | 85 eOpenOptionTruncate) 86 .Cases("a+", "ab+", "a+b", 87 eOpenOptionRead | eOpenOptionWrite | eOpenOptionAppend | 88 eOpenOptionCanCreate) 89 .Default(OpenOptions()); 90 if (opts) 91 return opts; 92 return llvm::createStringError( 93 llvm::inconvertibleErrorCode(), 94 "invalid mode, cannot convert to File::OpenOptions"); 95 } 96 97 int File::kInvalidDescriptor = -1; 98 FILE *File::kInvalidStream = nullptr; 99 100 Status File::Read(void *buf, size_t &num_bytes) { 101 return std::error_code(ENOTSUP, std::system_category()); 102 } 103 Status File::Write(const void *buf, size_t &num_bytes) { 104 return std::error_code(ENOTSUP, std::system_category()); 105 } 106 107 bool File::IsValid() const { return false; } 108 109 Status File::Close() { return Flush(); } 110 111 IOObject::WaitableHandle File::GetWaitableHandle() { 112 return IOObject::kInvalidHandleValue; 113 } 114 115 Status File::GetFileSpec(FileSpec &file_spec) const { 116 file_spec.Clear(); 117 return std::error_code(ENOTSUP, std::system_category()); 118 } 119 120 FILE *File::TakeStreamAndClear() { return nullptr; } 121 122 int File::GetDescriptor() const { return kInvalidDescriptor; } 123 124 FILE *File::GetStream() { return nullptr; } 125 126 off_t File::SeekFromStart(off_t offset, Status *error_ptr) { 127 if (error_ptr) 128 *error_ptr = std::error_code(ENOTSUP, std::system_category()); 129 return -1; 130 } 131 132 off_t File::SeekFromCurrent(off_t offset, Status *error_ptr) { 133 if (error_ptr) 134 *error_ptr = std::error_code(ENOTSUP, std::system_category()); 135 return -1; 136 } 137 138 off_t File::SeekFromEnd(off_t offset, Status *error_ptr) { 139 if (error_ptr) 140 *error_ptr = std::error_code(ENOTSUP, std::system_category()); 141 return -1; 142 } 143 144 Status File::Read(void *dst, size_t &num_bytes, off_t &offset) { 145 return std::error_code(ENOTSUP, std::system_category()); 146 } 147 148 Status File::Write(const void *src, size_t &num_bytes, off_t &offset) { 149 return std::error_code(ENOTSUP, std::system_category()); 150 } 151 152 Status File::Flush() { return Status(); } 153 154 Status File::Sync() { return Flush(); } 155 156 void File::CalculateInteractiveAndTerminal() { 157 const int fd = GetDescriptor(); 158 if (!DescriptorIsValid(fd)) { 159 m_is_interactive = eLazyBoolNo; 160 m_is_real_terminal = eLazyBoolNo; 161 m_supports_colors = eLazyBoolNo; 162 return; 163 } 164 m_is_interactive = eLazyBoolNo; 165 m_is_real_terminal = eLazyBoolNo; 166 #if defined(_WIN32) 167 if (_isatty(fd)) { 168 m_is_interactive = eLazyBoolYes; 169 m_is_real_terminal = eLazyBoolYes; 170 #if defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) 171 m_supports_colors = eLazyBoolYes; 172 #endif 173 } 174 #else 175 if (isatty(fd)) { 176 m_is_interactive = eLazyBoolYes; 177 struct winsize window_size; 178 if (::ioctl(fd, TIOCGWINSZ, &window_size) == 0) { 179 if (window_size.ws_col > 0) { 180 m_is_real_terminal = eLazyBoolYes; 181 if (llvm::sys::Process::FileDescriptorHasColors(fd)) 182 m_supports_colors = eLazyBoolYes; 183 } 184 } 185 } 186 #endif 187 } 188 189 bool File::GetIsInteractive() { 190 if (m_is_interactive == eLazyBoolCalculate) 191 CalculateInteractiveAndTerminal(); 192 return m_is_interactive == eLazyBoolYes; 193 } 194 195 bool File::GetIsRealTerminal() { 196 if (m_is_real_terminal == eLazyBoolCalculate) 197 CalculateInteractiveAndTerminal(); 198 return m_is_real_terminal == eLazyBoolYes; 199 } 200 201 bool File::GetIsTerminalWithColors() { 202 if (m_supports_colors == eLazyBoolCalculate) 203 CalculateInteractiveAndTerminal(); 204 return m_supports_colors == eLazyBoolYes; 205 } 206 207 size_t File::Printf(const char *format, ...) { 208 va_list args; 209 va_start(args, format); 210 size_t result = PrintfVarArg(format, args); 211 va_end(args); 212 return result; 213 } 214 215 size_t File::PrintfVarArg(const char *format, va_list args) { 216 size_t result = 0; 217 char *s = nullptr; 218 result = vasprintf(&s, format, args); 219 if (s != nullptr) { 220 if (result > 0) { 221 size_t s_len = result; 222 Write(s, s_len); 223 result = s_len; 224 } 225 free(s); 226 } 227 return result; 228 } 229 230 Expected<File::OpenOptions> File::GetOptions() const { 231 return llvm::createStringError( 232 llvm::inconvertibleErrorCode(), 233 "GetOptions() not implemented for this File class"); 234 } 235 236 uint32_t File::GetPermissions(Status &error) const { 237 int fd = GetDescriptor(); 238 if (!DescriptorIsValid(fd)) { 239 error = std::error_code(ENOTSUP, std::system_category()); 240 return 0; 241 } 242 struct stat file_stats; 243 if (::fstat(fd, &file_stats) == -1) { 244 error.SetErrorToErrno(); 245 return 0; 246 } 247 error.Clear(); 248 return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); 249 } 250 251 Expected<File::OpenOptions> NativeFile::GetOptions() const { return m_options; } 252 253 int NativeFile::GetDescriptor() const { 254 if (DescriptorIsValid()) 255 return m_descriptor; 256 257 // Don't open the file descriptor if we don't need to, just get it from the 258 // stream if we have one. 259 if (StreamIsValid()) { 260 #if defined(_WIN32) 261 return _fileno(m_stream); 262 #else 263 return fileno(m_stream); 264 #endif 265 } 266 267 // Invalid descriptor and invalid stream, return invalid descriptor. 268 return kInvalidDescriptor; 269 } 270 271 IOObject::WaitableHandle NativeFile::GetWaitableHandle() { 272 return GetDescriptor(); 273 } 274 275 FILE *NativeFile::GetStream() { 276 if (!StreamIsValid()) { 277 if (DescriptorIsValid()) { 278 auto mode = GetStreamOpenModeFromOptions(m_options); 279 if (!mode) 280 llvm::consumeError(mode.takeError()); 281 else { 282 if (!m_own_descriptor) { 283 // We must duplicate the file descriptor if we don't own it because when you 284 // call fdopen, the stream will own the fd 285 #ifdef _WIN32 286 m_descriptor = ::_dup(GetDescriptor()); 287 #else 288 m_descriptor = dup(GetDescriptor()); 289 #endif 290 m_own_descriptor = true; 291 } 292 293 m_stream = llvm::sys::RetryAfterSignal(nullptr, ::fdopen, m_descriptor, 294 mode.get()); 295 296 // If we got a stream, then we own the stream and should no longer own 297 // the descriptor because fclose() will close it for us 298 299 if (m_stream) { 300 m_own_stream = true; 301 m_own_descriptor = false; 302 } 303 } 304 } 305 } 306 return m_stream; 307 } 308 309 Status NativeFile::Close() { 310 Status error; 311 if (StreamIsValid()) { 312 if (m_own_stream) { 313 if (::fclose(m_stream) == EOF) 314 error.SetErrorToErrno(); 315 } else { 316 if (::fflush(m_stream) == EOF) 317 error.SetErrorToErrno(); 318 } 319 } 320 if (DescriptorIsValid() && m_own_descriptor) { 321 if (::close(m_descriptor) != 0) 322 error.SetErrorToErrno(); 323 } 324 m_descriptor = kInvalidDescriptor; 325 m_stream = kInvalidStream; 326 m_options = OpenOptions(0); 327 m_own_stream = false; 328 m_own_descriptor = false; 329 m_is_interactive = eLazyBoolCalculate; 330 m_is_real_terminal = eLazyBoolCalculate; 331 return error; 332 } 333 334 FILE *NativeFile::TakeStreamAndClear() { 335 FILE *stream = GetStream(); 336 m_stream = NULL; 337 m_descriptor = kInvalidDescriptor; 338 m_options = OpenOptions(); 339 m_own_stream = false; 340 m_own_descriptor = false; 341 m_is_interactive = m_supports_colors = m_is_real_terminal = 342 eLazyBoolCalculate; 343 return stream; 344 } 345 346 Status NativeFile::GetFileSpec(FileSpec &file_spec) const { 347 Status error; 348 #ifdef F_GETPATH 349 if (IsValid()) { 350 char path[PATH_MAX]; 351 if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1) 352 error.SetErrorToErrno(); 353 else 354 file_spec.SetFile(path, FileSpec::Style::native); 355 } else { 356 error.SetErrorString("invalid file handle"); 357 } 358 #elif defined(__linux__) 359 char proc[64]; 360 char path[PATH_MAX]; 361 if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0) 362 error.SetErrorString("cannot resolve file descriptor"); 363 else { 364 ssize_t len; 365 if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1) 366 error.SetErrorToErrno(); 367 else { 368 path[len] = '\0'; 369 file_spec.SetFile(path, FileSpec::Style::native); 370 } 371 } 372 #else 373 error.SetErrorString( 374 "NativeFile::GetFileSpec is not supported on this platform"); 375 #endif 376 377 if (error.Fail()) 378 file_spec.Clear(); 379 return error; 380 } 381 382 off_t NativeFile::SeekFromStart(off_t offset, Status *error_ptr) { 383 off_t result = 0; 384 if (DescriptorIsValid()) { 385 result = ::lseek(m_descriptor, offset, SEEK_SET); 386 387 if (error_ptr) { 388 if (result == -1) 389 error_ptr->SetErrorToErrno(); 390 else 391 error_ptr->Clear(); 392 } 393 } else if (StreamIsValid()) { 394 result = ::fseek(m_stream, offset, SEEK_SET); 395 396 if (error_ptr) { 397 if (result == -1) 398 error_ptr->SetErrorToErrno(); 399 else 400 error_ptr->Clear(); 401 } 402 } else if (error_ptr) { 403 error_ptr->SetErrorString("invalid file handle"); 404 } 405 return result; 406 } 407 408 off_t NativeFile::SeekFromCurrent(off_t offset, Status *error_ptr) { 409 off_t result = -1; 410 if (DescriptorIsValid()) { 411 result = ::lseek(m_descriptor, offset, SEEK_CUR); 412 413 if (error_ptr) { 414 if (result == -1) 415 error_ptr->SetErrorToErrno(); 416 else 417 error_ptr->Clear(); 418 } 419 } else if (StreamIsValid()) { 420 result = ::fseek(m_stream, offset, SEEK_CUR); 421 422 if (error_ptr) { 423 if (result == -1) 424 error_ptr->SetErrorToErrno(); 425 else 426 error_ptr->Clear(); 427 } 428 } else if (error_ptr) { 429 error_ptr->SetErrorString("invalid file handle"); 430 } 431 return result; 432 } 433 434 off_t NativeFile::SeekFromEnd(off_t offset, Status *error_ptr) { 435 off_t result = -1; 436 if (DescriptorIsValid()) { 437 result = ::lseek(m_descriptor, offset, SEEK_END); 438 439 if (error_ptr) { 440 if (result == -1) 441 error_ptr->SetErrorToErrno(); 442 else 443 error_ptr->Clear(); 444 } 445 } else if (StreamIsValid()) { 446 result = ::fseek(m_stream, offset, SEEK_END); 447 448 if (error_ptr) { 449 if (result == -1) 450 error_ptr->SetErrorToErrno(); 451 else 452 error_ptr->Clear(); 453 } 454 } else if (error_ptr) { 455 error_ptr->SetErrorString("invalid file handle"); 456 } 457 return result; 458 } 459 460 Status NativeFile::Flush() { 461 Status error; 462 if (StreamIsValid()) { 463 if (llvm::sys::RetryAfterSignal(EOF, ::fflush, m_stream) == EOF) 464 error.SetErrorToErrno(); 465 } else if (!DescriptorIsValid()) { 466 error.SetErrorString("invalid file handle"); 467 } 468 return error; 469 } 470 471 Status NativeFile::Sync() { 472 Status error; 473 if (DescriptorIsValid()) { 474 #ifdef _WIN32 475 int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor)); 476 if (err == 0) 477 error.SetErrorToGenericError(); 478 #else 479 if (llvm::sys::RetryAfterSignal(-1, ::fsync, m_descriptor) == -1) 480 error.SetErrorToErrno(); 481 #endif 482 } else { 483 error.SetErrorString("invalid file handle"); 484 } 485 return error; 486 } 487 488 #if defined(__APPLE__) 489 // Darwin kernels only can read/write <= INT_MAX bytes 490 #define MAX_READ_SIZE INT_MAX 491 #define MAX_WRITE_SIZE INT_MAX 492 #endif 493 494 Status NativeFile::Read(void *buf, size_t &num_bytes) { 495 Status error; 496 497 #if defined(MAX_READ_SIZE) 498 if (num_bytes > MAX_READ_SIZE) { 499 uint8_t *p = (uint8_t *)buf; 500 size_t bytes_left = num_bytes; 501 // Init the num_bytes read to zero 502 num_bytes = 0; 503 504 while (bytes_left > 0) { 505 size_t curr_num_bytes; 506 if (bytes_left > MAX_READ_SIZE) 507 curr_num_bytes = MAX_READ_SIZE; 508 else 509 curr_num_bytes = bytes_left; 510 511 error = Read(p + num_bytes, curr_num_bytes); 512 513 // Update how many bytes were read 514 num_bytes += curr_num_bytes; 515 if (bytes_left < curr_num_bytes) 516 bytes_left = 0; 517 else 518 bytes_left -= curr_num_bytes; 519 520 if (error.Fail()) 521 break; 522 } 523 return error; 524 } 525 #endif 526 527 ssize_t bytes_read = -1; 528 if (DescriptorIsValid()) { 529 bytes_read = llvm::sys::RetryAfterSignal(-1, ::read, m_descriptor, buf, num_bytes); 530 if (bytes_read == -1) { 531 error.SetErrorToErrno(); 532 num_bytes = 0; 533 } else 534 num_bytes = bytes_read; 535 } else if (StreamIsValid()) { 536 bytes_read = ::fread(buf, 1, num_bytes, m_stream); 537 538 if (bytes_read == 0) { 539 if (::feof(m_stream)) 540 error.SetErrorString("feof"); 541 else if (::ferror(m_stream)) 542 error.SetErrorString("ferror"); 543 num_bytes = 0; 544 } else 545 num_bytes = bytes_read; 546 } else { 547 num_bytes = 0; 548 error.SetErrorString("invalid file handle"); 549 } 550 return error; 551 } 552 553 Status NativeFile::Write(const void *buf, size_t &num_bytes) { 554 Status error; 555 556 #if defined(MAX_WRITE_SIZE) 557 if (num_bytes > MAX_WRITE_SIZE) { 558 const uint8_t *p = (const uint8_t *)buf; 559 size_t bytes_left = num_bytes; 560 // Init the num_bytes written to zero 561 num_bytes = 0; 562 563 while (bytes_left > 0) { 564 size_t curr_num_bytes; 565 if (bytes_left > MAX_WRITE_SIZE) 566 curr_num_bytes = MAX_WRITE_SIZE; 567 else 568 curr_num_bytes = bytes_left; 569 570 error = Write(p + num_bytes, curr_num_bytes); 571 572 // Update how many bytes were read 573 num_bytes += curr_num_bytes; 574 if (bytes_left < curr_num_bytes) 575 bytes_left = 0; 576 else 577 bytes_left -= curr_num_bytes; 578 579 if (error.Fail()) 580 break; 581 } 582 return error; 583 } 584 #endif 585 586 ssize_t bytes_written = -1; 587 if (DescriptorIsValid()) { 588 bytes_written = 589 llvm::sys::RetryAfterSignal(-1, ::write, m_descriptor, buf, num_bytes); 590 if (bytes_written == -1) { 591 error.SetErrorToErrno(); 592 num_bytes = 0; 593 } else 594 num_bytes = bytes_written; 595 } else if (StreamIsValid()) { 596 bytes_written = ::fwrite(buf, 1, num_bytes, m_stream); 597 598 if (bytes_written == 0) { 599 if (::feof(m_stream)) 600 error.SetErrorString("feof"); 601 else if (::ferror(m_stream)) 602 error.SetErrorString("ferror"); 603 num_bytes = 0; 604 } else 605 num_bytes = bytes_written; 606 607 } else { 608 num_bytes = 0; 609 error.SetErrorString("invalid file handle"); 610 } 611 612 return error; 613 } 614 615 Status NativeFile::Read(void *buf, size_t &num_bytes, off_t &offset) { 616 Status error; 617 618 #if defined(MAX_READ_SIZE) 619 if (num_bytes > MAX_READ_SIZE) { 620 uint8_t *p = (uint8_t *)buf; 621 size_t bytes_left = num_bytes; 622 // Init the num_bytes read to zero 623 num_bytes = 0; 624 625 while (bytes_left > 0) { 626 size_t curr_num_bytes; 627 if (bytes_left > MAX_READ_SIZE) 628 curr_num_bytes = MAX_READ_SIZE; 629 else 630 curr_num_bytes = bytes_left; 631 632 error = Read(p + num_bytes, curr_num_bytes, offset); 633 634 // Update how many bytes were read 635 num_bytes += curr_num_bytes; 636 if (bytes_left < curr_num_bytes) 637 bytes_left = 0; 638 else 639 bytes_left -= curr_num_bytes; 640 641 if (error.Fail()) 642 break; 643 } 644 return error; 645 } 646 #endif 647 648 #ifndef _WIN32 649 int fd = GetDescriptor(); 650 if (fd != kInvalidDescriptor) { 651 ssize_t bytes_read = 652 llvm::sys::RetryAfterSignal(-1, ::pread, fd, buf, num_bytes, offset); 653 if (bytes_read < 0) { 654 num_bytes = 0; 655 error.SetErrorToErrno(); 656 } else { 657 offset += bytes_read; 658 num_bytes = bytes_read; 659 } 660 } else { 661 num_bytes = 0; 662 error.SetErrorString("invalid file handle"); 663 } 664 #else 665 std::lock_guard<std::mutex> guard(offset_access_mutex); 666 long cur = ::lseek(m_descriptor, 0, SEEK_CUR); 667 SeekFromStart(offset); 668 error = Read(buf, num_bytes); 669 if (!error.Fail()) 670 SeekFromStart(cur); 671 #endif 672 return error; 673 } 674 675 Status NativeFile::Write(const void *buf, size_t &num_bytes, off_t &offset) { 676 Status error; 677 678 #if defined(MAX_WRITE_SIZE) 679 if (num_bytes > MAX_WRITE_SIZE) { 680 const uint8_t *p = (const uint8_t *)buf; 681 size_t bytes_left = num_bytes; 682 // Init the num_bytes written to zero 683 num_bytes = 0; 684 685 while (bytes_left > 0) { 686 size_t curr_num_bytes; 687 if (bytes_left > MAX_WRITE_SIZE) 688 curr_num_bytes = MAX_WRITE_SIZE; 689 else 690 curr_num_bytes = bytes_left; 691 692 error = Write(p + num_bytes, curr_num_bytes, offset); 693 694 // Update how many bytes were read 695 num_bytes += curr_num_bytes; 696 if (bytes_left < curr_num_bytes) 697 bytes_left = 0; 698 else 699 bytes_left -= curr_num_bytes; 700 701 if (error.Fail()) 702 break; 703 } 704 return error; 705 } 706 #endif 707 708 int fd = GetDescriptor(); 709 if (fd != kInvalidDescriptor) { 710 #ifndef _WIN32 711 ssize_t bytes_written = 712 llvm::sys::RetryAfterSignal(-1, ::pwrite, m_descriptor, buf, num_bytes, offset); 713 if (bytes_written < 0) { 714 num_bytes = 0; 715 error.SetErrorToErrno(); 716 } else { 717 offset += bytes_written; 718 num_bytes = bytes_written; 719 } 720 #else 721 std::lock_guard<std::mutex> guard(offset_access_mutex); 722 long cur = ::lseek(m_descriptor, 0, SEEK_CUR); 723 SeekFromStart(offset); 724 error = Write(buf, num_bytes); 725 long after = ::lseek(m_descriptor, 0, SEEK_CUR); 726 727 if (!error.Fail()) 728 SeekFromStart(cur); 729 730 offset = after; 731 #endif 732 } else { 733 num_bytes = 0; 734 error.SetErrorString("invalid file handle"); 735 } 736 return error; 737 } 738 739 size_t NativeFile::PrintfVarArg(const char *format, va_list args) { 740 if (StreamIsValid()) { 741 return ::vfprintf(m_stream, format, args); 742 } else { 743 return File::PrintfVarArg(format, args); 744 } 745 } 746 747 mode_t File::ConvertOpenOptionsForPOSIXOpen(OpenOptions open_options) { 748 mode_t mode = 0; 749 if (open_options & eOpenOptionRead && open_options & eOpenOptionWrite) 750 mode |= O_RDWR; 751 else if (open_options & eOpenOptionWrite) 752 mode |= O_WRONLY; 753 754 if (open_options & eOpenOptionAppend) 755 mode |= O_APPEND; 756 757 if (open_options & eOpenOptionTruncate) 758 mode |= O_TRUNC; 759 760 if (open_options & eOpenOptionNonBlocking) 761 mode |= O_NONBLOCK; 762 763 if (open_options & eOpenOptionCanCreateNewOnly) 764 mode |= O_CREAT | O_EXCL; 765 else if (open_options & eOpenOptionCanCreate) 766 mode |= O_CREAT; 767 768 return mode; 769 } 770 771 char File::ID = 0; 772 char NativeFile::ID = 0; 773