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