2016-05-12 23:43:17 +03:00
|
|
|
/*
|
2017-01-21 09:02:33 +03:00
|
|
|
* Copyright (c) 2016-present, Facebook, Inc.
|
2016-05-12 23:43:17 +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.
|
|
|
|
*
|
|
|
|
*/
|
2016-12-01 02:48:04 +03:00
|
|
|
#include "FileInode.h"
|
2016-05-12 23:43:17 +03:00
|
|
|
|
2016-09-19 22:48:09 +03:00
|
|
|
#include "EdenMount.h"
|
2016-05-17 00:48:25 +03:00
|
|
|
#include "FileData.h"
|
2016-12-01 02:48:04 +03:00
|
|
|
#include "FileHandle.h"
|
2016-12-15 02:35:01 +03:00
|
|
|
#include "InodeError.h"
|
2016-09-10 02:56:02 +03:00
|
|
|
#include "Overlay.h"
|
2016-06-09 04:59:51 +03:00
|
|
|
#include "eden/fs/model/Blob.h"
|
2016-05-28 04:16:30 +03:00
|
|
|
#include "eden/fs/model/Hash.h"
|
2016-06-09 04:59:51 +03:00
|
|
|
#include "eden/fs/store/ObjectStore.h"
|
2016-05-26 18:22:22 +03:00
|
|
|
#include "eden/utils/XAttr.h"
|
2016-05-12 23:43:17 +03:00
|
|
|
|
|
|
|
using folly::Future;
|
|
|
|
using folly::StringPiece;
|
2016-05-17 00:48:28 +03:00
|
|
|
using folly::checkUnixError;
|
2016-05-12 23:43:17 +03:00
|
|
|
using std::string;
|
|
|
|
using std::vector;
|
|
|
|
|
|
|
|
namespace facebook {
|
|
|
|
namespace eden {
|
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
FileInode::FileInode(
|
2016-05-12 23:43:17 +03:00
|
|
|
fuse_ino_t ino,
|
2016-12-13 04:48:45 +03:00
|
|
|
TreeInodePtr parentInode,
|
2016-12-13 04:48:43 +03:00
|
|
|
PathComponentPiece name,
|
2016-09-10 02:56:00 +03:00
|
|
|
TreeInode::Entry* entry)
|
2016-12-23 02:35:01 +03:00
|
|
|
: InodeBase(ino, std::move(parentInode), name),
|
2016-09-10 02:56:00 +03:00
|
|
|
entry_(entry),
|
2016-12-23 02:34:59 +03:00
|
|
|
data_(std::make_shared<FileData>(this, mutex_, entry)) {}
|
2016-05-12 23:43:17 +03:00
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
FileInode::FileInode(
|
2016-07-06 05:53:15 +03:00
|
|
|
fuse_ino_t ino,
|
2016-12-13 04:48:45 +03:00
|
|
|
TreeInodePtr parentInode,
|
2016-12-13 04:48:43 +03:00
|
|
|
PathComponentPiece name,
|
2016-09-10 02:56:00 +03:00
|
|
|
TreeInode::Entry* entry,
|
2016-07-06 05:53:15 +03:00
|
|
|
folly::File&& file)
|
2016-12-23 02:35:01 +03:00
|
|
|
: InodeBase(ino, std::move(parentInode), name),
|
2016-09-10 02:56:00 +03:00
|
|
|
entry_(entry),
|
2016-12-23 02:34:59 +03:00
|
|
|
data_(std::make_shared<FileData>(this, mutex_, entry_, std::move(file))) {
|
|
|
|
}
|
2016-07-06 05:53:15 +03:00
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
folly::Future<fusell::Dispatcher::Attr> FileInode::getattr() {
|
2016-05-17 00:48:30 +03:00
|
|
|
auto data = getOrLoadData();
|
2016-05-17 00:48:28 +03:00
|
|
|
|
2016-05-17 00:48:30 +03:00
|
|
|
// Future optimization opportunity: right now, if we have not already
|
|
|
|
// materialized the data from the entry_, we have to materialize it
|
|
|
|
// from the store. If we augmented our metadata we could avoid this,
|
|
|
|
// and this would speed up operations like `ls`.
|
2017-02-11 01:16:00 +03:00
|
|
|
data->materializeForRead(O_RDONLY);
|
2016-05-12 23:43:17 +03:00
|
|
|
|
2016-12-23 02:35:01 +03:00
|
|
|
fusell::Dispatcher::Attr attr(getMount()->getMountPoint());
|
2016-05-17 00:48:30 +03:00
|
|
|
attr.st = data->stat();
|
2016-06-20 23:38:37 +03:00
|
|
|
attr.st.st_ino = getNodeId();
|
2016-05-12 23:43:17 +03:00
|
|
|
return attr;
|
|
|
|
}
|
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
folly::Future<fusell::Dispatcher::Attr> FileInode::setattr(
|
2016-07-02 01:08:47 +03:00
|
|
|
const struct stat& attr,
|
|
|
|
int to_set) {
|
|
|
|
auto data = getOrLoadData();
|
|
|
|
int open_flags = O_RDWR;
|
|
|
|
|
|
|
|
// Minor optimization: if we know that the file is being completed truncated
|
|
|
|
// as part of this operation, there's no need to fetch the underlying data,
|
|
|
|
// so pass on the truncate flag our underlying open call
|
2016-07-06 05:53:16 +03:00
|
|
|
if ((to_set & FUSE_SET_ATTR_SIZE) && attr.st_size == 0) {
|
2016-07-02 01:08:47 +03:00
|
|
|
open_flags |= O_TRUNC;
|
|
|
|
}
|
|
|
|
|
2016-12-23 02:35:01 +03:00
|
|
|
getParentBuggy()->materializeDirAndParents();
|
2016-09-10 02:56:00 +03:00
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
data->materializeForWrite(open_flags);
|
2016-07-02 01:08:47 +03:00
|
|
|
|
2016-12-23 02:35:01 +03:00
|
|
|
fusell::Dispatcher::Attr result(getMount()->getMountPoint());
|
2016-07-02 01:08:47 +03:00
|
|
|
result.st = data->setAttr(attr, to_set);
|
|
|
|
result.st.st_ino = getNodeId();
|
2016-09-19 22:48:11 +03:00
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
auto path = getPath();
|
|
|
|
if (path.hasValue()) {
|
|
|
|
getMount()->getJournal().wlock()->addDelta(
|
|
|
|
std::make_unique<JournalDelta>(JournalDelta{path.value()}));
|
|
|
|
}
|
2016-09-19 22:48:11 +03:00
|
|
|
|
2016-07-02 01:08:47 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
folly::Future<std::string> FileInode::readlink() {
|
2016-05-17 00:48:28 +03:00
|
|
|
std::unique_lock<std::mutex> lock(mutex_);
|
|
|
|
|
2016-09-10 02:56:00 +03:00
|
|
|
DCHECK_NOTNULL(entry_);
|
|
|
|
if (!S_ISLNK(entry_->mode)) {
|
|
|
|
// man 2 readlink says: EINVAL The named file is not a symbolic link.
|
2016-12-15 02:35:01 +03:00
|
|
|
throw InodeError(EINVAL, inodePtrFromThis(), "not a symlink");
|
2016-09-10 02:56:00 +03:00
|
|
|
}
|
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
if (entry_->isMaterialized()) {
|
2016-05-17 00:48:28 +03:00
|
|
|
struct stat st;
|
|
|
|
auto localPath = getLocalPath();
|
|
|
|
|
|
|
|
// Figure out how much space we need to hold the symlink target.
|
|
|
|
checkUnixError(lstat(localPath.c_str(), &st));
|
|
|
|
|
|
|
|
// Allocate a string of the appropriate size.
|
|
|
|
std::string buf;
|
|
|
|
buf.resize(st.st_size, 0 /* filled with zeroes */);
|
|
|
|
|
|
|
|
// Read the link into the string buffer.
|
|
|
|
auto res = ::readlink(
|
|
|
|
localPath.c_str(), &buf[0], buf.size() + 1 /* for nul terminator */);
|
|
|
|
checkUnixError(res);
|
|
|
|
CHECK_EQ(st.st_size, res) << "symlink size TOCTOU";
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2016-09-10 02:56:00 +03:00
|
|
|
// Load the symlink contents from the store
|
2017-02-11 01:16:00 +03:00
|
|
|
auto blob = getMount()->getObjectStore()->getBlob(entry_->getHash());
|
2016-09-10 02:56:00 +03:00
|
|
|
auto buf = blob->getContents();
|
|
|
|
return buf.moveToFbString().toStdString();
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
std::shared_ptr<FileData> FileInode::getOrLoadData() {
|
2016-05-12 23:43:17 +03:00
|
|
|
std::unique_lock<std::mutex> lock(mutex_);
|
2016-05-17 00:48:25 +03:00
|
|
|
if (!data_) {
|
2016-12-23 02:34:59 +03:00
|
|
|
data_ = std::make_shared<FileData>(this, mutex_, entry_);
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
2016-05-17 00:48:29 +03:00
|
|
|
|
2016-05-17 00:48:25 +03:00
|
|
|
return data_;
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
void FileInode::fileHandleDidClose() {
|
2016-05-12 23:43:17 +03:00
|
|
|
std::unique_lock<std::mutex> lock(mutex_);
|
2016-05-17 00:48:25 +03:00
|
|
|
if (data_.unique()) {
|
|
|
|
// We're the only remaining user, no need to keep it around
|
|
|
|
data_.reset();
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
AbsolutePath FileInode::getLocalPath() const {
|
2017-02-11 01:16:00 +03:00
|
|
|
return getMount()->getOverlay()->getFilePath(getNodeId());
|
2016-05-17 00:48:28 +03:00
|
|
|
}
|
|
|
|
|
2017-02-16 07:31:48 +03:00
|
|
|
bool FileInode::isSameAs(const Blob& blob, mode_t mode) {
|
|
|
|
// When comparing mode bits, we only care about the
|
|
|
|
// file type and owner permissions.
|
|
|
|
auto relevantModeBits = [](mode_t m) { return (m & (S_IFMT | S_IRWXU)); };
|
|
|
|
if (relevantModeBits(entry_->mode) != relevantModeBits(mode)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!entry_->isMaterialized()) {
|
|
|
|
return entry_->getHash() == blob.getHash();
|
|
|
|
}
|
|
|
|
|
|
|
|
return getOrLoadData()->getSha1() == Hash::sha1(&blob.getContents());
|
|
|
|
}
|
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
folly::Future<std::shared_ptr<fusell::FileHandle>> FileInode::open(
|
2016-05-12 23:43:17 +03:00
|
|
|
const struct fuse_file_info& fi) {
|
2016-05-18 03:22:22 +03:00
|
|
|
auto data = getOrLoadData();
|
|
|
|
SCOPE_EXIT {
|
|
|
|
data.reset();
|
|
|
|
fileHandleDidClose();
|
|
|
|
};
|
2016-09-10 02:56:00 +03:00
|
|
|
if (fi.flags & (O_RDWR | O_WRONLY | O_CREAT | O_TRUNC)) {
|
2016-12-23 02:35:01 +03:00
|
|
|
getParentBuggy()->materializeDirAndParents();
|
2017-02-11 01:16:00 +03:00
|
|
|
data->materializeForWrite(fi.flags);
|
2016-09-10 02:56:00 +03:00
|
|
|
} else {
|
2017-02-11 01:16:00 +03:00
|
|
|
data->materializeForRead(fi.flags);
|
2016-09-10 02:56:00 +03:00
|
|
|
}
|
2016-05-17 00:48:28 +03:00
|
|
|
|
2017-01-18 02:01:37 +03:00
|
|
|
return std::make_shared<FileHandle>(inodePtrFromThis(), data, fi.flags);
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
2016-12-02 04:49:43 +03:00
|
|
|
std::shared_ptr<FileHandle> FileInode::finishCreate() {
|
2016-09-10 02:56:00 +03:00
|
|
|
auto data = getOrLoadData();
|
|
|
|
SCOPE_EXIT {
|
|
|
|
data.reset();
|
|
|
|
fileHandleDidClose();
|
|
|
|
};
|
2017-02-11 01:16:00 +03:00
|
|
|
data->materializeForWrite(0);
|
2016-09-10 02:56:00 +03:00
|
|
|
|
2017-01-18 02:01:37 +03:00
|
|
|
return std::make_shared<FileHandle>(inodePtrFromThis(), data, 0);
|
2016-09-10 02:56:00 +03:00
|
|
|
}
|
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
Future<vector<string>> FileInode::listxattr() {
|
2016-05-12 23:43:17 +03:00
|
|
|
// Currently, we only return a non-empty vector for regular files, and we
|
|
|
|
// assume that the SHA-1 is present without checking the ObjectStore.
|
|
|
|
vector<string> attributes;
|
|
|
|
|
2016-09-10 02:56:00 +03:00
|
|
|
if (S_ISREG(entry_->mode)) {
|
|
|
|
attributes.emplace_back(kXattrSha1.str());
|
|
|
|
}
|
2016-05-12 23:43:17 +03:00
|
|
|
return attributes;
|
|
|
|
}
|
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
Future<string> FileInode::getxattr(StringPiece name) {
|
2016-05-12 23:43:17 +03:00
|
|
|
// Currently, we only support the xattr for the SHA-1 of a regular file.
|
2016-05-26 18:22:22 +03:00
|
|
|
if (name != kXattrSha1) {
|
2016-12-15 02:35:01 +03:00
|
|
|
throw InodeError(kENOATTR, inodePtrFromThis());
|
2016-05-26 18:22:22 +03:00
|
|
|
}
|
|
|
|
|
2016-05-28 04:16:30 +03:00
|
|
|
return getSHA1().get().toString();
|
|
|
|
}
|
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
Future<Hash> FileInode::getSHA1() {
|
2016-05-26 18:22:22 +03:00
|
|
|
// Some ugly looking stuff to avoid materializing the file if we haven't
|
|
|
|
// done so already.
|
|
|
|
std::unique_lock<std::mutex> lock(mutex_);
|
|
|
|
if (data_) {
|
|
|
|
// We already have context, ask it to supply the results.
|
|
|
|
return data_->getSha1Locked(lock);
|
|
|
|
}
|
2016-09-10 02:56:00 +03:00
|
|
|
CHECK_NOTNULL(entry_);
|
|
|
|
|
|
|
|
if (!S_ISREG(entry_->mode)) {
|
|
|
|
// We only define a SHA-1 value for regular files
|
2016-12-15 02:35:01 +03:00
|
|
|
throw InodeError(kENOATTR, inodePtrFromThis());
|
2016-09-10 02:56:00 +03:00
|
|
|
}
|
2016-05-26 18:22:22 +03:00
|
|
|
|
2017-02-11 01:16:00 +03:00
|
|
|
if (entry_->isMaterialized()) {
|
2016-05-26 18:22:22 +03:00
|
|
|
// The O_NOFOLLOW here prevents us from attempting to read attributes
|
|
|
|
// from a symlink.
|
2016-09-10 02:56:00 +03:00
|
|
|
auto filePath = getLocalPath();
|
|
|
|
folly::File file(filePath.c_str(), O_RDONLY | O_NOFOLLOW);
|
2016-05-26 18:22:22 +03:00
|
|
|
|
|
|
|
// Return the property from the existing file.
|
|
|
|
// If it isn't set it means that someone was poking into the overlay and
|
|
|
|
// we'll return the standard kENOATTR back to the caller in that case.
|
2016-05-28 04:16:30 +03:00
|
|
|
return Hash(fgetxattr(file.fd(), kXattrSha1));
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(mbolin): Make this more fault-tolerant. Currently, there is no logic
|
|
|
|
// to account for the case where we don't have the SHA-1 for the blob, the
|
|
|
|
// hash doesn't correspond to a blob, etc.
|
2017-02-11 01:16:00 +03:00
|
|
|
return getMount()->getObjectStore()->getSha1ForBlob(entry_->getHash());
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
2016-12-01 02:48:04 +03:00
|
|
|
const TreeInode::Entry* FileInode::getEntry() const {
|
2016-05-12 23:43:17 +03:00
|
|
|
return entry_;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|