X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FFileUtil.h;h=0913b23d2385510a86928ee2e347e0b3d767df0f;hp=a28b0477971d8eb3319ebd50d0ca0e7e37d5292b;hb=f193a7e397b220a9d1ecc34183b6cffc279c9275;hpb=dee8a5180aa542d98d1b71c74f83a006e4627952 diff --git a/folly/FileUtil.h b/folly/FileUtil.h index a28b0477..0913b23d 100644 --- a/folly/FileUtil.h +++ b/folly/FileUtil.h @@ -1,5 +1,5 @@ /* - * Copyright 2016 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,18 @@ #pragma once -#include -#include -#include +#include +#include #include #include -#include -#include -#include -#include -#include + +#include +#include +#include +#include +#include +#include namespace folly { @@ -80,9 +81,7 @@ ssize_t writevNoInt(int fd, const iovec* iov, int count); ssize_t readFull(int fd, void* buf, size_t n); ssize_t preadFull(int fd, void* buf, size_t n, off_t offset); ssize_t readvFull(int fd, iovec* iov, int count); -#if FOLLY_HAVE_PREADV ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset); -#endif /** * Similar to readFull and preadFull above, wrappers around write() and @@ -97,13 +96,14 @@ ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset); * Note that writevFull and pwritevFull require iov to be non-const, unlike * writev and pwritev. The contents of iov after these functions return * is unspecified. + * + * These functions return -1 on error, or the total number of bytes written + * (which is always the same as the number of requested bytes) on success. */ ssize_t writeFull(int fd, const void* buf, size_t n); ssize_t pwriteFull(int fd, const void* buf, size_t n, off_t offset); ssize_t writevFull(int fd, iovec* iov, int count); -#if FOLLY_HAVE_PWRITEV ssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset); -#endif /** * Read entire file (if num_bytes is defaulted) or no more than @@ -116,26 +116,24 @@ ssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset); * errno will be set appropriately by the failing system primitive. */ template -bool readFile(const char* file_name, Container& out, - size_t num_bytes = std::numeric_limits::max()) { +bool readFile( + int fd, + Container& out, + size_t num_bytes = std::numeric_limits::max()) { static_assert(sizeof(out[0]) == 1, "readFile: only containers with byte-sized elements accepted"); - assert(file_name); - - const auto fd = openNoInt(file_name, O_RDONLY); - if (fd == -1) return false; size_t soFar = 0; // amount of bytes successfully read SCOPE_EXIT { - assert(out.size() >= soFar); // resize better doesn't throw + DCHECK(out.size() >= soFar); // resize better doesn't throw out.resize(soFar); - // Ignore errors when closing the file - closeNoInt(fd); }; // Obtain file size: struct stat buf; - if (fstat(fd, &buf) == -1) return false; + if (fstat(fd, &buf) == -1) { + return false; + } // Some files (notably under /proc and /sys on Linux) lie about // their size, so treat the size advertised by fstat under advise // but don't rely on it. In particular, if the size is zero, we @@ -144,8 +142,7 @@ bool readFile(const char* file_name, Container& out, constexpr size_t initialAlloc = 1024 * 4; out.resize( std::min( - buf.st_size > 0 ? folly::to(buf.st_size + 1) : initialAlloc, - num_bytes)); + buf.st_size > 0 ? (size_t(buf.st_size) + 1) : initialAlloc, num_bytes)); while (soFar < out.size()) { const auto actual = readFull(fd, &out[soFar], out.size() - soFar); @@ -165,6 +162,29 @@ bool readFile(const char* file_name, Container& out, return true; } +/** + * Same as above, but takes in a file name instead of fd + */ +template +bool readFile( + const char* file_name, + Container& out, + size_t num_bytes = std::numeric_limits::max()) { + DCHECK(file_name); + + const auto fd = openNoInt(file_name, O_RDONLY); + if (fd == -1) { + return false; + } + + SCOPE_EXIT { + // Ignore errors when closing the file + closeNoInt(fd); + }; + + return readFile(fd, out, num_bytes); +} + /** * Writes container to file. The container is assumed to be * contiguous, with element size equal to 1, and offering STL-like @@ -176,13 +196,19 @@ bool readFile(const char* file_name, Container& out, * * Returns: true on success or false on failure. In the latter case * errno will be set appropriately by the failing system primitive. + * + * Note that this function may leave the file in a partially written state on + * failure. Use writeFileAtomic() if you want to ensure that the existing file + * state will be unchanged on error. */ template -bool writeFile(const Container& data, const char* filename, - int flags = O_WRONLY | O_CREAT | O_TRUNC) { +bool writeFile(const Container& data, + const char* filename, + int flags = O_WRONLY | O_CREAT | O_TRUNC, + mode_t mode = 0666) { static_assert(sizeof(data[0]) == 1, "writeFile works with element size equal to 1"); - int fd = open(filename, flags, 0666); + int fd = open(filename, flags, mode); if (fd == -1) { return false; } @@ -191,4 +217,41 @@ bool writeFile(const Container& data, const char* filename, return closeNoInt(fd) == 0 && ok; } -} // namespaces +/** + * Write file contents "atomically". + * + * This writes the data to a temporary file in the destination directory, and + * then renames it to the specified path. This guarantees that the specified + * file will be replaced the the specified contents on success, or will not be + * modified on failure. + * + * Note that on platforms that do not provide atomic filesystem rename + * functionality (e.g., Windows) this behavior may not be truly atomic. + */ +void writeFileAtomic( + StringPiece filename, + iovec* iov, + int count, + mode_t permissions = 0644); +void writeFileAtomic( + StringPiece filename, + ByteRange data, + mode_t permissions = 0644); +void writeFileAtomic( + StringPiece filename, + StringPiece data, + mode_t permissions = 0644); + +/** + * A version of writeFileAtomic() that returns an errno value instead of + * throwing on error. + * + * Returns 0 on success or an errno value on error. + */ +int writeFileAtomicNoThrow( + StringPiece filename, + iovec* iov, + int count, + mode_t permissions = 0644); + +} // namespace folly