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 #include <optional>
17 
18 #ifdef _WIN32
19 #include "lldb/Host/windows/windows.h"
20 #else
21 #include <sys/ioctl.h>
22 #include <sys/stat.h>
23 #include <termios.h>
24 #include <unistd.h>
25 #endif
26 
27 #include "lldb/Host/Config.h"
28 #include "lldb/Host/FileSystem.h"
29 #include "lldb/Host/Host.h"
30 #include "lldb/Utility/DataBufferHeap.h"
31 #include "lldb/Utility/FileSpec.h"
32 #include "lldb/Utility/Log.h"
33 #include "lldb/Utility/VASPrintf.h"
34 #include "llvm/ADT/StringExtras.h"
35 #include "llvm/Support/ConvertUTF.h"
36 #include "llvm/Support/Errno.h"
37 #include "llvm/Support/FileSystem.h"
38 #include "llvm/Support/Process.h"
39 
40 using namespace lldb;
41 using namespace lldb_private;
42 using llvm::Expected;
43 
44 Expected<const char *>
45 File::GetStreamOpenModeFromOptions(File::OpenOptions options) {
46   File::OpenOptions rw =
47       options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
48                  File::eOpenOptionReadWrite);
49 
50   if (options & File::eOpenOptionAppend) {
51     if (rw == File::eOpenOptionReadWrite) {
52       if (options & File::eOpenOptionCanCreateNewOnly)
53         return "a+x";
54       else
55         return "a+";
56     } else if (rw == File::eOpenOptionWriteOnly) {
57       if (options & File::eOpenOptionCanCreateNewOnly)
58         return "ax";
59       else
60         return "a";
61     }
62   } else if (rw == File::eOpenOptionReadWrite) {
63     if (options & File::eOpenOptionCanCreate) {
64       if (options & File::eOpenOptionCanCreateNewOnly)
65         return "w+x";
66       else
67         return "w+";
68     } else
69       return "r+";
70   } else if (rw == File::eOpenOptionWriteOnly) {
71     return "w";
72   } else if (rw == File::eOpenOptionReadOnly) {
73     return "r";
74   }
75   return llvm::createStringError(
76       llvm::inconvertibleErrorCode(),
77       "invalid options, cannot convert to mode string");
78 }
79 
80 Expected<File::OpenOptions> File::GetOptionsFromMode(llvm::StringRef mode) {
81   OpenOptions opts =
82       llvm::StringSwitch<OpenOptions>(mode)
83           .Cases("r", "rb", eOpenOptionReadOnly)
84           .Cases("w", "wb", eOpenOptionWriteOnly)
85           .Cases("a", "ab",
86                  eOpenOptionWriteOnly | eOpenOptionAppend |
87                  eOpenOptionCanCreate)
88           .Cases("r+", "rb+", "r+b", eOpenOptionReadWrite)
89           .Cases("w+", "wb+", "w+b",
90                  eOpenOptionReadWrite | eOpenOptionCanCreate |
91                  eOpenOptionTruncate)
92           .Cases("a+", "ab+", "a+b",
93                  eOpenOptionReadWrite | eOpenOptionAppend |
94                      eOpenOptionCanCreate)
95           .Default(eOpenOptionInvalid);
96   if (opts != eOpenOptionInvalid)
97     return opts;
98   return llvm::createStringError(
99       llvm::inconvertibleErrorCode(),
100       "invalid mode, cannot convert to File::OpenOptions");
101 }
102 
103 int File::kInvalidDescriptor = -1;
104 FILE *File::kInvalidStream = nullptr;
105 
106 Status File::Read(void *buf, size_t &num_bytes) {
107   return std::error_code(ENOTSUP, std::system_category());
108 }
109 Status File::Write(const void *buf, size_t &num_bytes) {
110   return std::error_code(ENOTSUP, std::system_category());
111 }
112 
113 bool File::IsValid() const { return false; }
114 
115 Status File::Close() { return Flush(); }
116 
117 IOObject::WaitableHandle File::GetWaitableHandle() {
118   return IOObject::kInvalidHandleValue;
119 }
120 
121 Status File::GetFileSpec(FileSpec &file_spec) const {
122   file_spec.Clear();
123   return std::error_code(ENOTSUP, std::system_category());
124 }
125 
126 int File::GetDescriptor() const { return kInvalidDescriptor; }
127 
128 FILE *File::GetStream() { return nullptr; }
129 
130 off_t File::SeekFromStart(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::SeekFromCurrent(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 off_t File::SeekFromEnd(off_t offset, Status *error_ptr) {
143   if (error_ptr)
144     *error_ptr = std::error_code(ENOTSUP, std::system_category());
145   return -1;
146 }
147 
148 Status File::Read(void *dst, size_t &num_bytes, off_t &offset) {
149   return std::error_code(ENOTSUP, std::system_category());
150 }
151 
152 Status File::Write(const void *src, size_t &num_bytes, off_t &offset) {
153   return std::error_code(ENOTSUP, std::system_category());
154 }
155 
156 Status File::Flush() { return Status(); }
157 
158 Status File::Sync() { return Flush(); }
159 
160 void File::CalculateInteractiveAndTerminal() {
161   const int fd = GetDescriptor();
162   if (!DescriptorIsValid(fd)) {
163     m_is_interactive = eLazyBoolNo;
164     m_is_real_terminal = eLazyBoolNo;
165     m_supports_colors = eLazyBoolNo;
166     return;
167   }
168   m_is_interactive = eLazyBoolNo;
169   m_is_real_terminal = eLazyBoolNo;
170 #if defined(_WIN32)
171   if (_isatty(fd)) {
172     m_is_interactive = eLazyBoolYes;
173     m_is_real_terminal = eLazyBoolYes;
174 #if defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
175     m_supports_colors = eLazyBoolYes;
176 #endif
177   }
178 #else
179   if (isatty(fd)) {
180     m_is_interactive = eLazyBoolYes;
181     struct winsize window_size;
182     if (::ioctl(fd, TIOCGWINSZ, &window_size) == 0) {
183       if (window_size.ws_col > 0) {
184         m_is_real_terminal = eLazyBoolYes;
185         if (llvm::sys::Process::FileDescriptorHasColors(fd))
186           m_supports_colors = eLazyBoolYes;
187       }
188     }
189   }
190 #endif
191 }
192 
193 bool File::GetIsInteractive() {
194   if (m_is_interactive == eLazyBoolCalculate)
195     CalculateInteractiveAndTerminal();
196   return m_is_interactive == eLazyBoolYes;
197 }
198 
199 bool File::GetIsRealTerminal() {
200   if (m_is_real_terminal == eLazyBoolCalculate)
201     CalculateInteractiveAndTerminal();
202   return m_is_real_terminal == eLazyBoolYes;
203 }
204 
205 bool File::GetIsTerminalWithColors() {
206   if (m_supports_colors == eLazyBoolCalculate)
207     CalculateInteractiveAndTerminal();
208   return m_supports_colors == eLazyBoolYes;
209 }
210 
211 size_t File::Printf(const char *format, ...) {
212   va_list args;
213   va_start(args, format);
214   size_t result = PrintfVarArg(format, args);
215   va_end(args);
216   return result;
217 }
218 
219 size_t File::PrintfVarArg(const char *format, va_list args) {
220   llvm::SmallString<0> s;
221   if (VASprintf(s, format, args)) {
222     size_t written = s.size();
223     Write(s.data(), written);
224     return written;
225   }
226   return 0;
227 }
228 
229 Expected<File::OpenOptions> File::GetOptions() const {
230   return llvm::createStringError(
231       llvm::inconvertibleErrorCode(),
232       "GetOptions() not implemented for this File class");
233 }
234 
235 uint32_t File::GetPermissions(Status &error) const {
236   int fd = GetDescriptor();
237   if (!DescriptorIsValid(fd)) {
238     error = std::error_code(ENOTSUP, std::system_category());
239     return 0;
240   }
241   struct stat file_stats;
242   if (::fstat(fd, &file_stats) == -1) {
243     error.SetErrorToErrno();
244     return 0;
245   }
246   error.Clear();
247   return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
248 }
249 
250 bool NativeFile::IsValid() const {
251   std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex);
252   return DescriptorIsValidUnlocked() || StreamIsValidUnlocked();
253 }
254 
255 Expected<File::OpenOptions> NativeFile::GetOptions() const { return m_options; }
256 
257 int NativeFile::GetDescriptor() const {
258   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
259     return m_descriptor;
260   }
261 
262   // Don't open the file descriptor if we don't need to, just get it from the
263   // stream if we have one.
264   if (ValueGuard stream_guard = StreamIsValid()) {
265 #if defined(_WIN32)
266     return _fileno(m_stream);
267 #else
268     return fileno(m_stream);
269 #endif
270   }
271 
272   // Invalid descriptor and invalid stream, return invalid descriptor.
273   return kInvalidDescriptor;
274 }
275 
276 IOObject::WaitableHandle NativeFile::GetWaitableHandle() {
277   return GetDescriptor();
278 }
279 
280 FILE *NativeFile::GetStream() {
281   ValueGuard stream_guard = StreamIsValid();
282   if (!stream_guard) {
283     if (ValueGuard descriptor_guard = DescriptorIsValid()) {
284       auto mode = GetStreamOpenModeFromOptions(m_options);
285       if (!mode)
286         llvm::consumeError(mode.takeError());
287       else {
288         if (!m_own_descriptor) {
289 // We must duplicate the file descriptor if we don't own it because when you
290 // call fdopen, the stream will own the fd
291 #ifdef _WIN32
292           m_descriptor = ::_dup(m_descriptor);
293 #else
294           m_descriptor = dup(m_descriptor);
295 #endif
296           m_own_descriptor = true;
297         }
298 
299         m_stream = llvm::sys::RetryAfterSignal(nullptr, ::fdopen, m_descriptor,
300                                                mode.get());
301 
302         // If we got a stream, then we own the stream and should no longer own
303         // the descriptor because fclose() will close it for us
304 
305         if (m_stream) {
306           m_own_stream = true;
307           m_own_descriptor = false;
308         }
309       }
310     }
311   }
312   return m_stream;
313 }
314 
315 Status NativeFile::Close() {
316   std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex);
317 
318   Status error;
319 
320   if (StreamIsValidUnlocked()) {
321     if (m_own_stream) {
322       if (::fclose(m_stream) == EOF)
323         error.SetErrorToErrno();
324     } else {
325       File::OpenOptions rw =
326           m_options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
327                        File::eOpenOptionReadWrite);
328 
329       if (rw == eOpenOptionWriteOnly || rw == eOpenOptionReadWrite) {
330         if (::fflush(m_stream) == EOF)
331           error.SetErrorToErrno();
332       }
333     }
334   }
335 
336   if (DescriptorIsValidUnlocked() && m_own_descriptor) {
337     if (::close(m_descriptor) != 0)
338       error.SetErrorToErrno();
339   }
340 
341   m_stream = kInvalidStream;
342   m_own_stream = false;
343   m_descriptor = kInvalidDescriptor;
344   m_own_descriptor = false;
345   m_options = OpenOptions(0);
346   m_is_interactive = eLazyBoolCalculate;
347   m_is_real_terminal = eLazyBoolCalculate;
348   return error;
349 }
350 
351 Status NativeFile::GetFileSpec(FileSpec &file_spec) const {
352   Status error;
353 #ifdef F_GETPATH
354   if (IsValid()) {
355     char path[PATH_MAX];
356     if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1)
357       error.SetErrorToErrno();
358     else
359       file_spec.SetFile(path, FileSpec::Style::native);
360   } else {
361     error.SetErrorString("invalid file handle");
362   }
363 #elif defined(__linux__)
364   char proc[64];
365   char path[PATH_MAX];
366   if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0)
367     error.SetErrorString("cannot resolve file descriptor");
368   else {
369     ssize_t len;
370     if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1)
371       error.SetErrorToErrno();
372     else {
373       path[len] = '\0';
374       file_spec.SetFile(path, FileSpec::Style::native);
375     }
376   }
377 #else
378   error.SetErrorString(
379       "NativeFile::GetFileSpec is not supported on this platform");
380 #endif
381 
382   if (error.Fail())
383     file_spec.Clear();
384   return error;
385 }
386 
387 off_t NativeFile::SeekFromStart(off_t offset, Status *error_ptr) {
388   off_t result = 0;
389   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
390     result = ::lseek(m_descriptor, offset, SEEK_SET);
391 
392     if (error_ptr) {
393       if (result == -1)
394         error_ptr->SetErrorToErrno();
395       else
396         error_ptr->Clear();
397     }
398     return result;
399   }
400 
401   if (ValueGuard stream_guard = StreamIsValid()) {
402     result = ::fseek(m_stream, offset, SEEK_SET);
403 
404     if (error_ptr) {
405       if (result == -1)
406         error_ptr->SetErrorToErrno();
407       else
408         error_ptr->Clear();
409     }
410     return result;
411   }
412 
413   if (error_ptr)
414     error_ptr->SetErrorString("invalid file handle");
415   return result;
416 }
417 
418 off_t NativeFile::SeekFromCurrent(off_t offset, Status *error_ptr) {
419   off_t result = -1;
420   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
421     result = ::lseek(m_descriptor, offset, SEEK_CUR);
422 
423     if (error_ptr) {
424       if (result == -1)
425         error_ptr->SetErrorToErrno();
426       else
427         error_ptr->Clear();
428     }
429     return result;
430   }
431 
432   if (ValueGuard stream_guard = StreamIsValid()) {
433     result = ::fseek(m_stream, offset, SEEK_CUR);
434 
435     if (error_ptr) {
436       if (result == -1)
437         error_ptr->SetErrorToErrno();
438       else
439         error_ptr->Clear();
440     }
441     return result;
442   }
443 
444   if (error_ptr)
445     error_ptr->SetErrorString("invalid file handle");
446   return result;
447 }
448 
449 off_t NativeFile::SeekFromEnd(off_t offset, Status *error_ptr) {
450   off_t result = -1;
451   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
452     result = ::lseek(m_descriptor, offset, SEEK_END);
453 
454     if (error_ptr) {
455       if (result == -1)
456         error_ptr->SetErrorToErrno();
457       else
458         error_ptr->Clear();
459     }
460     return result;
461   }
462 
463   if (ValueGuard stream_guard = StreamIsValid()) {
464     result = ::fseek(m_stream, offset, SEEK_END);
465 
466     if (error_ptr) {
467       if (result == -1)
468         error_ptr->SetErrorToErrno();
469       else
470         error_ptr->Clear();
471     }
472   }
473 
474   if (error_ptr)
475     error_ptr->SetErrorString("invalid file handle");
476   return result;
477 }
478 
479 Status NativeFile::Flush() {
480   Status error;
481   if (ValueGuard stream_guard = StreamIsValid()) {
482     if (llvm::sys::RetryAfterSignal(EOF, ::fflush, m_stream) == EOF)
483       error.SetErrorToErrno();
484     return error;
485   }
486 
487   {
488     ValueGuard descriptor_guard = DescriptorIsValid();
489     if (!descriptor_guard)
490       error.SetErrorString("invalid file handle");
491   }
492   return error;
493 }
494 
495 Status NativeFile::Sync() {
496   Status error;
497   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
498 #ifdef _WIN32
499     int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor));
500     if (err == 0)
501       error.SetErrorToGenericError();
502 #else
503     if (llvm::sys::RetryAfterSignal(-1, ::fsync, m_descriptor) == -1)
504       error.SetErrorToErrno();
505 #endif
506   } else {
507     error.SetErrorString("invalid file handle");
508   }
509   return error;
510 }
511 
512 #if defined(__APPLE__)
513 // Darwin kernels only can read/write <= INT_MAX bytes
514 #define MAX_READ_SIZE INT_MAX
515 #define MAX_WRITE_SIZE INT_MAX
516 #endif
517 
518 Status NativeFile::Read(void *buf, size_t &num_bytes) {
519   Status error;
520 
521 #if defined(MAX_READ_SIZE)
522   if (num_bytes > MAX_READ_SIZE) {
523     uint8_t *p = (uint8_t *)buf;
524     size_t bytes_left = num_bytes;
525     // Init the num_bytes read to zero
526     num_bytes = 0;
527 
528     while (bytes_left > 0) {
529       size_t curr_num_bytes;
530       if (bytes_left > MAX_READ_SIZE)
531         curr_num_bytes = MAX_READ_SIZE;
532       else
533         curr_num_bytes = bytes_left;
534 
535       error = Read(p + num_bytes, curr_num_bytes);
536 
537       // Update how many bytes were read
538       num_bytes += curr_num_bytes;
539       if (bytes_left < curr_num_bytes)
540         bytes_left = 0;
541       else
542         bytes_left -= curr_num_bytes;
543 
544       if (error.Fail())
545         break;
546     }
547     return error;
548   }
549 #endif
550 
551   ssize_t bytes_read = -1;
552   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
553     bytes_read =
554         llvm::sys::RetryAfterSignal(-1, ::read, m_descriptor, buf, num_bytes);
555     if (bytes_read == -1) {
556       error.SetErrorToErrno();
557       num_bytes = 0;
558     } else
559       num_bytes = bytes_read;
560     return error;
561   }
562 
563   if (ValueGuard file_lock = StreamIsValid()) {
564     bytes_read = ::fread(buf, 1, num_bytes, m_stream);
565 
566     if (bytes_read == 0) {
567       if (::feof(m_stream))
568         error.SetErrorString("feof");
569       else if (::ferror(m_stream))
570         error.SetErrorString("ferror");
571       num_bytes = 0;
572     } else
573       num_bytes = bytes_read;
574     return error;
575   }
576 
577   num_bytes = 0;
578   error.SetErrorString("invalid file handle");
579   return error;
580 }
581 
582 Status NativeFile::Write(const void *buf, size_t &num_bytes) {
583   Status error;
584 
585 #if defined(MAX_WRITE_SIZE)
586   if (num_bytes > MAX_WRITE_SIZE) {
587     const uint8_t *p = (const uint8_t *)buf;
588     size_t bytes_left = num_bytes;
589     // Init the num_bytes written to zero
590     num_bytes = 0;
591 
592     while (bytes_left > 0) {
593       size_t curr_num_bytes;
594       if (bytes_left > MAX_WRITE_SIZE)
595         curr_num_bytes = MAX_WRITE_SIZE;
596       else
597         curr_num_bytes = bytes_left;
598 
599       error = Write(p + num_bytes, curr_num_bytes);
600 
601       // Update how many bytes were read
602       num_bytes += curr_num_bytes;
603       if (bytes_left < curr_num_bytes)
604         bytes_left = 0;
605       else
606         bytes_left -= curr_num_bytes;
607 
608       if (error.Fail())
609         break;
610     }
611     return error;
612   }
613 #endif
614 
615   ssize_t bytes_written = -1;
616   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
617     bytes_written =
618         llvm::sys::RetryAfterSignal(-1, ::write, m_descriptor, buf, num_bytes);
619     if (bytes_written == -1) {
620       error.SetErrorToErrno();
621       num_bytes = 0;
622     } else
623       num_bytes = bytes_written;
624     return error;
625   }
626 
627   if (ValueGuard stream_guard = StreamIsValid()) {
628     bytes_written = ::fwrite(buf, 1, num_bytes, m_stream);
629 
630     if (bytes_written == 0) {
631       if (::feof(m_stream))
632         error.SetErrorString("feof");
633       else if (::ferror(m_stream))
634         error.SetErrorString("ferror");
635       num_bytes = 0;
636     } else
637       num_bytes = bytes_written;
638     return error;
639   }
640 
641   num_bytes = 0;
642   error.SetErrorString("invalid file handle");
643   return error;
644 }
645 
646 Status NativeFile::Read(void *buf, size_t &num_bytes, off_t &offset) {
647   Status error;
648 
649 #if defined(MAX_READ_SIZE)
650   if (num_bytes > MAX_READ_SIZE) {
651     uint8_t *p = (uint8_t *)buf;
652     size_t bytes_left = num_bytes;
653     // Init the num_bytes read to zero
654     num_bytes = 0;
655 
656     while (bytes_left > 0) {
657       size_t curr_num_bytes;
658       if (bytes_left > MAX_READ_SIZE)
659         curr_num_bytes = MAX_READ_SIZE;
660       else
661         curr_num_bytes = bytes_left;
662 
663       error = Read(p + num_bytes, curr_num_bytes, offset);
664 
665       // Update how many bytes were read
666       num_bytes += curr_num_bytes;
667       if (bytes_left < curr_num_bytes)
668         bytes_left = 0;
669       else
670         bytes_left -= curr_num_bytes;
671 
672       if (error.Fail())
673         break;
674     }
675     return error;
676   }
677 #endif
678 
679 #ifndef _WIN32
680   int fd = GetDescriptor();
681   if (fd != kInvalidDescriptor) {
682     ssize_t bytes_read =
683         llvm::sys::RetryAfterSignal(-1, ::pread, fd, buf, num_bytes, offset);
684     if (bytes_read < 0) {
685       num_bytes = 0;
686       error.SetErrorToErrno();
687     } else {
688       offset += bytes_read;
689       num_bytes = bytes_read;
690     }
691   } else {
692     num_bytes = 0;
693     error.SetErrorString("invalid file handle");
694   }
695 #else
696   std::lock_guard<std::mutex> guard(offset_access_mutex);
697   long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
698   SeekFromStart(offset);
699   error = Read(buf, num_bytes);
700   if (!error.Fail())
701     SeekFromStart(cur);
702 #endif
703   return error;
704 }
705 
706 Status NativeFile::Write(const void *buf, size_t &num_bytes, off_t &offset) {
707   Status error;
708 
709 #if defined(MAX_WRITE_SIZE)
710   if (num_bytes > MAX_WRITE_SIZE) {
711     const uint8_t *p = (const uint8_t *)buf;
712     size_t bytes_left = num_bytes;
713     // Init the num_bytes written to zero
714     num_bytes = 0;
715 
716     while (bytes_left > 0) {
717       size_t curr_num_bytes;
718       if (bytes_left > MAX_WRITE_SIZE)
719         curr_num_bytes = MAX_WRITE_SIZE;
720       else
721         curr_num_bytes = bytes_left;
722 
723       error = Write(p + num_bytes, curr_num_bytes, offset);
724 
725       // Update how many bytes were read
726       num_bytes += curr_num_bytes;
727       if (bytes_left < curr_num_bytes)
728         bytes_left = 0;
729       else
730         bytes_left -= curr_num_bytes;
731 
732       if (error.Fail())
733         break;
734     }
735     return error;
736   }
737 #endif
738 
739   int fd = GetDescriptor();
740   if (fd != kInvalidDescriptor) {
741 #ifndef _WIN32
742     ssize_t bytes_written =
743         llvm::sys::RetryAfterSignal(-1, ::pwrite, m_descriptor, buf, num_bytes, offset);
744     if (bytes_written < 0) {
745       num_bytes = 0;
746       error.SetErrorToErrno();
747     } else {
748       offset += bytes_written;
749       num_bytes = bytes_written;
750     }
751 #else
752     std::lock_guard<std::mutex> guard(offset_access_mutex);
753     long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
754     SeekFromStart(offset);
755     error = Write(buf, num_bytes);
756     long after = ::lseek(m_descriptor, 0, SEEK_CUR);
757 
758     if (!error.Fail())
759       SeekFromStart(cur);
760 
761     offset = after;
762 #endif
763   } else {
764     num_bytes = 0;
765     error.SetErrorString("invalid file handle");
766   }
767   return error;
768 }
769 
770 size_t NativeFile::PrintfVarArg(const char *format, va_list args) {
771   if (StreamIsValid()) {
772     return ::vfprintf(m_stream, format, args);
773   } else {
774     return File::PrintfVarArg(format, args);
775   }
776 }
777 
778 mode_t File::ConvertOpenOptionsForPOSIXOpen(OpenOptions open_options) {
779   mode_t mode = 0;
780   File::OpenOptions rw =
781       open_options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
782                       File::eOpenOptionReadWrite);
783   if (rw == eOpenOptionReadWrite)
784     mode |= O_RDWR;
785   else if (rw == eOpenOptionWriteOnly)
786     mode |= O_WRONLY;
787   else if (rw == eOpenOptionReadOnly)
788     mode |= O_RDONLY;
789 
790   if (open_options & eOpenOptionAppend)
791     mode |= O_APPEND;
792 
793   if (open_options & eOpenOptionTruncate)
794     mode |= O_TRUNC;
795 
796   if (open_options & eOpenOptionNonBlocking)
797     mode |= O_NONBLOCK;
798 
799   if (open_options & eOpenOptionCanCreateNewOnly)
800     mode |= O_CREAT | O_EXCL;
801   else if (open_options & eOpenOptionCanCreate)
802     mode |= O_CREAT;
803 
804   return mode;
805 }
806 
807 llvm::Expected<SerialPort::Options>
808 SerialPort::OptionsFromURL(llvm::StringRef urlqs) {
809   SerialPort::Options serial_options;
810   for (llvm::StringRef x : llvm::split(urlqs, '&')) {
811     if (x.consume_front("baud=")) {
812       unsigned int baud_rate;
813       if (!llvm::to_integer(x, baud_rate, 10))
814         return llvm::createStringError(llvm::inconvertibleErrorCode(),
815                                        "Invalid baud rate: %s",
816                                        x.str().c_str());
817       serial_options.BaudRate = baud_rate;
818     } else if (x.consume_front("parity=")) {
819       serial_options.Parity =
820           llvm::StringSwitch<std::optional<Terminal::Parity>>(x)
821               .Case("no", Terminal::Parity::No)
822               .Case("even", Terminal::Parity::Even)
823               .Case("odd", Terminal::Parity::Odd)
824               .Case("mark", Terminal::Parity::Mark)
825               .Case("space", Terminal::Parity::Space)
826               .Default(std::nullopt);
827       if (!serial_options.Parity)
828         return llvm::createStringError(
829             llvm::inconvertibleErrorCode(),
830             "Invalid parity (must be no, even, odd, mark or space): %s",
831             x.str().c_str());
832     } else if (x.consume_front("parity-check=")) {
833       serial_options.ParityCheck =
834           llvm::StringSwitch<std::optional<Terminal::ParityCheck>>(x)
835               .Case("no", Terminal::ParityCheck::No)
836               .Case("replace", Terminal::ParityCheck::ReplaceWithNUL)
837               .Case("ignore", Terminal::ParityCheck::Ignore)
838               // "mark" mode is not currently supported as it requires special
839               // input processing
840               // .Case("mark", Terminal::ParityCheck::Mark)
841               .Default(std::nullopt);
842       if (!serial_options.ParityCheck)
843         return llvm::createStringError(
844             llvm::inconvertibleErrorCode(),
845             "Invalid parity-check (must be no, replace, ignore or mark): %s",
846             x.str().c_str());
847     } else if (x.consume_front("stop-bits=")) {
848       unsigned int stop_bits;
849       if (!llvm::to_integer(x, stop_bits, 10) ||
850           (stop_bits != 1 && stop_bits != 2))
851         return llvm::createStringError(
852             llvm::inconvertibleErrorCode(),
853             "Invalid stop bit number (must be 1 or 2): %s", x.str().c_str());
854       serial_options.StopBits = stop_bits;
855     } else
856       return llvm::createStringError(llvm::inconvertibleErrorCode(),
857                                      "Unknown parameter: %s", x.str().c_str());
858   }
859   return serial_options;
860 }
861 
862 llvm::Expected<std::unique_ptr<SerialPort>>
863 SerialPort::Create(int fd, OpenOptions options, Options serial_options,
864                    bool transfer_ownership) {
865   std::unique_ptr<SerialPort> out{
866       new SerialPort(fd, options, serial_options, transfer_ownership)};
867 
868   if (!out->GetIsInteractive())
869     return llvm::createStringError(llvm::inconvertibleErrorCode(),
870                                    "the specified file is not a teletype");
871 
872   Terminal term{fd};
873   if (llvm::Error error = term.SetRaw())
874     return std::move(error);
875   if (serial_options.BaudRate) {
876     if (llvm::Error error = term.SetBaudRate(*serial_options.BaudRate))
877       return std::move(error);
878   }
879   if (serial_options.Parity) {
880     if (llvm::Error error = term.SetParity(*serial_options.Parity))
881       return std::move(error);
882   }
883   if (serial_options.ParityCheck) {
884     if (llvm::Error error = term.SetParityCheck(*serial_options.ParityCheck))
885       return std::move(error);
886   }
887   if (serial_options.StopBits) {
888     if (llvm::Error error = term.SetStopBits(*serial_options.StopBits))
889       return std::move(error);
890   }
891 
892   return std::move(out);
893 }
894 
895 SerialPort::SerialPort(int fd, OpenOptions options,
896                        SerialPort::Options serial_options,
897                        bool transfer_ownership)
898     : NativeFile(fd, options, transfer_ownership), m_state(fd) {}
899 
900 Status SerialPort::Close() {
901   m_state.Restore();
902   return NativeFile::Close();
903 }
904 
905 char File::ID = 0;
906 char NativeFile::ID = 0;
907 char SerialPort::ID = 0;
908