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