Make ChRootEnv, EncryptedEnv, and TimedEnv into FileSystems (#7968)

Summary:
These classes were wraps of Env that provided only extensions to the FileSystem functionality.  Changed the classes to be FileSystems and the wraps to be of the CompositeEnvWrapper.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/7968

Reviewed By: anand1976

Differential Revision: D26900253

Pulled By: mrambacher

fbshipit-source-id: 94001d8024a3c54a1c11adadca2bac66c3af2a77
main
mrambacher 4 years ago committed by Facebook GitHub Bot
parent 0304352882
commit 1a343bc393
  1. 213
      env/env_chroot.cc
  2. 442
      env/env_encryption.cc
  3. 133
      include/rocksdb/env_encryption.h
  4. 162
      utilities/env_timed.cc

213
env/env_chroot.cc vendored

@ -16,14 +16,17 @@
#include <utility>
#include <vector>
#include "env/composite_env_wrapper.h"
#include "rocksdb/file_system.h"
#include "rocksdb/status.h"
namespace ROCKSDB_NAMESPACE {
class ChrootEnv : public EnvWrapper {
namespace {
class ChrootFileSystem : public FileSystemWrapper {
public:
ChrootEnv(Env* base_env, const std::string& chroot_dir)
: EnvWrapper(base_env) {
ChrootFileSystem(const std::shared_ptr<FileSystem>& base,
const std::string& chroot_dir)
: FileSystemWrapper(base) {
#if defined(OS_AIX)
char resolvedName[PATH_MAX];
char* real_chroot_dir = realpath(chroot_dir.c_str(), resolvedName);
@ -38,6 +41,7 @@ class ChrootEnv : public EnvWrapper {
#endif
}
const char* Name() const override { return "ChrootFS"; }
Status RegisterDbPaths(const std::vector<std::string>& paths) override {
std::vector<std::string> encoded_paths;
encoded_paths.reserve(paths.size());
@ -48,7 +52,7 @@ class ChrootEnv : public EnvWrapper {
}
encoded_paths.emplace_back(status_and_enc_path.second);
}
return EnvWrapper::Env::RegisterDbPaths(encoded_paths);
return FileSystemWrapper::RegisterDbPaths(encoded_paths);
}
Status UnregisterDbPaths(const std::vector<std::string>& paths) override {
@ -61,46 +65,49 @@ class ChrootEnv : public EnvWrapper {
}
encoded_paths.emplace_back(status_and_enc_path.second);
}
return EnvWrapper::Env::UnregisterDbPaths(encoded_paths);
return FileSystemWrapper::UnregisterDbPaths(encoded_paths);
}
Status NewSequentialFile(const std::string& fname,
std::unique_ptr<SequentialFile>* result,
const EnvOptions& options) override {
IOStatus NewSequentialFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSSequentialFile>* result,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePathWithNewBasename(fname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::NewSequentialFile(status_and_enc_path.second, result,
options);
return FileSystemWrapper::NewSequentialFile(status_and_enc_path.second,
options, result, dbg);
}
Status NewRandomAccessFile(const std::string& fname,
std::unique_ptr<RandomAccessFile>* result,
const EnvOptions& options) override {
IOStatus NewRandomAccessFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSRandomAccessFile>* result,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePathWithNewBasename(fname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::NewRandomAccessFile(status_and_enc_path.second, result,
options);
return FileSystemWrapper::NewRandomAccessFile(status_and_enc_path.second,
options, result, dbg);
}
Status NewWritableFile(const std::string& fname,
std::unique_ptr<WritableFile>* result,
const EnvOptions& options) override {
IOStatus NewWritableFile(const std::string& fname, const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePathWithNewBasename(fname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::NewWritableFile(status_and_enc_path.second, result,
options);
return FileSystemWrapper::NewWritableFile(status_and_enc_path.second,
options, result, dbg);
}
Status ReuseWritableFile(const std::string& fname,
IOStatus ReuseWritableFile(const std::string& fname,
const std::string& old_fname,
std::unique_ptr<WritableFile>* result,
const EnvOptions& options) override {
const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePathWithNewBasename(fname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
@ -109,109 +116,131 @@ class ChrootEnv : public EnvWrapper {
if (!status_and_old_enc_path.first.ok()) {
return status_and_old_enc_path.first;
}
return EnvWrapper::ReuseWritableFile(status_and_old_enc_path.second,
status_and_old_enc_path.second, result,
options);
return FileSystemWrapper::ReuseWritableFile(status_and_old_enc_path.second,
status_and_old_enc_path.second,
options, result, dbg);
}
Status NewRandomRWFile(const std::string& fname,
std::unique_ptr<RandomRWFile>* result,
const EnvOptions& options) override {
IOStatus NewRandomRWFile(const std::string& fname, const FileOptions& options,
std::unique_ptr<FSRandomRWFile>* result,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePathWithNewBasename(fname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::NewRandomRWFile(status_and_enc_path.second, result,
options);
return FileSystemWrapper::NewRandomRWFile(status_and_enc_path.second,
options, result, dbg);
}
Status NewDirectory(const std::string& dir,
std::unique_ptr<Directory>* result) override {
IOStatus NewDirectory(const std::string& dir, const IOOptions& options,
std::unique_ptr<FSDirectory>* result,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePathWithNewBasename(dir);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::NewDirectory(status_and_enc_path.second, result);
return FileSystemWrapper::NewDirectory(status_and_enc_path.second, options,
result, dbg);
}
Status FileExists(const std::string& fname) override {
IOStatus FileExists(const std::string& fname, const IOOptions& options,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePathWithNewBasename(fname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::FileExists(status_and_enc_path.second);
return FileSystemWrapper::FileExists(status_and_enc_path.second, options,
dbg);
}
Status GetChildren(const std::string& dir,
std::vector<std::string>* result) override {
IOStatus GetChildren(const std::string& dir, const IOOptions& options,
std::vector<std::string>* result,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePath(dir);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::GetChildren(status_and_enc_path.second, result);
return FileSystemWrapper::GetChildren(status_and_enc_path.second, options,
result, dbg);
}
Status GetChildrenFileAttributes(
const std::string& dir, std::vector<FileAttributes>* result) override {
IOStatus GetChildrenFileAttributes(const std::string& dir,
const IOOptions& options,
std::vector<FileAttributes>* result,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePath(dir);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::GetChildrenFileAttributes(status_and_enc_path.second,
result);
return FileSystemWrapper::GetChildrenFileAttributes(
status_and_enc_path.second, options, result, dbg);
}
Status DeleteFile(const std::string& fname) override {
IOStatus DeleteFile(const std::string& fname, const IOOptions& options,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePath(fname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::DeleteFile(status_and_enc_path.second);
return FileSystemWrapper::DeleteFile(status_and_enc_path.second, options,
dbg);
}
Status CreateDir(const std::string& dirname) override {
IOStatus CreateDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePathWithNewBasename(dirname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::CreateDir(status_and_enc_path.second);
return FileSystemWrapper::CreateDir(status_and_enc_path.second, options,
dbg);
}
Status CreateDirIfMissing(const std::string& dirname) override {
IOStatus CreateDirIfMissing(const std::string& dirname,
const IOOptions& options,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePathWithNewBasename(dirname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::CreateDirIfMissing(status_and_enc_path.second);
return FileSystemWrapper::CreateDirIfMissing(status_and_enc_path.second,
options, dbg);
}
Status DeleteDir(const std::string& dirname) override {
IOStatus DeleteDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePath(dirname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::DeleteDir(status_and_enc_path.second);
return FileSystemWrapper::DeleteDir(status_and_enc_path.second, options,
dbg);
}
Status GetFileSize(const std::string& fname, uint64_t* file_size) override {
IOStatus GetFileSize(const std::string& fname, const IOOptions& options,
uint64_t* file_size, IODebugContext* dbg) override {
auto status_and_enc_path = EncodePath(fname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::GetFileSize(status_and_enc_path.second, file_size);
return FileSystemWrapper::GetFileSize(status_and_enc_path.second, options,
file_size, dbg);
}
Status GetFileModificationTime(const std::string& fname,
uint64_t* file_mtime) override {
IOStatus GetFileModificationTime(const std::string& fname,
const IOOptions& options,
uint64_t* file_mtime,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePath(fname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::GetFileModificationTime(status_and_enc_path.second,
file_mtime);
return FileSystemWrapper::GetFileModificationTime(
status_and_enc_path.second, options, file_mtime, dbg);
}
Status RenameFile(const std::string& src, const std::string& dest) override {
IOStatus RenameFile(const std::string& src, const std::string& dest,
const IOOptions& options, IODebugContext* dbg) override {
auto status_and_src_enc_path = EncodePath(src);
if (!status_and_src_enc_path.first.ok()) {
return status_and_src_enc_path.first;
@ -220,11 +249,13 @@ class ChrootEnv : public EnvWrapper {
if (!status_and_dest_enc_path.first.ok()) {
return status_and_dest_enc_path.first;
}
return EnvWrapper::RenameFile(status_and_src_enc_path.second,
status_and_dest_enc_path.second);
return FileSystemWrapper::RenameFile(status_and_src_enc_path.second,
status_and_dest_enc_path.second,
options, dbg);
}
Status LinkFile(const std::string& src, const std::string& dest) override {
IOStatus LinkFile(const std::string& src, const std::string& dest,
const IOOptions& options, IODebugContext* dbg) override {
auto status_and_src_enc_path = EncodePath(src);
if (!status_and_src_enc_path.first.ok()) {
return status_and_src_enc_path.first;
@ -233,11 +264,13 @@ class ChrootEnv : public EnvWrapper {
if (!status_and_dest_enc_path.first.ok()) {
return status_and_dest_enc_path.first;
}
return EnvWrapper::LinkFile(status_and_src_enc_path.second,
status_and_dest_enc_path.second);
return FileSystemWrapper::LinkFile(status_and_src_enc_path.second,
status_and_dest_enc_path.second, options,
dbg);
}
Status LockFile(const std::string& fname, FileLock** lock) override {
IOStatus LockFile(const std::string& fname, const IOOptions& options,
FileLock** lock, IODebugContext* dbg) override {
auto status_and_enc_path = EncodePathWithNewBasename(fname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
@ -245,10 +278,12 @@ class ChrootEnv : public EnvWrapper {
// FileLock subclasses may store path (e.g., PosixFileLock stores it). We
// can skip stripping the chroot directory from this path because callers
// shouldn't use it.
return EnvWrapper::LockFile(status_and_enc_path.second, lock);
return FileSystemWrapper::LockFile(status_and_enc_path.second, options,
lock, dbg);
}
Status GetTestDirectory(std::string* path) override {
IOStatus GetTestDirectory(const IOOptions& options, std::string* path,
IODebugContext* dbg) override {
// Adapted from PosixEnv's implementation since it doesn't provide a way to
// create directory in the chroot.
char buf[256];
@ -256,36 +291,40 @@ class ChrootEnv : public EnvWrapper {
*path = buf;
// Directory may already exist, so ignore return
return CreateDirIfMissing(*path);
return CreateDirIfMissing(*path, options, dbg);
}
Status NewLogger(const std::string& fname,
std::shared_ptr<Logger>* result) override {
IOStatus NewLogger(const std::string& fname, const IOOptions& options,
std::shared_ptr<Logger>* result,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePathWithNewBasename(fname);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::NewLogger(status_and_enc_path.second, result);
return FileSystemWrapper::NewLogger(status_and_enc_path.second, options,
result, dbg);
}
Status GetAbsolutePath(const std::string& db_path,
std::string* output_path) override {
IOStatus GetAbsolutePath(const std::string& db_path, const IOOptions& options,
std::string* output_path,
IODebugContext* dbg) override {
auto status_and_enc_path = EncodePath(db_path);
if (!status_and_enc_path.first.ok()) {
return status_and_enc_path.first;
}
return EnvWrapper::GetAbsolutePath(status_and_enc_path.second, output_path);
return FileSystemWrapper::GetAbsolutePath(status_and_enc_path.second,
options, output_path, dbg);
}
private:
// Returns status and expanded absolute path including the chroot directory.
// Checks whether the provided path breaks out of the chroot. If it returns
// non-OK status, the returned path should not be used.
std::pair<Status, std::string> EncodePath(const std::string& path) {
std::pair<IOStatus, std::string> EncodePath(const std::string& path) {
if (path.empty() || path[0] != '/') {
return {Status::InvalidArgument(path, "Not an absolute path"), ""};
return {IOStatus::InvalidArgument(path, "Not an absolute path"), ""};
}
std::pair<Status, std::string> res;
std::pair<IOStatus, std::string> res;
res.second = chroot_dir_ + path;
#if defined(OS_AIX)
char resolvedName[PATH_MAX];
@ -294,14 +333,14 @@ class ChrootEnv : public EnvWrapper {
char* normalized_path = realpath(res.second.c_str(), nullptr);
#endif
if (normalized_path == nullptr) {
res.first = Status::NotFound(res.second, strerror(errno));
res.first = IOStatus::NotFound(res.second, strerror(errno));
} else if (strlen(normalized_path) < chroot_dir_.size() ||
strncmp(normalized_path, chroot_dir_.c_str(),
chroot_dir_.size()) != 0) {
res.first = Status::IOError(res.second,
res.first = IOStatus::IOError(res.second,
"Attempted to access path outside chroot");
} else {
res.first = Status::OK();
res.first = IOStatus::OK();
}
#if !defined(OS_AIX)
free(normalized_path);
@ -311,10 +350,10 @@ class ChrootEnv : public EnvWrapper {
// Similar to EncodePath() except assumes the basename in the path hasn't been
// created yet.
std::pair<Status, std::string> EncodePathWithNewBasename(
std::pair<IOStatus, std::string> EncodePathWithNewBasename(
const std::string& path) {
if (path.empty() || path[0] != '/') {
return {Status::InvalidArgument(path, "Not an absolute path"), ""};
return {IOStatus::InvalidArgument(path, "Not an absolute path"), ""};
}
// Basename may be followed by trailing slashes
size_t final_idx = path.find_last_not_of('/');
@ -333,12 +372,20 @@ class ChrootEnv : public EnvWrapper {
std::string chroot_dir_;
};
} // namespace
std::shared_ptr<FileSystem> NewChrootFileSystem(
const std::shared_ptr<FileSystem>& base, const std::string& chroot_dir) {
return std::make_shared<ChrootFileSystem>(base, chroot_dir);
}
Env* NewChrootEnv(Env* base_env, const std::string& chroot_dir) {
if (!base_env->FileExists(chroot_dir).ok()) {
return nullptr;
}
return new ChrootEnv(base_env, chroot_dir);
std::shared_ptr<FileSystem> chroot_fs =
NewChrootFileSystem(base_env->GetFileSystem(), chroot_dir);
return new CompositeEnvWrapper(base_env, chroot_fs);
}
} // namespace ROCKSDB_NAMESPACE

@ -12,9 +12,11 @@
#include <cctype>
#include <iostream>
#include "env/composite_env_wrapper.h"
#include "env/env_encryption_ctr.h"
#include "monitoring/perf_context_imp.h"
#include "rocksdb/convenience.h"
#include "rocksdb/io_status.h"
#include "rocksdb/system_clock.h"
#include "util/aligned_buffer.h"
#include "util/coding.h"
@ -85,19 +87,24 @@ std::shared_ptr<EncryptionProvider> EncryptionProvider::NewCTRProvider(
// If an error was encountered, returns a non-OK status.
//
// REQUIRES: External synchronization
Status EncryptedSequentialFile::Read(size_t n, Slice* result, char* scratch) {
IOStatus EncryptedSequentialFile::Read(size_t n, const IOOptions& options,
Slice* result, char* scratch,
IODebugContext* dbg) {
assert(scratch);
Status status = file_->Read(n, result, scratch);
if (!status.ok()) {
return status;
IOStatus io_s = file_->Read(n, options, result, scratch, dbg);
if (!io_s.ok()) {
return io_s;
}
{
PERF_TIMER_GUARD(decrypt_data_nanos);
status = stream_->Decrypt(offset_, (char*)result->data(), result->size());
io_s = status_to_io_status(
stream_->Decrypt(offset_, (char*)result->data(), result->size()));
}
if (io_s.ok()) {
offset_ += result->size(); // We've already ready data from disk, so update
// offset_ even if decryption fails.
return status;
}
return io_s;
}
// Skip "n" bytes from the file. This is guaranteed to be no
@ -107,7 +114,7 @@ Status EncryptedSequentialFile::Read(size_t n, Slice* result, char* scratch) {
// file, and Skip will return OK.
//
// REQUIRES: External synchronization
Status EncryptedSequentialFile::Skip(uint64_t n) {
IOStatus EncryptedSequentialFile::Skip(uint64_t n) {
auto status = file_->Skip(n);
if (!status.ok()) {
return status;
@ -131,26 +138,30 @@ size_t EncryptedSequentialFile::GetRequiredBufferAlignment() const {
// Remove any kind of caching of data from the offset to offset+length
// of this file. If the length is 0, then it refers to the end of file.
// If the system is not caching the file contents, then this is a noop.
Status EncryptedSequentialFile::InvalidateCache(size_t offset, size_t length) {
IOStatus EncryptedSequentialFile::InvalidateCache(size_t offset,
size_t length) {
return file_->InvalidateCache(offset + prefixLength_, length);
}
// Positioned Read for direct I/O
// If Direct I/O enabled, offset, n, and scratch should be properly aligned
Status EncryptedSequentialFile::PositionedRead(uint64_t offset, size_t n,
Slice* result, char* scratch) {
IOStatus EncryptedSequentialFile::PositionedRead(uint64_t offset, size_t n,
const IOOptions& options,
Slice* result, char* scratch,
IODebugContext* dbg) {
assert(scratch);
offset += prefixLength_; // Skip prefix
auto status = file_->PositionedRead(offset, n, result, scratch);
if (!status.ok()) {
return status;
auto io_s = file_->PositionedRead(offset, n, options, result, scratch, dbg);
if (!io_s.ok()) {
return io_s;
}
offset_ = offset + result->size();
{
PERF_TIMER_GUARD(decrypt_data_nanos);
status = stream_->Decrypt(offset, (char*)result->data(), result->size());
io_s = status_to_io_status(
stream_->Decrypt(offset, (char*)result->data(), result->size()));
}
return status;
return io_s;
}
// Read up to "n" bytes from the file starting at "offset".
@ -163,25 +174,30 @@ Status EncryptedSequentialFile::PositionedRead(uint64_t offset, size_t n,
//
// Safe for concurrent use by multiple threads.
// If Direct I/O enabled, offset, n, and scratch should be aligned properly.
Status EncryptedRandomAccessFile::Read(uint64_t offset, size_t n, Slice* result,
char* scratch) const {
IOStatus EncryptedRandomAccessFile::Read(uint64_t offset, size_t n,
const IOOptions& options,
Slice* result, char* scratch,
IODebugContext* dbg) const {
assert(scratch);
offset += prefixLength_;
auto status = file_->Read(offset, n, result, scratch);
if (!status.ok()) {
return status;
auto io_s = file_->Read(offset, n, options, result, scratch, dbg);
if (!io_s.ok()) {
return io_s;
}
{
PERF_TIMER_GUARD(decrypt_data_nanos);
status = stream_->Decrypt(offset, (char*)result->data(), result->size());
io_s = status_to_io_status(
stream_->Decrypt(offset, (char*)result->data(), result->size()));
}
return status;
return io_s;
}
// Readahead the file starting from offset by n bytes for caching.
Status EncryptedRandomAccessFile::Prefetch(uint64_t offset, size_t n) {
IOStatus EncryptedRandomAccessFile::Prefetch(uint64_t offset, size_t n,
const IOOptions& options,
IODebugContext* dbg) {
// return Status::OK();
return file_->Prefetch(offset + prefixLength_, n);
return file_->Prefetch(offset + prefixLength_, n, options, dbg);
}
// Tries to get an unique ID for this file that will be the same each time
@ -222,7 +238,7 @@ size_t EncryptedRandomAccessFile::GetRequiredBufferAlignment() const {
// Remove any kind of caching of data from the offset to offset+length
// of this file. If the length is 0, then it refers to the end of file.
// If the system is not caching the file contents, then this is a noop.
Status EncryptedRandomAccessFile::InvalidateCache(size_t offset,
IOStatus EncryptedRandomAccessFile::InvalidateCache(size_t offset,
size_t length) {
return file_->InvalidateCache(offset + prefixLength_, length);
}
@ -230,12 +246,13 @@ Status EncryptedRandomAccessFile::InvalidateCache(size_t offset,
// A file abstraction for sequential writing. The implementation
// must provide buffering since callers may append small fragments
// at a time to the file.
Status EncryptedWritableFile::Append(const Slice& data) {
IOStatus EncryptedWritableFile::Append(const Slice& data,
const IOOptions& options,
IODebugContext* dbg) {
AlignedBuffer buf;
Status status;
Slice dataToAppend(data);
if (data.size() > 0) {
auto offset = file_->GetFileSize(); // size including prefix
auto offset = file_->GetFileSize(options, dbg); // size including prefix
// Encrypt in cloned buffer
buf.Alignment(GetRequiredBufferAlignment());
buf.AllocateNewBuffer(data.size());
@ -243,26 +260,25 @@ Status EncryptedWritableFile::Append(const Slice& data) {
// so that the next two lines can be replaced with buf.Append().
memmove(buf.BufferStart(), data.data(), data.size());
buf.Size(data.size());
IOStatus io_s;
{
PERF_TIMER_GUARD(encrypt_data_nanos);
status = stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize());
io_s = status_to_io_status(
stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize()));
}
if (!status.ok()) {
return status;
if (!io_s.ok()) {
return io_s;
}
dataToAppend = Slice(buf.BufferStart(), buf.CurrentSize());
}
status = file_->Append(dataToAppend);
if (!status.ok()) {
return status;
}
return status;
return file_->Append(dataToAppend, options, dbg);
}
Status EncryptedWritableFile::PositionedAppend(const Slice& data,
uint64_t offset) {
IOStatus EncryptedWritableFile::PositionedAppend(const Slice& data,
uint64_t offset,
const IOOptions& options,
IODebugContext* dbg) {
AlignedBuffer buf;
Status status;
Slice dataToAppend(data);
offset += prefixLength_;
if (data.size() > 0) {
@ -271,20 +287,18 @@ Status EncryptedWritableFile::PositionedAppend(const Slice& data,
buf.AllocateNewBuffer(data.size());
memmove(buf.BufferStart(), data.data(), data.size());
buf.Size(data.size());
IOStatus io_s;
{
PERF_TIMER_GUARD(encrypt_data_nanos);
status = stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize());
io_s = status_to_io_status(
stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize()));
}
if (!status.ok()) {
return status;
if (!io_s.ok()) {
return io_s;
}
dataToAppend = Slice(buf.BufferStart(), buf.CurrentSize());
}
status = file_->PositionedAppend(dataToAppend, offset);
if (!status.ok()) {
return status;
}
return status;
return file_->PositionedAppend(dataToAppend, offset, options, dbg);
}
// Indicates the upper layers if the current WritableFile implementation
@ -302,23 +316,26 @@ size_t EncryptedWritableFile::GetRequiredBufferAlignment() const {
/*
* Get the size of valid data in the file.
*/
uint64_t EncryptedWritableFile::GetFileSize() {
return file_->GetFileSize() - prefixLength_;
uint64_t EncryptedWritableFile::GetFileSize(const IOOptions& options,
IODebugContext* dbg) {
return file_->GetFileSize(options, dbg) - prefixLength_;
}
// Truncate is necessary to trim the file to the correct size
// before closing. It is not always possible to keep track of the file
// size due to whole pages writes. The behavior is undefined if called
// with other writes to follow.
Status EncryptedWritableFile::Truncate(uint64_t size) {
return file_->Truncate(size + prefixLength_);
IOStatus EncryptedWritableFile::Truncate(uint64_t size,
const IOOptions& options,
IODebugContext* dbg) {
return file_->Truncate(size + prefixLength_, options, dbg);
}
// Remove any kind of caching of data from the offset to offset+length
// of this file. If the length is 0, then it refers to the end of file.
// If the system is not caching the file contents, then this is a noop.
// This call has no effect on dirty pages in the cache.
Status EncryptedWritableFile::InvalidateCache(size_t offset, size_t length) {
IOStatus EncryptedWritableFile::InvalidateCache(size_t offset, size_t length) {
return file_->InvalidateCache(offset + prefixLength_, length);
}
@ -328,8 +345,10 @@ Status EncryptedWritableFile::InvalidateCache(size_t offset, size_t length) {
// This asks the OS to initiate flushing the cached data to disk,
// without waiting for completion.
// Default implementation does nothing.
Status EncryptedWritableFile::RangeSync(uint64_t offset, uint64_t nbytes) {
return file_->RangeSync(offset + prefixLength_, nbytes);
IOStatus EncryptedWritableFile::RangeSync(uint64_t offset, uint64_t nbytes,
const IOOptions& options,
IODebugContext* dbg) {
return file_->RangeSync(offset + prefixLength_, nbytes, options, dbg);
}
// PrepareWrite performs any necessary preparation for a write
@ -337,13 +356,32 @@ Status EncryptedWritableFile::RangeSync(uint64_t offset, uint64_t nbytes) {
// of space on devices where it can result in less file
// fragmentation and/or less waste from over-zealous filesystem
// pre-allocation.
void EncryptedWritableFile::PrepareWrite(size_t offset, size_t len) {
file_->PrepareWrite(offset + prefixLength_, len);
void EncryptedWritableFile::PrepareWrite(size_t offset, size_t len,
const IOOptions& options,
IODebugContext* dbg) {
file_->PrepareWrite(offset + prefixLength_, len, options, dbg);
}
// Pre-allocates space for a file.
Status EncryptedWritableFile::Allocate(uint64_t offset, uint64_t len) {
return file_->Allocate(offset + prefixLength_, len);
IOStatus EncryptedWritableFile::Allocate(uint64_t offset, uint64_t len,
const IOOptions& options,
IODebugContext* dbg) {
return file_->Allocate(offset + prefixLength_, len, options, dbg);
}
IOStatus EncryptedWritableFile::Flush(const IOOptions& options,
IODebugContext* dbg) {
return file_->Flush(options, dbg);
}
IOStatus EncryptedWritableFile::Sync(const IOOptions& options,
IODebugContext* dbg) {
return file_->Sync(options, dbg);
}
IOStatus EncryptedWritableFile::Close(const IOOptions& options,
IODebugContext* dbg) {
return file_->Close(options, dbg);
}
// A file abstraction for random reading and writing.
@ -362,9 +400,10 @@ size_t EncryptedRandomRWFile::GetRequiredBufferAlignment() const {
// Write bytes in `data` at offset `offset`, Returns Status::OK() on success.
// Pass aligned buffer when use_direct_io() returns true.
Status EncryptedRandomRWFile::Write(uint64_t offset, const Slice& data) {
IOStatus EncryptedRandomRWFile::Write(uint64_t offset, const Slice& data,
const IOOptions& options,
IODebugContext* dbg) {
AlignedBuffer buf;
Status status;
Slice dataToWrite(data);
offset += prefixLength_;
if (data.size() > 0) {
@ -373,71 +412,89 @@ Status EncryptedRandomRWFile::Write(uint64_t offset, const Slice& data) {
buf.AllocateNewBuffer(data.size());
memmove(buf.BufferStart(), data.data(), data.size());
buf.Size(data.size());
IOStatus io_s;
{
PERF_TIMER_GUARD(encrypt_data_nanos);
status = stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize());
io_s = status_to_io_status(
stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize()));
}
if (!status.ok()) {
return status;
if (!io_s.ok()) {
return io_s;
}
dataToWrite = Slice(buf.BufferStart(), buf.CurrentSize());
}
status = file_->Write(offset, dataToWrite);
return status;
return file_->Write(offset, dataToWrite, options, dbg);
}
// Read up to `n` bytes starting from offset `offset` and store them in
// result, provided `scratch` size should be at least `n`.
// Returns Status::OK() on success.
Status EncryptedRandomRWFile::Read(uint64_t offset, size_t n, Slice* result,
char* scratch) const {
IOStatus EncryptedRandomRWFile::Read(uint64_t offset, size_t n,
const IOOptions& options, Slice* result,
char* scratch, IODebugContext* dbg) const {
assert(scratch);
offset += prefixLength_;
auto status = file_->Read(offset, n, result, scratch);
auto status = file_->Read(offset, n, options, result, scratch, dbg);
if (!status.ok()) {
return status;
}
{
PERF_TIMER_GUARD(decrypt_data_nanos);
status = stream_->Decrypt(offset, (char*)result->data(), result->size());
status = status_to_io_status(
stream_->Decrypt(offset, (char*)result->data(), result->size()));
}
return status;
}
Status EncryptedRandomRWFile::Flush() { return file_->Flush(); }
IOStatus EncryptedRandomRWFile::Flush(const IOOptions& options,
IODebugContext* dbg) {
return file_->Flush(options, dbg);
}
Status EncryptedRandomRWFile::Sync() { return file_->Sync(); }
IOStatus EncryptedRandomRWFile::Sync(const IOOptions& options,
IODebugContext* dbg) {
return file_->Sync(options, dbg);
}
Status EncryptedRandomRWFile::Fsync() { return file_->Fsync(); }
IOStatus EncryptedRandomRWFile::Fsync(const IOOptions& options,
IODebugContext* dbg) {
return file_->Fsync(options, dbg);
}
Status EncryptedRandomRWFile::Close() { return file_->Close(); }
IOStatus EncryptedRandomRWFile::Close(const IOOptions& options,
IODebugContext* dbg) {
return file_->Close(options, dbg);
}
// EncryptedEnv implements an Env wrapper that adds encryption to files stored
// on disk.
class EncryptedEnvImpl : public EnvWrapper {
namespace {
// EncryptedFileSystemImpl implements an FileSystemWrapper that adds encryption
// to files stored on disk.
class EncryptedFileSystemImpl : public EncryptedFileSystem {
public:
const char* Name() const override { return "EncryptedFS"; }
// Returns the raw encryption provider that should be used to write the input
// encrypted file. If there is no such provider, NotFound is returned.
Status GetWritableProvider(const std::string& /*fname*/,
IOStatus GetWritableProvider(const std::string& /*fname*/,
EncryptionProvider** result) {
if (provider_) {
*result = provider_.get();
return Status::OK();
return IOStatus::OK();
} else {
*result = nullptr;
return Status::NotFound("No WriteProvider specified");
return IOStatus::NotFound("No WriteProvider specified");
}
}
// Returns the raw encryption provider that should be used to read the input
// encrypted file. If there is no such provider, NotFound is returned.
Status GetReadableProvider(const std::string& /*fname*/,
IOStatus GetReadableProvider(const std::string& /*fname*/,
EncryptionProvider** result) {
if (provider_) {
*result = provider_.get();
return Status::OK();
return IOStatus::OK();
} else {
*result = nullptr;
return Status::NotFound("No Provider specified");
return IOStatus::NotFound("No Provider specified");
}
}
@ -453,13 +510,13 @@ class EncryptedEnvImpl : public EnvWrapper {
// should be encrypted
// @return OK on success, non-OK on failure.
template <class TypeFile>
Status CreateWritableCipherStream(
IOStatus CreateWritableCipherStream(
const std::string& fname, const std::unique_ptr<TypeFile>& underlying,
const EnvOptions& options, size_t* prefix_length,
std::unique_ptr<BlockAccessCipherStream>* stream) {
const FileOptions& options, size_t* prefix_length,
std::unique_ptr<BlockAccessCipherStream>* stream, IODebugContext* dbg) {
EncryptionProvider* provider = nullptr;
*prefix_length = 0;
Status status = GetWritableProvider(fname, &provider);
IOStatus status = GetWritableProvider(fname, &provider);
if (!status.ok()) {
return status;
} else if (provider != nullptr) {
@ -471,34 +528,36 @@ class EncryptedEnvImpl : public EnvWrapper {
// Initialize prefix
buffer.Alignment(underlying->GetRequiredBufferAlignment());
buffer.AllocateNewBuffer(*prefix_length);
status = provider->CreateNewPrefix(fname, buffer.BufferStart(),
*prefix_length);
status = status_to_io_status(provider->CreateNewPrefix(
fname, buffer.BufferStart(), *prefix_length));
if (status.ok()) {
buffer.Size(*prefix_length);
prefix = Slice(buffer.BufferStart(), buffer.CurrentSize());
// Write prefix
status = underlying->Append(prefix);
status = underlying->Append(prefix, options.io_options, dbg);
}
if (!status.ok()) {
return status;
}
}
// Create cipher stream
status = provider->CreateCipherStream(fname, options, prefix, stream);
status = status_to_io_status(
provider->CreateCipherStream(fname, options, prefix, stream));
}
return status;
}
template <class TypeFile>
Status CreateWritableEncryptedFile(const std::string& fname,
IOStatus CreateWritableEncryptedFile(const std::string& fname,
std::unique_ptr<TypeFile>& underlying,
const EnvOptions& options,
std::unique_ptr<TypeFile>* result) {
const FileOptions& options,
std::unique_ptr<TypeFile>* result,
IODebugContext* dbg) {
// Create cipher stream
std::unique_ptr<BlockAccessCipherStream> stream;
size_t prefix_length;
Status status = CreateWritableCipherStream(fname, underlying, options,
&prefix_length, &stream);
IOStatus status = CreateWritableCipherStream(fname, underlying, options,
&prefix_length, &stream, dbg);
if (status.ok()) {
if (stream) {
result->reset(new EncryptedWritableFile(
@ -522,15 +581,15 @@ class EncryptedEnvImpl : public EnvWrapper {
// should be encrypted
// @return OK on success, non-OK on failure.
template <class TypeFile>
Status CreateRandomWriteCipherStream(
IOStatus CreateRandomWriteCipherStream(
const std::string& fname, const std::unique_ptr<TypeFile>& underlying,
const EnvOptions& options, size_t* prefix_length,
std::unique_ptr<BlockAccessCipherStream>* stream) {
const FileOptions& options, size_t* prefix_length,
std::unique_ptr<BlockAccessCipherStream>* stream, IODebugContext* dbg) {
EncryptionProvider* provider = nullptr;
*prefix_length = 0;
Status status = GetWritableProvider(fname, &provider);
if (!status.ok()) {
return status;
IOStatus io_s = GetWritableProvider(fname, &provider);
if (!io_s.ok()) {
return io_s;
} else if (provider != nullptr) {
// Initialize & write prefix (if needed)
AlignedBuffer buffer;
@ -540,22 +599,23 @@ class EncryptedEnvImpl : public EnvWrapper {
// Initialize prefix
buffer.Alignment(underlying->GetRequiredBufferAlignment());
buffer.AllocateNewBuffer(*prefix_length);
status = provider->CreateNewPrefix(fname, buffer.BufferStart(),
*prefix_length);
if (status.ok()) {
io_s = status_to_io_status(provider->CreateNewPrefix(
fname, buffer.BufferStart(), *prefix_length));
if (io_s.ok()) {
buffer.Size(*prefix_length);
prefix = Slice(buffer.BufferStart(), buffer.CurrentSize());
// Write prefix
status = underlying->Write(0, prefix);
io_s = underlying->Write(0, prefix, options.io_options, dbg);
}
if (!status.ok()) {
return status;
if (!io_s.ok()) {
return io_s;
}
}
// Create cipher stream
status = provider->CreateCipherStream(fname, options, prefix, stream);
io_s = status_to_io_status(
provider->CreateCipherStream(fname, options, prefix, stream));
}
return status;
return io_s;
}
// Creates a CipherStream for the underlying file/name using the options
@ -570,10 +630,10 @@ class EncryptedEnvImpl : public EnvWrapper {
// is encrypted
// @return OK on success, non-OK on failure.
template <class TypeFile>
Status CreateSequentialCipherStream(
IOStatus CreateSequentialCipherStream(
const std::string& fname, const std::unique_ptr<TypeFile>& underlying,
const EnvOptions& options, size_t* prefix_length,
std::unique_ptr<BlockAccessCipherStream>* stream) {
const FileOptions& options, size_t* prefix_length,
std::unique_ptr<BlockAccessCipherStream>* stream, IODebugContext* dbg) {
// Read prefix (if needed)
AlignedBuffer buffer;
Slice prefix;
@ -582,14 +642,15 @@ class EncryptedEnvImpl : public EnvWrapper {
// Read prefix
buffer.Alignment(underlying->GetRequiredBufferAlignment());
buffer.AllocateNewBuffer(*prefix_length);
Status status =
underlying->Read(*prefix_length, &prefix, buffer.BufferStart());
IOStatus status = underlying->Read(*prefix_length, options.io_options,
&prefix, buffer.BufferStart(), dbg);
if (!status.ok()) {
return status;
}
buffer.Size(*prefix_length);
}
return provider_->CreateCipherStream(fname, options, prefix, stream);
return status_to_io_status(
provider_->CreateCipherStream(fname, options, prefix, stream));
}
// Creates a CipherStream for the underlying file/name using the options
@ -604,10 +665,10 @@ class EncryptedEnvImpl : public EnvWrapper {
// is encrypted
// @return OK on success, non-OK on failure.
template <class TypeFile>
Status CreateRandomReadCipherStream(
IOStatus CreateRandomReadCipherStream(
const std::string& fname, const std::unique_ptr<TypeFile>& underlying,
const EnvOptions& options, size_t* prefix_length,
std::unique_ptr<BlockAccessCipherStream>* stream) {
const FileOptions& options, size_t* prefix_length,
std::unique_ptr<BlockAccessCipherStream>* stream, IODebugContext* dbg) {
// Read prefix (if needed)
AlignedBuffer buffer;
Slice prefix;
@ -616,39 +677,48 @@ class EncryptedEnvImpl : public EnvWrapper {
// Read prefix
buffer.Alignment(underlying->GetRequiredBufferAlignment());
buffer.AllocateNewBuffer(*prefix_length);
Status status =
underlying->Read(0, *prefix_length, &prefix, buffer.BufferStart());
IOStatus status = underlying->Read(0, *prefix_length, options.io_options,
&prefix, buffer.BufferStart(), dbg);
if (!status.ok()) {
return status;
}
buffer.Size(*prefix_length);
}
return provider_->CreateCipherStream(fname, options, prefix, stream);
return status_to_io_status(
provider_->CreateCipherStream(fname, options, prefix, stream));
}
public:
EncryptedEnvImpl(Env* base_env,
EncryptedFileSystemImpl(const std::shared_ptr<FileSystem>& base,
const std::shared_ptr<EncryptionProvider>& provider)
: EnvWrapper(base_env) {
: EncryptedFileSystem(base) {
provider_ = provider;
}
Status AddCipher(const std::string& descriptor, const char* cipher,
size_t len, bool for_write) override {
return provider_->AddCipher(descriptor, cipher, len, for_write);
}
// NewSequentialFile opens a file for sequential reading.
virtual Status NewSequentialFile(const std::string& fname,
std::unique_ptr<SequentialFile>* result,
const EnvOptions& options) override {
IOStatus NewSequentialFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSSequentialFile>* result,
IODebugContext* dbg) override {
result->reset();
if (options.use_mmap_reads) {
return Status::InvalidArgument();
return IOStatus::InvalidArgument();
}
// Open file using underlying Env implementation
std::unique_ptr<SequentialFile> underlying;
auto status = EnvWrapper::NewSequentialFile(fname, &underlying, options);
std::unique_ptr<FSSequentialFile> underlying;
auto status =
FileSystemWrapper::NewSequentialFile(fname, options, &underlying, dbg);
if (!status.ok()) {
return status;
}
uint64_t file_size;
status = EnvWrapper::GetFileSize(fname, &file_size);
status = FileSystemWrapper::GetFileSize(fname, options.io_options,
&file_size, dbg);
if (!status.ok()) {
return status;
}
@ -660,7 +730,7 @@ class EncryptedEnvImpl : public EnvWrapper {
std::unique_ptr<BlockAccessCipherStream> stream;
size_t prefix_length;
status = CreateSequentialCipherStream(fname, underlying, options,
&prefix_length, &stream);
&prefix_length, &stream, dbg);
if (status.ok()) {
result->reset(new EncryptedSequentialFile(
std::move(underlying), std::move(stream), prefix_length));
@ -669,23 +739,25 @@ class EncryptedEnvImpl : public EnvWrapper {
}
// NewRandomAccessFile opens a file for random read access.
virtual Status NewRandomAccessFile(const std::string& fname,
std::unique_ptr<RandomAccessFile>* result,
const EnvOptions& options) override {
IOStatus NewRandomAccessFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSRandomAccessFile>* result,
IODebugContext* dbg) override {
result->reset();
if (options.use_mmap_reads) {
return Status::InvalidArgument();
return IOStatus::InvalidArgument();
}
// Open file using underlying Env implementation
std::unique_ptr<RandomAccessFile> underlying;
auto status = EnvWrapper::NewRandomAccessFile(fname, &underlying, options);
std::unique_ptr<FSRandomAccessFile> underlying;
auto status = FileSystemWrapper::NewRandomAccessFile(fname, options,
&underlying, dbg);
if (!status.ok()) {
return status;
}
std::unique_ptr<BlockAccessCipherStream> stream;
size_t prefix_length;
status = CreateRandomReadCipherStream(fname, underlying, options,
&prefix_length, &stream);
&prefix_length, &stream, dbg);
if (status.ok()) {
if (stream) {
result->reset(new EncryptedRandomAccessFile(
@ -698,20 +770,21 @@ class EncryptedEnvImpl : public EnvWrapper {
}
// NewWritableFile opens a file for sequential writing.
virtual Status NewWritableFile(const std::string& fname,
std::unique_ptr<WritableFile>* result,
const EnvOptions& options) override {
IOStatus NewWritableFile(const std::string& fname, const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override {
result->reset();
if (options.use_mmap_writes) {
return Status::InvalidArgument();
return IOStatus::InvalidArgument();
}
// Open file using underlying Env implementation
std::unique_ptr<WritableFile> underlying;
Status status = EnvWrapper::NewWritableFile(fname, &underlying, options);
std::unique_ptr<FSWritableFile> underlying;
IOStatus status =
FileSystemWrapper::NewWritableFile(fname, options, &underlying, dbg);
if (!status.ok()) {
return status;
}
return CreateWritableEncryptedFile(fname, underlying, options, result);
return CreateWritableEncryptedFile(fname, underlying, options, result, dbg);
}
// Create an object that writes to a new file with the specified
@ -721,39 +794,42 @@ class EncryptedEnvImpl : public EnvWrapper {
// returns non-OK.
//
// The returned file will only be accessed by one thread at a time.
virtual Status ReopenWritableFile(const std::string& fname,
std::unique_ptr<WritableFile>* result,
const EnvOptions& options) override {
IOStatus ReopenWritableFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override {
result->reset();
if (options.use_mmap_writes) {
return Status::InvalidArgument();
return IOStatus::InvalidArgument();
}
// Open file using underlying Env implementation
std::unique_ptr<WritableFile> underlying;
Status status = EnvWrapper::ReopenWritableFile(fname, &underlying, options);
std::unique_ptr<FSWritableFile> underlying;
IOStatus status =
FileSystemWrapper::ReopenWritableFile(fname, options, &underlying, dbg);
if (!status.ok()) {
return status;
}
return CreateWritableEncryptedFile(fname, underlying, options, result);
return CreateWritableEncryptedFile(fname, underlying, options, result, dbg);
}
// Reuse an existing file by renaming it and opening it as writable.
virtual Status ReuseWritableFile(const std::string& fname,
IOStatus ReuseWritableFile(const std::string& fname,
const std::string& old_fname,
std::unique_ptr<WritableFile>* result,
const EnvOptions& options) override {
const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override {
result->reset();
if (options.use_mmap_writes) {
return Status::InvalidArgument();
return IOStatus::InvalidArgument();
}
// Open file using underlying Env implementation
std::unique_ptr<WritableFile> underlying;
Status status =
EnvWrapper::ReuseWritableFile(fname, old_fname, &underlying, options);
std::unique_ptr<FSWritableFile> underlying;
auto status = FileSystemWrapper::ReuseWritableFile(
fname, old_fname, options, &underlying, dbg);
if (!status.ok()) {
return status;
}
return CreateWritableEncryptedFile(fname, underlying, options, result);
return CreateWritableEncryptedFile(fname, underlying, options, result, dbg);
}
// Open `fname` for random read and write, if file doesn't exist the file
@ -761,19 +837,20 @@ class EncryptedEnvImpl : public EnvWrapper {
// *result and returns OK. On failure returns non-OK.
//
// The returned file will only be accessed by one thread at a time.
virtual Status NewRandomRWFile(const std::string& fname,
std::unique_ptr<RandomRWFile>* result,
const EnvOptions& options) override {
IOStatus NewRandomRWFile(const std::string& fname, const FileOptions& options,
std::unique_ptr<FSRandomRWFile>* result,
IODebugContext* dbg) override {
result->reset();
if (options.use_mmap_reads || options.use_mmap_writes) {
return Status::InvalidArgument();
return IOStatus::InvalidArgument();
}
// Check file exists
bool isNewFile = !FileExists(fname).ok();
bool isNewFile = !FileExists(fname, options.io_options, dbg).ok();
// Open file using underlying Env implementation
std::unique_ptr<RandomRWFile> underlying;
Status status = EnvWrapper::NewRandomRWFile(fname, &underlying, options);
std::unique_ptr<FSRandomRWFile> underlying;
auto status =
FileSystemWrapper::NewRandomRWFile(fname, options, &underlying, dbg);
if (!status.ok()) {
return status;
}
@ -783,10 +860,10 @@ class EncryptedEnvImpl : public EnvWrapper {
if (!isNewFile) {
// File already exists, read prefix
status = CreateRandomReadCipherStream(fname, underlying, options,
&prefix_length, &stream);
&prefix_length, &stream, dbg);
} else {
status = CreateRandomWriteCipherStream(fname, underlying, options,
&prefix_length, &stream);
&prefix_length, &stream, dbg);
}
if (status.ok()) {
if (stream) {
@ -813,9 +890,12 @@ class EncryptedEnvImpl : public EnvWrapper {
// have
// permission to access "dir", or if "dir" is invalid.
// IOError if an IO Error was encountered
virtual Status GetChildrenFileAttributes(
const std::string& dir, std::vector<FileAttributes>* result) override {
auto status = EnvWrapper::GetChildrenFileAttributes(dir, result);
IOStatus GetChildrenFileAttributes(const std::string& dir,
const IOOptions& options,
std::vector<FileAttributes>* result,
IODebugContext* dbg) override {
auto status =
FileSystemWrapper::GetChildrenFileAttributes(dir, options, result, dbg);
if (!status.ok()) {
return status;
}
@ -833,13 +913,14 @@ class EncryptedEnvImpl : public EnvWrapper {
it->size_bytes -= provider->GetPrefixLength();
}
}
return Status::OK();
return IOStatus::OK();
}
// Store the size of fname in *file_size.
virtual Status GetFileSize(const std::string& fname,
uint64_t* file_size) override {
auto status = EnvWrapper::GetFileSize(fname, file_size);
IOStatus GetFileSize(const std::string& fname, const IOOptions& options,
uint64_t* file_size, IODebugContext* dbg) override {
auto status =
FileSystemWrapper::GetFileSize(fname, options, file_size, dbg);
if (!status.ok() || !(*file_size)) {
return status;
}
@ -856,12 +937,19 @@ class EncryptedEnvImpl : public EnvWrapper {
private:
std::shared_ptr<EncryptionProvider> provider_;
};
} // namespace
std::shared_ptr<FileSystem> NewEncryptedFS(
const std::shared_ptr<FileSystem>& base,
const std::shared_ptr<EncryptionProvider>& provider) {
return std::make_shared<EncryptedFileSystemImpl>(base, provider);
}
// Returns an Env that encrypts data when stored on disk and decrypts data when
// read from disk.
Env* NewEncryptedEnv(Env* base_env,
const std::shared_ptr<EncryptionProvider>& provider) {
return new EncryptedEnvImpl(base_env, provider);
return new CompositeEnvWrapper(
base_env, NewEncryptedFS(base_env->GetFileSystem(), provider));
}
// Encrypt one or more (partial) blocks of data at the file offset.

@ -10,6 +10,7 @@
#include <string>
#include "rocksdb/env.h"
#include "rocksdb/file_system.h"
#include "rocksdb/rocksdb_namespace.h"
namespace ROCKSDB_NAMESPACE {
@ -171,9 +172,9 @@ class EncryptionProvider {
virtual Status TEST_Initialize() { return Status::OK(); }
};
class EncryptedSequentialFile : public SequentialFile {
class EncryptedSequentialFile : public FSSequentialFile {
protected:
std::unique_ptr<SequentialFile> file_;
std::unique_ptr<FSSequentialFile> file_;
std::unique_ptr<BlockAccessCipherStream> stream_;
uint64_t offset_;
size_t prefixLength_;
@ -181,7 +182,7 @@ class EncryptedSequentialFile : public SequentialFile {
public:
// Default ctor. Given underlying sequential file is supposed to be at
// offset == prefixLength.
EncryptedSequentialFile(std::unique_ptr<SequentialFile>&& f,
EncryptedSequentialFile(std::unique_ptr<FSSequentialFile>&& f,
std::unique_ptr<BlockAccessCipherStream>&& s,
size_t prefixLength)
: file_(std::move(f)),
@ -197,7 +198,8 @@ class EncryptedSequentialFile : public SequentialFile {
// If an error was encountered, returns a non-OK status.
//
// REQUIRES: External synchronization
virtual Status Read(size_t n, Slice* result, char* scratch) override;
IOStatus Read(size_t n, const IOOptions& options, Slice* result,
char* scratch, IODebugContext* dbg) override;
// Skip "n" bytes from the file. This is guaranteed to be no
// slower that reading the same data, but may be faster.
@ -206,36 +208,37 @@ class EncryptedSequentialFile : public SequentialFile {
// file, and Skip will return OK.
//
// REQUIRES: External synchronization
virtual Status Skip(uint64_t n) override;
IOStatus Skip(uint64_t n) override;
// Indicates the upper layers if the current SequentialFile implementation
// uses direct IO.
virtual bool use_direct_io() const override;
bool use_direct_io() const override;
// Use the returned alignment value to allocate
// aligned buffer for Direct I/O
virtual size_t GetRequiredBufferAlignment() const override;
size_t GetRequiredBufferAlignment() const override;
// Remove any kind of caching of data from the offset to offset+length
// of this file. If the length is 0, then it refers to the end of file.
// If the system is not caching the file contents, then this is a noop.
virtual Status InvalidateCache(size_t offset, size_t length) override;
IOStatus InvalidateCache(size_t offset, size_t length) override;
// Positioned Read for direct I/O
// If Direct I/O enabled, offset, n, and scratch should be properly aligned
virtual Status PositionedRead(uint64_t offset, size_t n, Slice* result,
char* scratch) override;
IOStatus PositionedRead(uint64_t offset, size_t n, const IOOptions& options,
Slice* result, char* scratch,
IODebugContext* dbg) override;
};
// A file abstraction for randomly reading the contents of a file.
class EncryptedRandomAccessFile : public RandomAccessFile {
class EncryptedRandomAccessFile : public FSRandomAccessFile {
protected:
std::unique_ptr<RandomAccessFile> file_;
std::unique_ptr<FSRandomAccessFile> file_;
std::unique_ptr<BlockAccessCipherStream> stream_;
size_t prefixLength_;
public:
EncryptedRandomAccessFile(std::unique_ptr<RandomAccessFile>&& f,
EncryptedRandomAccessFile(std::unique_ptr<FSRandomAccessFile>&& f,
std::unique_ptr<BlockAccessCipherStream>&& s,
size_t prefixLength)
: file_(std::move(f)),
@ -252,11 +255,13 @@ class EncryptedRandomAccessFile : public RandomAccessFile {
//
// Safe for concurrent use by multiple threads.
// If Direct I/O enabled, offset, n, and scratch should be aligned properly.
virtual Status Read(uint64_t offset, size_t n, Slice* result,
char* scratch) const override;
IOStatus Read(uint64_t offset, size_t n, const IOOptions& options,
Slice* result, char* scratch,
IODebugContext* dbg) const override;
// Readahead the file starting from offset by n bytes for caching.
virtual Status Prefetch(uint64_t offset, size_t n) override;
IOStatus Prefetch(uint64_t offset, size_t n, const IOOptions& options,
IODebugContext* dbg) override;
// Tries to get an unique ID for this file that will be the same each time
// the file is opened (and will stay the same while the file is open).
@ -273,71 +278,76 @@ class EncryptedRandomAccessFile : public RandomAccessFile {
// a single varint.
//
// Note: these IDs are only valid for the duration of the process.
virtual size_t GetUniqueId(char* id, size_t max_size) const override;
size_t GetUniqueId(char* id, size_t max_size) const override;
virtual void Hint(AccessPattern pattern) override;
void Hint(AccessPattern pattern) override;
// Indicates the upper layers if the current RandomAccessFile implementation
// uses direct IO.
virtual bool use_direct_io() const override;
bool use_direct_io() const override;
// Use the returned alignment value to allocate
// aligned buffer for Direct I/O
virtual size_t GetRequiredBufferAlignment() const override;
size_t GetRequiredBufferAlignment() const override;
// Remove any kind of caching of data from the offset to offset+length
// of this file. If the length is 0, then it refers to the end of file.
// If the system is not caching the file contents, then this is a noop.
virtual Status InvalidateCache(size_t offset, size_t length) override;
IOStatus InvalidateCache(size_t offset, size_t length) override;
};
// A file abstraction for sequential writing. The implementation
// must provide buffering since callers may append small fragments
// at a time to the file.
class EncryptedWritableFile : public WritableFileWrapper {
class EncryptedWritableFile : public FSWritableFile {
protected:
std::unique_ptr<WritableFile> file_;
std::unique_ptr<FSWritableFile> file_;
std::unique_ptr<BlockAccessCipherStream> stream_;
size_t prefixLength_;
public:
// Default ctor. Prefix is assumed to be written already.
EncryptedWritableFile(std::unique_ptr<WritableFile>&& f,
EncryptedWritableFile(std::unique_ptr<FSWritableFile>&& f,
std::unique_ptr<BlockAccessCipherStream>&& s,
size_t prefixLength)
: WritableFileWrapper(f.get()),
file_(std::move(f)),
: file_(std::move(f)),
stream_(std::move(s)),
prefixLength_(prefixLength) {}
Status Append(const Slice& data) override;
using FSWritableFile::Append;
IOStatus Append(const Slice& data, const IOOptions& options,
IODebugContext* dbg) override;
Status PositionedAppend(const Slice& data, uint64_t offset) override;
using FSWritableFile::PositionedAppend;
IOStatus PositionedAppend(const Slice& data, uint64_t offset,
const IOOptions& options,
IODebugContext* dbg) override;
// Indicates the upper layers if the current WritableFile implementation
// uses direct IO.
virtual bool use_direct_io() const override;
bool use_direct_io() const override;
// Use the returned alignment value to allocate
// aligned buffer for Direct I/O
virtual size_t GetRequiredBufferAlignment() const override;
size_t GetRequiredBufferAlignment() const override;
/*
* Get the size of valid data in the file.
*/
virtual uint64_t GetFileSize() override;
uint64_t GetFileSize(const IOOptions& options, IODebugContext* dbg) override;
// Truncate is necessary to trim the file to the correct size
// before closing. It is not always possible to keep track of the file
// size due to whole pages writes. The behavior is undefined if called
// with other writes to follow.
virtual Status Truncate(uint64_t size) override;
IOStatus Truncate(uint64_t size, const IOOptions& options,
IODebugContext* dbg) override;
// Remove any kind of caching of data from the offset to offset+length
// of this file. If the length is 0, then it refers to the end of file.
// If the system is not caching the file contents, then this is a noop.
// This call has no effect on dirty pages in the cache.
virtual Status InvalidateCache(size_t offset, size_t length) override;
IOStatus InvalidateCache(size_t offset, size_t length) override;
// Sync a file range with disk.
// offset is the starting byte of the file range to be synchronized.
@ -345,28 +355,37 @@ class EncryptedWritableFile : public WritableFileWrapper {
// This asks the OS to initiate flushing the cached data to disk,
// without waiting for completion.
// Default implementation does nothing.
virtual Status RangeSync(uint64_t offset, uint64_t nbytes) override;
IOStatus RangeSync(uint64_t offset, uint64_t nbytes, const IOOptions& options,
IODebugContext* dbg) override;
// PrepareWrite performs any necessary preparation for a write
// before the write actually occurs. This allows for pre-allocation
// of space on devices where it can result in less file
// fragmentation and/or less waste from over-zealous filesystem
// pre-allocation.
virtual void PrepareWrite(size_t offset, size_t len) override;
void PrepareWrite(size_t offset, size_t len, const IOOptions& options,
IODebugContext* dbg) override;
// Pre-allocates space for a file.
virtual Status Allocate(uint64_t offset, uint64_t len) override;
IOStatus Allocate(uint64_t offset, uint64_t len, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus Flush(const IOOptions& options, IODebugContext* dbg) override;
IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override;
IOStatus Close(const IOOptions& options, IODebugContext* dbg) override;
};
// A file abstraction for random reading and writing.
class EncryptedRandomRWFile : public RandomRWFile {
class EncryptedRandomRWFile : public FSRandomRWFile {
protected:
std::unique_ptr<RandomRWFile> file_;
std::unique_ptr<FSRandomRWFile> file_;
std::unique_ptr<BlockAccessCipherStream> stream_;
size_t prefixLength_;
public:
EncryptedRandomRWFile(std::unique_ptr<RandomRWFile>&& f,
EncryptedRandomRWFile(std::unique_ptr<FSRandomRWFile>&& f,
std::unique_ptr<BlockAccessCipherStream>&& s,
size_t prefixLength)
: file_(std::move(f)),
@ -375,31 +394,49 @@ class EncryptedRandomRWFile : public RandomRWFile {
// Indicates if the class makes use of direct I/O
// If false you must pass aligned buffer to Write()
virtual bool use_direct_io() const override;
bool use_direct_io() const override;
// Use the returned alignment value to allocate
// aligned buffer for Direct I/O
virtual size_t GetRequiredBufferAlignment() const override;
size_t GetRequiredBufferAlignment() const override;
// Write bytes in `data` at offset `offset`, Returns Status::OK() on success.
// Pass aligned buffer when use_direct_io() returns true.
virtual Status Write(uint64_t offset, const Slice& data) override;
IOStatus Write(uint64_t offset, const Slice& data, const IOOptions& options,
IODebugContext* dbg) override;
// Read up to `n` bytes starting from offset `offset` and store them in
// result, provided `scratch` size should be at least `n`.
// Returns Status::OK() on success.
virtual Status Read(uint64_t offset, size_t n, Slice* result,
char* scratch) const override;
IOStatus Read(uint64_t offset, size_t n, const IOOptions& options,
Slice* result, char* scratch,
IODebugContext* dbg) const override;
virtual Status Flush() override;
IOStatus Flush(const IOOptions& options, IODebugContext* dbg) override;
virtual Status Sync() override;
IOStatus Sync(const IOOptions& options, IODebugContext* dbg) override;
virtual Status Fsync() override;
IOStatus Fsync(const IOOptions& options, IODebugContext* dbg) override;
virtual Status Close() override;
IOStatus Close(const IOOptions& options, IODebugContext* dbg) override;
};
class EncryptedFileSystem : public FileSystemWrapper {
public:
explicit EncryptedFileSystem(const std::shared_ptr<FileSystem>& base)
: FileSystemWrapper(base) {}
// Method to add a new cipher key for use by the EncryptionProvider.
// @param description Descriptor for this key.
// @param cipher The cryptographic key to use
// @param len The length of the cipher key
// @param for_write If true, this cipher should be used for writing files.
// If false, this cipher should only be used for reading
// files
// @return OK if the cipher was successfully added to the provider, non-OK
// otherwise
virtual Status AddCipher(const std::string& descriptor, const char* cipher,
size_t len, bool for_write) = 0;
};
} // namespace ROCKSDB_NAMESPACE
#endif // !defined(ROCKSDB_LITE)

@ -3,138 +3,176 @@
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#include "env/composite_env_wrapper.h"
#include "monitoring/perf_context_imp.h"
#include "rocksdb/env.h"
#include "rocksdb/file_system.h"
#include "rocksdb/status.h"
namespace ROCKSDB_NAMESPACE {
#ifndef ROCKSDB_LITE
// An environment that measures function call times for filesystem
// operations, reporting results to variables in PerfContext.
class TimedEnv : public EnvWrapper {
namespace {
class TimedFileSystem : public FileSystemWrapper {
public:
explicit TimedEnv(Env* base_env) : EnvWrapper(base_env) {}
Status NewSequentialFile(const std::string& fname,
std::unique_ptr<SequentialFile>* result,
const EnvOptions& options) override {
explicit TimedFileSystem(const std::shared_ptr<FileSystem>& base)
: FileSystemWrapper(base) {}
const char* Name() const override { return "TimedFS"; }
IOStatus NewSequentialFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSSequentialFile>* result,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_new_sequential_file_nanos);
return EnvWrapper::NewSequentialFile(fname, result, options);
return FileSystemWrapper::NewSequentialFile(fname, options, result, dbg);
}
Status NewRandomAccessFile(const std::string& fname,
std::unique_ptr<RandomAccessFile>* result,
const EnvOptions& options) override {
IOStatus NewRandomAccessFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSRandomAccessFile>* result,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_new_random_access_file_nanos);
return EnvWrapper::NewRandomAccessFile(fname, result, options);
return FileSystemWrapper::NewRandomAccessFile(fname, options, result, dbg);
}
Status NewWritableFile(const std::string& fname,
std::unique_ptr<WritableFile>* result,
const EnvOptions& options) override {
IOStatus NewWritableFile(const std::string& fname, const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_new_writable_file_nanos);
return EnvWrapper::NewWritableFile(fname, result, options);
return FileSystemWrapper::NewWritableFile(fname, options, result, dbg);
}
Status ReuseWritableFile(const std::string& fname,
IOStatus ReuseWritableFile(const std::string& fname,
const std::string& old_fname,
std::unique_ptr<WritableFile>* result,
const EnvOptions& options) override {
const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_reuse_writable_file_nanos);
return EnvWrapper::ReuseWritableFile(fname, old_fname, result, options);
return FileSystemWrapper::ReuseWritableFile(fname, old_fname, options,
result, dbg);
}
Status NewRandomRWFile(const std::string& fname,
std::unique_ptr<RandomRWFile>* result,
const EnvOptions& options) override {
IOStatus NewRandomRWFile(const std::string& fname, const FileOptions& options,
std::unique_ptr<FSRandomRWFile>* result,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_new_random_rw_file_nanos);
return EnvWrapper::NewRandomRWFile(fname, result, options);
return FileSystemWrapper::NewRandomRWFile(fname, options, result, dbg);
}
Status NewDirectory(const std::string& name,
std::unique_ptr<Directory>* result) override {
IOStatus NewDirectory(const std::string& name, const IOOptions& options,
std::unique_ptr<FSDirectory>* result,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_new_directory_nanos);
return EnvWrapper::NewDirectory(name, result);
return FileSystemWrapper::NewDirectory(name, options, result, dbg);
}
Status FileExists(const std::string& fname) override {
IOStatus FileExists(const std::string& fname, const IOOptions& options,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_file_exists_nanos);
return EnvWrapper::FileExists(fname);
return FileSystemWrapper::FileExists(fname, options, dbg);
}
Status GetChildren(const std::string& dir,
std::vector<std::string>* result) override {
IOStatus GetChildren(const std::string& dir, const IOOptions& options,
std::vector<std::string>* result,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_get_children_nanos);
return EnvWrapper::GetChildren(dir, result);
return FileSystemWrapper::GetChildren(dir, options, result, dbg);
}
Status GetChildrenFileAttributes(
const std::string& dir, std::vector<FileAttributes>* result) override {
IOStatus GetChildrenFileAttributes(const std::string& dir,
const IOOptions& options,
std::vector<FileAttributes>* result,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_get_children_file_attributes_nanos);
return EnvWrapper::GetChildrenFileAttributes(dir, result);
return FileSystemWrapper::GetChildrenFileAttributes(dir, options, result,
dbg);
}
Status DeleteFile(const std::string& fname) override {
IOStatus DeleteFile(const std::string& fname, const IOOptions& options,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_delete_file_nanos);
return EnvWrapper::DeleteFile(fname);
return FileSystemWrapper::DeleteFile(fname, options, dbg);
}
Status CreateDir(const std::string& dirname) override {
IOStatus CreateDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_create_dir_nanos);
return EnvWrapper::CreateDir(dirname);
return FileSystemWrapper::CreateDir(dirname, options, dbg);
}
Status CreateDirIfMissing(const std::string& dirname) override {
IOStatus CreateDirIfMissing(const std::string& dirname,
const IOOptions& options,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_create_dir_if_missing_nanos);
return EnvWrapper::CreateDirIfMissing(dirname);
return FileSystemWrapper::CreateDirIfMissing(dirname, options, dbg);
}
Status DeleteDir(const std::string& dirname) override {
IOStatus DeleteDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_delete_dir_nanos);
return EnvWrapper::DeleteDir(dirname);
return FileSystemWrapper::DeleteDir(dirname, options, dbg);
}
Status GetFileSize(const std::string& fname, uint64_t* file_size) override {
IOStatus GetFileSize(const std::string& fname, const IOOptions& options,
uint64_t* file_size, IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_get_file_size_nanos);
return EnvWrapper::GetFileSize(fname, file_size);
return FileSystemWrapper::GetFileSize(fname, options, file_size, dbg);
}
Status GetFileModificationTime(const std::string& fname,
uint64_t* file_mtime) override {
IOStatus GetFileModificationTime(const std::string& fname,
const IOOptions& options,
uint64_t* file_mtime,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_get_file_modification_time_nanos);
return EnvWrapper::GetFileModificationTime(fname, file_mtime);
return FileSystemWrapper::GetFileModificationTime(fname, options,
file_mtime, dbg);
}
Status RenameFile(const std::string& src, const std::string& dst) override {
IOStatus RenameFile(const std::string& src, const std::string& dst,
const IOOptions& options, IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_rename_file_nanos);
return EnvWrapper::RenameFile(src, dst);
return FileSystemWrapper::RenameFile(src, dst, options, dbg);
}
Status LinkFile(const std::string& src, const std::string& dst) override {
IOStatus LinkFile(const std::string& src, const std::string& dst,
const IOOptions& options, IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_link_file_nanos);
return EnvWrapper::LinkFile(src, dst);
return FileSystemWrapper::LinkFile(src, dst, options, dbg);
}
Status LockFile(const std::string& fname, FileLock** lock) override {
IOStatus LockFile(const std::string& fname, const IOOptions& options,
FileLock** lock, IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_lock_file_nanos);
return EnvWrapper::LockFile(fname, lock);
return FileSystemWrapper::LockFile(fname, options, lock, dbg);
}
Status UnlockFile(FileLock* lock) override {
IOStatus UnlockFile(FileLock* lock, const IOOptions& options,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_unlock_file_nanos);
return EnvWrapper::UnlockFile(lock);
return FileSystemWrapper::UnlockFile(lock, options, dbg);
}
Status NewLogger(const std::string& fname,
std::shared_ptr<Logger>* result) override {
IOStatus NewLogger(const std::string& fname, const IOOptions& options,
std::shared_ptr<Logger>* result,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_new_logger_nanos);
return EnvWrapper::NewLogger(fname, result);
return FileSystemWrapper::NewLogger(fname, options, result, dbg);
}
};
} // namespace
Env* NewTimedEnv(Env* base_env) { return new TimedEnv(base_env); }
std::shared_ptr<FileSystem> NewTimedFileSystem(
const std::shared_ptr<FileSystem>& base) {
return std::make_shared<TimedFileSystem>(base);
}
// An environment that measures function call times for filesystem
// operations, reporting results to variables in PerfContext.
Env* NewTimedEnv(Env* base_env) {
std::shared_ptr<FileSystem> timed_fs =
NewTimedFileSystem(base_env->GetFileSystem());
return new CompositeEnvWrapper(base_env, timed_fs);
}
#else // ROCKSDB_LITE

Loading…
Cancel
Save