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