summaryrefslogtreecommitdiff
path: root/klm/util/file.cc
diff options
context:
space:
mode:
Diffstat (limited to 'klm/util/file.cc')
-rw-r--r--klm/util/file.cc196
1 files changed, 170 insertions, 26 deletions
diff --git a/klm/util/file.cc b/klm/util/file.cc
index b9a77cf9..9a6d2e64 100644
--- a/klm/util/file.cc
+++ b/klm/util/file.cc
@@ -1,12 +1,17 @@
+#define _LARGEFILE64_SOURCE
+#define _FILE_OFFSET_BITS 64
+
#include "util/file.hh"
#include "util/exception.hh"
#include <cstdlib>
#include <cstdio>
+#include <sstream>
#include <iostream>
#include <assert.h>
+#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -37,6 +42,18 @@ scoped_FILE::~scoped_FILE() {
}
}
+// Note that ErrnoException records errno before NameFromFD is called.
+FDException::FDException(int fd) throw() : fd_(fd), name_guess_(NameFromFD(fd)) {
+ *this << "in " << name_guess_ << ' ';
+}
+
+FDException::~FDException() throw() {}
+
+EndOfFileException::EndOfFileException() throw() {
+ *this << "End of file";
+}
+EndOfFileException::~EndOfFileException() throw() {}
+
int OpenReadOrThrow(const char *name) {
int ret;
#if defined(_WIN32) || defined(_WIN64)
@@ -61,19 +78,36 @@ uint64_t SizeFile(int fd) {
#if defined(_WIN32) || defined(_WIN64)
__int64 ret = _filelengthi64(fd);
return (ret == -1) ? kBadSize : ret;
+#else // Not windows.
+
+#ifdef OS_ANDROID
+ struct stat64 sb;
+ int ret = fstat64(fd, &sb);
#else
struct stat sb;
- if (fstat(fd, &sb) == -1 || (!sb.st_size && !S_ISREG(sb.st_mode))) return kBadSize;
+ int ret = fstat(fd, &sb);
+#endif
+ if (ret == -1 || (!sb.st_size && !S_ISREG(sb.st_mode))) return kBadSize;
return sb.st_size;
#endif
}
+uint64_t SizeOrThrow(int fd) {
+ uint64_t ret = SizeFile(fd);
+ UTIL_THROW_IF_ARG(ret == kBadSize, FDException, (fd), "Failed to size");
+ return ret;
+}
+
void ResizeOrThrow(int fd, uint64_t to) {
+ UTIL_THROW_IF_ARG(
#if defined(_WIN32) || defined(_WIN64)
- UTIL_THROW_IF(_chsize_s(fd, to), ErrnoException, "Resizing to " << to << " bytes failed");
+ _chsize_s
+#elif defined(OS_ANDROID)
+ ftruncate64
#else
- UTIL_THROW_IF(ftruncate(fd, to), ErrnoException, "Resizing to " << to << " bytes failed");
+ ftruncate
#endif
+ (fd, to), FDException, (fd), "while resizing to " << to << " bytes");
}
std::size_t PartialRead(int fd, void *to, std::size_t amount) {
@@ -81,9 +115,13 @@ std::size_t PartialRead(int fd, void *to, std::size_t amount) {
amount = min(static_cast<std::size_t>(INT_MAX), amount);
int ret = _read(fd, to, amount);
#else
- ssize_t ret = read(fd, to, amount);
+ errno = 0;
+ ssize_t ret;
+ do {
+ ret = read(fd, to, amount);
+ } while (ret == -1 && errno == EINTR);
#endif
- UTIL_THROW_IF(ret < 0, ErrnoException, "Reading " << amount << " from fd " << fd << " failed.");
+ UTIL_THROW_IF_ARG(ret < 0, FDException, (fd), "while reading " << amount << " bytes");
return static_cast<std::size_t>(ret);
}
@@ -91,7 +129,7 @@ void ReadOrThrow(int fd, void *to_void, std::size_t amount) {
uint8_t *to = static_cast<uint8_t*>(to_void);
while (amount) {
std::size_t ret = PartialRead(fd, to, amount);
- UTIL_THROW_IF(ret == 0, EndOfFileException, " in fd " << fd << " but there should be " << amount << " more bytes to read.");
+ UTIL_THROW_IF(ret == 0, EndOfFileException, " in " << NameFromFD(fd) << " but there should be " << amount << " more bytes to read.");
amount -= ret;
to += ret;
}
@@ -109,40 +147,86 @@ std::size_t ReadOrEOF(int fd, void *to_void, std::size_t amount) {
return amount;
}
+void PReadOrThrow(int fd, void *to_void, std::size_t size, uint64_t off) {
+ uint8_t *to = static_cast<uint8_t*>(to_void);
+#if defined(_WIN32) || defined(_WIN64)
+ UTIL_THROW(Exception, "TODO: PReadOrThrow for windows using ReadFile http://stackoverflow.com/questions/766477/are-there-equivalents-to-pread-on-different-platforms");
+#else
+ for (;size ;) {
+ ssize_t ret;
+ errno = 0;
+ do {
+#ifdef OS_ANDROID
+ ret = pread64(fd, to, size, off);
+#else
+ ret = pread(fd, to, size, off);
+#endif
+ } while (ret == -1 && errno == EINTR);
+ if (ret <= 0) {
+ UTIL_THROW_IF(ret == 0, EndOfFileException, " for reading " << size << " bytes at " << off << " from " << NameFromFD(fd));
+ UTIL_THROW_ARG(FDException, (fd), "while reading " << size << " bytes at offset " << off);
+ }
+ size -= ret;
+ off += ret;
+ to += ret;
+ }
+#endif
+}
+
void WriteOrThrow(int fd, const void *data_void, std::size_t size) {
const uint8_t *data = static_cast<const uint8_t*>(data_void);
while (size) {
#if defined(_WIN32) || defined(_WIN64)
int ret = write(fd, data, min(static_cast<std::size_t>(INT_MAX), size));
#else
- ssize_t ret = write(fd, data, size);
+ errno = 0;
+ ssize_t ret;
+ do {
+ ret = write(fd, data, size);
+ } while (ret == -1 && errno == EINTR);
#endif
- if (ret < 1) UTIL_THROW(util::ErrnoException, "Write failed");
+ UTIL_THROW_IF_ARG(ret < 1, FDException, (fd), "while writing " << size << " bytes");
data += ret;
size -= ret;
}
}
void WriteOrThrow(FILE *to, const void *data, std::size_t size) {
- assert(size);
- UTIL_THROW_IF(1 != std::fwrite(data, size, 1, to), util::ErrnoException, "Short write; requested size " << size);
+ if (!size) return;
+ UTIL_THROW_IF(1 != std::fwrite(data, size, 1, to), ErrnoException, "Short write; requested size " << size);
}
void FSyncOrThrow(int fd) {
// Apparently windows doesn't have fsync?
#if !defined(_WIN32) && !defined(_WIN64)
- UTIL_THROW_IF(-1 == fsync(fd), ErrnoException, "Sync of " << fd << " failed.");
+ UTIL_THROW_IF_ARG(-1 == fsync(fd), FDException, (fd), "while syncing");
#endif
}
namespace {
+
+// Static assert for 64-bit off_t size.
+#if !defined(_WIN32) && !defined(_WIN64) && !defined(OS_ANDROID)
+template <unsigned> struct CheckOffT;
+template <> struct CheckOffT<8> {
+ struct True {};
+};
+// If there's a compiler error on the next line, then off_t isn't 64 bit. And
+// that makes me a sad panda.
+typedef CheckOffT<sizeof(off_t)>::True IgnoredType;
+#endif
+
+// Can't we all just get along?
void InternalSeek(int fd, int64_t off, int whence) {
+ UTIL_THROW_IF_ARG(
#if defined(_WIN32) || defined(_WIN64)
- UTIL_THROW_IF((__int64)-1 == _lseeki64(fd, off, whence), ErrnoException, "Windows seek failed");
-
+ (__int64)-1 == _lseeki64(fd, off, whence),
+#elif defined(OS_ANDROID)
+ (off64_t)-1 == lseek64(fd, off, whence),
#else
- UTIL_THROW_IF((off_t)-1 == lseek(fd, off, whence), ErrnoException, "Seek failed");
+ (off_t)-1 == lseek(fd, off, whence),
#endif
+ FDException, (fd), "while seeking to " << off << " whence " << whence);
}
} // namespace
@@ -160,22 +244,18 @@ void SeekEnd(int fd) {
std::FILE *FDOpenOrThrow(scoped_fd &file) {
std::FILE *ret = fdopen(file.get(), "r+b");
- if (!ret) UTIL_THROW(util::ErrnoException, "Could not fdopen descriptor " << file.get());
+ UTIL_THROW_IF_ARG(!ret, FDException, (file.get()), "Could not fdopen for write");
file.release();
return ret;
}
std::FILE *FDOpenReadOrThrow(scoped_fd &file) {
std::FILE *ret = fdopen(file.get(), "rb");
- if (!ret) UTIL_THROW(util::ErrnoException, "Could not fdopen descriptor " << file.get());
+ UTIL_THROW_IF_ARG(!ret, FDException, (file.get()), "Could not fdopen for read");
file.release();
return ret;
}
-TempMaker::TempMaker(const std::string &prefix) : base_(prefix) {
- base_ += "XXXXXX";
-}
-
// Sigh. Windows temporary file creation is full of race conditions.
#if defined(_WIN32) || defined(_WIN64)
/* mkstemp extracted from libc/sysdeps/posix/tempname.c. Copyright
@@ -292,23 +372,87 @@ int
mkstemp_and_unlink(char *tmpl) {
int ret = mkstemp(tmpl);
if (ret != -1) {
- UTIL_THROW_IF(unlink(tmpl), util::ErrnoException, "Failed to delete " << tmpl);
+ UTIL_THROW_IF(unlink(tmpl), ErrnoException, "while deleting delete " << tmpl);
}
return ret;
}
#endif
-int TempMaker::Make() const {
- std::string name(base_);
+// If it's a directory, add a /. This lets users say -T /tmp without creating
+// /tmpAAAAAA
+void NormalizeTempPrefix(std::string &base) {
+ if (base.empty()) return;
+ if (base[base.size() - 1] == '/') return;
+ struct stat sb;
+ // It's fine for it to not exist.
+ if (-1 == stat(base.c_str(), &sb)) return;
+ if (S_ISDIR(sb.st_mode)) base += '/';
+}
+
+int MakeTemp(const std::string &base) {
+ std::string name(base);
+ name += "XXXXXX";
name.push_back(0);
int ret;
- UTIL_THROW_IF(-1 == (ret = mkstemp_and_unlink(&name[0])), util::ErrnoException, "Failed to make a temporary based on " << base_);
+ UTIL_THROW_IF(-1 == (ret = mkstemp_and_unlink(&name[0])), ErrnoException, "while making a temporary based on " << base);
return ret;
}
-std::FILE *TempMaker::MakeFile() const {
- util::scoped_fd file(Make());
+std::FILE *FMakeTemp(const std::string &base) {
+ util::scoped_fd file(MakeTemp(base));
return FDOpenOrThrow(file);
}
+int DupOrThrow(int fd) {
+ int ret = dup(fd);
+ UTIL_THROW_IF_ARG(ret == -1, FDException, (fd), "in duplicating the file descriptor");
+ return ret;
+}
+
+namespace {
+// Try to name things but be willing to fail too.
+bool TryName(int fd, std::string &out) {
+#if defined(_WIN32) || defined(_WIN64)
+ return false;
+#else
+ std::string name("/proc/self/fd/");
+ std::ostringstream convert;
+ convert << fd;
+ name += convert.str();
+
+ struct stat sb;
+ if (-1 == lstat(name.c_str(), &sb))
+ return false;
+ out.resize(sb.st_size + 1);
+ ssize_t ret = readlink(name.c_str(), &out[0], sb.st_size + 1);
+ if (-1 == ret)
+ return false;
+ if (ret > sb.st_size) {
+ // Increased in size?!
+ return false;
+ }
+ out.resize(ret);
+ // Don't use the non-file names.
+ if (!out.empty() && out[0] != '/')
+ return false;
+ return true;
+#endif
+}
+} // namespace
+
+std::string NameFromFD(int fd) {
+ std::string ret;
+ if (TryName(fd, ret)) return ret;
+ switch (fd) {
+ case 0: return "stdin";
+ case 1: return "stdout";
+ case 2: return "stderr";
+ }
+ ret = "fd ";
+ std::ostringstream convert;
+ convert << fd;
+ ret += convert.str();
+ return ret;
+}
+
} // namespace util