xref: /freebsd/contrib/llvm-project/lldb/source/Host/common/File.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 *>
GetStreamOpenModeFromOptions(File::OpenOptions options)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 
GetOptionsFromMode(llvm::StringRef mode)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 
Read(void * buf,size_t & num_bytes)106 Status File::Read(void *buf, size_t &num_bytes) {
107   return std::error_code(ENOTSUP, std::system_category());
108 }
Write(const void * buf,size_t & num_bytes)109 Status File::Write(const void *buf, size_t &num_bytes) {
110   return std::error_code(ENOTSUP, std::system_category());
111 }
112 
IsValid() const113 bool File::IsValid() const { return false; }
114 
Close()115 Status File::Close() { return Flush(); }
116 
GetWaitableHandle()117 IOObject::WaitableHandle File::GetWaitableHandle() {
118   return IOObject::kInvalidHandleValue;
119 }
120 
GetFileSpec(FileSpec & file_spec) const121 Status File::GetFileSpec(FileSpec &file_spec) const {
122   file_spec.Clear();
123   return std::error_code(ENOTSUP, std::system_category());
124 }
125 
GetDescriptor() const126 int File::GetDescriptor() const { return kInvalidDescriptor; }
127 
GetStream()128 FILE *File::GetStream() { return nullptr; }
129 
SeekFromStart(off_t offset,Status * error_ptr)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 
SeekFromCurrent(off_t offset,Status * error_ptr)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 
SeekFromEnd(off_t offset,Status * error_ptr)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 
Read(void * dst,size_t & num_bytes,off_t & offset)148 Status File::Read(void *dst, size_t &num_bytes, off_t &offset) {
149   return std::error_code(ENOTSUP, std::system_category());
150 }
151 
Write(const void * src,size_t & num_bytes,off_t & offset)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 
Flush()156 Status File::Flush() { return Status(); }
157 
Sync()158 Status File::Sync() { return Flush(); }
159 
CalculateInteractiveAndTerminal()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 
GetIsInteractive()193 bool File::GetIsInteractive() {
194   if (m_is_interactive == eLazyBoolCalculate)
195     CalculateInteractiveAndTerminal();
196   return m_is_interactive == eLazyBoolYes;
197 }
198 
GetIsRealTerminal()199 bool File::GetIsRealTerminal() {
200   if (m_is_real_terminal == eLazyBoolCalculate)
201     CalculateInteractiveAndTerminal();
202   return m_is_real_terminal == eLazyBoolYes;
203 }
204 
GetIsTerminalWithColors()205 bool File::GetIsTerminalWithColors() {
206   if (m_supports_colors == eLazyBoolCalculate)
207     CalculateInteractiveAndTerminal();
208   return m_supports_colors == eLazyBoolYes;
209 }
210 
Printf(const char * format,...)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 
PrintfVarArg(const char * format,va_list args)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 
GetOptions() const229 Expected<File::OpenOptions> File::GetOptions() const {
230   return llvm::createStringError(
231       llvm::inconvertibleErrorCode(),
232       "GetOptions() not implemented for this File class");
233 }
234 
GetPermissions(Status & error) const235 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 = Status::FromErrno();
244     return 0;
245   }
246   error.Clear();
247   return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
248 }
249 
IsValid() const250 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 
GetOptions() const255 Expected<File::OpenOptions> NativeFile::GetOptions() const { return m_options; }
256 
GetDescriptor() const257 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 
GetWaitableHandle()276 IOObject::WaitableHandle NativeFile::GetWaitableHandle() {
277 #ifdef _WIN32
278   return (HANDLE)_get_osfhandle(GetDescriptor());
279 #else
280   return GetDescriptor();
281 #endif
282 }
283 
GetStream()284 FILE *NativeFile::GetStream() {
285   ValueGuard stream_guard = StreamIsValid();
286   if (!stream_guard) {
287     if (ValueGuard descriptor_guard = DescriptorIsValid()) {
288       auto mode = GetStreamOpenModeFromOptions(m_options);
289       if (!mode)
290         llvm::consumeError(mode.takeError());
291       else {
292         if (!m_own_descriptor) {
293 // We must duplicate the file descriptor if we don't own it because when you
294 // call fdopen, the stream will own the fd
295 #ifdef _WIN32
296           m_descriptor = ::_dup(m_descriptor);
297 #else
298           m_descriptor = dup(m_descriptor);
299 #endif
300           m_own_descriptor = true;
301         }
302 
303         m_stream = llvm::sys::RetryAfterSignal(nullptr, ::fdopen, m_descriptor,
304                                                mode.get());
305 
306         // If we got a stream, then we own the stream and should no longer own
307         // the descriptor because fclose() will close it for us
308 
309         if (m_stream) {
310           m_own_stream = true;
311           m_own_descriptor = false;
312         }
313       }
314     }
315   }
316   return m_stream;
317 }
318 
Close()319 Status NativeFile::Close() {
320   std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex);
321 
322   Status error;
323 
324   if (StreamIsValidUnlocked()) {
325     if (m_own_stream) {
326       if (::fclose(m_stream) == EOF)
327         error = Status::FromErrno();
328     } else {
329       File::OpenOptions rw =
330           m_options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
331                        File::eOpenOptionReadWrite);
332 
333       if (rw == eOpenOptionWriteOnly || rw == eOpenOptionReadWrite) {
334         if (::fflush(m_stream) == EOF)
335           error = Status::FromErrno();
336       }
337     }
338   }
339 
340   if (DescriptorIsValidUnlocked() && m_own_descriptor) {
341     if (::close(m_descriptor) != 0)
342       error = Status::FromErrno();
343   }
344 
345   m_stream = kInvalidStream;
346   m_own_stream = false;
347   m_descriptor = kInvalidDescriptor;
348   m_own_descriptor = false;
349   m_options = OpenOptions(0);
350   m_is_interactive = eLazyBoolCalculate;
351   m_is_real_terminal = eLazyBoolCalculate;
352   return error;
353 }
354 
GetFileSpec(FileSpec & file_spec) const355 Status NativeFile::GetFileSpec(FileSpec &file_spec) const {
356   Status error;
357 #ifdef F_GETPATH
358   if (IsValid()) {
359     char path[PATH_MAX];
360     if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1)
361       error = Status::FromErrno();
362     else
363       file_spec.SetFile(path, FileSpec::Style::native);
364   } else {
365     error = Status::FromErrorString("invalid file handle");
366   }
367 #elif defined(__linux__)
368   char proc[64];
369   char path[PATH_MAX];
370   if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0)
371     error = Status::FromErrorString("cannot resolve file descriptor");
372   else {
373     ssize_t len;
374     if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1)
375       error = Status::FromErrno();
376     else {
377       path[len] = '\0';
378       file_spec.SetFile(path, FileSpec::Style::native);
379     }
380   }
381 #else
382   error = Status::FromErrorString(
383       "NativeFile::GetFileSpec is not supported on this platform");
384 #endif
385 
386   if (error.Fail())
387     file_spec.Clear();
388   return error;
389 }
390 
SeekFromStart(off_t offset,Status * error_ptr)391 off_t NativeFile::SeekFromStart(off_t offset, Status *error_ptr) {
392   off_t result = 0;
393   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
394     result = ::lseek(m_descriptor, offset, SEEK_SET);
395 
396     if (error_ptr) {
397       if (result == -1)
398         *error_ptr = Status::FromErrno();
399       else
400         error_ptr->Clear();
401     }
402     return result;
403   }
404 
405   if (ValueGuard stream_guard = StreamIsValid()) {
406     result = ::fseek(m_stream, offset, SEEK_SET);
407 
408     if (error_ptr) {
409       if (result == -1)
410         *error_ptr = Status::FromErrno();
411       else
412         error_ptr->Clear();
413     }
414     return result;
415   }
416 
417   if (error_ptr)
418     *error_ptr = Status::FromErrorString("invalid file handle");
419   return result;
420 }
421 
SeekFromCurrent(off_t offset,Status * error_ptr)422 off_t NativeFile::SeekFromCurrent(off_t offset, Status *error_ptr) {
423   off_t result = -1;
424   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
425     result = ::lseek(m_descriptor, offset, SEEK_CUR);
426 
427     if (error_ptr) {
428       if (result == -1)
429         *error_ptr = Status::FromErrno();
430       else
431         error_ptr->Clear();
432     }
433     return result;
434   }
435 
436   if (ValueGuard stream_guard = StreamIsValid()) {
437     result = ::fseek(m_stream, offset, SEEK_CUR);
438 
439     if (error_ptr) {
440       if (result == -1)
441         *error_ptr = Status::FromErrno();
442       else
443         error_ptr->Clear();
444     }
445     return result;
446   }
447 
448   if (error_ptr)
449     *error_ptr = Status::FromErrorString("invalid file handle");
450   return result;
451 }
452 
SeekFromEnd(off_t offset,Status * error_ptr)453 off_t NativeFile::SeekFromEnd(off_t offset, Status *error_ptr) {
454   off_t result = -1;
455   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
456     result = ::lseek(m_descriptor, offset, SEEK_END);
457 
458     if (error_ptr) {
459       if (result == -1)
460         *error_ptr = Status::FromErrno();
461       else
462         error_ptr->Clear();
463     }
464     return result;
465   }
466 
467   if (ValueGuard stream_guard = StreamIsValid()) {
468     result = ::fseek(m_stream, offset, SEEK_END);
469 
470     if (error_ptr) {
471       if (result == -1)
472         *error_ptr = Status::FromErrno();
473       else
474         error_ptr->Clear();
475     }
476   }
477 
478   if (error_ptr)
479     *error_ptr = Status::FromErrorString("invalid file handle");
480   return result;
481 }
482 
Flush()483 Status NativeFile::Flush() {
484   Status error;
485   if (ValueGuard stream_guard = StreamIsValid()) {
486     if (llvm::sys::RetryAfterSignal(EOF, ::fflush, m_stream) == EOF)
487       error = Status::FromErrno();
488     return error;
489   }
490 
491   {
492     ValueGuard descriptor_guard = DescriptorIsValid();
493     if (!descriptor_guard)
494       error = Status::FromErrorString("invalid file handle");
495   }
496   return error;
497 }
498 
Sync()499 Status NativeFile::Sync() {
500   Status error;
501   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
502 #ifdef _WIN32
503     int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor));
504     if (err == 0)
505       error = Status::FromErrorString("unknown error");
506 #else
507     if (llvm::sys::RetryAfterSignal(-1, ::fsync, m_descriptor) == -1)
508       error = Status::FromErrno();
509 #endif
510   } else {
511     error = Status::FromErrorString("invalid file handle");
512   }
513   return error;
514 }
515 
516 #if defined(__APPLE__)
517 // Darwin kernels only can read/write <= INT_MAX bytes
518 #define MAX_READ_SIZE INT_MAX
519 #define MAX_WRITE_SIZE INT_MAX
520 #endif
521 
Read(void * buf,size_t & num_bytes)522 Status NativeFile::Read(void *buf, size_t &num_bytes) {
523   Status error;
524 
525 #if defined(MAX_READ_SIZE)
526   if (num_bytes > MAX_READ_SIZE) {
527     uint8_t *p = (uint8_t *)buf;
528     size_t bytes_left = num_bytes;
529     // Init the num_bytes read to zero
530     num_bytes = 0;
531 
532     while (bytes_left > 0) {
533       size_t curr_num_bytes;
534       if (bytes_left > MAX_READ_SIZE)
535         curr_num_bytes = MAX_READ_SIZE;
536       else
537         curr_num_bytes = bytes_left;
538 
539       error = Read(p + num_bytes, curr_num_bytes);
540 
541       // Update how many bytes were read
542       num_bytes += curr_num_bytes;
543       if (bytes_left < curr_num_bytes)
544         bytes_left = 0;
545       else
546         bytes_left -= curr_num_bytes;
547 
548       if (error.Fail())
549         break;
550     }
551     return error;
552   }
553 #endif
554 
555   ssize_t bytes_read = -1;
556   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
557     bytes_read =
558         llvm::sys::RetryAfterSignal(-1, ::read, m_descriptor, buf, num_bytes);
559     if (bytes_read == -1) {
560       error = Status::FromErrno();
561       num_bytes = 0;
562     } else
563       num_bytes = bytes_read;
564     return error;
565   }
566 
567   if (ValueGuard file_lock = StreamIsValid()) {
568     bytes_read = ::fread(buf, 1, num_bytes, m_stream);
569 
570     if (bytes_read == 0) {
571       if (::feof(m_stream))
572         error = Status::FromErrorString("feof");
573       else if (::ferror(m_stream))
574         error = Status::FromErrorString("ferror");
575       num_bytes = 0;
576     } else
577       num_bytes = bytes_read;
578     return error;
579   }
580 
581   num_bytes = 0;
582   error = Status::FromErrorString("invalid file handle");
583   return error;
584 }
585 
Write(const void * buf,size_t & num_bytes)586 Status NativeFile::Write(const void *buf, size_t &num_bytes) {
587   Status error;
588 
589 #if defined(MAX_WRITE_SIZE)
590   if (num_bytes > MAX_WRITE_SIZE) {
591     const uint8_t *p = (const uint8_t *)buf;
592     size_t bytes_left = num_bytes;
593     // Init the num_bytes written to zero
594     num_bytes = 0;
595 
596     while (bytes_left > 0) {
597       size_t curr_num_bytes;
598       if (bytes_left > MAX_WRITE_SIZE)
599         curr_num_bytes = MAX_WRITE_SIZE;
600       else
601         curr_num_bytes = bytes_left;
602 
603       error = Write(p + num_bytes, curr_num_bytes);
604 
605       // Update how many bytes were read
606       num_bytes += curr_num_bytes;
607       if (bytes_left < curr_num_bytes)
608         bytes_left = 0;
609       else
610         bytes_left -= curr_num_bytes;
611 
612       if (error.Fail())
613         break;
614     }
615     return error;
616   }
617 #endif
618 
619   ssize_t bytes_written = -1;
620   if (ValueGuard descriptor_guard = DescriptorIsValid()) {
621     bytes_written =
622         llvm::sys::RetryAfterSignal(-1, ::write, m_descriptor, buf, num_bytes);
623     if (bytes_written == -1) {
624       error = Status::FromErrno();
625       num_bytes = 0;
626     } else
627       num_bytes = bytes_written;
628     return error;
629   }
630 
631   if (ValueGuard stream_guard = StreamIsValid()) {
632     bytes_written = ::fwrite(buf, 1, num_bytes, m_stream);
633 
634     if (bytes_written == 0) {
635       if (::feof(m_stream))
636         error = Status::FromErrorString("feof");
637       else if (::ferror(m_stream))
638         error = Status::FromErrorString("ferror");
639       num_bytes = 0;
640     } else
641       num_bytes = bytes_written;
642     return error;
643   }
644 
645   num_bytes = 0;
646   error = Status::FromErrorString("invalid file handle");
647   return error;
648 }
649 
Read(void * buf,size_t & num_bytes,off_t & offset)650 Status NativeFile::Read(void *buf, size_t &num_bytes, off_t &offset) {
651   Status error;
652 
653 #if defined(MAX_READ_SIZE)
654   if (num_bytes > MAX_READ_SIZE) {
655     uint8_t *p = (uint8_t *)buf;
656     size_t bytes_left = num_bytes;
657     // Init the num_bytes read to zero
658     num_bytes = 0;
659 
660     while (bytes_left > 0) {
661       size_t curr_num_bytes;
662       if (bytes_left > MAX_READ_SIZE)
663         curr_num_bytes = MAX_READ_SIZE;
664       else
665         curr_num_bytes = bytes_left;
666 
667       error = Read(p + num_bytes, curr_num_bytes, offset);
668 
669       // Update how many bytes were read
670       num_bytes += curr_num_bytes;
671       if (bytes_left < curr_num_bytes)
672         bytes_left = 0;
673       else
674         bytes_left -= curr_num_bytes;
675 
676       if (error.Fail())
677         break;
678     }
679     return error;
680   }
681 #endif
682 
683 #ifndef _WIN32
684   int fd = GetDescriptor();
685   if (fd != kInvalidDescriptor) {
686     ssize_t bytes_read =
687         llvm::sys::RetryAfterSignal(-1, ::pread, fd, buf, num_bytes, offset);
688     if (bytes_read < 0) {
689       num_bytes = 0;
690       error = Status::FromErrno();
691     } else {
692       offset += bytes_read;
693       num_bytes = bytes_read;
694     }
695   } else {
696     num_bytes = 0;
697     error = Status::FromErrorString("invalid file handle");
698   }
699 #else
700   std::lock_guard<std::mutex> guard(offset_access_mutex);
701   long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
702   SeekFromStart(offset);
703   error = Read(buf, num_bytes);
704   if (!error.Fail())
705     SeekFromStart(cur);
706 #endif
707   return error;
708 }
709 
Write(const void * buf,size_t & num_bytes,off_t & offset)710 Status NativeFile::Write(const void *buf, size_t &num_bytes, off_t &offset) {
711   Status error;
712 
713 #if defined(MAX_WRITE_SIZE)
714   if (num_bytes > MAX_WRITE_SIZE) {
715     const uint8_t *p = (const uint8_t *)buf;
716     size_t bytes_left = num_bytes;
717     // Init the num_bytes written to zero
718     num_bytes = 0;
719 
720     while (bytes_left > 0) {
721       size_t curr_num_bytes;
722       if (bytes_left > MAX_WRITE_SIZE)
723         curr_num_bytes = MAX_WRITE_SIZE;
724       else
725         curr_num_bytes = bytes_left;
726 
727       error = Write(p + num_bytes, curr_num_bytes, offset);
728 
729       // Update how many bytes were read
730       num_bytes += curr_num_bytes;
731       if (bytes_left < curr_num_bytes)
732         bytes_left = 0;
733       else
734         bytes_left -= curr_num_bytes;
735 
736       if (error.Fail())
737         break;
738     }
739     return error;
740   }
741 #endif
742 
743   int fd = GetDescriptor();
744   if (fd != kInvalidDescriptor) {
745 #ifndef _WIN32
746     ssize_t bytes_written =
747         llvm::sys::RetryAfterSignal(-1, ::pwrite, m_descriptor, buf, num_bytes, offset);
748     if (bytes_written < 0) {
749       num_bytes = 0;
750       error = Status::FromErrno();
751     } else {
752       offset += bytes_written;
753       num_bytes = bytes_written;
754     }
755 #else
756     std::lock_guard<std::mutex> guard(offset_access_mutex);
757     long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
758     SeekFromStart(offset);
759     error = Write(buf, num_bytes);
760     long after = ::lseek(m_descriptor, 0, SEEK_CUR);
761 
762     if (!error.Fail())
763       SeekFromStart(cur);
764 
765     offset = after;
766 #endif
767   } else {
768     num_bytes = 0;
769     error = Status::FromErrorString("invalid file handle");
770   }
771   return error;
772 }
773 
PrintfVarArg(const char * format,va_list args)774 size_t NativeFile::PrintfVarArg(const char *format, va_list args) {
775   if (StreamIsValid()) {
776     return ::vfprintf(m_stream, format, args);
777   } else {
778     return File::PrintfVarArg(format, args);
779   }
780 }
781 
ConvertOpenOptionsForPOSIXOpen(OpenOptions open_options)782 mode_t File::ConvertOpenOptionsForPOSIXOpen(OpenOptions open_options) {
783   mode_t mode = 0;
784   File::OpenOptions rw =
785       open_options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
786                       File::eOpenOptionReadWrite);
787   if (rw == eOpenOptionReadWrite)
788     mode |= O_RDWR;
789   else if (rw == eOpenOptionWriteOnly)
790     mode |= O_WRONLY;
791   else if (rw == eOpenOptionReadOnly)
792     mode |= O_RDONLY;
793 
794   if (open_options & eOpenOptionAppend)
795     mode |= O_APPEND;
796 
797   if (open_options & eOpenOptionTruncate)
798     mode |= O_TRUNC;
799 
800   if (open_options & eOpenOptionNonBlocking)
801     mode |= O_NONBLOCK;
802 
803   if (open_options & eOpenOptionCanCreateNewOnly)
804     mode |= O_CREAT | O_EXCL;
805   else if (open_options & eOpenOptionCanCreate)
806     mode |= O_CREAT;
807 
808   return mode;
809 }
810 
811 llvm::Expected<SerialPort::Options>
OptionsFromURL(llvm::StringRef urlqs)812 SerialPort::OptionsFromURL(llvm::StringRef urlqs) {
813   SerialPort::Options serial_options;
814   for (llvm::StringRef x : llvm::split(urlqs, '&')) {
815     if (x.consume_front("baud=")) {
816       unsigned int baud_rate;
817       if (!llvm::to_integer(x, baud_rate, 10))
818         return llvm::createStringError(llvm::inconvertibleErrorCode(),
819                                        "Invalid baud rate: %s",
820                                        x.str().c_str());
821       serial_options.BaudRate = baud_rate;
822     } else if (x.consume_front("parity=")) {
823       serial_options.Parity =
824           llvm::StringSwitch<std::optional<Terminal::Parity>>(x)
825               .Case("no", Terminal::Parity::No)
826               .Case("even", Terminal::Parity::Even)
827               .Case("odd", Terminal::Parity::Odd)
828               .Case("mark", Terminal::Parity::Mark)
829               .Case("space", Terminal::Parity::Space)
830               .Default(std::nullopt);
831       if (!serial_options.Parity)
832         return llvm::createStringError(
833             llvm::inconvertibleErrorCode(),
834             "Invalid parity (must be no, even, odd, mark or space): %s",
835             x.str().c_str());
836     } else if (x.consume_front("parity-check=")) {
837       serial_options.ParityCheck =
838           llvm::StringSwitch<std::optional<Terminal::ParityCheck>>(x)
839               .Case("no", Terminal::ParityCheck::No)
840               .Case("replace", Terminal::ParityCheck::ReplaceWithNUL)
841               .Case("ignore", Terminal::ParityCheck::Ignore)
842               // "mark" mode is not currently supported as it requires special
843               // input processing
844               // .Case("mark", Terminal::ParityCheck::Mark)
845               .Default(std::nullopt);
846       if (!serial_options.ParityCheck)
847         return llvm::createStringError(
848             llvm::inconvertibleErrorCode(),
849             "Invalid parity-check (must be no, replace, ignore or mark): %s",
850             x.str().c_str());
851     } else if (x.consume_front("stop-bits=")) {
852       unsigned int stop_bits;
853       if (!llvm::to_integer(x, stop_bits, 10) ||
854           (stop_bits != 1 && stop_bits != 2))
855         return llvm::createStringError(
856             llvm::inconvertibleErrorCode(),
857             "Invalid stop bit number (must be 1 or 2): %s", x.str().c_str());
858       serial_options.StopBits = stop_bits;
859     } else
860       return llvm::createStringError(llvm::inconvertibleErrorCode(),
861                                      "Unknown parameter: %s", x.str().c_str());
862   }
863   return serial_options;
864 }
865 
866 llvm::Expected<std::unique_ptr<SerialPort>>
Create(int fd,OpenOptions options,Options serial_options,bool transfer_ownership)867 SerialPort::Create(int fd, OpenOptions options, Options serial_options,
868                    bool transfer_ownership) {
869   std::unique_ptr<SerialPort> out{
870       new SerialPort(fd, options, serial_options, transfer_ownership)};
871 
872   if (!out->GetIsInteractive())
873     return llvm::createStringError(llvm::inconvertibleErrorCode(),
874                                    "the specified file is not a teletype");
875 
876   Terminal term{fd};
877   if (llvm::Error error = term.SetRaw())
878     return std::move(error);
879   if (serial_options.BaudRate) {
880     if (llvm::Error error = term.SetBaudRate(*serial_options.BaudRate))
881       return std::move(error);
882   }
883   if (serial_options.Parity) {
884     if (llvm::Error error = term.SetParity(*serial_options.Parity))
885       return std::move(error);
886   }
887   if (serial_options.ParityCheck) {
888     if (llvm::Error error = term.SetParityCheck(*serial_options.ParityCheck))
889       return std::move(error);
890   }
891   if (serial_options.StopBits) {
892     if (llvm::Error error = term.SetStopBits(*serial_options.StopBits))
893       return std::move(error);
894   }
895 
896   return std::move(out);
897 }
898 
SerialPort(int fd,OpenOptions options,SerialPort::Options serial_options,bool transfer_ownership)899 SerialPort::SerialPort(int fd, OpenOptions options,
900                        SerialPort::Options serial_options,
901                        bool transfer_ownership)
902     : NativeFile(fd, options, transfer_ownership), m_state(fd) {}
903 
Close()904 Status SerialPort::Close() {
905   m_state.Restore();
906   return NativeFile::Close();
907 }
908 
909 char File::ID = 0;
910 char NativeFile::ID = 0;
911 char SerialPort::ID = 0;
912