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