2021-01-23 13:47:36 +08:00
|
|
|
/*
|
|
|
|
* AsyncFileEncrypted.actor.cpp
|
|
|
|
*
|
|
|
|
* This source file is part of the FoundationDB open source project
|
|
|
|
*
|
2022-03-22 04:36:23 +08:00
|
|
|
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
2021-01-23 13:47:36 +08:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "fdbrpc/AsyncFileEncrypted.h"
|
|
|
|
#include "flow/StreamCipher.h"
|
|
|
|
#include "flow/UnitTest.h"
|
|
|
|
#include "flow/xxhash.h"
|
|
|
|
#include "flow/actorcompiler.h" // must be last include
|
|
|
|
|
|
|
|
class AsyncFileEncryptedImpl {
|
|
|
|
public:
|
2021-03-29 13:14:37 +08:00
|
|
|
// Determine the initialization for the first block of a file based on a hash of
|
|
|
|
// the filename.
|
2021-01-23 13:47:36 +08:00
|
|
|
static auto getFirstBlockIV(const std::string& filename) {
|
|
|
|
StreamCipher::IV iv;
|
2021-06-26 13:33:26 +08:00
|
|
|
auto salt = basename(filename);
|
|
|
|
auto pos = salt.find('.');
|
|
|
|
salt = salt.substr(0, pos);
|
|
|
|
auto hash = XXH3_128bits(salt.c_str(), salt.size());
|
2021-08-01 03:27:58 +08:00
|
|
|
auto pHigh = reinterpret_cast<unsigned char*>(&hash.high64);
|
|
|
|
auto pLow = reinterpret_cast<unsigned char*>(&hash.low64);
|
|
|
|
std::copy(pHigh, pHigh + 8, &iv[0]);
|
|
|
|
std::copy(pLow, pLow + 4, &iv[8]);
|
|
|
|
uint32_t blockZero = 0;
|
|
|
|
auto pBlock = reinterpret_cast<unsigned char*>(&blockZero);
|
|
|
|
std::copy(pBlock, pBlock + 4, &iv[12]);
|
2021-01-23 13:47:36 +08:00
|
|
|
return iv;
|
|
|
|
}
|
|
|
|
|
2021-03-29 13:14:37 +08:00
|
|
|
// Read a single block of size ENCRYPTION_BLOCK_SIZE bytes, and decrypt.
|
2021-08-01 02:39:28 +08:00
|
|
|
ACTOR static Future<Standalone<StringRef>> readBlock(AsyncFileEncrypted* self, uint32_t block) {
|
2021-01-23 13:47:36 +08:00
|
|
|
state Arena arena;
|
|
|
|
state unsigned char* encrypted = new (arena) unsigned char[FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE];
|
|
|
|
int bytes = wait(
|
|
|
|
self->file->read(encrypted, FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE, FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE * block));
|
Upgrade AES 128 GCM -> AES 256, StreamCipher code refactor (#6314)
* Upgrade AES 128 GCM -> AES 256, StreamCipher code refactor
Major changes proposed are:
1. Refactor StreamCipher code to enable instantiation of
multiple encryption keys. However, code still retains
a globalEncryption key semantics used in Backup file
encryption usecase.
2. Enhance StreamCipher to provide HMAC signature digest
generation. Further, the class implements HMAC encryption
key derivation function.
3. Upgrade StreamCipher to use AES 256 GCM mode from currently
supported AES 128 GCM mode.
Note: The code changes the encryption key size, however, the
feature is NOT currently in use, hence, should be OK.
3. Add EncryptionOps validation and benchmark toml supported
workload, it does the following:
a. Allow user to configure encrypt-decrypt of a fixed size
buffer or variable size buffer [100, 512K]
b. Allow user to configure number of interactions of the runs,
in each iteration: generate random data, derive an encryption
key using HMAC SHA256 method, encrypt data and
then decrypt data. It collects following metrics:
i) time taken to derive encryption key.
ii) time taken to encrypt the buffer.
iii) time taken to decrypt the buffer.
iv) total bytes encrypted and/or decrypted
c. Along with stats it basic basic validations on the encrypted
and decrypted buffer
d. On completion for test, records the above mentioned metrics
in trace files.
2022-02-01 09:52:44 +08:00
|
|
|
StreamCipherKey const* cipherKey = StreamCipherKey::getGlobalCipherKey();
|
|
|
|
DecryptionStreamCipher decryptor(cipherKey, self->getIV(block));
|
2021-01-23 13:47:36 +08:00
|
|
|
auto decrypted = decryptor.decrypt(encrypted, bytes, arena);
|
|
|
|
return Standalone<StringRef>(decrypted, arena);
|
|
|
|
}
|
|
|
|
|
2021-08-01 02:33:53 +08:00
|
|
|
ACTOR static Future<int> read(AsyncFileEncrypted* self, void* data, int length, int64_t offset) {
|
2021-08-01 02:39:28 +08:00
|
|
|
state const uint32_t firstBlock = offset / FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE;
|
|
|
|
state const uint32_t lastBlock = (offset + length - 1) / FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE;
|
|
|
|
state uint32_t block;
|
2021-01-23 13:47:36 +08:00
|
|
|
state unsigned char* output = reinterpret_cast<unsigned char*>(data);
|
|
|
|
state int bytesRead = 0;
|
2021-06-28 09:55:57 +08:00
|
|
|
ASSERT(self->mode == AsyncFileEncrypted::Mode::READ_ONLY);
|
2021-01-23 13:47:36 +08:00
|
|
|
for (block = firstBlock; block <= lastBlock; ++block) {
|
2021-08-01 02:33:53 +08:00
|
|
|
state Standalone<StringRef> plaintext;
|
2021-01-30 15:57:24 +08:00
|
|
|
|
|
|
|
auto cachedBlock = self->readBuffers.get(block);
|
|
|
|
if (cachedBlock.present()) {
|
|
|
|
plaintext = cachedBlock.get();
|
2021-01-23 13:47:36 +08:00
|
|
|
} else {
|
2021-08-01 02:33:53 +08:00
|
|
|
wait(store(plaintext, readBlock(self, block)));
|
|
|
|
self->readBuffers.insert(block, plaintext);
|
2021-01-23 13:47:36 +08:00
|
|
|
}
|
|
|
|
auto start = (block == firstBlock) ? plaintext.begin() + (offset % FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE)
|
|
|
|
: plaintext.begin();
|
|
|
|
auto end = (block == lastBlock)
|
|
|
|
? plaintext.begin() + ((offset + length) % FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE)
|
|
|
|
: plaintext.end();
|
|
|
|
if ((offset + length) % FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE == 0) {
|
|
|
|
end = plaintext.end();
|
|
|
|
}
|
2021-08-01 02:33:53 +08:00
|
|
|
|
|
|
|
// The block could be short if it includes or is after the end of the file.
|
|
|
|
end = std::min(end, plaintext.end());
|
|
|
|
// If the start position is at or after the end of the block, the read is complete.
|
|
|
|
if (start == end || start >= plaintext.end()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-01-23 13:47:36 +08:00
|
|
|
std::copy(start, end, output);
|
|
|
|
output += (end - start);
|
|
|
|
bytesRead += (end - start);
|
|
|
|
}
|
|
|
|
return bytesRead;
|
|
|
|
}
|
|
|
|
|
|
|
|
ACTOR static Future<Void> write(AsyncFileEncrypted* self, void const* data, int length, int64_t offset) {
|
2021-06-28 09:55:57 +08:00
|
|
|
ASSERT(self->mode == AsyncFileEncrypted::Mode::APPEND_ONLY);
|
2021-05-04 06:26:27 +08:00
|
|
|
// All writes must append to the end of the file:
|
2021-06-26 13:33:26 +08:00
|
|
|
ASSERT_EQ(offset, self->currentBlock * FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE + self->offsetInBlock);
|
2021-01-23 13:47:36 +08:00
|
|
|
state unsigned char const* input = reinterpret_cast<unsigned char const*>(data);
|
|
|
|
while (length > 0) {
|
|
|
|
const auto chunkSize = std::min(length, FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE - self->offsetInBlock);
|
|
|
|
Arena arena;
|
|
|
|
auto encrypted = self->encryptor->encrypt(input, chunkSize, arena);
|
|
|
|
std::copy(encrypted.begin(), encrypted.end(), &self->writeBuffer[self->offsetInBlock]);
|
|
|
|
offset += encrypted.size();
|
|
|
|
self->offsetInBlock += chunkSize;
|
|
|
|
length -= chunkSize;
|
|
|
|
input += chunkSize;
|
|
|
|
if (self->offsetInBlock == FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE) {
|
|
|
|
wait(self->writeLastBlockToFile());
|
|
|
|
self->offsetInBlock = 0;
|
2021-08-01 02:39:28 +08:00
|
|
|
ASSERT_LT(self->currentBlock, std::numeric_limits<uint32_t>::max());
|
2021-01-23 13:47:36 +08:00
|
|
|
++self->currentBlock;
|
Upgrade AES 128 GCM -> AES 256, StreamCipher code refactor (#6314)
* Upgrade AES 128 GCM -> AES 256, StreamCipher code refactor
Major changes proposed are:
1. Refactor StreamCipher code to enable instantiation of
multiple encryption keys. However, code still retains
a globalEncryption key semantics used in Backup file
encryption usecase.
2. Enhance StreamCipher to provide HMAC signature digest
generation. Further, the class implements HMAC encryption
key derivation function.
3. Upgrade StreamCipher to use AES 256 GCM mode from currently
supported AES 128 GCM mode.
Note: The code changes the encryption key size, however, the
feature is NOT currently in use, hence, should be OK.
3. Add EncryptionOps validation and benchmark toml supported
workload, it does the following:
a. Allow user to configure encrypt-decrypt of a fixed size
buffer or variable size buffer [100, 512K]
b. Allow user to configure number of interactions of the runs,
in each iteration: generate random data, derive an encryption
key using HMAC SHA256 method, encrypt data and
then decrypt data. It collects following metrics:
i) time taken to derive encryption key.
ii) time taken to encrypt the buffer.
iii) time taken to decrypt the buffer.
iv) total bytes encrypted and/or decrypted
c. Along with stats it basic basic validations on the encrypted
and decrypted buffer
d. On completion for test, records the above mentioned metrics
in trace files.
2022-02-01 09:52:44 +08:00
|
|
|
self->encryptor = std::make_unique<EncryptionStreamCipher>(StreamCipherKey::getGlobalCipherKey(),
|
2021-02-04 10:13:39 +08:00
|
|
|
self->getIV(self->currentBlock));
|
2021-01-23 13:47:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
|
|
|
ACTOR static Future<Void> sync(AsyncFileEncrypted* self) {
|
2021-06-28 09:55:57 +08:00
|
|
|
ASSERT(self->mode == AsyncFileEncrypted::Mode::APPEND_ONLY);
|
2021-01-23 13:47:36 +08:00
|
|
|
wait(self->writeLastBlockToFile());
|
|
|
|
wait(self->file->sync());
|
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
|
|
|
ACTOR static Future<Void> zeroRange(AsyncFileEncrypted* self, int64_t offset, int64_t length) {
|
2021-06-28 09:55:57 +08:00
|
|
|
ASSERT(self->mode == AsyncFileEncrypted::Mode::APPEND_ONLY);
|
2021-01-23 13:47:36 +08:00
|
|
|
// TODO: Could optimize this
|
|
|
|
Arena arena;
|
|
|
|
auto zeroes = new (arena) unsigned char[length];
|
|
|
|
memset(zeroes, 0, length);
|
|
|
|
wait(self->write(zeroes, length, offset));
|
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-06-28 09:55:57 +08:00
|
|
|
AsyncFileEncrypted::AsyncFileEncrypted(Reference<IAsyncFile> file, Mode mode)
|
2021-07-23 13:48:27 +08:00
|
|
|
: file(file), mode(mode), readBuffers(FLOW_KNOBS->MAX_DECRYPTED_BLOCKS), currentBlock(0) {
|
2021-01-23 13:47:36 +08:00
|
|
|
firstBlockIV = AsyncFileEncryptedImpl::getFirstBlockIV(file->getFilename());
|
2021-06-28 09:55:57 +08:00
|
|
|
if (mode == Mode::APPEND_ONLY) {
|
Upgrade AES 128 GCM -> AES 256, StreamCipher code refactor (#6314)
* Upgrade AES 128 GCM -> AES 256, StreamCipher code refactor
Major changes proposed are:
1. Refactor StreamCipher code to enable instantiation of
multiple encryption keys. However, code still retains
a globalEncryption key semantics used in Backup file
encryption usecase.
2. Enhance StreamCipher to provide HMAC signature digest
generation. Further, the class implements HMAC encryption
key derivation function.
3. Upgrade StreamCipher to use AES 256 GCM mode from currently
supported AES 128 GCM mode.
Note: The code changes the encryption key size, however, the
feature is NOT currently in use, hence, should be OK.
3. Add EncryptionOps validation and benchmark toml supported
workload, it does the following:
a. Allow user to configure encrypt-decrypt of a fixed size
buffer or variable size buffer [100, 512K]
b. Allow user to configure number of interactions of the runs,
in each iteration: generate random data, derive an encryption
key using HMAC SHA256 method, encrypt data and
then decrypt data. It collects following metrics:
i) time taken to derive encryption key.
ii) time taken to encrypt the buffer.
iii) time taken to decrypt the buffer.
iv) total bytes encrypted and/or decrypted
c. Along with stats it basic basic validations on the encrypted
and decrypted buffer
d. On completion for test, records the above mentioned metrics
in trace files.
2022-02-01 09:52:44 +08:00
|
|
|
encryptor =
|
|
|
|
std::make_unique<EncryptionStreamCipher>(StreamCipherKey::getGlobalCipherKey(), getIV(currentBlock));
|
2021-01-23 13:47:36 +08:00
|
|
|
writeBuffer = std::vector<unsigned char>(FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AsyncFileEncrypted::addref() {
|
|
|
|
ReferenceCounted<AsyncFileEncrypted>::addref();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AsyncFileEncrypted::delref() {
|
|
|
|
ReferenceCounted<AsyncFileEncrypted>::delref();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<int> AsyncFileEncrypted::read(void* data, int length, int64_t offset) {
|
|
|
|
return AsyncFileEncryptedImpl::read(this, data, length, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> AsyncFileEncrypted::write(void const* data, int length, int64_t offset) {
|
|
|
|
return AsyncFileEncryptedImpl::write(this, data, length, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> AsyncFileEncrypted::zeroRange(int64_t offset, int64_t length) {
|
|
|
|
return AsyncFileEncryptedImpl::zeroRange(this, offset, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> AsyncFileEncrypted::truncate(int64_t size) {
|
2021-06-28 09:55:57 +08:00
|
|
|
ASSERT(mode == Mode::APPEND_ONLY);
|
|
|
|
return file->truncate(size);
|
2021-01-23 13:47:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> AsyncFileEncrypted::sync() {
|
2021-06-28 09:55:57 +08:00
|
|
|
ASSERT(mode == Mode::APPEND_ONLY);
|
2021-01-23 13:47:36 +08:00
|
|
|
return AsyncFileEncryptedImpl::sync(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> AsyncFileEncrypted::flush() {
|
2021-06-28 09:55:57 +08:00
|
|
|
ASSERT(mode == Mode::APPEND_ONLY);
|
2021-01-23 13:47:36 +08:00
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<int64_t> AsyncFileEncrypted::size() const {
|
2021-06-28 09:55:57 +08:00
|
|
|
ASSERT(mode == Mode::READ_ONLY);
|
2021-06-26 13:33:26 +08:00
|
|
|
return file->size();
|
2021-01-23 13:47:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string AsyncFileEncrypted::getFilename() const {
|
|
|
|
return file->getFilename();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> AsyncFileEncrypted::readZeroCopy(void** data, int* length, int64_t offset) {
|
2021-05-04 06:26:27 +08:00
|
|
|
throw io_error();
|
2021-01-23 13:47:36 +08:00
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AsyncFileEncrypted::releaseZeroCopy(void* data, int length, int64_t offset) {
|
2021-05-04 06:26:27 +08:00
|
|
|
throw io_error();
|
2021-01-23 13:47:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int64_t AsyncFileEncrypted::debugFD() const {
|
2021-06-28 09:55:57 +08:00
|
|
|
return file->debugFD();
|
2021-01-23 13:47:36 +08:00
|
|
|
}
|
|
|
|
|
2021-08-01 02:39:28 +08:00
|
|
|
StreamCipher::IV AsyncFileEncrypted::getIV(uint32_t block) const {
|
2021-01-23 13:47:36 +08:00
|
|
|
auto iv = firstBlockIV;
|
2021-08-01 03:27:58 +08:00
|
|
|
|
|
|
|
auto pBlock = reinterpret_cast<unsigned char*>(&block);
|
|
|
|
std::copy(pBlock, pBlock + 4, &iv[12]);
|
|
|
|
|
2021-01-23 13:47:36 +08:00
|
|
|
return iv;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> AsyncFileEncrypted::writeLastBlockToFile() {
|
|
|
|
return file->write(&writeBuffer[0], offsetInBlock, currentBlock * FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE);
|
|
|
|
}
|
|
|
|
|
2021-01-30 16:12:05 +08:00
|
|
|
size_t AsyncFileEncrypted::RandomCache::evict() {
|
2021-06-27 02:15:12 +08:00
|
|
|
ASSERT_EQ(vec.size(), maxSize);
|
2021-01-30 16:12:05 +08:00
|
|
|
auto index = deterministicRandom()->randomInt(0, maxSize);
|
|
|
|
hashMap.erase(vec[index]);
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
AsyncFileEncrypted::RandomCache::RandomCache(size_t maxSize) : maxSize(maxSize) {
|
|
|
|
vec.reserve(maxSize);
|
|
|
|
}
|
|
|
|
|
2021-08-01 02:39:28 +08:00
|
|
|
void AsyncFileEncrypted::RandomCache::insert(uint32_t block, const Standalone<StringRef>& value) {
|
2021-01-30 16:12:05 +08:00
|
|
|
auto [_, found] = hashMap.insert({ block, value });
|
|
|
|
if (found) {
|
|
|
|
return;
|
|
|
|
} else if (vec.size() < maxSize) {
|
|
|
|
vec.push_back(block);
|
|
|
|
} else {
|
|
|
|
auto index = evict();
|
|
|
|
vec[index] = block;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-01 02:39:28 +08:00
|
|
|
Optional<Standalone<StringRef>> AsyncFileEncrypted::RandomCache::get(uint32_t block) const {
|
2021-01-30 16:12:05 +08:00
|
|
|
auto it = hashMap.find(block);
|
|
|
|
if (it == hashMap.end()) {
|
|
|
|
return {};
|
|
|
|
} else {
|
|
|
|
return it->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-29 13:14:37 +08:00
|
|
|
// This test writes random data into an encrypted file in random increments,
|
|
|
|
// then reads this data back from the file in random increments, then confirms that
|
|
|
|
// the bytes read match the bytes written.
|
2021-01-23 13:47:36 +08:00
|
|
|
TEST_CASE("fdbrpc/AsyncFileEncrypted") {
|
|
|
|
state const int bytes = FLOW_KNOBS->ENCRYPTION_BLOCK_SIZE * deterministicRandom()->randomInt(0, 1000);
|
|
|
|
state std::vector<unsigned char> writeBuffer(bytes, 0);
|
|
|
|
generateRandomData(&writeBuffer.front(), bytes);
|
|
|
|
state std::vector<unsigned char> readBuffer(bytes, 0);
|
|
|
|
ASSERT(g_network->isSimulated());
|
Upgrade AES 128 GCM -> AES 256, StreamCipher code refactor (#6314)
* Upgrade AES 128 GCM -> AES 256, StreamCipher code refactor
Major changes proposed are:
1. Refactor StreamCipher code to enable instantiation of
multiple encryption keys. However, code still retains
a globalEncryption key semantics used in Backup file
encryption usecase.
2. Enhance StreamCipher to provide HMAC signature digest
generation. Further, the class implements HMAC encryption
key derivation function.
3. Upgrade StreamCipher to use AES 256 GCM mode from currently
supported AES 128 GCM mode.
Note: The code changes the encryption key size, however, the
feature is NOT currently in use, hence, should be OK.
3. Add EncryptionOps validation and benchmark toml supported
workload, it does the following:
a. Allow user to configure encrypt-decrypt of a fixed size
buffer or variable size buffer [100, 512K]
b. Allow user to configure number of interactions of the runs,
in each iteration: generate random data, derive an encryption
key using HMAC SHA256 method, encrypt data and
then decrypt data. It collects following metrics:
i) time taken to derive encryption key.
ii) time taken to encrypt the buffer.
iii) time taken to decrypt the buffer.
iv) total bytes encrypted and/or decrypted
c. Along with stats it basic basic validations on the encrypted
and decrypted buffer
d. On completion for test, records the above mentioned metrics
in trace files.
2022-02-01 09:52:44 +08:00
|
|
|
StreamCipherKey::initializeGlobalRandomTestKey();
|
2021-01-23 13:47:36 +08:00
|
|
|
int flags = IAsyncFile::OPEN_READWRITE | IAsyncFile::OPEN_CREATE | IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE |
|
|
|
|
IAsyncFile::OPEN_UNBUFFERED | IAsyncFile::OPEN_ENCRYPTED | IAsyncFile::OPEN_UNCACHED |
|
|
|
|
IAsyncFile::OPEN_NO_AIO;
|
2021-06-26 05:11:21 +08:00
|
|
|
state Reference<IAsyncFile> file =
|
|
|
|
wait(IAsyncFileSystem::filesystem()->open(joinPath(params.getDataDir(), "test-encrypted-file"), flags, 0600));
|
2021-01-23 13:47:36 +08:00
|
|
|
state int bytesWritten = 0;
|
2021-08-01 02:35:02 +08:00
|
|
|
state int chunkSize;
|
2021-01-23 13:47:36 +08:00
|
|
|
while (bytesWritten < bytes) {
|
|
|
|
chunkSize = std::min(deterministicRandom()->randomInt(0, 100), bytes - bytesWritten);
|
|
|
|
wait(file->write(&writeBuffer[bytesWritten], chunkSize, bytesWritten));
|
|
|
|
bytesWritten += chunkSize;
|
|
|
|
}
|
|
|
|
wait(file->sync());
|
|
|
|
state int bytesRead = 0;
|
|
|
|
while (bytesRead < bytes) {
|
|
|
|
chunkSize = std::min(deterministicRandom()->randomInt(0, 100), bytes - bytesRead);
|
|
|
|
int bytesReadInChunk = wait(file->read(&readBuffer[bytesRead], chunkSize, bytesRead));
|
2021-06-27 02:15:12 +08:00
|
|
|
ASSERT_EQ(bytesReadInChunk, chunkSize);
|
2021-01-23 13:47:36 +08:00
|
|
|
bytesRead += bytesReadInChunk;
|
|
|
|
}
|
|
|
|
ASSERT(writeBuffer == readBuffer);
|
|
|
|
return Void();
|
|
|
|
}
|