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