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 <errno.h>
12 #include <fcntl.h>
13 #include <limits.h>
14 #include <stdarg.h>
15 #include <stdio.h>
16
17 #ifdef _WIN32
18 #include "lldb/Host/windows/windows.h"
19 #else
20 #include <sys/ioctl.h>
21 #include <sys/stat.h>
22 #include <termios.h>
23 #include <unistd.h>
24 #endif
25
26 #include "llvm/Support/ConvertUTF.h"
27 #include "llvm/Support/Errno.h"
28 #include "llvm/Support/FileSystem.h"
29 #include "llvm/Support/Process.h"
30
31 #include "lldb/Host/Config.h"
32 #include "lldb/Host/FileSystem.h"
33 #include "lldb/Host/Host.h"
34 #include "lldb/Utility/DataBufferHeap.h"
35 #include "lldb/Utility/FileSpec.h"
36 #include "lldb/Utility/Log.h"
37
38 using namespace lldb;
39 using namespace lldb_private;
40 using llvm::Expected;
41
42 Expected<const char *>
GetStreamOpenModeFromOptions(File::OpenOptions options)43 File::GetStreamOpenModeFromOptions(File::OpenOptions options) {
44 if (options & File::eOpenOptionAppend) {
45 if (options & File::eOpenOptionRead) {
46 if (options & File::eOpenOptionCanCreateNewOnly)
47 return "a+x";
48 else
49 return "a+";
50 } else if (options & File::eOpenOptionWrite) {
51 if (options & File::eOpenOptionCanCreateNewOnly)
52 return "ax";
53 else
54 return "a";
55 }
56 } else if (options & File::eOpenOptionRead &&
57 options & File::eOpenOptionWrite) {
58 if (options & File::eOpenOptionCanCreate) {
59 if (options & File::eOpenOptionCanCreateNewOnly)
60 return "w+x";
61 else
62 return "w+";
63 } else
64 return "r+";
65 } else if (options & File::eOpenOptionRead) {
66 return "r";
67 } else if (options & File::eOpenOptionWrite) {
68 return "w";
69 }
70 return llvm::createStringError(
71 llvm::inconvertibleErrorCode(),
72 "invalid options, cannot convert to mode string");
73 }
74
GetOptionsFromMode(llvm::StringRef mode)75 Expected<File::OpenOptions> File::GetOptionsFromMode(llvm::StringRef mode) {
76 OpenOptions opts =
77 llvm::StringSwitch<OpenOptions>(mode)
78 .Cases("r", "rb", eOpenOptionRead)
79 .Cases("w", "wb", eOpenOptionWrite)
80 .Cases("a", "ab",
81 eOpenOptionWrite | eOpenOptionAppend | eOpenOptionCanCreate)
82 .Cases("r+", "rb+", "r+b", eOpenOptionRead | eOpenOptionWrite)
83 .Cases("w+", "wb+", "w+b",
84 eOpenOptionRead | eOpenOptionWrite | eOpenOptionCanCreate |
85 eOpenOptionTruncate)
86 .Cases("a+", "ab+", "a+b",
87 eOpenOptionRead | eOpenOptionWrite | eOpenOptionAppend |
88 eOpenOptionCanCreate)
89 .Default(OpenOptions());
90 if (opts)
91 return opts;
92 return llvm::createStringError(
93 llvm::inconvertibleErrorCode(),
94 "invalid mode, cannot convert to File::OpenOptions");
95 }
96
97 int File::kInvalidDescriptor = -1;
98 FILE *File::kInvalidStream = nullptr;
99
Read(void * buf,size_t & num_bytes)100 Status File::Read(void *buf, size_t &num_bytes) {
101 return std::error_code(ENOTSUP, std::system_category());
102 }
Write(const void * buf,size_t & num_bytes)103 Status File::Write(const void *buf, size_t &num_bytes) {
104 return std::error_code(ENOTSUP, std::system_category());
105 }
106
IsValid() const107 bool File::IsValid() const { return false; }
108
Close()109 Status File::Close() { return Flush(); }
110
GetWaitableHandle()111 IOObject::WaitableHandle File::GetWaitableHandle() {
112 return IOObject::kInvalidHandleValue;
113 }
114
GetFileSpec(FileSpec & file_spec) const115 Status File::GetFileSpec(FileSpec &file_spec) const {
116 file_spec.Clear();
117 return std::error_code(ENOTSUP, std::system_category());
118 }
119
GetDescriptor() const120 int File::GetDescriptor() const { return kInvalidDescriptor; }
121
GetStream()122 FILE *File::GetStream() { return nullptr; }
123
SeekFromStart(off_t offset,Status * error_ptr)124 off_t File::SeekFromStart(off_t offset, Status *error_ptr) {
125 if (error_ptr)
126 *error_ptr = std::error_code(ENOTSUP, std::system_category());
127 return -1;
128 }
129
SeekFromCurrent(off_t offset,Status * error_ptr)130 off_t File::SeekFromCurrent(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
SeekFromEnd(off_t offset,Status * error_ptr)136 off_t File::SeekFromEnd(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
Read(void * dst,size_t & num_bytes,off_t & offset)142 Status File::Read(void *dst, size_t &num_bytes, off_t &offset) {
143 return std::error_code(ENOTSUP, std::system_category());
144 }
145
Write(const void * src,size_t & num_bytes,off_t & offset)146 Status File::Write(const void *src, size_t &num_bytes, off_t &offset) {
147 return std::error_code(ENOTSUP, std::system_category());
148 }
149
Flush()150 Status File::Flush() { return Status(); }
151
Sync()152 Status File::Sync() { return Flush(); }
153
CalculateInteractiveAndTerminal()154 void File::CalculateInteractiveAndTerminal() {
155 const int fd = GetDescriptor();
156 if (!DescriptorIsValid(fd)) {
157 m_is_interactive = eLazyBoolNo;
158 m_is_real_terminal = eLazyBoolNo;
159 m_supports_colors = eLazyBoolNo;
160 return;
161 }
162 m_is_interactive = eLazyBoolNo;
163 m_is_real_terminal = eLazyBoolNo;
164 #if defined(_WIN32)
165 if (_isatty(fd)) {
166 m_is_interactive = eLazyBoolYes;
167 m_is_real_terminal = eLazyBoolYes;
168 #if defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
169 m_supports_colors = eLazyBoolYes;
170 #endif
171 }
172 #else
173 if (isatty(fd)) {
174 m_is_interactive = eLazyBoolYes;
175 struct winsize window_size;
176 if (::ioctl(fd, TIOCGWINSZ, &window_size) == 0) {
177 if (window_size.ws_col > 0) {
178 m_is_real_terminal = eLazyBoolYes;
179 if (llvm::sys::Process::FileDescriptorHasColors(fd))
180 m_supports_colors = eLazyBoolYes;
181 }
182 }
183 }
184 #endif
185 }
186
GetIsInteractive()187 bool File::GetIsInteractive() {
188 if (m_is_interactive == eLazyBoolCalculate)
189 CalculateInteractiveAndTerminal();
190 return m_is_interactive == eLazyBoolYes;
191 }
192
GetIsRealTerminal()193 bool File::GetIsRealTerminal() {
194 if (m_is_real_terminal == eLazyBoolCalculate)
195 CalculateInteractiveAndTerminal();
196 return m_is_real_terminal == eLazyBoolYes;
197 }
198
GetIsTerminalWithColors()199 bool File::GetIsTerminalWithColors() {
200 if (m_supports_colors == eLazyBoolCalculate)
201 CalculateInteractiveAndTerminal();
202 return m_supports_colors == eLazyBoolYes;
203 }
204
Printf(const char * format,...)205 size_t File::Printf(const char *format, ...) {
206 va_list args;
207 va_start(args, format);
208 size_t result = PrintfVarArg(format, args);
209 va_end(args);
210 return result;
211 }
212
PrintfVarArg(const char * format,va_list args)213 size_t File::PrintfVarArg(const char *format, va_list args) {
214 size_t result = 0;
215 char *s = nullptr;
216 result = vasprintf(&s, format, args);
217 if (s != nullptr) {
218 if (result > 0) {
219 size_t s_len = result;
220 Write(s, s_len);
221 result = s_len;
222 }
223 free(s);
224 }
225 return result;
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 if (m_options & eOpenOptionWrite) {
314 if (::fflush(m_stream) == EOF)
315 error.SetErrorToErrno();
316 }
317 }
318 if (DescriptorIsValid() && m_own_descriptor) {
319 if (::close(m_descriptor) != 0)
320 error.SetErrorToErrno();
321 }
322 m_descriptor = kInvalidDescriptor;
323 m_stream = kInvalidStream;
324 m_options = OpenOptions(0);
325 m_own_stream = false;
326 m_own_descriptor = false;
327 m_is_interactive = eLazyBoolCalculate;
328 m_is_real_terminal = eLazyBoolCalculate;
329 return error;
330 }
331
GetFileSpec(FileSpec & file_spec) const332 Status NativeFile::GetFileSpec(FileSpec &file_spec) const {
333 Status error;
334 #ifdef F_GETPATH
335 if (IsValid()) {
336 char path[PATH_MAX];
337 if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1)
338 error.SetErrorToErrno();
339 else
340 file_spec.SetFile(path, FileSpec::Style::native);
341 } else {
342 error.SetErrorString("invalid file handle");
343 }
344 #elif defined(__linux__)
345 char proc[64];
346 char path[PATH_MAX];
347 if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0)
348 error.SetErrorString("cannot resolve file descriptor");
349 else {
350 ssize_t len;
351 if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1)
352 error.SetErrorToErrno();
353 else {
354 path[len] = '\0';
355 file_spec.SetFile(path, FileSpec::Style::native);
356 }
357 }
358 #else
359 error.SetErrorString(
360 "NativeFile::GetFileSpec is not supported on this platform");
361 #endif
362
363 if (error.Fail())
364 file_spec.Clear();
365 return error;
366 }
367
SeekFromStart(off_t offset,Status * error_ptr)368 off_t NativeFile::SeekFromStart(off_t offset, Status *error_ptr) {
369 off_t result = 0;
370 if (DescriptorIsValid()) {
371 result = ::lseek(m_descriptor, offset, SEEK_SET);
372
373 if (error_ptr) {
374 if (result == -1)
375 error_ptr->SetErrorToErrno();
376 else
377 error_ptr->Clear();
378 }
379 } else if (StreamIsValid()) {
380 result = ::fseek(m_stream, offset, SEEK_SET);
381
382 if (error_ptr) {
383 if (result == -1)
384 error_ptr->SetErrorToErrno();
385 else
386 error_ptr->Clear();
387 }
388 } else if (error_ptr) {
389 error_ptr->SetErrorString("invalid file handle");
390 }
391 return result;
392 }
393
SeekFromCurrent(off_t offset,Status * error_ptr)394 off_t NativeFile::SeekFromCurrent(off_t offset, Status *error_ptr) {
395 off_t result = -1;
396 if (DescriptorIsValid()) {
397 result = ::lseek(m_descriptor, offset, SEEK_CUR);
398
399 if (error_ptr) {
400 if (result == -1)
401 error_ptr->SetErrorToErrno();
402 else
403 error_ptr->Clear();
404 }
405 } else if (StreamIsValid()) {
406 result = ::fseek(m_stream, offset, SEEK_CUR);
407
408 if (error_ptr) {
409 if (result == -1)
410 error_ptr->SetErrorToErrno();
411 else
412 error_ptr->Clear();
413 }
414 } else if (error_ptr) {
415 error_ptr->SetErrorString("invalid file handle");
416 }
417 return result;
418 }
419
SeekFromEnd(off_t offset,Status * error_ptr)420 off_t NativeFile::SeekFromEnd(off_t offset, Status *error_ptr) {
421 off_t result = -1;
422 if (DescriptorIsValid()) {
423 result = ::lseek(m_descriptor, offset, SEEK_END);
424
425 if (error_ptr) {
426 if (result == -1)
427 error_ptr->SetErrorToErrno();
428 else
429 error_ptr->Clear();
430 }
431 } else if (StreamIsValid()) {
432 result = ::fseek(m_stream, offset, SEEK_END);
433
434 if (error_ptr) {
435 if (result == -1)
436 error_ptr->SetErrorToErrno();
437 else
438 error_ptr->Clear();
439 }
440 } else if (error_ptr) {
441 error_ptr->SetErrorString("invalid file handle");
442 }
443 return result;
444 }
445
Flush()446 Status NativeFile::Flush() {
447 Status error;
448 if (StreamIsValid()) {
449 if (llvm::sys::RetryAfterSignal(EOF, ::fflush, m_stream) == EOF)
450 error.SetErrorToErrno();
451 } else if (!DescriptorIsValid()) {
452 error.SetErrorString("invalid file handle");
453 }
454 return error;
455 }
456
Sync()457 Status NativeFile::Sync() {
458 Status error;
459 if (DescriptorIsValid()) {
460 #ifdef _WIN32
461 int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor));
462 if (err == 0)
463 error.SetErrorToGenericError();
464 #else
465 if (llvm::sys::RetryAfterSignal(-1, ::fsync, m_descriptor) == -1)
466 error.SetErrorToErrno();
467 #endif
468 } else {
469 error.SetErrorString("invalid file handle");
470 }
471 return error;
472 }
473
474 #if defined(__APPLE__)
475 // Darwin kernels only can read/write <= INT_MAX bytes
476 #define MAX_READ_SIZE INT_MAX
477 #define MAX_WRITE_SIZE INT_MAX
478 #endif
479
Read(void * buf,size_t & num_bytes)480 Status NativeFile::Read(void *buf, size_t &num_bytes) {
481 Status error;
482
483 #if defined(MAX_READ_SIZE)
484 if (num_bytes > MAX_READ_SIZE) {
485 uint8_t *p = (uint8_t *)buf;
486 size_t bytes_left = num_bytes;
487 // Init the num_bytes read to zero
488 num_bytes = 0;
489
490 while (bytes_left > 0) {
491 size_t curr_num_bytes;
492 if (bytes_left > MAX_READ_SIZE)
493 curr_num_bytes = MAX_READ_SIZE;
494 else
495 curr_num_bytes = bytes_left;
496
497 error = Read(p + num_bytes, curr_num_bytes);
498
499 // Update how many bytes were read
500 num_bytes += curr_num_bytes;
501 if (bytes_left < curr_num_bytes)
502 bytes_left = 0;
503 else
504 bytes_left -= curr_num_bytes;
505
506 if (error.Fail())
507 break;
508 }
509 return error;
510 }
511 #endif
512
513 ssize_t bytes_read = -1;
514 if (DescriptorIsValid()) {
515 bytes_read = llvm::sys::RetryAfterSignal(-1, ::read, m_descriptor, buf, num_bytes);
516 if (bytes_read == -1) {
517 error.SetErrorToErrno();
518 num_bytes = 0;
519 } else
520 num_bytes = bytes_read;
521 } else if (StreamIsValid()) {
522 bytes_read = ::fread(buf, 1, num_bytes, m_stream);
523
524 if (bytes_read == 0) {
525 if (::feof(m_stream))
526 error.SetErrorString("feof");
527 else if (::ferror(m_stream))
528 error.SetErrorString("ferror");
529 num_bytes = 0;
530 } else
531 num_bytes = bytes_read;
532 } else {
533 num_bytes = 0;
534 error.SetErrorString("invalid file handle");
535 }
536 return error;
537 }
538
Write(const void * buf,size_t & num_bytes)539 Status NativeFile::Write(const void *buf, size_t &num_bytes) {
540 Status error;
541
542 #if defined(MAX_WRITE_SIZE)
543 if (num_bytes > MAX_WRITE_SIZE) {
544 const uint8_t *p = (const uint8_t *)buf;
545 size_t bytes_left = num_bytes;
546 // Init the num_bytes written to zero
547 num_bytes = 0;
548
549 while (bytes_left > 0) {
550 size_t curr_num_bytes;
551 if (bytes_left > MAX_WRITE_SIZE)
552 curr_num_bytes = MAX_WRITE_SIZE;
553 else
554 curr_num_bytes = bytes_left;
555
556 error = Write(p + num_bytes, curr_num_bytes);
557
558 // Update how many bytes were read
559 num_bytes += curr_num_bytes;
560 if (bytes_left < curr_num_bytes)
561 bytes_left = 0;
562 else
563 bytes_left -= curr_num_bytes;
564
565 if (error.Fail())
566 break;
567 }
568 return error;
569 }
570 #endif
571
572 ssize_t bytes_written = -1;
573 if (DescriptorIsValid()) {
574 bytes_written =
575 llvm::sys::RetryAfterSignal(-1, ::write, m_descriptor, buf, num_bytes);
576 if (bytes_written == -1) {
577 error.SetErrorToErrno();
578 num_bytes = 0;
579 } else
580 num_bytes = bytes_written;
581 } else if (StreamIsValid()) {
582 bytes_written = ::fwrite(buf, 1, num_bytes, m_stream);
583
584 if (bytes_written == 0) {
585 if (::feof(m_stream))
586 error.SetErrorString("feof");
587 else if (::ferror(m_stream))
588 error.SetErrorString("ferror");
589 num_bytes = 0;
590 } else
591 num_bytes = bytes_written;
592
593 } else {
594 num_bytes = 0;
595 error.SetErrorString("invalid file handle");
596 }
597
598 return error;
599 }
600
Read(void * buf,size_t & num_bytes,off_t & offset)601 Status NativeFile::Read(void *buf, size_t &num_bytes, off_t &offset) {
602 Status error;
603
604 #if defined(MAX_READ_SIZE)
605 if (num_bytes > MAX_READ_SIZE) {
606 uint8_t *p = (uint8_t *)buf;
607 size_t bytes_left = num_bytes;
608 // Init the num_bytes read to zero
609 num_bytes = 0;
610
611 while (bytes_left > 0) {
612 size_t curr_num_bytes;
613 if (bytes_left > MAX_READ_SIZE)
614 curr_num_bytes = MAX_READ_SIZE;
615 else
616 curr_num_bytes = bytes_left;
617
618 error = Read(p + num_bytes, curr_num_bytes, offset);
619
620 // Update how many bytes were read
621 num_bytes += curr_num_bytes;
622 if (bytes_left < curr_num_bytes)
623 bytes_left = 0;
624 else
625 bytes_left -= curr_num_bytes;
626
627 if (error.Fail())
628 break;
629 }
630 return error;
631 }
632 #endif
633
634 #ifndef _WIN32
635 int fd = GetDescriptor();
636 if (fd != kInvalidDescriptor) {
637 ssize_t bytes_read =
638 llvm::sys::RetryAfterSignal(-1, ::pread, fd, buf, num_bytes, offset);
639 if (bytes_read < 0) {
640 num_bytes = 0;
641 error.SetErrorToErrno();
642 } else {
643 offset += bytes_read;
644 num_bytes = bytes_read;
645 }
646 } else {
647 num_bytes = 0;
648 error.SetErrorString("invalid file handle");
649 }
650 #else
651 std::lock_guard<std::mutex> guard(offset_access_mutex);
652 long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
653 SeekFromStart(offset);
654 error = Read(buf, num_bytes);
655 if (!error.Fail())
656 SeekFromStart(cur);
657 #endif
658 return error;
659 }
660
Write(const void * buf,size_t & num_bytes,off_t & offset)661 Status NativeFile::Write(const void *buf, size_t &num_bytes, off_t &offset) {
662 Status error;
663
664 #if defined(MAX_WRITE_SIZE)
665 if (num_bytes > MAX_WRITE_SIZE) {
666 const uint8_t *p = (const uint8_t *)buf;
667 size_t bytes_left = num_bytes;
668 // Init the num_bytes written to zero
669 num_bytes = 0;
670
671 while (bytes_left > 0) {
672 size_t curr_num_bytes;
673 if (bytes_left > MAX_WRITE_SIZE)
674 curr_num_bytes = MAX_WRITE_SIZE;
675 else
676 curr_num_bytes = bytes_left;
677
678 error = Write(p + num_bytes, curr_num_bytes, offset);
679
680 // Update how many bytes were read
681 num_bytes += curr_num_bytes;
682 if (bytes_left < curr_num_bytes)
683 bytes_left = 0;
684 else
685 bytes_left -= curr_num_bytes;
686
687 if (error.Fail())
688 break;
689 }
690 return error;
691 }
692 #endif
693
694 int fd = GetDescriptor();
695 if (fd != kInvalidDescriptor) {
696 #ifndef _WIN32
697 ssize_t bytes_written =
698 llvm::sys::RetryAfterSignal(-1, ::pwrite, m_descriptor, buf, num_bytes, offset);
699 if (bytes_written < 0) {
700 num_bytes = 0;
701 error.SetErrorToErrno();
702 } else {
703 offset += bytes_written;
704 num_bytes = bytes_written;
705 }
706 #else
707 std::lock_guard<std::mutex> guard(offset_access_mutex);
708 long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
709 SeekFromStart(offset);
710 error = Write(buf, num_bytes);
711 long after = ::lseek(m_descriptor, 0, SEEK_CUR);
712
713 if (!error.Fail())
714 SeekFromStart(cur);
715
716 offset = after;
717 #endif
718 } else {
719 num_bytes = 0;
720 error.SetErrorString("invalid file handle");
721 }
722 return error;
723 }
724
PrintfVarArg(const char * format,va_list args)725 size_t NativeFile::PrintfVarArg(const char *format, va_list args) {
726 if (StreamIsValid()) {
727 return ::vfprintf(m_stream, format, args);
728 } else {
729 return File::PrintfVarArg(format, args);
730 }
731 }
732
ConvertOpenOptionsForPOSIXOpen(OpenOptions open_options)733 mode_t File::ConvertOpenOptionsForPOSIXOpen(OpenOptions open_options) {
734 mode_t mode = 0;
735 if (open_options & eOpenOptionRead && open_options & eOpenOptionWrite)
736 mode |= O_RDWR;
737 else if (open_options & eOpenOptionWrite)
738 mode |= O_WRONLY;
739
740 if (open_options & eOpenOptionAppend)
741 mode |= O_APPEND;
742
743 if (open_options & eOpenOptionTruncate)
744 mode |= O_TRUNC;
745
746 if (open_options & eOpenOptionNonBlocking)
747 mode |= O_NONBLOCK;
748
749 if (open_options & eOpenOptionCanCreateNewOnly)
750 mode |= O_CREAT | O_EXCL;
751 else if (open_options & eOpenOptionCanCreate)
752 mode |= O_CREAT;
753
754 return mode;
755 }
756
757 char File::ID = 0;
758 char NativeFile::ID = 0;
759