| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937 |
- // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
- // This source code is licensed under both the GPLv2 (found in the
- // COPYING file in the root directory) and Apache 2.0 License
- // (found in the LICENSE.Apache file in the root directory).
- #ifndef ROCKSDB_LITE
- #include <algorithm>
- #include <cassert>
- #include <cctype>
- #include <iostream>
- #include "rocksdb/env_encryption.h"
- #include "util/aligned_buffer.h"
- #include "util/coding.h"
- #include "util/random.h"
- #endif
- namespace ROCKSDB_NAMESPACE {
- #ifndef ROCKSDB_LITE
- class EncryptedSequentialFile : public SequentialFile {
- private:
- std::unique_ptr<SequentialFile> file_;
- std::unique_ptr<BlockAccessCipherStream> stream_;
- uint64_t offset_;
- size_t prefixLength_;
- public:
- // Default ctor. Given underlying sequential file is supposed to be at
- // offset == prefixLength.
- EncryptedSequentialFile(SequentialFile* f, BlockAccessCipherStream* s, size_t prefixLength)
- : file_(f), stream_(s), offset_(prefixLength), prefixLength_(prefixLength) {
- }
- // Read up to "n" bytes from the file. "scratch[0..n-1]" may be
- // written by this routine. Sets "*result" to the data that was
- // read (including if fewer than "n" bytes were successfully read).
- // May set "*result" to point at data in "scratch[0..n-1]", so
- // "scratch[0..n-1]" must be live when "*result" is used.
- // If an error was encountered, returns a non-OK status.
- //
- // REQUIRES: External synchronization
- Status Read(size_t n, Slice* result, char* scratch) override {
- assert(scratch);
- Status status = file_->Read(n, result, scratch);
- if (!status.ok()) {
- return status;
- }
- status = stream_->Decrypt(offset_, (char*)result->data(), result->size());
- offset_ += result->size(); // We've already ready data from disk, so update offset_ even if decryption fails.
- return status;
- }
- // Skip "n" bytes from the file. This is guaranteed to be no
- // slower that reading the same data, but may be faster.
- //
- // If end of file is reached, skipping will stop at the end of the
- // file, and Skip will return OK.
- //
- // REQUIRES: External synchronization
- Status Skip(uint64_t n) override {
- auto status = file_->Skip(n);
- if (!status.ok()) {
- return status;
- }
- offset_ += n;
- return status;
- }
- // Indicates the upper layers if the current SequentialFile implementation
- // uses direct IO.
- bool use_direct_io() const override { return file_->use_direct_io(); }
- // Use the returned alignment value to allocate
- // aligned buffer for Direct I/O
- size_t GetRequiredBufferAlignment() const override {
- return file_->GetRequiredBufferAlignment();
- }
- // 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 InvalidateCache(size_t offset, size_t length) override {
- 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 PositionedRead(uint64_t offset, size_t n, Slice* result,
- char* scratch) override {
- assert(scratch);
- offset += prefixLength_; // Skip prefix
- auto status = file_->PositionedRead(offset, n, result, scratch);
- if (!status.ok()) {
- return status;
- }
- offset_ = offset + result->size();
- status = stream_->Decrypt(offset, (char*)result->data(), result->size());
- return status;
- }
- };
- // A file abstraction for randomly reading the contents of a file.
- class EncryptedRandomAccessFile : public RandomAccessFile {
- private:
- std::unique_ptr<RandomAccessFile> file_;
- std::unique_ptr<BlockAccessCipherStream> stream_;
- size_t prefixLength_;
- public:
- EncryptedRandomAccessFile(RandomAccessFile* f, BlockAccessCipherStream* s, size_t prefixLength)
- : file_(f), stream_(s), prefixLength_(prefixLength) { }
- // Read up to "n" bytes from the file starting at "offset".
- // "scratch[0..n-1]" may be written by this routine. Sets "*result"
- // to the data that was read (including if fewer than "n" bytes were
- // successfully read). May set "*result" to point at data in
- // "scratch[0..n-1]", so "scratch[0..n-1]" must be live when
- // "*result" is used. If an error was encountered, returns a non-OK
- // status.
- //
- // Safe for concurrent use by multiple threads.
- // If Direct I/O enabled, offset, n, and scratch should be aligned properly.
- Status Read(uint64_t offset, size_t n, Slice* result,
- char* scratch) const override {
- assert(scratch);
- offset += prefixLength_;
- auto status = file_->Read(offset, n, result, scratch);
- if (!status.ok()) {
- return status;
- }
- status = stream_->Decrypt(offset, (char*)result->data(), result->size());
- return status;
- }
- // Readahead the file starting from offset by n bytes for caching.
- Status Prefetch(uint64_t offset, size_t n) override {
- //return Status::OK();
- return file_->Prefetch(offset + prefixLength_, n);
- }
- // 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).
- // Furthermore, it tries to make this ID at most "max_size" bytes. If such an
- // ID can be created this function returns the length of the ID and places it
- // in "id"; otherwise, this function returns 0, in which case "id"
- // may not have been modified.
- //
- // This function guarantees, for IDs from a given environment, two unique ids
- // cannot be made equal to each other by adding arbitrary bytes to one of
- // them. That is, no unique ID is the prefix of another.
- //
- // This function guarantees that the returned ID will not be interpretable as
- // a single varint.
- //
- // Note: these IDs are only valid for the duration of the process.
- size_t GetUniqueId(char* id, size_t max_size) const override {
- return file_->GetUniqueId(id, max_size);
- };
- void Hint(AccessPattern pattern) override { file_->Hint(pattern); }
- // Indicates the upper layers if the current RandomAccessFile implementation
- // uses direct IO.
- bool use_direct_io() const override { return file_->use_direct_io(); }
- // Use the returned alignment value to allocate
- // aligned buffer for Direct I/O
- size_t GetRequiredBufferAlignment() const override {
- return file_->GetRequiredBufferAlignment();
- }
- // 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 InvalidateCache(size_t offset, size_t length) override {
- return file_->InvalidateCache(offset + prefixLength_, length);
- }
- };
- // 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 {
- private:
- std::unique_ptr<WritableFile> file_;
- std::unique_ptr<BlockAccessCipherStream> stream_;
- size_t prefixLength_;
- public:
- // Default ctor. Prefix is assumed to be written already.
- EncryptedWritableFile(WritableFile* f, BlockAccessCipherStream* s, size_t prefixLength)
- : WritableFileWrapper(f), file_(f), stream_(s), prefixLength_(prefixLength) { }
- Status Append(const Slice& data) override {
- AlignedBuffer buf;
- Status status;
- Slice dataToAppend(data);
- if (data.size() > 0) {
- auto offset = file_->GetFileSize(); // size including prefix
- // Encrypt in cloned buffer
- buf.Alignment(GetRequiredBufferAlignment());
- buf.AllocateNewBuffer(data.size());
- // TODO (sagar0): Modify AlignedBuffer.Append to allow doing a memmove
- // so that the next two lines can be replaced with buf.Append().
- memmove(buf.BufferStart(), data.data(), data.size());
- buf.Size(data.size());
- status = stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize());
- if (!status.ok()) {
- return status;
- }
- dataToAppend = Slice(buf.BufferStart(), buf.CurrentSize());
- }
- status = file_->Append(dataToAppend);
- if (!status.ok()) {
- return status;
- }
- return status;
- }
- Status PositionedAppend(const Slice& data, uint64_t offset) override {
- AlignedBuffer buf;
- Status status;
- Slice dataToAppend(data);
- offset += prefixLength_;
- if (data.size() > 0) {
- // Encrypt in cloned buffer
- buf.Alignment(GetRequiredBufferAlignment());
- buf.AllocateNewBuffer(data.size());
- memmove(buf.BufferStart(), data.data(), data.size());
- buf.Size(data.size());
- status = stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize());
- if (!status.ok()) {
- return status;
- }
- dataToAppend = Slice(buf.BufferStart(), buf.CurrentSize());
- }
- status = file_->PositionedAppend(dataToAppend, offset);
- if (!status.ok()) {
- return status;
- }
- return status;
- }
- // Indicates the upper layers if the current WritableFile implementation
- // uses direct IO.
- bool use_direct_io() const override { return file_->use_direct_io(); }
- // Use the returned alignment value to allocate
- // aligned buffer for Direct I/O
- size_t GetRequiredBufferAlignment() const override {
- return file_->GetRequiredBufferAlignment();
- }
- /*
- * Get the size of valid data in the file.
- */
- uint64_t GetFileSize() override {
- return file_->GetFileSize() - 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 Truncate(uint64_t size) override {
- return file_->Truncate(size + prefixLength_);
- }
- // 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 InvalidateCache(size_t offset, size_t length) override {
- return file_->InvalidateCache(offset + prefixLength_, length);
- }
- // Sync a file range with disk.
- // offset is the starting byte of the file range to be synchronized.
- // nbytes specifies the length of the range to be synchronized.
- // This asks the OS to initiate flushing the cached data to disk,
- // without waiting for completion.
- // Default implementation does nothing.
- Status RangeSync(uint64_t offset, uint64_t nbytes) override {
- return file_->RangeSync(offset + prefixLength_, nbytes);
- }
- // 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.
- void PrepareWrite(size_t offset, size_t len) override {
- file_->PrepareWrite(offset + prefixLength_, len);
- }
- // Pre-allocates space for a file.
- Status Allocate(uint64_t offset, uint64_t len) override {
- return file_->Allocate(offset + prefixLength_, len);
- }
- };
- // A file abstraction for random reading and writing.
- class EncryptedRandomRWFile : public RandomRWFile {
- private:
- std::unique_ptr<RandomRWFile> file_;
- std::unique_ptr<BlockAccessCipherStream> stream_;
- size_t prefixLength_;
- public:
- EncryptedRandomRWFile(RandomRWFile* f, BlockAccessCipherStream* s, size_t prefixLength)
- : file_(f), stream_(s), prefixLength_(prefixLength) {}
- // Indicates if the class makes use of direct I/O
- // If false you must pass aligned buffer to Write()
- bool use_direct_io() const override { return file_->use_direct_io(); }
- // Use the returned alignment value to allocate
- // aligned buffer for Direct I/O
- size_t GetRequiredBufferAlignment() const override {
- return file_->GetRequiredBufferAlignment();
- }
- // Write bytes in `data` at offset `offset`, Returns Status::OK() on success.
- // Pass aligned buffer when use_direct_io() returns true.
- Status Write(uint64_t offset, const Slice& data) override {
- AlignedBuffer buf;
- Status status;
- Slice dataToWrite(data);
- offset += prefixLength_;
- if (data.size() > 0) {
- // Encrypt in cloned buffer
- buf.Alignment(GetRequiredBufferAlignment());
- buf.AllocateNewBuffer(data.size());
- memmove(buf.BufferStart(), data.data(), data.size());
- buf.Size(data.size());
- status = stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize());
- if (!status.ok()) {
- return status;
- }
- dataToWrite = Slice(buf.BufferStart(), buf.CurrentSize());
- }
- status = file_->Write(offset, dataToWrite);
- return status;
- }
- // 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 Read(uint64_t offset, size_t n, Slice* result,
- char* scratch) const override {
- assert(scratch);
- offset += prefixLength_;
- auto status = file_->Read(offset, n, result, scratch);
- if (!status.ok()) {
- return status;
- }
- status = stream_->Decrypt(offset, (char*)result->data(), result->size());
- return status;
- }
- Status Flush() override { return file_->Flush(); }
- Status Sync() override { return file_->Sync(); }
- Status Fsync() override { return file_->Fsync(); }
- Status Close() override { return file_->Close(); }
- };
- // EncryptedEnv implements an Env wrapper that adds encryption to files stored on disk.
- class EncryptedEnv : public EnvWrapper {
- public:
- EncryptedEnv(Env* base_env, EncryptionProvider *provider)
- : EnvWrapper(base_env) {
- provider_ = provider;
- }
- // NewSequentialFile opens a file for sequential reading.
- Status NewSequentialFile(const std::string& fname,
- std::unique_ptr<SequentialFile>* result,
- const EnvOptions& options) override {
- result->reset();
- if (options.use_mmap_reads) {
- return Status::InvalidArgument();
- }
- // Open file using underlying Env implementation
- std::unique_ptr<SequentialFile> underlying;
- auto status = EnvWrapper::NewSequentialFile(fname, &underlying, options);
- if (!status.ok()) {
- return status;
- }
- // Read prefix (if needed)
- AlignedBuffer prefixBuf;
- Slice prefixSlice;
- size_t prefixLength = provider_->GetPrefixLength();
- if (prefixLength > 0) {
- // Read prefix
- prefixBuf.Alignment(underlying->GetRequiredBufferAlignment());
- prefixBuf.AllocateNewBuffer(prefixLength);
- status = underlying->Read(prefixLength, &prefixSlice, prefixBuf.BufferStart());
- if (!status.ok()) {
- return status;
- }
- prefixBuf.Size(prefixLength);
- }
- // Create cipher stream
- std::unique_ptr<BlockAccessCipherStream> stream;
- status = provider_->CreateCipherStream(fname, options, prefixSlice, &stream);
- if (!status.ok()) {
- return status;
- }
- (*result) = std::unique_ptr<SequentialFile>(new EncryptedSequentialFile(underlying.release(), stream.release(), prefixLength));
- return Status::OK();
- }
- // NewRandomAccessFile opens a file for random read access.
- Status NewRandomAccessFile(const std::string& fname,
- std::unique_ptr<RandomAccessFile>* result,
- const EnvOptions& options) override {
- result->reset();
- if (options.use_mmap_reads) {
- return Status::InvalidArgument();
- }
- // Open file using underlying Env implementation
- std::unique_ptr<RandomAccessFile> underlying;
- auto status = EnvWrapper::NewRandomAccessFile(fname, &underlying, options);
- if (!status.ok()) {
- return status;
- }
- // Read prefix (if needed)
- AlignedBuffer prefixBuf;
- Slice prefixSlice;
- size_t prefixLength = provider_->GetPrefixLength();
- if (prefixLength > 0) {
- // Read prefix
- prefixBuf.Alignment(underlying->GetRequiredBufferAlignment());
- prefixBuf.AllocateNewBuffer(prefixLength);
- status = underlying->Read(0, prefixLength, &prefixSlice, prefixBuf.BufferStart());
- if (!status.ok()) {
- return status;
- }
- prefixBuf.Size(prefixLength);
- }
- // Create cipher stream
- std::unique_ptr<BlockAccessCipherStream> stream;
- status = provider_->CreateCipherStream(fname, options, prefixSlice, &stream);
- if (!status.ok()) {
- return status;
- }
- (*result) = std::unique_ptr<RandomAccessFile>(new EncryptedRandomAccessFile(underlying.release(), stream.release(), prefixLength));
- return Status::OK();
- }
- // NewWritableFile opens a file for sequential writing.
- Status NewWritableFile(const std::string& fname,
- std::unique_ptr<WritableFile>* result,
- const EnvOptions& options) override {
- result->reset();
- if (options.use_mmap_writes) {
- return Status::InvalidArgument();
- }
- // Open file using underlying Env implementation
- std::unique_ptr<WritableFile> underlying;
- Status status = EnvWrapper::NewWritableFile(fname, &underlying, options);
- if (!status.ok()) {
- return status;
- }
- // Initialize & write prefix (if needed)
- AlignedBuffer prefixBuf;
- Slice prefixSlice;
- size_t prefixLength = provider_->GetPrefixLength();
- if (prefixLength > 0) {
- // Initialize prefix
- prefixBuf.Alignment(underlying->GetRequiredBufferAlignment());
- prefixBuf.AllocateNewBuffer(prefixLength);
- provider_->CreateNewPrefix(fname, prefixBuf.BufferStart(), prefixLength);
- prefixBuf.Size(prefixLength);
- prefixSlice = Slice(prefixBuf.BufferStart(), prefixBuf.CurrentSize());
- // Write prefix
- status = underlying->Append(prefixSlice);
- if (!status.ok()) {
- return status;
- }
- }
- // Create cipher stream
- std::unique_ptr<BlockAccessCipherStream> stream;
- status = provider_->CreateCipherStream(fname, options, prefixSlice, &stream);
- if (!status.ok()) {
- return status;
- }
- (*result) = std::unique_ptr<WritableFile>(new EncryptedWritableFile(underlying.release(), stream.release(), prefixLength));
- return Status::OK();
- }
- // Create an object that writes to a new file with the specified
- // name. Deletes any existing file with the same name and creates a
- // new file. On success, stores a pointer to the new file in
- // *result and returns OK. On failure stores nullptr in *result and
- // returns non-OK.
- //
- // The returned file will only be accessed by one thread at a time.
- Status ReopenWritableFile(const std::string& fname,
- std::unique_ptr<WritableFile>* result,
- const EnvOptions& options) override {
- result->reset();
- if (options.use_mmap_writes) {
- return Status::InvalidArgument();
- }
- // Open file using underlying Env implementation
- std::unique_ptr<WritableFile> underlying;
- Status status = EnvWrapper::ReopenWritableFile(fname, &underlying, options);
- if (!status.ok()) {
- return status;
- }
- // Initialize & write prefix (if needed)
- AlignedBuffer prefixBuf;
- Slice prefixSlice;
- size_t prefixLength = provider_->GetPrefixLength();
- if (prefixLength > 0) {
- // Initialize prefix
- prefixBuf.Alignment(underlying->GetRequiredBufferAlignment());
- prefixBuf.AllocateNewBuffer(prefixLength);
- provider_->CreateNewPrefix(fname, prefixBuf.BufferStart(), prefixLength);
- prefixBuf.Size(prefixLength);
- prefixSlice = Slice(prefixBuf.BufferStart(), prefixBuf.CurrentSize());
- // Write prefix
- status = underlying->Append(prefixSlice);
- if (!status.ok()) {
- return status;
- }
- }
- // Create cipher stream
- std::unique_ptr<BlockAccessCipherStream> stream;
- status = provider_->CreateCipherStream(fname, options, prefixSlice, &stream);
- if (!status.ok()) {
- return status;
- }
- (*result) = std::unique_ptr<WritableFile>(new EncryptedWritableFile(underlying.release(), stream.release(), prefixLength));
- return Status::OK();
- }
- // Reuse an existing file by renaming it and opening it as writable.
- Status ReuseWritableFile(const std::string& fname,
- const std::string& old_fname,
- std::unique_ptr<WritableFile>* result,
- const EnvOptions& options) override {
- result->reset();
- if (options.use_mmap_writes) {
- return Status::InvalidArgument();
- }
- // Open file using underlying Env implementation
- std::unique_ptr<WritableFile> underlying;
- Status status = EnvWrapper::ReuseWritableFile(fname, old_fname, &underlying, options);
- if (!status.ok()) {
- return status;
- }
- // Initialize & write prefix (if needed)
- AlignedBuffer prefixBuf;
- Slice prefixSlice;
- size_t prefixLength = provider_->GetPrefixLength();
- if (prefixLength > 0) {
- // Initialize prefix
- prefixBuf.Alignment(underlying->GetRequiredBufferAlignment());
- prefixBuf.AllocateNewBuffer(prefixLength);
- provider_->CreateNewPrefix(fname, prefixBuf.BufferStart(), prefixLength);
- prefixBuf.Size(prefixLength);
- prefixSlice = Slice(prefixBuf.BufferStart(), prefixBuf.CurrentSize());
- // Write prefix
- status = underlying->Append(prefixSlice);
- if (!status.ok()) {
- return status;
- }
- }
- // Create cipher stream
- std::unique_ptr<BlockAccessCipherStream> stream;
- status = provider_->CreateCipherStream(fname, options, prefixSlice, &stream);
- if (!status.ok()) {
- return status;
- }
- (*result) = std::unique_ptr<WritableFile>(new EncryptedWritableFile(underlying.release(), stream.release(), prefixLength));
- return Status::OK();
- }
- // Open `fname` for random read and write, if file doesn't exist the file
- // will be created. On success, stores a pointer to the new file in
- // *result and returns OK. On failure returns non-OK.
- //
- // The returned file will only be accessed by one thread at a time.
- Status NewRandomRWFile(const std::string& fname,
- std::unique_ptr<RandomRWFile>* result,
- const EnvOptions& options) override {
- result->reset();
- if (options.use_mmap_reads || options.use_mmap_writes) {
- return Status::InvalidArgument();
- }
- // Check file exists
- bool isNewFile = !FileExists(fname).ok();
- // Open file using underlying Env implementation
- std::unique_ptr<RandomRWFile> underlying;
- Status status = EnvWrapper::NewRandomRWFile(fname, &underlying, options);
- if (!status.ok()) {
- return status;
- }
- // Read or Initialize & write prefix (if needed)
- AlignedBuffer prefixBuf;
- Slice prefixSlice;
- size_t prefixLength = provider_->GetPrefixLength();
- if (prefixLength > 0) {
- prefixBuf.Alignment(underlying->GetRequiredBufferAlignment());
- prefixBuf.AllocateNewBuffer(prefixLength);
- if (!isNewFile) {
- // File already exists, read prefix
- status = underlying->Read(0, prefixLength, &prefixSlice, prefixBuf.BufferStart());
- if (!status.ok()) {
- return status;
- }
- prefixBuf.Size(prefixLength);
- } else {
- // File is new, initialize & write prefix
- provider_->CreateNewPrefix(fname, prefixBuf.BufferStart(), prefixLength);
- prefixBuf.Size(prefixLength);
- prefixSlice = Slice(prefixBuf.BufferStart(), prefixBuf.CurrentSize());
- // Write prefix
- status = underlying->Write(0, prefixSlice);
- if (!status.ok()) {
- return status;
- }
- }
- }
- // Create cipher stream
- std::unique_ptr<BlockAccessCipherStream> stream;
- status = provider_->CreateCipherStream(fname, options, prefixSlice, &stream);
- if (!status.ok()) {
- return status;
- }
- (*result) = std::unique_ptr<RandomRWFile>(new EncryptedRandomRWFile(underlying.release(), stream.release(), prefixLength));
- return Status::OK();
- }
- // Store in *result the attributes of the children of the specified directory.
- // In case the implementation lists the directory prior to iterating the files
- // and files are concurrently deleted, the deleted files will be omitted from
- // result.
- // The name attributes are relative to "dir".
- // Original contents of *results are dropped.
- // Returns OK if "dir" exists and "*result" contains its children.
- // NotFound if "dir" does not exist, the calling process does not have
- // permission to access "dir", or if "dir" is invalid.
- // IOError if an IO Error was encountered
- Status GetChildrenFileAttributes(
- const std::string& dir, std::vector<FileAttributes>* result) override {
- auto status = EnvWrapper::GetChildrenFileAttributes(dir, result);
- if (!status.ok()) {
- return status;
- }
- size_t prefixLength = provider_->GetPrefixLength();
- for (auto it = std::begin(*result); it!=std::end(*result); ++it) {
- assert(it->size_bytes >= prefixLength);
- it->size_bytes -= prefixLength;
- }
- return Status::OK();
- }
- // Store the size of fname in *file_size.
- Status GetFileSize(const std::string& fname, uint64_t* file_size) override {
- auto status = EnvWrapper::GetFileSize(fname, file_size);
- if (!status.ok()) {
- return status;
- }
- size_t prefixLength = provider_->GetPrefixLength();
- assert(*file_size >= prefixLength);
- *file_size -= prefixLength;
- return Status::OK();
- }
- private:
- EncryptionProvider *provider_;
- };
- // Returns an Env that encrypts data when stored on disk and decrypts data when
- // read from disk.
- Env* NewEncryptedEnv(Env* base_env, EncryptionProvider* provider) {
- return new EncryptedEnv(base_env, provider);
- }
- // Encrypt one or more (partial) blocks of data at the file offset.
- // Length of data is given in dataSize.
- Status BlockAccessCipherStream::Encrypt(uint64_t fileOffset, char *data, size_t dataSize) {
- // Calculate block index
- auto blockSize = BlockSize();
- uint64_t blockIndex = fileOffset / blockSize;
- size_t blockOffset = fileOffset % blockSize;
- std::unique_ptr<char[]> blockBuffer;
- std::string scratch;
- AllocateScratch(scratch);
- // Encrypt individual blocks.
- while (1) {
- char *block = data;
- size_t n = std::min(dataSize, blockSize - blockOffset);
- if (n != blockSize) {
- // We're not encrypting a full block.
- // Copy data to blockBuffer
- if (!blockBuffer.get()) {
- // Allocate buffer
- blockBuffer = std::unique_ptr<char[]>(new char[blockSize]);
- }
- block = blockBuffer.get();
- // Copy plain data to block buffer
- memmove(block + blockOffset, data, n);
- }
- auto status = EncryptBlock(blockIndex, block, (char*)scratch.data());
- if (!status.ok()) {
- return status;
- }
- if (block != data) {
- // Copy encrypted data back to `data`.
- memmove(data, block + blockOffset, n);
- }
- dataSize -= n;
- if (dataSize == 0) {
- return Status::OK();
- }
- data += n;
- blockOffset = 0;
- blockIndex++;
- }
- }
- // Decrypt one or more (partial) blocks of data at the file offset.
- // Length of data is given in dataSize.
- Status BlockAccessCipherStream::Decrypt(uint64_t fileOffset, char *data, size_t dataSize) {
- // Calculate block index
- auto blockSize = BlockSize();
- uint64_t blockIndex = fileOffset / blockSize;
- size_t blockOffset = fileOffset % blockSize;
- std::unique_ptr<char[]> blockBuffer;
- std::string scratch;
- AllocateScratch(scratch);
- // Decrypt individual blocks.
- while (1) {
- char *block = data;
- size_t n = std::min(dataSize, blockSize - blockOffset);
- if (n != blockSize) {
- // We're not decrypting a full block.
- // Copy data to blockBuffer
- if (!blockBuffer.get()) {
- // Allocate buffer
- blockBuffer = std::unique_ptr<char[]>(new char[blockSize]);
- }
- block = blockBuffer.get();
- // Copy encrypted data to block buffer
- memmove(block + blockOffset, data, n);
- }
- auto status = DecryptBlock(blockIndex, block, (char*)scratch.data());
- if (!status.ok()) {
- return status;
- }
- if (block != data) {
- // Copy decrypted data back to `data`.
- memmove(data, block + blockOffset, n);
- }
- // Simply decrementing dataSize by n could cause it to underflow,
- // which will very likely make it read over the original bounds later
- assert(dataSize >= n);
- if (dataSize < n) {
- return Status::Corruption("Cannot decrypt data at given offset");
- }
- dataSize -= n;
- if (dataSize == 0) {
- return Status::OK();
- }
- data += n;
- blockOffset = 0;
- blockIndex++;
- }
- }
- // Encrypt a block of data.
- // Length of data is equal to BlockSize().
- Status ROT13BlockCipher::Encrypt(char *data) {
- for (size_t i = 0; i < blockSize_; ++i) {
- data[i] += 13;
- }
- return Status::OK();
- }
- // Decrypt a block of data.
- // Length of data is equal to BlockSize().
- Status ROT13BlockCipher::Decrypt(char *data) {
- return Encrypt(data);
- }
- // Allocate scratch space which is passed to EncryptBlock/DecryptBlock.
- void CTRCipherStream::AllocateScratch(std::string& scratch) {
- auto blockSize = cipher_.BlockSize();
- scratch.reserve(blockSize);
- }
- // Encrypt a block of data at the given block index.
- // Length of data is equal to BlockSize();
- Status CTRCipherStream::EncryptBlock(uint64_t blockIndex, char *data, char* scratch) {
- // Create nonce + counter
- auto blockSize = cipher_.BlockSize();
- memmove(scratch, iv_.data(), blockSize);
- EncodeFixed64(scratch, blockIndex + initialCounter_);
- // Encrypt nonce+counter
- auto status = cipher_.Encrypt(scratch);
- if (!status.ok()) {
- return status;
- }
- // XOR data with ciphertext.
- for (size_t i = 0; i < blockSize; i++) {
- data[i] = data[i] ^ scratch[i];
- }
- return Status::OK();
- }
- // Decrypt a block of data at the given block index.
- // Length of data is equal to BlockSize();
- Status CTRCipherStream::DecryptBlock(uint64_t blockIndex, char *data, char* scratch) {
- // For CTR decryption & encryption are the same
- return EncryptBlock(blockIndex, data, scratch);
- }
- // GetPrefixLength returns the length of the prefix that is added to every file
- // and used for storing encryption options.
- // For optimal performance, the prefix length should be a multiple of
- // the page size.
- size_t CTREncryptionProvider::GetPrefixLength() {
- return defaultPrefixLength;
- }
- // decodeCTRParameters decodes the initial counter & IV from the given
- // (plain text) prefix.
- static void decodeCTRParameters(const char *prefix, size_t blockSize, uint64_t &initialCounter, Slice &iv) {
- // First block contains 64-bit initial counter
- initialCounter = DecodeFixed64(prefix);
- // Second block contains IV
- iv = Slice(prefix + blockSize, blockSize);
- }
- // CreateNewPrefix initialized an allocated block of prefix memory
- // for a new file.
- Status CTREncryptionProvider::CreateNewPrefix(const std::string& /*fname*/,
- char* prefix,
- size_t prefixLength) {
- // Create & seed rnd.
- Random rnd((uint32_t)Env::Default()->NowMicros());
- // Fill entire prefix block with random values.
- for (size_t i = 0; i < prefixLength; i++) {
- prefix[i] = rnd.Uniform(256) & 0xFF;
- }
- // Take random data to extract initial counter & IV
- auto blockSize = cipher_.BlockSize();
- uint64_t initialCounter;
- Slice prefixIV;
- decodeCTRParameters(prefix, blockSize, initialCounter, prefixIV);
- // Now populate the rest of the prefix, starting from the third block.
- PopulateSecretPrefixPart(prefix + (2 * blockSize), prefixLength - (2 * blockSize), blockSize);
- // Encrypt the prefix, starting from block 2 (leave block 0, 1 with initial counter & IV unencrypted)
- CTRCipherStream cipherStream(cipher_, prefixIV.data(), initialCounter);
- auto status = cipherStream.Encrypt(0, prefix + (2 * blockSize), prefixLength - (2 * blockSize));
- if (!status.ok()) {
- return status;
- }
- return Status::OK();
- }
- // PopulateSecretPrefixPart initializes the data into a new prefix block
- // in plain text.
- // Returns the amount of space (starting from the start of the prefix)
- // that has been initialized.
- size_t CTREncryptionProvider::PopulateSecretPrefixPart(char* /*prefix*/,
- size_t /*prefixLength*/,
- size_t /*blockSize*/) {
- // Nothing to do here, put in custom data in override when needed.
- return 0;
- }
- Status CTREncryptionProvider::CreateCipherStream(
- const std::string& fname, const EnvOptions& options, Slice& prefix,
- std::unique_ptr<BlockAccessCipherStream>* result) {
- // Read plain text part of prefix.
- auto blockSize = cipher_.BlockSize();
- uint64_t initialCounter;
- Slice iv;
- decodeCTRParameters(prefix.data(), blockSize, initialCounter, iv);
- // If the prefix is smaller than twice the block size, we would below read a
- // very large chunk of the file (and very likely read over the bounds)
- assert(prefix.size() >= 2 * blockSize);
- if (prefix.size() < 2 * blockSize) {
- return Status::Corruption("Unable to read from file " + fname +
- ": read attempt would read beyond file bounds");
- }
- // Decrypt the encrypted part of the prefix, starting from block 2 (block 0, 1 with initial counter & IV are unencrypted)
- CTRCipherStream cipherStream(cipher_, iv.data(), initialCounter);
- auto status = cipherStream.Decrypt(0, (char*)prefix.data() + (2 * blockSize), prefix.size() - (2 * blockSize));
- if (!status.ok()) {
- return status;
- }
- // Create cipher stream
- return CreateCipherStreamFromPrefix(fname, options, initialCounter, iv, prefix, result);
- }
- // CreateCipherStreamFromPrefix creates a block access cipher stream for a file given
- // given name and options. The given prefix is already decrypted.
- Status CTREncryptionProvider::CreateCipherStreamFromPrefix(
- const std::string& /*fname*/, const EnvOptions& /*options*/,
- uint64_t initialCounter, const Slice& iv, const Slice& /*prefix*/,
- std::unique_ptr<BlockAccessCipherStream>* result) {
- (*result) = std::unique_ptr<BlockAccessCipherStream>(
- new CTRCipherStream(cipher_, iv.data(), initialCounter));
- return Status::OK();
- }
- #endif // ROCKSDB_LITE
- } // namespace ROCKSDB_NAMESPACE
|