2016-09-10 02:56:02 +03:00
|
|
|
/*
|
2017-01-21 09:02:33 +03:00
|
|
|
* Copyright (c) 2016-present, Facebook, Inc.
|
2016-09-10 02:56:02 +03:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#include "Overlay.h"
|
2017-02-11 01:16:00 +03:00
|
|
|
#include <boost/filesystem.hpp>
|
2016-09-10 02:56:02 +03:00
|
|
|
#include <folly/Exception.h>
|
2017-02-11 01:16:00 +03:00
|
|
|
#include <folly/File.h>
|
2016-09-10 02:56:02 +03:00
|
|
|
#include <folly/FileUtil.h>
|
2017-07-04 10:18:17 +03:00
|
|
|
#include <folly/io/Cursor.h>
|
|
|
|
#include <folly/io/IOBuf.h>
|
2016-09-10 02:56:02 +03:00
|
|
|
#include <thrift/lib/cpp2/protocol/Serializer.h>
|
|
|
|
#include "eden/fs/inodes/gen-cpp2/overlay_types.h"
|
2017-04-14 21:31:48 +03:00
|
|
|
#include "eden/fs/utils/PathFuncs.h"
|
2016-09-10 02:56:02 +03:00
|
|
|
|
|
|
|
namespace facebook {
|
|
|
|
namespace eden {
|
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
using apache::thrift::CompactSerializer;
|
|
|
|
using folly::ByteRange;
|
2016-09-10 02:56:02 +03:00
|
|
|
using folly::fbstring;
|
|
|
|
using folly::fbvector;
|
2017-02-11 01:16:00 +03:00
|
|
|
using folly::File;
|
|
|
|
using folly::MutableStringPiece;
|
|
|
|
using folly::Optional;
|
|
|
|
using folly::StringPiece;
|
|
|
|
using std::make_unique;
|
|
|
|
using std::string;
|
|
|
|
using std::unique_ptr;
|
2016-09-10 02:56:02 +03:00
|
|
|
|
|
|
|
/* Relative to the localDir, the metaFile holds the serialized rendition
|
|
|
|
* of the overlay_ data. We use thrift CompactSerialization for this.
|
|
|
|
*/
|
|
|
|
constexpr StringPiece kMetaDir{"overlay"};
|
|
|
|
constexpr StringPiece kMetaFile{"dirdata"};
|
2017-02-11 01:16:00 +03:00
|
|
|
constexpr StringPiece kInfoFile{"info"};
|
2017-07-14 03:11:36 +03:00
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
/**
|
|
|
|
* 4-byte magic identifier to put at the start of the info file.
|
|
|
|
* This merely helps confirm that we are in fact reading an overlay info file
|
|
|
|
*/
|
|
|
|
constexpr StringPiece kInfoHeaderMagic{"\xed\xe0\x00\x01"};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A version number for the overlay directory format.
|
|
|
|
*
|
|
|
|
* If we change the overlay storage format in the future we can bump this
|
|
|
|
* version number to help identify when eden is reading overlay data created by
|
|
|
|
* an older version of the code.
|
|
|
|
*/
|
|
|
|
constexpr uint32_t kOverlayVersion = 1;
|
|
|
|
constexpr size_t kInfoHeaderSize =
|
|
|
|
kInfoHeaderMagic.size() + sizeof(kOverlayVersion);
|
2016-09-10 02:56:02 +03:00
|
|
|
|
|
|
|
/* Relative to the localDir, the overlay tree is where we create the
|
|
|
|
* materialized directory structure; directories and files are created
|
|
|
|
* here. */
|
|
|
|
constexpr StringPiece kOverlayTree{"tree"};
|
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
namespace {
|
|
|
|
/**
|
|
|
|
* Get the name of the subdirectory to use for the overlay data for the
|
|
|
|
* specified inode number.
|
|
|
|
*
|
|
|
|
* We shard the inode files across the 256 subdirectories using the least
|
|
|
|
* significant byte. Inode numbers are allocated in monotonically increasing
|
|
|
|
* order, so this helps spread them out across the subdirectories.
|
|
|
|
*/
|
|
|
|
void formatSubdirPath(MutableStringPiece subdirPath, fuse_ino_t inode) {
|
|
|
|
constexpr char hexdigit[] = "0123456789abcdef";
|
|
|
|
DCHECK_EQ(subdirPath.size(), 2);
|
|
|
|
subdirPath[0] = hexdigit[(inode >> 4) & 0xf];
|
|
|
|
subdirPath[1] = hexdigit[inode & 0xf];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-14 03:11:36 +03:00
|
|
|
constexpr folly::StringPiece Overlay::kHeaderIdentifierDir;
|
|
|
|
constexpr folly::StringPiece Overlay::kHeaderIdentifierFile;
|
|
|
|
constexpr uint32_t Overlay::kHeaderVersion;
|
|
|
|
constexpr size_t Overlay::kHeaderLength;
|
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
Overlay::Overlay(AbsolutePathPiece localDir) : localDir_(localDir) {
|
|
|
|
initOverlay();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Overlay::initOverlay() {
|
|
|
|
// Read the overlay version file. If it does not exist, create it.
|
|
|
|
//
|
|
|
|
// First check for an old-format overlay directory, before we wrote out
|
|
|
|
// version numbers. This is only to warn developers if they try to use
|
|
|
|
// eden with an existing older client. We can probably delete this check in
|
|
|
|
// a few weeks.
|
|
|
|
if (isOldFormatOverlay()) {
|
|
|
|
throw std::runtime_error(
|
|
|
|
"The eden overlay format has been upgraded. "
|
|
|
|
"This version of eden cannot use the old overlay directory at " +
|
|
|
|
localDir_.value().toStdString());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the info file.
|
|
|
|
auto infoPath = localDir_ + PathComponentPiece{kInfoFile};
|
|
|
|
int fd = folly::openNoInt(infoPath.value().c_str(), O_RDONLY);
|
|
|
|
if (fd >= 0) {
|
|
|
|
// This is an existing overlay directory.
|
|
|
|
// Read the info file and make sure we are compatible with its version.
|
2017-07-01 05:00:03 +03:00
|
|
|
infoFile_ = File{fd, true};
|
|
|
|
readExistingOverlay(infoFile_.fd());
|
2017-02-11 01:16:00 +03:00
|
|
|
} else if (errno != ENOENT) {
|
|
|
|
folly::throwSystemError(
|
|
|
|
"error reading eden overlay info file ", infoPath.stringPiece());
|
|
|
|
} else {
|
|
|
|
// This is a brand new overlay directory.
|
|
|
|
initNewOverlay();
|
2017-07-01 05:00:03 +03:00
|
|
|
infoFile_ = File{infoPath.value().c_str(), O_RDONLY};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!infoFile_.try_lock()) {
|
|
|
|
folly::throwSystemError("failed to acquire overlay lock on ", infoPath);
|
2016-09-10 02:56:02 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
bool Overlay::isOldFormatOverlay() const {
|
|
|
|
auto oldDir = localDir_ + PathComponentPiece{kOverlayTree};
|
|
|
|
struct stat s;
|
|
|
|
if (lstat(oldDir.value().c_str(), &s) == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2016-09-10 02:56:02 +03:00
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
void Overlay::readExistingOverlay(int infoFD) {
|
|
|
|
// Read the info file header
|
|
|
|
std::array<uint8_t, kInfoHeaderSize> infoHeader;
|
|
|
|
auto sizeRead = folly::readFull(infoFD, infoHeader.data(), infoHeader.size());
|
|
|
|
folly::checkUnixError(
|
|
|
|
sizeRead,
|
|
|
|
"error reading from overlay info file in ",
|
|
|
|
localDir_.stringPiece());
|
|
|
|
if (sizeRead != infoHeader.size()) {
|
|
|
|
throw std::runtime_error(folly::to<string>(
|
|
|
|
"truncated info file in overlay directory ", localDir_));
|
|
|
|
}
|
|
|
|
// Verify the magic value is correct
|
|
|
|
if (memcmp(
|
|
|
|
infoHeader.data(),
|
|
|
|
kInfoHeaderMagic.data(),
|
|
|
|
kInfoHeaderMagic.size()) != 0) {
|
|
|
|
throw std::runtime_error(
|
|
|
|
folly::to<string>("bad data in overlay info file for ", localDir_));
|
|
|
|
}
|
|
|
|
// Extract the version number
|
|
|
|
uint32_t version;
|
|
|
|
memcpy(
|
|
|
|
&version, infoHeader.data() + kInfoHeaderMagic.size(), sizeof(version));
|
|
|
|
version = folly::Endian::big(version);
|
|
|
|
|
|
|
|
// Make sure we understand this version number
|
|
|
|
if (version != kOverlayVersion) {
|
|
|
|
throw std::runtime_error(folly::to<string>(
|
|
|
|
"Unsupported eden overlay format ", version, " in ", localDir_));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Overlay::initNewOverlay() {
|
|
|
|
// Make sure the overlay directory itself exists. It's fine if it already
|
|
|
|
// exists (although presumably it should be empty).
|
|
|
|
auto result = ::mkdir(localDir_.value().c_str(), 0755);
|
|
|
|
if (result != 0 && errno != EEXIST) {
|
|
|
|
folly::throwSystemError(
|
|
|
|
"error creating eden overlay directory ", localDir_.stringPiece());
|
|
|
|
}
|
|
|
|
auto localDirFile = File(localDir_.stringPiece(), O_RDONLY);
|
|
|
|
|
|
|
|
// We split the inode files across 256 subdirectories.
|
|
|
|
// Populate these subdirectories now.
|
|
|
|
std::array<char, 3> subdirPath;
|
|
|
|
subdirPath[2] = '\0';
|
|
|
|
for (int n = 0; n < 256; ++n) {
|
|
|
|
formatSubdirPath(MutableStringPiece{subdirPath.data(), 2}, n);
|
|
|
|
result = ::mkdirat(localDirFile.fd(), subdirPath.data(), 0755);
|
|
|
|
if (result != 0 && errno != EEXIST) {
|
|
|
|
folly::throwSystemError(
|
|
|
|
"error creating eden overlay directory ",
|
|
|
|
StringPiece{subdirPath.data()});
|
2016-09-10 02:56:02 +03:00
|
|
|
}
|
|
|
|
}
|
2017-02-11 01:16:00 +03:00
|
|
|
|
|
|
|
// For now we just write a simple header, with a magic number to identify
|
|
|
|
// this as an eden overlay file, and the version number of the overlay
|
|
|
|
// format.
|
|
|
|
std::array<uint8_t, kInfoHeaderSize> infoHeader;
|
|
|
|
memcpy(infoHeader.data(), kInfoHeaderMagic.data(), kInfoHeaderMagic.size());
|
|
|
|
auto version = folly::Endian::big(kOverlayVersion);
|
|
|
|
memcpy(
|
|
|
|
infoHeader.data() + kInfoHeaderMagic.size(), &version, sizeof(version));
|
|
|
|
|
|
|
|
auto infoPath = localDir_ + PathComponentPiece{kInfoFile};
|
|
|
|
folly::writeFileAtomic(
|
|
|
|
infoPath.stringPiece(), ByteRange(infoHeader.data(), infoHeader.size()));
|
|
|
|
}
|
|
|
|
|
|
|
|
Optional<TreeInode::Dir> Overlay::loadOverlayDir(fuse_ino_t inodeNumber) const {
|
2017-08-11 21:34:52 +03:00
|
|
|
TreeInode::Dir result;
|
|
|
|
auto dirData = deserializeOverlayDir(inodeNumber, result.timeStamps);
|
2017-02-11 01:16:00 +03:00
|
|
|
if (!dirData.hasValue()) {
|
|
|
|
return folly::none;
|
|
|
|
}
|
|
|
|
const auto& dir = dirData.value();
|
2016-09-10 02:56:02 +03:00
|
|
|
|
|
|
|
for (auto& iter : dir.entries) {
|
|
|
|
const auto& name = iter.first;
|
|
|
|
const auto& value = iter.second;
|
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
unique_ptr<TreeInode::Entry> entry;
|
|
|
|
if (value.inodeNumber == 0) {
|
|
|
|
auto hash = Hash(folly::ByteRange(folly::StringPiece(value.hash)));
|
|
|
|
entry = make_unique<TreeInode::Entry>(value.mode, hash);
|
|
|
|
} else {
|
|
|
|
entry = make_unique<TreeInode::Entry>(value.mode, value.inodeNumber);
|
2016-09-10 02:56:02 +03:00
|
|
|
}
|
|
|
|
result.entries.emplace(PathComponentPiece(name), std::move(entry));
|
|
|
|
}
|
|
|
|
|
|
|
|
return folly::Optional<TreeInode::Dir>(std::move(result));
|
|
|
|
}
|
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
void Overlay::saveOverlayDir(
|
|
|
|
fuse_ino_t inodeNumber,
|
|
|
|
const TreeInode::Dir* dir) {
|
2017-07-26 05:52:59 +03:00
|
|
|
// TODO: T20282158 clean up access of child inode information.
|
|
|
|
//
|
2016-09-10 02:56:02 +03:00
|
|
|
// Translate the data to the thrift equivalents
|
|
|
|
overlay::OverlayDir odir;
|
|
|
|
|
2017-07-08 04:32:44 +03:00
|
|
|
DCHECK(dir->isMaterialized());
|
2016-09-10 02:56:02 +03:00
|
|
|
for (auto& entIter : dir->entries) {
|
|
|
|
const auto& entName = entIter.first;
|
|
|
|
const auto ent = entIter.second.get();
|
|
|
|
|
|
|
|
overlay::OverlayEntry oent;
|
2017-07-26 05:52:59 +03:00
|
|
|
oent.mode = ent->getModeUnsafe();
|
2017-02-11 01:16:00 +03:00
|
|
|
if (ent->isMaterialized()) {
|
|
|
|
oent.inodeNumber = ent->getInodeNumber();
|
|
|
|
DCHECK_NE(oent.inodeNumber, 0);
|
|
|
|
} else {
|
|
|
|
oent.inodeNumber = 0;
|
2017-04-18 01:30:38 +03:00
|
|
|
auto entHash = ent->getHash();
|
|
|
|
auto bytes = entHash.getBytes();
|
2016-09-10 02:56:02 +03:00
|
|
|
oent.hash = std::string(
|
|
|
|
reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
odir.entries.emplace(
|
|
|
|
std::make_pair(entName.stringPiece().str(), std::move(oent)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ask thrift to serialize it.
|
|
|
|
auto serializedData = CompactSerializer::serialize<std::string>(odir);
|
|
|
|
|
2017-07-04 10:18:17 +03:00
|
|
|
// Add header to the overlay directory.
|
|
|
|
auto header = createHeader(
|
2017-08-11 21:34:52 +03:00
|
|
|
kHeaderIdentifierDir,
|
|
|
|
kHeaderVersion,
|
|
|
|
dir->timeStamps.atime,
|
|
|
|
dir->timeStamps.ctime,
|
|
|
|
dir->timeStamps.mtime);
|
2017-07-04 10:18:17 +03:00
|
|
|
|
|
|
|
auto iov = header.getIov();
|
|
|
|
iov.push_back(
|
|
|
|
{const_cast<char*>(serializedData.data()), serializedData.size()});
|
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
// And update the file on disk
|
|
|
|
folly::writeFileAtomic(
|
2017-07-04 10:18:17 +03:00
|
|
|
getFilePath(inodeNumber).stringPiece(), iov.data(), iov.size());
|
2017-02-11 01:16:00 +03:00
|
|
|
}
|
2016-09-10 02:56:02 +03:00
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
void Overlay::removeOverlayData(fuse_ino_t inodeNumber) const {
|
|
|
|
auto path = getFilePath(inodeNumber);
|
|
|
|
if (::unlink(path.value().c_str()) != 0 && errno != ENOENT) {
|
|
|
|
folly::throwSystemError("error unlinking overlay file: ", path);
|
2016-09-19 22:48:09 +03:00
|
|
|
}
|
2017-02-11 01:16:00 +03:00
|
|
|
}
|
2016-09-10 02:56:02 +03:00
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
fuse_ino_t Overlay::getMaxRecordedInode() {
|
|
|
|
// TODO: We should probably store the max inode number in the header file
|
|
|
|
// during graceful unmount. When opening an overlay we can then simply read
|
|
|
|
// back the max inode number from this file if the overlay was shut down
|
|
|
|
// cleanly last time.
|
|
|
|
//
|
|
|
|
// We would only then need to do a scan if the overlay was not cleanly shut
|
|
|
|
// down.
|
|
|
|
//
|
|
|
|
// For now we always do a scan.
|
2016-09-10 02:56:02 +03:00
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
// Walk the root directory downwards to find all (non-unlinked) directory
|
|
|
|
// inodes stored in the overlay.
|
|
|
|
//
|
|
|
|
// TODO: It would be nicer if each overlay file contained a short header so
|
|
|
|
// we could tell if it was a file or directory. This way we could do a
|
|
|
|
// simpler scan of opening every single file. For now we have to walk the
|
|
|
|
// directory tree from the root downwards.
|
|
|
|
fuse_ino_t maxInode = FUSE_ROOT_ID;
|
|
|
|
std::vector<fuse_ino_t> toProcess;
|
|
|
|
toProcess.push_back(FUSE_ROOT_ID);
|
|
|
|
while (!toProcess.empty()) {
|
|
|
|
auto dirInodeNumber = toProcess.back();
|
|
|
|
toProcess.pop_back();
|
2016-09-10 02:56:02 +03:00
|
|
|
|
2017-08-11 21:34:52 +03:00
|
|
|
InodeBase::InodeTimestamps timeStamps;
|
|
|
|
auto dir = deserializeOverlayDir(dirInodeNumber, timeStamps);
|
2017-02-11 01:16:00 +03:00
|
|
|
if (!dir.hasValue()) {
|
|
|
|
continue;
|
|
|
|
}
|
2016-09-10 02:56:02 +03:00
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
for (const auto& entry : dir.value().entries) {
|
|
|
|
auto entryInode = static_cast<fuse_ino_t>(entry.second.inodeNumber);
|
|
|
|
if (entryInode == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
maxInode = std::max(maxInode, entryInode);
|
|
|
|
if (mode_to_dtype(entry.second.mode) == dtype_t::Dir) {
|
|
|
|
toProcess.push_back(entry.second.inodeNumber);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-09-10 02:56:02 +03:00
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
// Look through the subdirectories and increment maxInode based on the
|
|
|
|
// filenames we see. This is needed in case there are unlinked inodes
|
|
|
|
// present.
|
|
|
|
std::array<char, 2> subdir;
|
|
|
|
for (int n = 0; n < 256; ++n) {
|
|
|
|
formatSubdirPath(MutableStringPiece{subdir.data(), subdir.size()}, n);
|
|
|
|
auto subdirPath = localDir_ +
|
|
|
|
PathComponentPiece{StringPiece{subdir.data(), subdir.size()}};
|
2016-09-10 02:56:02 +03:00
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
auto boostPath = boost::filesystem::path{subdirPath.value().c_str()};
|
|
|
|
for (const auto& entry : boost::filesystem::directory_iterator(boostPath)) {
|
|
|
|
auto entryInodeNumber =
|
|
|
|
folly::tryTo<fuse_ino_t>(entry.path().filename().string());
|
|
|
|
if (entryInodeNumber.hasValue()) {
|
|
|
|
maxInode = std::max(maxInode, entryInodeNumber.value());
|
|
|
|
}
|
|
|
|
}
|
2016-09-19 22:48:09 +03:00
|
|
|
}
|
2017-02-11 01:16:00 +03:00
|
|
|
|
|
|
|
return maxInode;
|
2016-09-10 02:56:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
const AbsolutePath& Overlay::getLocalDir() const {
|
|
|
|
return localDir_;
|
|
|
|
}
|
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
AbsolutePath Overlay::getFilePath(fuse_ino_t inodeNumber) const {
|
|
|
|
std::array<char, 2> subdir;
|
|
|
|
formatSubdirPath(
|
|
|
|
MutableStringPiece{subdir.data(), subdir.size()}, inodeNumber);
|
|
|
|
auto numberStr = folly::to<string>(inodeNumber);
|
|
|
|
return localDir_ +
|
|
|
|
PathComponentPiece{StringPiece{subdir.data(), subdir.size()}} +
|
|
|
|
PathComponentPiece{numberStr};
|
|
|
|
}
|
|
|
|
|
|
|
|
Optional<overlay::OverlayDir> Overlay::deserializeOverlayDir(
|
2017-08-05 06:14:18 +03:00
|
|
|
fuse_ino_t inodeNumber,
|
2017-08-11 21:34:52 +03:00
|
|
|
InodeBase::InodeTimestamps& timeStamps) const {
|
2017-02-11 01:16:00 +03:00
|
|
|
auto path = getFilePath(inodeNumber);
|
|
|
|
|
|
|
|
// Read the file and de-serialize it into data
|
|
|
|
std::string serializedData;
|
|
|
|
if (!folly::readFile(path.value().c_str(), serializedData)) {
|
|
|
|
int err = errno;
|
|
|
|
if (err == ENOENT) {
|
|
|
|
// There is no overlay here
|
|
|
|
return folly::none;
|
|
|
|
}
|
|
|
|
folly::throwSystemErrorExplicit(err, "failed to read ", path);
|
|
|
|
}
|
2017-07-04 10:18:17 +03:00
|
|
|
|
|
|
|
// Removing header and deserializing the contents
|
|
|
|
if (serializedData.size() < kHeaderLength) {
|
|
|
|
// Something Wrong with the file(may be corrupted)
|
|
|
|
folly::throwSystemErrorExplicit(
|
|
|
|
EIO,
|
|
|
|
"Overlay file ",
|
|
|
|
path,
|
|
|
|
" is too short for header: size=",
|
|
|
|
serializedData.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
StringPiece header{serializedData, 0, kHeaderLength};
|
2017-08-05 06:14:18 +03:00
|
|
|
// validate header and get the timestamps
|
2017-08-11 21:34:52 +03:00
|
|
|
parseHeader(header, kHeaderIdentifierDir, timeStamps);
|
2017-08-05 06:14:18 +03:00
|
|
|
|
2017-07-04 10:18:17 +03:00
|
|
|
StringPiece contents{serializedData};
|
|
|
|
contents.advance(kHeaderLength);
|
|
|
|
|
|
|
|
return CompactSerializer::deserialize<overlay::OverlayDir>(contents);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Overlay version number is currently 32 bit,
|
|
|
|
// so making version uint32_t instead of uint16_t
|
|
|
|
folly::IOBuf Overlay::createHeader(
|
|
|
|
StringPiece identifier,
|
|
|
|
uint32_t version,
|
|
|
|
const struct timespec& atime,
|
|
|
|
const struct timespec& ctime,
|
|
|
|
const struct timespec& mtime) {
|
|
|
|
folly::IOBuf header(folly::IOBuf::CREATE, kHeaderLength);
|
|
|
|
folly::io::Appender appender(&header, 0);
|
|
|
|
appender.push(identifier);
|
|
|
|
appender.writeBE(version);
|
|
|
|
appender.writeBE<uint64_t>(atime.tv_sec);
|
|
|
|
appender.writeBE<uint64_t>(atime.tv_nsec);
|
|
|
|
appender.writeBE<uint64_t>(ctime.tv_sec);
|
|
|
|
appender.writeBE<uint64_t>(ctime.tv_nsec);
|
|
|
|
appender.writeBE<uint64_t>(mtime.tv_sec);
|
|
|
|
appender.writeBE<uint64_t>(mtime.tv_nsec);
|
|
|
|
auto paddingSize = kHeaderLength - header.length();
|
|
|
|
appender.ensure(paddingSize);
|
|
|
|
memset(appender.writableData(), 0, paddingSize);
|
|
|
|
appender.append(paddingSize);
|
|
|
|
|
|
|
|
return header;
|
2016-09-10 02:56:02 +03:00
|
|
|
}
|
2017-07-14 03:11:36 +03:00
|
|
|
|
|
|
|
// Helper function to open,validate,
|
|
|
|
// get file pointer of an overlay file
|
2017-08-05 06:14:18 +03:00
|
|
|
folly::File Overlay::openFile(
|
|
|
|
folly::StringPiece filePath,
|
|
|
|
folly::StringPiece headerId,
|
2017-08-11 21:34:52 +03:00
|
|
|
InodeBase::InodeTimestamps& timeStamps) {
|
2017-07-14 03:11:36 +03:00
|
|
|
// Open the overlay file
|
|
|
|
folly::File file(filePath, O_RDWR);
|
|
|
|
|
|
|
|
// Read the contents
|
|
|
|
std::string contents;
|
2017-08-05 06:14:18 +03:00
|
|
|
folly::readFile(file.fd(), contents, kHeaderLength);
|
2017-07-14 03:11:36 +03:00
|
|
|
|
2017-08-05 06:14:18 +03:00
|
|
|
StringPiece header{contents};
|
2017-08-11 21:34:52 +03:00
|
|
|
parseHeader(header, headerId, timeStamps);
|
2017-07-14 03:11:36 +03:00
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper function to add header to the materialized file
|
|
|
|
void Overlay::addHeaderToOverlayFile(int fd) {
|
2017-08-05 06:14:18 +03:00
|
|
|
struct timespec currentTime;
|
|
|
|
clock_gettime(CLOCK_REALTIME, ¤tTime);
|
2017-07-14 03:11:36 +03:00
|
|
|
auto header = createHeader(
|
2017-08-05 06:14:18 +03:00
|
|
|
kHeaderIdentifierFile,
|
|
|
|
kHeaderVersion,
|
|
|
|
currentTime,
|
|
|
|
currentTime,
|
|
|
|
currentTime);
|
2017-07-14 03:11:36 +03:00
|
|
|
|
|
|
|
auto data = header.coalesce();
|
|
|
|
auto wrote = folly::writeFull(fd, data.data(), data.size());
|
|
|
|
|
|
|
|
if (wrote == -1) {
|
|
|
|
folly::throwSystemError("writeNoInt failed");
|
|
|
|
}
|
|
|
|
if (wrote != data.size()) {
|
|
|
|
folly::throwSystemError(
|
|
|
|
"writeNoInt wrote only ", wrote, " of ", data.size(), " bytes");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper function to create an overlay file
|
|
|
|
folly::File Overlay::createOverlayFile(fuse_ino_t childNumber) {
|
|
|
|
auto filePath = getFilePath(childNumber);
|
|
|
|
folly::File file(filePath.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
|
|
|
|
|
|
|
|
SCOPE_FAIL {
|
|
|
|
::unlink(filePath.c_str());
|
|
|
|
};
|
|
|
|
|
|
|
|
addHeaderToOverlayFile(file.fd());
|
|
|
|
return file;
|
|
|
|
}
|
2017-08-05 06:14:18 +03:00
|
|
|
|
|
|
|
void Overlay::parseHeader(
|
|
|
|
folly::StringPiece header,
|
|
|
|
folly::StringPiece headerId,
|
2017-08-11 21:34:52 +03:00
|
|
|
InodeBase::InodeTimestamps& timeStamps) {
|
2017-08-05 06:14:18 +03:00
|
|
|
folly::IOBuf buf(folly::IOBuf::WRAP_BUFFER, ByteRange{header});
|
|
|
|
folly::io::Cursor cursor(&buf);
|
|
|
|
|
|
|
|
// Validate header identifier
|
|
|
|
auto id = cursor.readFixedString(kHeaderIdentifierDir.size());
|
|
|
|
StringPiece identifier{id};
|
|
|
|
if (identifier.compare(headerId) != 0) {
|
|
|
|
folly::throwSystemError(
|
|
|
|
EIO,
|
|
|
|
"unexpected overlay header identifier : ",
|
|
|
|
folly::hexlify(ByteRange{identifier}));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate header version
|
|
|
|
auto version = cursor.readBE<uint32_t>();
|
|
|
|
if (version != kHeaderVersion) {
|
|
|
|
folly::throwSystemError(EIO, "Unexpected overlay version :", version);
|
|
|
|
}
|
2017-08-11 21:34:52 +03:00
|
|
|
timeStamps.atime.tv_sec = cursor.readBE<uint64_t>();
|
|
|
|
timeStamps.atime.tv_nsec = cursor.readBE<uint64_t>();
|
|
|
|
timeStamps.ctime.tv_sec = cursor.readBE<uint64_t>();
|
|
|
|
timeStamps.ctime.tv_nsec = cursor.readBE<uint64_t>();
|
|
|
|
timeStamps.mtime.tv_sec = cursor.readBE<uint64_t>();
|
|
|
|
timeStamps.mtime.tv_nsec = cursor.readBE<uint64_t>();
|
2017-08-05 06:14:18 +03:00
|
|
|
}
|
|
|
|
// Helper function to update timestamps into overlay file
|
2017-08-11 21:34:52 +03:00
|
|
|
void Overlay::updateTimestampToHeader(
|
|
|
|
int fd,
|
|
|
|
const InodeBase::InodeTimestamps& timeStamps) {
|
2017-08-05 06:14:18 +03:00
|
|
|
// Create a string piece with timestamps
|
|
|
|
std::array<uint64_t, 6> buf;
|
|
|
|
folly::IOBuf timestamps(folly::IOBuf::WRAP_BUFFER, buf.data(), sizeof(buf));
|
|
|
|
timestamps.clear();
|
|
|
|
|
|
|
|
folly::io::Appender appender(×tamps, 0);
|
2017-08-11 21:34:52 +03:00
|
|
|
appender.writeBE<uint64_t>(timeStamps.atime.tv_sec);
|
|
|
|
appender.writeBE<uint64_t>(timeStamps.atime.tv_nsec);
|
|
|
|
appender.writeBE<uint64_t>(timeStamps.ctime.tv_sec);
|
|
|
|
appender.writeBE<uint64_t>(timeStamps.ctime.tv_nsec);
|
|
|
|
appender.writeBE<uint64_t>(timeStamps.mtime.tv_sec);
|
|
|
|
appender.writeBE<uint64_t>(timeStamps.mtime.tv_nsec);
|
2017-08-05 06:14:18 +03:00
|
|
|
|
|
|
|
// replace the timestamps of current header with the new timestamps
|
|
|
|
auto newHeader = timestamps.coalesce();
|
|
|
|
auto wrote = folly::pwriteNoInt(
|
|
|
|
fd,
|
|
|
|
newHeader.data(),
|
|
|
|
newHeader.size(),
|
|
|
|
kHeaderIdentifierDir.size() + sizeof(kHeaderVersion));
|
|
|
|
if (wrote == -1) {
|
|
|
|
folly::throwSystemError("pwriteNoInt failed");
|
|
|
|
}
|
|
|
|
if (wrote != newHeader.size()) {
|
|
|
|
folly::throwSystemError(
|
|
|
|
"writeNoInt wrote only ", wrote, " of ", newHeader.size(), " bytes");
|
|
|
|
}
|
|
|
|
}
|
2016-09-10 02:56:02 +03:00
|
|
|
}
|
|
|
|
}
|