1 //===-- File.cpp ------------------------------------------------*- C++ -*-===//
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 
41 static const char *GetStreamOpenModeFromOptions(uint32_t options) {
42   if (options & File::eOpenOptionAppend) {
43     if (options & File::eOpenOptionRead) {
44       if (options & File::eOpenOptionCanCreateNewOnly)
45         return "a+x";
46       else
47         return "a+";
48     } else if (options & File::eOpenOptionWrite) {
49       if (options & File::eOpenOptionCanCreateNewOnly)
50         return "ax";
51       else
52         return "a";
53     }
54   } else if (options & File::eOpenOptionRead &&
55              options & File::eOpenOptionWrite) {
56     if (options & File::eOpenOptionCanCreate) {
57       if (options & File::eOpenOptionCanCreateNewOnly)
58         return "w+x";
59       else
60         return "w+";
61     } else
62       return "r+";
63   } else if (options & File::eOpenOptionRead) {
64     return "r";
65   } else if (options & File::eOpenOptionWrite) {
66     return "w";
67   }
68   return nullptr;
69 }
70 
71 int File::kInvalidDescriptor = -1;
72 FILE *File::kInvalidStream = nullptr;
73 
74 File::~File() { Close(); }
75 
76 int File::GetDescriptor() const {
77   if (DescriptorIsValid())
78     return m_descriptor;
79 
80   // Don't open the file descriptor if we don't need to, just get it from the
81   // stream if we have one.
82   if (StreamIsValid()) {
83 #if defined(_WIN32)
84     return _fileno(m_stream);
85 #else
86     return fileno(m_stream);
87 #endif
88   }
89 
90   // Invalid descriptor and invalid stream, return invalid descriptor.
91   return kInvalidDescriptor;
92 }
93 
94 IOObject::WaitableHandle File::GetWaitableHandle() { return GetDescriptor(); }
95 
96 FILE *File::GetStream() {
97   if (!StreamIsValid()) {
98     if (DescriptorIsValid()) {
99       const char *mode = GetStreamOpenModeFromOptions(m_options);
100       if (mode) {
101         if (!m_should_close_fd) {
102 // We must duplicate the file descriptor if we don't own it because when you
103 // call fdopen, the stream will own the fd
104 #ifdef _WIN32
105           m_descriptor = ::_dup(GetDescriptor());
106 #else
107           m_descriptor = dup(GetDescriptor());
108 #endif
109           m_should_close_fd = true;
110         }
111 
112         m_stream =
113             llvm::sys::RetryAfterSignal(nullptr, ::fdopen, m_descriptor, mode);
114 
115         // If we got a stream, then we own the stream and should no longer own
116         // the descriptor because fclose() will close it for us
117 
118         if (m_stream) {
119           m_own_stream = true;
120           m_should_close_fd = false;
121         }
122       }
123     }
124   }
125   return m_stream;
126 }
127 
128 uint32_t File::GetPermissions(Status &error) const {
129   int fd = GetDescriptor();
130   if (fd != kInvalidDescriptor) {
131     struct stat file_stats;
132     if (::fstat(fd, &file_stats) == -1)
133       error.SetErrorToErrno();
134     else {
135       error.Clear();
136       return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
137     }
138   } else {
139     error.SetErrorString("invalid file descriptor");
140   }
141   return 0;
142 }
143 
144 Status File::Close() {
145   Status error;
146   if (StreamIsValid() && m_own_stream) {
147     if (::fclose(m_stream) == EOF)
148       error.SetErrorToErrno();
149   }
150 
151   if (DescriptorIsValid() && m_should_close_fd) {
152     if (::close(m_descriptor) != 0)
153       error.SetErrorToErrno();
154   }
155   m_descriptor = kInvalidDescriptor;
156   m_stream = kInvalidStream;
157   m_options = 0;
158   m_own_stream = false;
159   m_should_close_fd = false;
160   m_is_interactive = eLazyBoolCalculate;
161   m_is_real_terminal = eLazyBoolCalculate;
162   return error;
163 }
164 
165 void File::Clear() {
166   m_stream = nullptr;
167   m_descriptor = kInvalidDescriptor;
168   m_options = 0;
169   m_own_stream = false;
170   m_is_interactive = m_supports_colors = m_is_real_terminal =
171       eLazyBoolCalculate;
172 }
173 
174 Status File::GetFileSpec(FileSpec &file_spec) const {
175   Status error;
176 #ifdef F_GETPATH
177   if (IsValid()) {
178     char path[PATH_MAX];
179     if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1)
180       error.SetErrorToErrno();
181     else
182       file_spec.SetFile(path, FileSpec::Style::native);
183   } else {
184     error.SetErrorString("invalid file handle");
185   }
186 #elif defined(__linux__)
187   char proc[64];
188   char path[PATH_MAX];
189   if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0)
190     error.SetErrorString("cannot resolve file descriptor");
191   else {
192     ssize_t len;
193     if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1)
194       error.SetErrorToErrno();
195     else {
196       path[len] = '\0';
197       file_spec.SetFile(path, FileSpec::Style::native);
198     }
199   }
200 #else
201   error.SetErrorString("File::GetFileSpec is not supported on this platform");
202 #endif
203 
204   if (error.Fail())
205     file_spec.Clear();
206   return error;
207 }
208 
209 off_t File::SeekFromStart(off_t offset, Status *error_ptr) {
210   off_t result = 0;
211   if (DescriptorIsValid()) {
212     result = ::lseek(m_descriptor, offset, SEEK_SET);
213 
214     if (error_ptr) {
215       if (result == -1)
216         error_ptr->SetErrorToErrno();
217       else
218         error_ptr->Clear();
219     }
220   } else if (StreamIsValid()) {
221     result = ::fseek(m_stream, offset, SEEK_SET);
222 
223     if (error_ptr) {
224       if (result == -1)
225         error_ptr->SetErrorToErrno();
226       else
227         error_ptr->Clear();
228     }
229   } else if (error_ptr) {
230     error_ptr->SetErrorString("invalid file handle");
231   }
232   return result;
233 }
234 
235 off_t File::SeekFromCurrent(off_t offset, Status *error_ptr) {
236   off_t result = -1;
237   if (DescriptorIsValid()) {
238     result = ::lseek(m_descriptor, offset, SEEK_CUR);
239 
240     if (error_ptr) {
241       if (result == -1)
242         error_ptr->SetErrorToErrno();
243       else
244         error_ptr->Clear();
245     }
246   } else if (StreamIsValid()) {
247     result = ::fseek(m_stream, offset, SEEK_CUR);
248 
249     if (error_ptr) {
250       if (result == -1)
251         error_ptr->SetErrorToErrno();
252       else
253         error_ptr->Clear();
254     }
255   } else if (error_ptr) {
256     error_ptr->SetErrorString("invalid file handle");
257   }
258   return result;
259 }
260 
261 off_t File::SeekFromEnd(off_t offset, Status *error_ptr) {
262   off_t result = -1;
263   if (DescriptorIsValid()) {
264     result = ::lseek(m_descriptor, offset, SEEK_END);
265 
266     if (error_ptr) {
267       if (result == -1)
268         error_ptr->SetErrorToErrno();
269       else
270         error_ptr->Clear();
271     }
272   } else if (StreamIsValid()) {
273     result = ::fseek(m_stream, offset, SEEK_END);
274 
275     if (error_ptr) {
276       if (result == -1)
277         error_ptr->SetErrorToErrno();
278       else
279         error_ptr->Clear();
280     }
281   } else if (error_ptr) {
282     error_ptr->SetErrorString("invalid file handle");
283   }
284   return result;
285 }
286 
287 Status File::Flush() {
288   Status error;
289   if (StreamIsValid()) {
290     if (llvm::sys::RetryAfterSignal(EOF, ::fflush, m_stream) == EOF)
291       error.SetErrorToErrno();
292   } else if (!DescriptorIsValid()) {
293     error.SetErrorString("invalid file handle");
294   }
295   return error;
296 }
297 
298 Status File::Sync() {
299   Status error;
300   if (DescriptorIsValid()) {
301 #ifdef _WIN32
302     int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor));
303     if (err == 0)
304       error.SetErrorToGenericError();
305 #else
306     if (llvm::sys::RetryAfterSignal(-1, ::fsync, m_descriptor) == -1)
307       error.SetErrorToErrno();
308 #endif
309   } else {
310     error.SetErrorString("invalid file handle");
311   }
312   return error;
313 }
314 
315 #if defined(__APPLE__)
316 // Darwin kernels only can read/write <= INT_MAX bytes
317 #define MAX_READ_SIZE INT_MAX
318 #define MAX_WRITE_SIZE INT_MAX
319 #endif
320 
321 Status File::Read(void *buf, size_t &num_bytes) {
322   Status error;
323 
324 #if defined(MAX_READ_SIZE)
325   if (num_bytes > MAX_READ_SIZE) {
326     uint8_t *p = (uint8_t *)buf;
327     size_t bytes_left = num_bytes;
328     // Init the num_bytes read to zero
329     num_bytes = 0;
330 
331     while (bytes_left > 0) {
332       size_t curr_num_bytes;
333       if (bytes_left > MAX_READ_SIZE)
334         curr_num_bytes = MAX_READ_SIZE;
335       else
336         curr_num_bytes = bytes_left;
337 
338       error = Read(p + num_bytes, curr_num_bytes);
339 
340       // Update how many bytes were read
341       num_bytes += curr_num_bytes;
342       if (bytes_left < curr_num_bytes)
343         bytes_left = 0;
344       else
345         bytes_left -= curr_num_bytes;
346 
347       if (error.Fail())
348         break;
349     }
350     return error;
351   }
352 #endif
353 
354   ssize_t bytes_read = -1;
355   if (DescriptorIsValid()) {
356     bytes_read = llvm::sys::RetryAfterSignal(-1, ::read, m_descriptor, buf, num_bytes);
357     if (bytes_read == -1) {
358       error.SetErrorToErrno();
359       num_bytes = 0;
360     } else
361       num_bytes = bytes_read;
362   } else if (StreamIsValid()) {
363     bytes_read = ::fread(buf, 1, num_bytes, m_stream);
364 
365     if (bytes_read == 0) {
366       if (::feof(m_stream))
367         error.SetErrorString("feof");
368       else if (::ferror(m_stream))
369         error.SetErrorString("ferror");
370       num_bytes = 0;
371     } else
372       num_bytes = bytes_read;
373   } else {
374     num_bytes = 0;
375     error.SetErrorString("invalid file handle");
376   }
377   return error;
378 }
379 
380 Status File::Write(const void *buf, size_t &num_bytes) {
381   Status error;
382 
383 #if defined(MAX_WRITE_SIZE)
384   if (num_bytes > MAX_WRITE_SIZE) {
385     const uint8_t *p = (const uint8_t *)buf;
386     size_t bytes_left = num_bytes;
387     // Init the num_bytes written to zero
388     num_bytes = 0;
389 
390     while (bytes_left > 0) {
391       size_t curr_num_bytes;
392       if (bytes_left > MAX_WRITE_SIZE)
393         curr_num_bytes = MAX_WRITE_SIZE;
394       else
395         curr_num_bytes = bytes_left;
396 
397       error = Write(p + num_bytes, curr_num_bytes);
398 
399       // Update how many bytes were read
400       num_bytes += curr_num_bytes;
401       if (bytes_left < curr_num_bytes)
402         bytes_left = 0;
403       else
404         bytes_left -= curr_num_bytes;
405 
406       if (error.Fail())
407         break;
408     }
409     return error;
410   }
411 #endif
412 
413   ssize_t bytes_written = -1;
414   if (DescriptorIsValid()) {
415     bytes_written =
416         llvm::sys::RetryAfterSignal(-1, ::write, m_descriptor, buf, num_bytes);
417     if (bytes_written == -1) {
418       error.SetErrorToErrno();
419       num_bytes = 0;
420     } else
421       num_bytes = bytes_written;
422   } else if (StreamIsValid()) {
423     bytes_written = ::fwrite(buf, 1, num_bytes, m_stream);
424 
425     if (bytes_written == 0) {
426       if (::feof(m_stream))
427         error.SetErrorString("feof");
428       else if (::ferror(m_stream))
429         error.SetErrorString("ferror");
430       num_bytes = 0;
431     } else
432       num_bytes = bytes_written;
433 
434   } else {
435     num_bytes = 0;
436     error.SetErrorString("invalid file handle");
437   }
438 
439   return error;
440 }
441 
442 Status File::Read(void *buf, size_t &num_bytes, off_t &offset) {
443   Status error;
444 
445 #if defined(MAX_READ_SIZE)
446   if (num_bytes > MAX_READ_SIZE) {
447     uint8_t *p = (uint8_t *)buf;
448     size_t bytes_left = num_bytes;
449     // Init the num_bytes read to zero
450     num_bytes = 0;
451 
452     while (bytes_left > 0) {
453       size_t curr_num_bytes;
454       if (bytes_left > MAX_READ_SIZE)
455         curr_num_bytes = MAX_READ_SIZE;
456       else
457         curr_num_bytes = bytes_left;
458 
459       error = Read(p + num_bytes, curr_num_bytes, offset);
460 
461       // Update how many bytes were read
462       num_bytes += curr_num_bytes;
463       if (bytes_left < curr_num_bytes)
464         bytes_left = 0;
465       else
466         bytes_left -= curr_num_bytes;
467 
468       if (error.Fail())
469         break;
470     }
471     return error;
472   }
473 #endif
474 
475 #ifndef _WIN32
476   int fd = GetDescriptor();
477   if (fd != kInvalidDescriptor) {
478     ssize_t bytes_read =
479         llvm::sys::RetryAfterSignal(-1, ::pread, fd, buf, num_bytes, offset);
480     if (bytes_read < 0) {
481       num_bytes = 0;
482       error.SetErrorToErrno();
483     } else {
484       offset += bytes_read;
485       num_bytes = bytes_read;
486     }
487   } else {
488     num_bytes = 0;
489     error.SetErrorString("invalid file handle");
490   }
491 #else
492   std::lock_guard<std::mutex> guard(offset_access_mutex);
493   long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
494   SeekFromStart(offset);
495   error = Read(buf, num_bytes);
496   if (!error.Fail())
497     SeekFromStart(cur);
498 #endif
499   return error;
500 }
501 
502 Status File::Write(const void *buf, size_t &num_bytes, off_t &offset) {
503   Status error;
504 
505 #if defined(MAX_WRITE_SIZE)
506   if (num_bytes > MAX_WRITE_SIZE) {
507     const uint8_t *p = (const uint8_t *)buf;
508     size_t bytes_left = num_bytes;
509     // Init the num_bytes written to zero
510     num_bytes = 0;
511 
512     while (bytes_left > 0) {
513       size_t curr_num_bytes;
514       if (bytes_left > MAX_WRITE_SIZE)
515         curr_num_bytes = MAX_WRITE_SIZE;
516       else
517         curr_num_bytes = bytes_left;
518 
519       error = Write(p + num_bytes, curr_num_bytes, offset);
520 
521       // Update how many bytes were read
522       num_bytes += curr_num_bytes;
523       if (bytes_left < curr_num_bytes)
524         bytes_left = 0;
525       else
526         bytes_left -= curr_num_bytes;
527 
528       if (error.Fail())
529         break;
530     }
531     return error;
532   }
533 #endif
534 
535   int fd = GetDescriptor();
536   if (fd != kInvalidDescriptor) {
537 #ifndef _WIN32
538     ssize_t bytes_written =
539         llvm::sys::RetryAfterSignal(-1, ::pwrite, m_descriptor, buf, num_bytes, offset);
540     if (bytes_written < 0) {
541       num_bytes = 0;
542       error.SetErrorToErrno();
543     } else {
544       offset += bytes_written;
545       num_bytes = bytes_written;
546     }
547 #else
548     std::lock_guard<std::mutex> guard(offset_access_mutex);
549     long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
550     SeekFromStart(offset);
551     error = Write(buf, num_bytes);
552     long after = ::lseek(m_descriptor, 0, SEEK_CUR);
553 
554     if (!error.Fail())
555       SeekFromStart(cur);
556 
557     offset = after;
558 #endif
559   } else {
560     num_bytes = 0;
561     error.SetErrorString("invalid file handle");
562   }
563   return error;
564 }
565 
566 // Print some formatted output to the stream.
567 size_t File::Printf(const char *format, ...) {
568   va_list args;
569   va_start(args, format);
570   size_t result = PrintfVarArg(format, args);
571   va_end(args);
572   return result;
573 }
574 
575 // Print some formatted output to the stream.
576 size_t File::PrintfVarArg(const char *format, va_list args) {
577   size_t result = 0;
578   if (DescriptorIsValid()) {
579     char *s = nullptr;
580     result = vasprintf(&s, format, args);
581     if (s != nullptr) {
582       if (result > 0) {
583         size_t s_len = result;
584         Write(s, s_len);
585         result = s_len;
586       }
587       free(s);
588     }
589   } else if (StreamIsValid()) {
590     result = ::vfprintf(m_stream, format, args);
591   }
592   return result;
593 }
594 
595 mode_t File::ConvertOpenOptionsForPOSIXOpen(uint32_t open_options) {
596   mode_t mode = 0;
597   if (open_options & eOpenOptionRead && open_options & eOpenOptionWrite)
598     mode |= O_RDWR;
599   else if (open_options & eOpenOptionWrite)
600     mode |= O_WRONLY;
601 
602   if (open_options & eOpenOptionAppend)
603     mode |= O_APPEND;
604 
605   if (open_options & eOpenOptionTruncate)
606     mode |= O_TRUNC;
607 
608   if (open_options & eOpenOptionNonBlocking)
609     mode |= O_NONBLOCK;
610 
611   if (open_options & eOpenOptionCanCreateNewOnly)
612     mode |= O_CREAT | O_EXCL;
613   else if (open_options & eOpenOptionCanCreate)
614     mode |= O_CREAT;
615 
616   return mode;
617 }
618 
619 void File::CalculateInteractiveAndTerminal() {
620   const int fd = GetDescriptor();
621   if (fd >= 0) {
622     m_is_interactive = eLazyBoolNo;
623     m_is_real_terminal = eLazyBoolNo;
624 #if defined(_WIN32)
625     if (_isatty(fd)) {
626       m_is_interactive = eLazyBoolYes;
627       m_is_real_terminal = eLazyBoolYes;
628 #if defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
629       m_supports_colors = eLazyBoolYes;
630 #endif
631     }
632 #else
633     if (isatty(fd)) {
634       m_is_interactive = eLazyBoolYes;
635       struct winsize window_size;
636       if (::ioctl(fd, TIOCGWINSZ, &window_size) == 0) {
637         if (window_size.ws_col > 0) {
638           m_is_real_terminal = eLazyBoolYes;
639           if (llvm::sys::Process::FileDescriptorHasColors(fd))
640             m_supports_colors = eLazyBoolYes;
641         }
642       }
643     }
644 #endif
645   }
646 }
647 
648 bool File::GetIsInteractive() {
649   if (m_is_interactive == eLazyBoolCalculate)
650     CalculateInteractiveAndTerminal();
651   return m_is_interactive == eLazyBoolYes;
652 }
653 
654 bool File::GetIsRealTerminal() {
655   if (m_is_real_terminal == eLazyBoolCalculate)
656     CalculateInteractiveAndTerminal();
657   return m_is_real_terminal == eLazyBoolYes;
658 }
659 
660 bool File::GetIsTerminalWithColors() {
661   if (m_supports_colors == eLazyBoolCalculate)
662     CalculateInteractiveAndTerminal();
663   return m_supports_colors == eLazyBoolYes;
664 }
665