blob: 500b03ce42ef7e886e68c4458eb6f87f5cf3668e [file] [log] [blame]
//===-- File.cpp ------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/Host/File.h"
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/stat.h>
#ifdef _WIN32
#include "lldb/Host/windows/windows.h"
#else
#include <sys/ioctl.h>
#endif
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Core/Error.h"
#include "lldb/Host/Config.h"
#include "lldb/Host/FileSpec.h"
using namespace lldb;
using namespace lldb_private;
static const char *
GetStreamOpenModeFromOptions (uint32_t options)
{
if (options & File::eOpenOptionAppend)
{
if (options & File::eOpenOptionRead)
{
if (options & File::eOpenOptionCanCreateNewOnly)
return "a+x";
else
return "a+";
}
else if (options & File::eOpenOptionWrite)
{
if (options & File::eOpenOptionCanCreateNewOnly)
return "ax";
else
return "a";
}
}
else if (options & File::eOpenOptionRead && options & File::eOpenOptionWrite)
{
if (options & File::eOpenOptionCanCreate)
{
if (options & File::eOpenOptionCanCreateNewOnly)
return "w+x";
else
return "w+";
}
else
return "r+";
}
else if (options & File::eOpenOptionRead)
{
return "r";
}
else if (options & File::eOpenOptionWrite)
{
return "w";
}
return NULL;
}
int File::kInvalidDescriptor = -1;
FILE * File::kInvalidStream = NULL;
File::File(const char *path, uint32_t options, uint32_t permissions) :
m_descriptor (kInvalidDescriptor),
m_stream (kInvalidStream),
m_options (),
m_own_stream (false),
m_own_descriptor (false),
m_is_interactive (eLazyBoolCalculate),
m_is_real_terminal (eLazyBoolCalculate)
{
Open (path, options, permissions);
}
File::File (const FileSpec& filespec,
uint32_t options,
uint32_t permissions) :
m_descriptor (kInvalidDescriptor),
m_stream (kInvalidStream),
m_options (0),
m_own_stream (false),
m_own_descriptor (false),
m_is_interactive (eLazyBoolCalculate),
m_is_real_terminal (eLazyBoolCalculate)
{
if (filespec)
{
Open (filespec.GetPath().c_str(), options, permissions);
}
}
File::File (const File &rhs) :
m_descriptor (kInvalidDescriptor),
m_stream (kInvalidStream),
m_options (0),
m_own_stream (false),
m_own_descriptor (false),
m_is_interactive (eLazyBoolCalculate),
m_is_real_terminal (eLazyBoolCalculate)
{
Duplicate (rhs);
}
File &
File::operator = (const File &rhs)
{
if (this != &rhs)
Duplicate (rhs);
return *this;
}
File::~File()
{
Close ();
}
int
File::GetDescriptor() const
{
if (DescriptorIsValid())
return m_descriptor;
// Don't open the file descriptor if we don't need to, just get it from the
// stream if we have one.
if (StreamIsValid())
return fileno (m_stream);
// Invalid descriptor and invalid stream, return invalid descriptor.
return kInvalidDescriptor;
}
void
File::SetDescriptor (int fd, bool transfer_ownership)
{
if (IsValid())
Close();
m_descriptor = fd;
m_own_descriptor = transfer_ownership;
}
FILE *
File::GetStream ()
{
if (!StreamIsValid())
{
if (DescriptorIsValid())
{
const char *mode = GetStreamOpenModeFromOptions (m_options);
if (mode)
{
if (!m_own_descriptor)
{
// We must duplicate the file descriptor if we don't own it because
// when you call fdopen, the stream will own the fd
#ifdef _WIN32
m_descriptor = ::_dup(GetDescriptor());
#else
m_descriptor = ::fcntl(GetDescriptor(), F_DUPFD);
#endif
m_own_descriptor = true;
}
do
{
m_stream = ::fdopen (m_descriptor, mode);
} while (m_stream == NULL && errno == EINTR);
// If we got a stream, then we own the stream and should no
// longer own the descriptor because fclose() will close it for us
if (m_stream)
{
m_own_stream = true;
m_own_descriptor = false;
}
}
}
}
return m_stream;
}
void
File::SetStream (FILE *fh, bool transfer_ownership)
{
if (IsValid())
Close();
m_stream = fh;
m_own_stream = transfer_ownership;
}
Error
File::Duplicate (const File &rhs)
{
Error error;
if (IsValid ())
Close();
if (rhs.DescriptorIsValid())
{
#ifdef _WIN32
m_descriptor = ::_dup(rhs.GetDescriptor());
#else
m_descriptor = ::fcntl(rhs.GetDescriptor(), F_DUPFD);
#endif
if (!DescriptorIsValid())
error.SetErrorToErrno();
else
{
m_options = rhs.m_options;
m_own_descriptor = true;
}
}
else
{
error.SetErrorString ("invalid file to duplicate");
}
return error;
}
Error
File::Open (const char *path, uint32_t options, uint32_t permissions)
{
Error error;
if (IsValid())
Close ();
int oflag = 0;
const bool read = options & eOpenOptionRead;
const bool write = options & eOpenOptionWrite;
if (write)
{
if (read)
oflag |= O_RDWR;
else
oflag |= O_WRONLY;
if (options & eOpenOptionAppend)
oflag |= O_APPEND;
if (options & eOpenOptionTruncate)
oflag |= O_TRUNC;
if (options & eOpenOptionCanCreate)
oflag |= O_CREAT;
if (options & eOpenOptionCanCreateNewOnly)
oflag |= O_CREAT | O_EXCL;
}
else if (read)
{
oflag |= O_RDONLY;
#ifndef _WIN32
if (options & eOpenoptionDontFollowSymlinks)
oflag |= O_NOFOLLOW;
#endif
}
#ifndef _WIN32
if (options & eOpenOptionNonBlocking)
oflag |= O_NONBLOCK;
#else
oflag |= O_BINARY;
#endif
mode_t mode = 0;
if (oflag & O_CREAT)
{
if (permissions & lldb::eFilePermissionsUserRead) mode |= S_IRUSR;
if (permissions & lldb::eFilePermissionsUserWrite) mode |= S_IWUSR;
if (permissions & lldb::eFilePermissionsUserExecute) mode |= S_IXUSR;
if (permissions & lldb::eFilePermissionsGroupRead) mode |= S_IRGRP;
if (permissions & lldb::eFilePermissionsGroupWrite) mode |= S_IWGRP;
if (permissions & lldb::eFilePermissionsGroupExecute) mode |= S_IXGRP;
if (permissions & lldb::eFilePermissionsWorldRead) mode |= S_IROTH;
if (permissions & lldb::eFilePermissionsWorldWrite) mode |= S_IWOTH;
if (permissions & lldb::eFilePermissionsWorldExecute) mode |= S_IXOTH;
}
do
{
m_descriptor = ::open(path, oflag, mode);
} while (m_descriptor < 0 && errno == EINTR);
if (!DescriptorIsValid())
error.SetErrorToErrno();
else
{
m_own_descriptor = true;
m_options = options;
}
return error;
}
uint32_t
File::GetPermissions (const char *path, Error &error)
{
if (path && path[0])
{
struct stat file_stats;
if (::stat (path, &file_stats) == -1)
error.SetErrorToErrno();
else
{
error.Clear();
return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
}
}
else
{
if (path)
error.SetErrorString ("invalid path");
else
error.SetErrorString ("empty path");
}
return 0;
}
uint32_t
File::GetPermissions(Error &error) const
{
int fd = GetDescriptor();
if (fd != kInvalidDescriptor)
{
struct stat file_stats;
if (::fstat (fd, &file_stats) == -1)
error.SetErrorToErrno();
else
{
error.Clear();
return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
}
}
else
{
error.SetErrorString ("invalid file descriptor");
}
return 0;
}
Error
File::Close ()
{
Error error;
if (StreamIsValid() && m_own_stream)
{
if (::fclose (m_stream) == EOF)
error.SetErrorToErrno();
}
if (DescriptorIsValid() && m_own_descriptor)
{
if (::close (m_descriptor) != 0)
error.SetErrorToErrno();
}
m_descriptor = kInvalidDescriptor;
m_stream = kInvalidStream;
m_options = 0;
m_own_stream = false;
m_own_descriptor = false;
m_is_interactive = eLazyBoolCalculate;
m_is_real_terminal = eLazyBoolCalculate;
return error;
}
Error
File::GetFileSpec (FileSpec &file_spec) const
{
Error error;
#ifdef LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED
if (IsValid ())
{
char path[PATH_MAX];
if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1)
error.SetErrorToErrno();
else
file_spec.SetFile (path, false);
}
else
{
error.SetErrorString("invalid file handle");
}
#elif defined(__linux__)
char proc[64];
char path[PATH_MAX];
if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0)
error.SetErrorString ("cannot resolve file descriptor");
else
{
ssize_t len;
if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1)
error.SetErrorToErrno();
else
{
path[len] = '\0';
file_spec.SetFile (path, false);
}
}
#else
error.SetErrorString ("File::GetFileSpec is not supported on this platform");
#endif
if (error.Fail())
file_spec.Clear();
return error;
}
off_t
File::SeekFromStart (off_t offset, Error *error_ptr)
{
off_t result = 0;
if (DescriptorIsValid())
{
result = ::lseek (m_descriptor, offset, SEEK_SET);
if (error_ptr)
{
if (result == -1)
error_ptr->SetErrorToErrno();
else
error_ptr->Clear();
}
}
else if (StreamIsValid ())
{
result = ::fseek(m_stream, offset, SEEK_SET);
if (error_ptr)
{
if (result == -1)
error_ptr->SetErrorToErrno();
else
error_ptr->Clear();
}
}
else if (error_ptr)
{
error_ptr->SetErrorString("invalid file handle");
}
return result;
}
off_t
File::SeekFromCurrent (off_t offset, Error *error_ptr)
{
off_t result = -1;
if (DescriptorIsValid())
{
result = ::lseek (m_descriptor, offset, SEEK_CUR);
if (error_ptr)
{
if (result == -1)
error_ptr->SetErrorToErrno();
else
error_ptr->Clear();
}
}
else if (StreamIsValid ())
{
result = ::fseek(m_stream, offset, SEEK_CUR);
if (error_ptr)
{
if (result == -1)
error_ptr->SetErrorToErrno();
else
error_ptr->Clear();
}
}
else if (error_ptr)
{
error_ptr->SetErrorString("invalid file handle");
}
return result;
}
off_t
File::SeekFromEnd (off_t offset, Error *error_ptr)
{
off_t result = -1;
if (DescriptorIsValid())
{
result = ::lseek (m_descriptor, offset, SEEK_END);
if (error_ptr)
{
if (result == -1)
error_ptr->SetErrorToErrno();
else
error_ptr->Clear();
}
}
else if (StreamIsValid ())
{
result = ::fseek(m_stream, offset, SEEK_END);
if (error_ptr)
{
if (result == -1)
error_ptr->SetErrorToErrno();
else
error_ptr->Clear();
}
}
else if (error_ptr)
{
error_ptr->SetErrorString("invalid file handle");
}
return result;
}
Error
File::Flush ()
{
Error error;
if (StreamIsValid())
{
int err = 0;
do
{
err = ::fflush (m_stream);
} while (err == EOF && errno == EINTR);
if (err == EOF)
error.SetErrorToErrno();
}
else if (!DescriptorIsValid())
{
error.SetErrorString("invalid file handle");
}
return error;
}
Error
File::Sync ()
{
Error error;
if (DescriptorIsValid())
{
#ifdef _WIN32
int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor));
if (err == 0)
error.SetErrorToGenericError();
#else
int err = 0;
do
{
err = ::fsync (m_descriptor);
} while (err == -1 && errno == EINTR);
if (err == -1)
error.SetErrorToErrno();
#endif
}
else
{
error.SetErrorString("invalid file handle");
}
return error;
}
Error
File::Read (void *buf, size_t &num_bytes)
{
Error error;
ssize_t bytes_read = -1;
if (DescriptorIsValid())
{
do
{
bytes_read = ::read (m_descriptor, buf, num_bytes);
} while (bytes_read < 0 && errno == EINTR);
if (bytes_read == -1)
{
error.SetErrorToErrno();
num_bytes = 0;
}
else
num_bytes = bytes_read;
}
else if (StreamIsValid())
{
bytes_read = ::fread (buf, 1, num_bytes, m_stream);
if (bytes_read == 0)
{
if (::feof(m_stream))
error.SetErrorString ("feof");
else if (::ferror (m_stream))
error.SetErrorString ("ferror");
num_bytes = 0;
}
else
num_bytes = bytes_read;
}
else
{
num_bytes = 0;
error.SetErrorString("invalid file handle");
}
return error;
}
Error
File::Write (const void *buf, size_t &num_bytes)
{
Error error;
ssize_t bytes_written = -1;
if (DescriptorIsValid())
{
do
{
bytes_written = ::write (m_descriptor, buf, num_bytes);
} while (bytes_written < 0 && errno == EINTR);
if (bytes_written == -1)
{
error.SetErrorToErrno();
num_bytes = 0;
}
else
num_bytes = bytes_written;
}
else if (StreamIsValid())
{
bytes_written = ::fwrite (buf, 1, num_bytes, m_stream);
if (bytes_written == 0)
{
if (::feof(m_stream))
error.SetErrorString ("feof");
else if (::ferror (m_stream))
error.SetErrorString ("ferror");
num_bytes = 0;
}
else
num_bytes = bytes_written;
}
else
{
num_bytes = 0;
error.SetErrorString("invalid file handle");
}
return error;
}
Error
File::Read (void *buf, size_t &num_bytes, off_t &offset)
{
#ifndef _WIN32
Error error;
int fd = GetDescriptor();
if (fd != kInvalidDescriptor)
{
ssize_t bytes_read = -1;
do
{
bytes_read = ::pread (fd, buf, num_bytes, offset);
} while (bytes_read < 0 && errno == EINTR);
if (bytes_read < 0)
{
num_bytes = 0;
error.SetErrorToErrno();
}
else
{
offset += bytes_read;
num_bytes = bytes_read;
}
}
else
{
num_bytes = 0;
error.SetErrorString("invalid file handle");
}
return error;
#else
long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
SeekFromStart(offset);
Error error = Read(buf, num_bytes);
if (!error.Fail())
SeekFromStart(cur);
return error;
#endif
}
Error
File::Read (size_t &num_bytes, off_t &offset, bool null_terminate, DataBufferSP &data_buffer_sp)
{
Error error;
if (num_bytes > 0)
{
int fd = GetDescriptor();
if (fd != kInvalidDescriptor)
{
struct stat file_stats;
if (::fstat (fd, &file_stats) == 0)
{
if (file_stats.st_size > offset)
{
const size_t bytes_left = file_stats.st_size - offset;
if (num_bytes > bytes_left)
num_bytes = bytes_left;
std::unique_ptr<DataBufferHeap> data_heap_ap;
data_heap_ap.reset(new DataBufferHeap(num_bytes + (null_terminate ? 1 : 0), '\0'));
if (data_heap_ap.get())
{
error = Read (data_heap_ap->GetBytes(), num_bytes, offset);
if (error.Success())
{
// Make sure we read exactly what we asked for and if we got
// less, adjust the array
if (num_bytes < data_heap_ap->GetByteSize())
data_heap_ap->SetByteSize(num_bytes);
data_buffer_sp.reset(data_heap_ap.release());
return error;
}
}
}
else
error.SetErrorString("file is empty");
}
else
error.SetErrorToErrno();
}
else
error.SetErrorString("invalid file handle");
}
else
error.SetErrorString("invalid file handle");
num_bytes = 0;
data_buffer_sp.reset();
return error;
}
Error
File::Write (const void *buf, size_t &num_bytes, off_t &offset)
{
Error error;
int fd = GetDescriptor();
if (fd != kInvalidDescriptor)
{
#ifndef _WIN32
ssize_t bytes_written = -1;
do
{
bytes_written = ::pwrite (m_descriptor, buf, num_bytes, offset);
} while (bytes_written < 0 && errno == EINTR);
if (bytes_written < 0)
{
num_bytes = 0;
error.SetErrorToErrno();
}
else
{
offset += bytes_written;
num_bytes = bytes_written;
}
#else
long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
error = Write(buf, num_bytes);
long after = ::lseek(m_descriptor, 0, SEEK_CUR);
if (!error.Fail())
SeekFromStart(cur);
ssize_t bytes_written = after - cur;
offset = after;
#endif
}
else
{
num_bytes = 0;
error.SetErrorString("invalid file handle");
}
return error;
}
//------------------------------------------------------------------
// Print some formatted output to the stream.
//------------------------------------------------------------------
size_t
File::Printf (const char *format, ...)
{
va_list args;
va_start (args, format);
size_t result = PrintfVarArg (format, args);
va_end (args);
return result;
}
//------------------------------------------------------------------
// Print some formatted output to the stream.
//------------------------------------------------------------------
size_t
File::PrintfVarArg (const char *format, va_list args)
{
size_t result = 0;
if (DescriptorIsValid())
{
char *s = NULL;
result = vasprintf(&s, format, args);
if (s != NULL)
{
if (result > 0)
{
size_t s_len = result;
Write (s, s_len);
result = s_len;
}
free (s);
}
}
else if (StreamIsValid())
{
result = ::vfprintf (m_stream, format, args);
}
return result;
}
mode_t
File::ConvertOpenOptionsForPOSIXOpen (uint32_t open_options)
{
mode_t mode = 0;
if (open_options & eOpenOptionRead && open_options & eOpenOptionWrite)
mode |= O_RDWR;
else if (open_options & eOpenOptionWrite)
mode |= O_WRONLY;
if (open_options & eOpenOptionAppend)
mode |= O_APPEND;
if (open_options & eOpenOptionTruncate)
mode |= O_TRUNC;
if (open_options & eOpenOptionNonBlocking)
mode |= O_NONBLOCK;
if (open_options & eOpenOptionCanCreateNewOnly)
mode |= O_CREAT | O_EXCL;
else if (open_options & eOpenOptionCanCreate)
mode |= O_CREAT;
return mode;
}
void
File::CalculateInteractiveAndTerminal ()
{
const int fd = GetDescriptor();
if (fd >= 0)
{
m_is_interactive = eLazyBoolNo;
m_is_real_terminal = eLazyBoolNo;
#ifdef _WIN32
if (_isatty(fd))
{
m_is_interactive = eLazyBoolYes;
m_is_real_terminal = eLazyBoolYes;
}
#else
if (isatty(fd))
{
m_is_interactive = eLazyBoolYes;
struct winsize window_size;
if (::ioctl (fd, TIOCGWINSZ, &window_size) == 0)
{
if (window_size.ws_col > 0)
m_is_real_terminal = eLazyBoolYes;
}
}
#endif
}
}
bool
File::GetIsInteractive ()
{
if (m_is_interactive == eLazyBoolCalculate)
CalculateInteractiveAndTerminal ();
return m_is_interactive == eLazyBoolYes;
}
bool
File::GetIsRealTerminal ()
{
if (m_is_real_terminal == eLazyBoolCalculate)
CalculateInteractiveAndTerminal();
return m_is_real_terminal == eLazyBoolYes;
}