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 | EC: llvm::inconvertibleErrorCode(), |
77 | Msg: "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(S0: "r" , S1: "rb" , Value: eOpenOptionReadOnly) |
84 | .Cases(S0: "w" , S1: "wb" , Value: eOpenOptionWriteOnly) |
85 | .Cases(S0: "a" , S1: "ab" , |
86 | Value: eOpenOptionWriteOnly | eOpenOptionAppend | |
87 | eOpenOptionCanCreate) |
88 | .Cases(S0: "r+" , S1: "rb+" , S2: "r+b" , Value: eOpenOptionReadWrite) |
89 | .Cases(S0: "w+" , S1: "wb+" , S2: "w+b" , |
90 | Value: eOpenOptionReadWrite | eOpenOptionCanCreate | |
91 | eOpenOptionTruncate) |
92 | .Cases(S0: "a+" , S1: "ab+" , S2: "a+b" , |
93 | Value: eOpenOptionReadWrite | eOpenOptionAppend | |
94 | eOpenOptionCanCreate) |
95 | .Default(Value: eOpenOptionInvalid); |
96 | if (opts != eOpenOptionInvalid) |
97 | return opts; |
98 | return llvm::createStringError( |
99 | EC: llvm::inconvertibleErrorCode(), |
100 | Msg: "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(descriptor: 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: fd)) { |
180 | m_is_interactive = eLazyBoolYes; |
181 | struct winsize window_size; |
182 | if (::ioctl(fd: 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(buf&: s, fmt: format, args)) { |
222 | size_t written = s.size(); |
223 | Write(buf: s.data(), num_bytes&: written); |
224 | return written; |
225 | } |
226 | return 0; |
227 | } |
228 | |
229 | Expected<File::OpenOptions> File::GetOptions() const { |
230 | return llvm::createStringError( |
231 | EC: llvm::inconvertibleErrorCode(), |
232 | Msg: "GetOptions() not implemented for this File class" ); |
233 | } |
234 | |
235 | uint32_t File::GetPermissions(Status &error) const { |
236 | int fd = GetDescriptor(); |
237 | if (!DescriptorIsValid(descriptor: fd)) { |
238 | error = std::error_code(ENOTSUP, std::system_category()); |
239 | return 0; |
240 | } |
241 | struct stat file_stats; |
242 | if (::fstat(fd: fd, buf: &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(stream: 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(options: m_options); |
285 | if (!mode) |
286 | llvm::consumeError(Err: 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(fd: m_descriptor); |
295 | #endif |
296 | m_own_descriptor = true; |
297 | } |
298 | |
299 | m_stream = llvm::sys::RetryAfterSignal(Fail: nullptr, F&: ::fdopen, As: m_descriptor, |
300 | As: 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(stream: 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(stream: m_stream) == EOF) |
331 | error.SetErrorToErrno(); |
332 | } |
333 | } |
334 | } |
335 | |
336 | if (DescriptorIsValidUnlocked() && m_own_descriptor) { |
337 | if (::close(fd: 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(s: proc, maxlen: sizeof(proc), format: "/proc/self/fd/%d" , GetDescriptor()) < 0) |
367 | error.SetErrorString("cannot resolve file descriptor" ); |
368 | else { |
369 | ssize_t len; |
370 | if ((len = ::readlink(path: proc, buf: path, len: sizeof(path) - 1)) == -1) |
371 | error.SetErrorToErrno(); |
372 | else { |
373 | path[len] = '\0'; |
374 | file_spec.SetFile(path, style: 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(fd: m_descriptor, offset: 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(stream: m_stream, off: 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(fd: m_descriptor, offset: 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(stream: m_stream, off: 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(fd: m_descriptor, offset: 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(stream: m_stream, off: 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, F&: ::fflush, As: 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(Fail: -1, F&: ::fsync, As: 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(Fail: -1, F&: ::read, As: m_descriptor, As: buf, As: 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(ptr: buf, size: 1, n: num_bytes, stream: m_stream); |
565 | |
566 | if (bytes_read == 0) { |
567 | if (::feof(stream: m_stream)) |
568 | error.SetErrorString("feof" ); |
569 | else if (::ferror(stream: 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(Fail: -1, F&: ::write, As: m_descriptor, As: buf, As: 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(ptr: buf, size: 1, n: num_bytes, s: m_stream); |
629 | |
630 | if (bytes_written == 0) { |
631 | if (::feof(stream: m_stream)) |
632 | error.SetErrorString("feof" ); |
633 | else if (::ferror(stream: 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(Fail: -1, F&: ::pread, As: fd, As: buf, As: num_bytes, As: 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(Fail: -1, F&: ::pwrite, As: m_descriptor, As: buf, As: num_bytes, As: 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(s: m_stream, format: format, arg: 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(Str: urlqs, Separator: '&')) { |
811 | if (x.consume_front(Prefix: "baud=" )) { |
812 | unsigned int baud_rate; |
813 | if (!llvm::to_integer(S: x, Num&: baud_rate, Base: 10)) |
814 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
815 | Fmt: "Invalid baud rate: %s" , |
816 | Vals: x.str().c_str()); |
817 | serial_options.BaudRate = baud_rate; |
818 | } else if (x.consume_front(Prefix: "parity=" )) { |
819 | serial_options.Parity = |
820 | llvm::StringSwitch<std::optional<Terminal::Parity>>(x) |
821 | .Case(S: "no" , Value: Terminal::Parity::No) |
822 | .Case(S: "even" , Value: Terminal::Parity::Even) |
823 | .Case(S: "odd" , Value: Terminal::Parity::Odd) |
824 | .Case(S: "mark" , Value: Terminal::Parity::Mark) |
825 | .Case(S: "space" , Value: Terminal::Parity::Space) |
826 | .Default(Value: std::nullopt); |
827 | if (!serial_options.Parity) |
828 | return llvm::createStringError( |
829 | EC: llvm::inconvertibleErrorCode(), |
830 | Fmt: "Invalid parity (must be no, even, odd, mark or space): %s" , |
831 | Vals: x.str().c_str()); |
832 | } else if (x.consume_front(Prefix: "parity-check=" )) { |
833 | serial_options.ParityCheck = |
834 | llvm::StringSwitch<std::optional<Terminal::ParityCheck>>(x) |
835 | .Case(S: "no" , Value: Terminal::ParityCheck::No) |
836 | .Case(S: "replace" , Value: Terminal::ParityCheck::ReplaceWithNUL) |
837 | .Case(S: "ignore" , Value: 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(Value: std::nullopt); |
842 | if (!serial_options.ParityCheck) |
843 | return llvm::createStringError( |
844 | EC: llvm::inconvertibleErrorCode(), |
845 | Fmt: "Invalid parity-check (must be no, replace, ignore or mark): %s" , |
846 | Vals: x.str().c_str()); |
847 | } else if (x.consume_front(Prefix: "stop-bits=" )) { |
848 | unsigned int stop_bits; |
849 | if (!llvm::to_integer(S: x, Num&: stop_bits, Base: 10) || |
850 | (stop_bits != 1 && stop_bits != 2)) |
851 | return llvm::createStringError( |
852 | EC: llvm::inconvertibleErrorCode(), |
853 | Fmt: "Invalid stop bit number (must be 1 or 2): %s" , Vals: x.str().c_str()); |
854 | serial_options.StopBits = stop_bits; |
855 | } else |
856 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
857 | Fmt: "Unknown parameter: %s" , Vals: 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(EC: llvm::inconvertibleErrorCode(), |
870 | Msg: "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 | |