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