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