#pragma once #ifndef MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_FSBLOBVIEW_H #define MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_FSBLOBVIEW_H #include #include namespace cryfs { //TODO Test class FsBlobView final : public blobstore::Blob { public: //TODO Rename to "Type" or similar enum class BlobType : uint8_t { DIR = 0x00, FILE = 0x01, SYMLINK = 0x02 }; FsBlobView(cpputils::unique_ref baseBlob): _baseBlob(std::move(baseBlob)), _parentPointer(blockstore::BlockId::Null()) { _checkHeader(*_baseBlob); _loadParentPointer(); } static void InitializeBlob(blobstore::Blob *baseBlob, BlobType blobType, const blockstore::BlockId &parent) { baseBlob->resize(HEADER_SIZE); baseBlob->write(&FORMAT_VERSION_HEADER, 0, sizeof(FORMAT_VERSION_HEADER)); uint8_t blobTypeInt = static_cast(blobType); baseBlob->write(&blobTypeInt, sizeof(FORMAT_VERSION_HEADER), sizeof(uint8_t)); baseBlob->write(parent.data().data(), sizeof(FORMAT_VERSION_HEADER) + sizeof(uint8_t), blockstore::BlockId::BINARY_LENGTH); static_assert(HEADER_SIZE == sizeof(FORMAT_VERSION_HEADER) + sizeof(uint8_t) + blockstore::BlockId::BINARY_LENGTH, "If this fails, the header is not initialized correctly in this function."); } static BlobType blobType(const blobstore::Blob &blob) { _checkHeader(blob); return _blobType(blob); } BlobType blobType() const { return _blobType(*_baseBlob); } const blockstore::BlockId &parentPointer() const { return _parentPointer; } void setParentPointer(const blockstore::BlockId &parentId) { _parentPointer = parentId; _storeParentPointer(); } const blockstore::BlockId &blockId() const override { return _baseBlob->blockId(); } uint64_t size() const override { return _baseBlob->size() - HEADER_SIZE; } void resize(uint64_t numBytes) override { return _baseBlob->resize(numBytes + HEADER_SIZE); } cpputils::Data readAll() const override { cpputils::Data data = _baseBlob->readAll(); cpputils::Data dataWithoutHeader(data.size() - HEADER_SIZE); //Can we avoid this memcpy? Maybe by having Data::subdata() that returns a reference to the same memory region? Should we? std::memcpy(dataWithoutHeader.data(), data.dataOffset(HEADER_SIZE), dataWithoutHeader.size()); return dataWithoutHeader; } void read(void *target, uint64_t offset, uint64_t size) const override { return _baseBlob->read(target, offset + HEADER_SIZE, size); } uint64_t tryRead(void *target, uint64_t offset, uint64_t size) const override { return _baseBlob->tryRead(target, offset + HEADER_SIZE, size); } void write(const void *source, uint64_t offset, uint64_t size) override { return _baseBlob->write(source, offset + HEADER_SIZE, size); } void flush() override { return _baseBlob->flush(); } uint32_t numNodes() const override { return _baseBlob->numNodes(); } cpputils::unique_ref releaseBaseBlob() { return std::move(_baseBlob); } static uint16_t getFormatVersionHeader(const blobstore::Blob &blob) { static_assert(sizeof(uint16_t) == sizeof(FORMAT_VERSION_HEADER), "Wrong type used to read format version header"); uint16_t actualFormatVersion = 0; blob.read(&actualFormatVersion, 0, sizeof(FORMAT_VERSION_HEADER)); return actualFormatVersion; } #ifndef CRYFS_NO_COMPATIBILITY static void migrate(blobstore::Blob *blob, const blockstore::BlockId &parentId); #endif private: static constexpr uint16_t FORMAT_VERSION_HEADER = 1; static constexpr unsigned int HEADER_SIZE = sizeof(FORMAT_VERSION_HEADER) + sizeof(uint8_t) + blockstore::BlockId::BINARY_LENGTH; static void _checkHeader(const blobstore::Blob &blob) { uint16_t actualFormatVersion = getFormatVersionHeader(blob); if (FORMAT_VERSION_HEADER != actualFormatVersion) { throw std::runtime_error("This file system entity has the wrong format. Was it created with a newer version of CryFS?"); } } static BlobType _blobType(const blobstore::Blob &blob) { uint8_t result = 0; blob.read(&result, sizeof(FORMAT_VERSION_HEADER), sizeof(uint8_t)); return static_cast(result); } void _loadParentPointer() { auto idData = cpputils::FixedSizeData::Null(); _baseBlob->read(idData.data(), sizeof(FORMAT_VERSION_HEADER) + sizeof(uint8_t), blockstore::BlockId::BINARY_LENGTH); _parentPointer = blockstore::BlockId(idData); } void _storeParentPointer() { _baseBlob->write(_parentPointer.data().data(), sizeof(FORMAT_VERSION_HEADER) + sizeof(uint8_t), blockstore::BlockId::BINARY_LENGTH); } cpputils::unique_ref _baseBlob; blockstore::BlockId _parentPointer; DISALLOW_COPY_AND_ASSIGN(FsBlobView); }; } #endif