00001 #define _LARGEFILE64_SOURCE
00002 #define _FILE_OFFSET_BITS 64
00003
00004 #include "util/file.hh"
00005
00006 #include "util/exception.hh"
00007
00008 #include <cstdlib>
00009 #include <cstdio>
00010 #include <sstream>
00011 #include <iostream>
00012
00013 #include <assert.h>
00014 #include <errno.h>
00015 #include <sys/types.h>
00016 #include <sys/stat.h>
00017 #include <fcntl.h>
00018 #include <stdint.h>
00019
00020 #if defined(_WIN32) || defined(_WIN64)
00021 #include <windows.h>
00022 #include <io.h>
00023 #include <algorithm>
00024 #include <limits.h>
00025 #include <limits>
00026 #else
00027 #include <unistd.h>
00028 #endif
00029
00030 namespace util {
00031
00032 scoped_fd::~scoped_fd() {
00033 if (fd_ != -1 && close(fd_)) {
00034 std::cerr << "Could not close file " << fd_ << std::endl;
00035 std::abort();
00036 }
00037 }
00038
00039 scoped_FILE::~scoped_FILE() {
00040 if (file_ && std::fclose(file_)) {
00041 std::cerr << "Could not close file " << std::endl;
00042 std::abort();
00043 }
00044 }
00045
00046
00047 FDException::FDException(int fd) throw() : fd_(fd), name_guess_(NameFromFD(fd)) {
00048 *this << "in " << name_guess_ << ' ';
00049 }
00050
00051 FDException::~FDException() throw() {}
00052
00053 EndOfFileException::EndOfFileException() throw() {
00054 *this << "End of file";
00055 }
00056 EndOfFileException::~EndOfFileException() throw() {}
00057
00058 int OpenReadOrThrow(const char *name) {
00059 int ret;
00060 #if defined(_WIN32) || defined(_WIN64)
00061 UTIL_THROW_IF(-1 == (ret = _open(name, _O_BINARY | _O_RDONLY)), ErrnoException, "while opening " << name);
00062 #else
00063 UTIL_THROW_IF(-1 == (ret = open(name, O_RDONLY)), ErrnoException, "while opening " << name);
00064 #endif
00065 return ret;
00066 }
00067
00068 int CreateOrThrow(const char *name) {
00069 int ret;
00070 #if defined(_WIN32) || defined(_WIN64)
00071 UTIL_THROW_IF(-1 == (ret = _open(name, _O_CREAT | _O_TRUNC | _O_RDWR | _O_BINARY, _S_IREAD | _S_IWRITE)), ErrnoException, "while creating " << name);
00072 #else
00073 UTIL_THROW_IF(-1 == (ret = open(name, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)), ErrnoException, "while creating " << name);
00074 #endif
00075 return ret;
00076 }
00077
00078 uint64_t SizeFile(int fd) {
00079 #if defined(_WIN32) || defined(_WIN64)
00080 __int64 ret = _filelengthi64(fd);
00081 return (ret == -1) ? kBadSize : ret;
00082 #else // Not windows.
00083
00084 #ifdef OS_ANDROID
00085 struct stat64 sb;
00086 int ret = fstat64(fd, &sb);
00087 #else
00088 struct stat sb;
00089 int ret = fstat(fd, &sb);
00090 #endif
00091 if (ret == -1 || (!sb.st_size && !S_ISREG(sb.st_mode))) return kBadSize;
00092 return sb.st_size;
00093 #endif
00094 }
00095
00096 uint64_t SizeOrThrow(int fd) {
00097 uint64_t ret = SizeFile(fd);
00098 UTIL_THROW_IF_ARG(ret == kBadSize, FDException, (fd), "Failed to size");
00099 return ret;
00100 }
00101
00102 void ResizeOrThrow(int fd, uint64_t to) {
00103 #if defined(_WIN32) || defined(_WIN64)
00104 errno_t ret = _chsize_s
00105 #elif defined(OS_ANDROID)
00106 int ret = ftruncate64
00107 #else
00108 int ret = ftruncate
00109 #endif
00110 (fd, to);
00111 UTIL_THROW_IF_ARG(ret, FDException, (fd), "while resizing to " << to << " bytes");
00112 }
00113
00114 namespace {
00115 std::size_t GuardLarge(std::size_t size) {
00116
00117
00118 #if defined(_WIN32) || defined(_WIN64) || defined(__APPLE__) || defined(OS_ANDROID)
00119 return std::min(static_cast<std::size_t>(INT_MAX), size);
00120 #else
00121 return size;
00122 #endif
00123 }
00124 }
00125
00126 std::size_t PartialRead(int fd, void *to, std::size_t amount) {
00127 #if defined(_WIN32) || defined(_WIN64)
00128 int ret = _read(fd, to, GuardLarge(amount));
00129 #else
00130 errno = 0;
00131 ssize_t ret;
00132 do {
00133 ret = read(fd, to, GuardLarge(amount));
00134 } while (ret == -1 && errno == EINTR);
00135 #endif
00136 UTIL_THROW_IF_ARG(ret < 0, FDException, (fd), "while reading " << amount << " bytes");
00137 return static_cast<std::size_t>(ret);
00138 }
00139
00140 void ReadOrThrow(int fd, void *to_void, std::size_t amount) {
00141 uint8_t *to = static_cast<uint8_t*>(to_void);
00142 while (amount) {
00143 std::size_t ret = PartialRead(fd, to, amount);
00144 UTIL_THROW_IF(ret == 0, EndOfFileException, " in " << NameFromFD(fd) << " but there should be " << amount << " more bytes to read.");
00145 amount -= ret;
00146 to += ret;
00147 }
00148 }
00149
00150 std::size_t ReadOrEOF(int fd, void *to_void, std::size_t amount) {
00151 uint8_t *to = static_cast<uint8_t*>(to_void);
00152 std::size_t remaining = amount;
00153 while (remaining) {
00154 std::size_t ret = PartialRead(fd, to, remaining);
00155 if (!ret) return amount - remaining;
00156 remaining -= ret;
00157 to += ret;
00158 }
00159 return amount;
00160 }
00161
00162 void PReadOrThrow(int fd, void *to_void, std::size_t size, uint64_t off) {
00163 uint8_t *to = static_cast<uint8_t*>(to_void);
00164 #if defined(_WIN32) || defined(_WIN64)
00165 UTIL_THROW(Exception, "This pread implementation for windows is broken. Please send me a patch that does not change the file pointer. Atomically. Or send me an implementation of pwrite that is allowed to change the file pointer but can be called concurrently with pread.");
00166 const std::size_t kMaxDWORD = static_cast<std::size_t>(4294967295UL);
00167 #endif
00168 for (;size ;) {
00169 #if defined(_WIN32) || defined(_WIN64)
00170
00171
00172 DWORD reading = static_cast<DWORD>(std::min<std::size_t>(kMaxDWORD, size));
00173 DWORD ret;
00174 OVERLAPPED overlapped;
00175 memset(&overlapped, 0, sizeof(OVERLAPPED));
00176 overlapped.Offset = static_cast<DWORD>(off);
00177 overlapped.OffsetHigh = static_cast<DWORD>(off >> 32);
00178 UTIL_THROW_IF(!ReadFile((HANDLE)_get_osfhandle(fd), to, reading, &ret, &overlapped), Exception, "ReadFile failed for offset " << off);
00179 #else
00180 ssize_t ret;
00181 errno = 0;
00182 do {
00183 ret =
00184 #ifdef OS_ANDROID
00185 pread64
00186 #else
00187 pread
00188 #endif
00189 (fd, to, GuardLarge(size), off);
00190 } while (ret == -1 && errno == EINTR);
00191 if (ret <= 0) {
00192 UTIL_THROW_IF(ret == 0, EndOfFileException, " for reading " << size << " bytes at " << off << " from " << NameFromFD(fd));
00193 UTIL_THROW_ARG(FDException, (fd), "while reading " << size << " bytes at offset " << off);
00194 }
00195 #endif
00196 size -= ret;
00197 off += ret;
00198 to += ret;
00199 }
00200 }
00201
00202 void WriteOrThrow(int fd, const void *data_void, std::size_t size) {
00203 const uint8_t *data = static_cast<const uint8_t*>(data_void);
00204 while (size) {
00205 #if defined(_WIN32) || defined(_WIN64)
00206 int ret;
00207 #else
00208 ssize_t ret;
00209 #endif
00210 errno = 0;
00211 do {
00212 ret =
00213 #if defined(_WIN32) || defined(_WIN64)
00214 _write
00215 #else
00216 write
00217 #endif
00218 (fd, data, GuardLarge(size));
00219 } while (ret == -1 && errno == EINTR);
00220 UTIL_THROW_IF_ARG(ret < 1, FDException, (fd), "while writing " << size << " bytes");
00221 data += ret;
00222 size -= ret;
00223 }
00224 }
00225
00226 void WriteOrThrow(FILE *to, const void *data, std::size_t size) {
00227 if (!size) return;
00228 UTIL_THROW_IF(1 != std::fwrite(data, size, 1, to), ErrnoException, "Short write; requested size " << size);
00229 }
00230
00231 void FSyncOrThrow(int fd) {
00232
00233 #if !defined(_WIN32) && !defined(_WIN64)
00234 UTIL_THROW_IF_ARG(-1 == fsync(fd), FDException, (fd), "while syncing");
00235 #endif
00236 }
00237
00238 namespace {
00239
00240
00241 #if !defined(_WIN32) && !defined(_WIN64) && !defined(OS_ANDROID)
00242 template <unsigned> struct CheckOffT;
00243 template <> struct CheckOffT<8> {
00244 struct True {};
00245 };
00246
00247
00248 typedef CheckOffT<sizeof(off_t)>::True IgnoredType;
00249 #endif
00250
00251
00252 void InternalSeek(int fd, int64_t off, int whence) {
00253 if (
00254 #if defined(_WIN32) || defined(_WIN64)
00255 (__int64)-1 == _lseeki64(fd, off, whence)
00256 #elif defined(OS_ANDROID)
00257 (off64_t)-1 == lseek64(fd, off, whence)
00258 #else
00259 (off_t)-1 == lseek(fd, off, whence)
00260 #endif
00261 ) UTIL_THROW_ARG(FDException, (fd), "while seeking to " << off << " whence " << whence);
00262 }
00263 }
00264
00265 void SeekOrThrow(int fd, uint64_t off) {
00266 InternalSeek(fd, off, SEEK_SET);
00267 }
00268
00269 void AdvanceOrThrow(int fd, int64_t off) {
00270 InternalSeek(fd, off, SEEK_CUR);
00271 }
00272
00273 void SeekEnd(int fd) {
00274 InternalSeek(fd, 0, SEEK_END);
00275 }
00276
00277 std::FILE *FDOpenOrThrow(scoped_fd &file) {
00278 std::FILE *ret = fdopen(file.get(), "r+b");
00279 UTIL_THROW_IF_ARG(!ret, FDException, (file.get()), "Could not fdopen for write");
00280 file.release();
00281 return ret;
00282 }
00283
00284 std::FILE *FDOpenReadOrThrow(scoped_fd &file) {
00285 std::FILE *ret = fdopen(file.get(), "rb");
00286 UTIL_THROW_IF_ARG(!ret, FDException, (file.get()), "Could not fdopen for read");
00287 file.release();
00288 return ret;
00289 }
00290
00291
00292 #if defined(_WIN32) || defined(_WIN64)
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304 static const char letters[] =
00305 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
00306
00307
00308
00309
00310
00311 int
00312 mkstemp_and_unlink(char *tmpl)
00313 {
00314 int len;
00315 char *XXXXXX;
00316 static unsigned long long value;
00317 unsigned long long random_time_bits;
00318 unsigned int count;
00319 int fd = -1;
00320 int save_errno = errno;
00321
00322
00323
00324
00325
00326
00327
00328 #define ATTEMPTS_MIN (62 * 62 * 62)
00329
00330
00331
00332 #if ATTEMPTS_MIN < TMP_MAX
00333 unsigned int attempts = TMP_MAX;
00334 #else
00335 unsigned int attempts = ATTEMPTS_MIN;
00336 #endif
00337
00338 len = strlen (tmpl);
00339 if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
00340 {
00341 errno = EINVAL;
00342 return -1;
00343 }
00344
00345
00346 XXXXXX = &tmpl[len - 6];
00347
00348
00349 {
00350 SYSTEMTIME stNow;
00351 FILETIME ftNow;
00352
00353
00354 GetSystemTime(&stNow);
00355 stNow.wMilliseconds = 500;
00356 if (!SystemTimeToFileTime(&stNow, &ftNow))
00357 {
00358 errno = -1;
00359 return -1;
00360 }
00361
00362 random_time_bits = (((unsigned long long)ftNow.dwHighDateTime << 32)
00363 | (unsigned long long)ftNow.dwLowDateTime);
00364 }
00365 value += random_time_bits ^ (unsigned long long)GetCurrentThreadId ();
00366
00367 for (count = 0; count < attempts; value += 7777, ++count)
00368 {
00369 unsigned long long v = value;
00370
00371
00372 XXXXXX[0] = letters[v % 62];
00373 v /= 62;
00374 XXXXXX[1] = letters[v % 62];
00375 v /= 62;
00376 XXXXXX[2] = letters[v % 62];
00377 v /= 62;
00378 XXXXXX[3] = letters[v % 62];
00379 v /= 62;
00380 XXXXXX[4] = letters[v % 62];
00381 v /= 62;
00382 XXXXXX[5] = letters[v % 62];
00383
00384
00385
00386 int flags = _O_RDWR | _O_CREAT | _O_EXCL | _O_BINARY;
00387 flags |= _O_TEMPORARY;
00388 fd = _open (tmpl, flags, _S_IREAD | _S_IWRITE);
00389 if (fd >= 0)
00390 {
00391 errno = save_errno;
00392 return fd;
00393 }
00394 else if (errno != EEXIST)
00395 return -1;
00396 }
00397
00398
00399 errno = EEXIST;
00400 return -1;
00401 }
00402 #else
00403 int
00404 mkstemp_and_unlink(char *tmpl) {
00405 int ret = mkstemp(tmpl);
00406 if (ret != -1) {
00407 UTIL_THROW_IF(unlink(tmpl), ErrnoException, "while deleting delete " << tmpl);
00408 }
00409 return ret;
00410 }
00411 #endif
00412
00413
00414
00415 void NormalizeTempPrefix(std::string &base) {
00416 if (base.empty()) return;
00417 if (base[base.size() - 1] == '/') return;
00418 struct stat sb;
00419
00420 if (-1 == stat(base.c_str(), &sb)) return;
00421 if (
00422 #if defined(_WIN32) || defined(_WIN64)
00423 sb.st_mode & _S_IFDIR
00424 #else
00425 S_ISDIR(sb.st_mode)
00426 #endif
00427 ) base += '/';
00428 }
00429
00430 int MakeTemp(const std::string &base) {
00431 std::string name(base);
00432 name += "XXXXXX";
00433 name.push_back(0);
00434 int ret;
00435 UTIL_THROW_IF(-1 == (ret = mkstemp_and_unlink(&name[0])), ErrnoException, "while making a temporary based on " << base);
00436 return ret;
00437 }
00438
00439 std::FILE *FMakeTemp(const std::string &base) {
00440 util::scoped_fd file(MakeTemp(base));
00441 return FDOpenOrThrow(file);
00442 }
00443
00444 int DupOrThrow(int fd) {
00445 int ret = dup(fd);
00446 UTIL_THROW_IF_ARG(ret == -1, FDException, (fd), "in duplicating the file descriptor");
00447 return ret;
00448 }
00449
00450 namespace {
00451
00452 bool TryName(int fd, std::string &out) {
00453 #if defined(_WIN32) || defined(_WIN64)
00454 return false;
00455 #else
00456 std::string name("/proc/self/fd/");
00457 std::ostringstream convert;
00458 convert << fd;
00459 name += convert.str();
00460
00461 struct stat sb;
00462 if (-1 == lstat(name.c_str(), &sb))
00463 return false;
00464 out.resize(sb.st_size + 1);
00465 ssize_t ret = readlink(name.c_str(), &out[0], sb.st_size + 1);
00466 if (-1 == ret)
00467 return false;
00468 if (ret > sb.st_size) {
00469
00470 return false;
00471 }
00472 out.resize(ret);
00473
00474 if (!out.empty() && out[0] != '/')
00475 return false;
00476 return true;
00477 #endif
00478 }
00479 }
00480
00481 std::string NameFromFD(int fd) {
00482 std::string ret;
00483 if (TryName(fd, ret)) return ret;
00484 switch (fd) {
00485 case 0: return "stdin";
00486 case 1: return "stdout";
00487 case 2: return "stderr";
00488 }
00489 ret = "fd ";
00490 std::ostringstream convert;
00491 convert << fd;
00492 ret += convert.str();
00493 return ret;
00494 }
00495
00496 }