Hello community, here is the log from the commit of package cryfs for openSUSE:Factory checked in at 2019-02-08 12:15:47 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/cryfs (Old) and /work/SRC/openSUSE:Factory/.cryfs.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "cryfs" Fri Feb 8 12:15:47 2019 rev:3 rq:672572 version:0.9.10 Changes: -------- --- /work/SRC/openSUSE:Factory/cryfs/cryfs.changes 2018-03-07 10:40:07.622684610 +0100 +++ /work/SRC/openSUSE:Factory/.cryfs.new.28833/cryfs.changes 2019-02-08 12:15:48.781415131 +0100 @@ -1,0 +2,8 @@ +Sat Feb 2 14:24:24 UTC 2019 - Klaas Freitag <[email protected]> + +- Update to upstream version 0.9.10 + * Fixed occasional deadlock (#64) + * Fix for reading empty files out of bounds + * Fixed race condition (#224 and #243) + +------------------------------------------------------------------- Old: ---- cryfs-0.9.9.tar.xz New: ---- cryfs-0.9.10.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ cryfs.spec ++++++ --- /var/tmp/diff_new_pack.yPUpMS/_old 2019-02-08 12:15:49.657414803 +0100 +++ /var/tmp/diff_new_pack.yPUpMS/_new 2019-02-08 12:15:49.661414801 +0100 @@ -1,7 +1,7 @@ # # spec file for package cryfs # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # Copyright (c) 2007-2011 Klaas Freitag <[email protected]> # # All modifications and additions to the file contributed by third parties @@ -13,14 +13,15 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # + Name: cryfs -Version: 0.9.9 +Version: 0.9.10 Release: 0 Summary: CryFS encryption -License: LGPL-3.0 +License: LGPL-3.0-only Group: System/Filesystems Source: %{name}-%{version}.tar.xz URL: https://github.com/cryfs/cryfs @@ -41,11 +42,11 @@ %endif BuildRequires: pkgconfig -BuildRequires: pkgconfig(libcurl) BuildRequires: pkgconfig(fuse) +BuildRequires: pkgconfig(libcurl) # BuildRequires: pkgconfig(libopenssl) -BuildRequires: libopenssl-devel BuildRequires: libcryptopp-devel +BuildRequires: libopenssl-devel #================================= @@ -84,4 +85,5 @@ %{_mandir}/man?/cryfs* %doc README.md ChangeLog.txt %license LICENSE + %changelog ++++++ cryfs-0.9.9.tar.xz -> cryfs-0.9.10.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.travis.yml new/.travis.yml --- old/.travis.yml 2018-02-05 04:16:25.000000000 +0100 +++ new/.travis.yml 2019-01-21 04:39:01.000000000 +0100 @@ -18,6 +18,8 @@ - libcrypto++-dev - libfuse-dev install: +# Workaround homebrew bug, see https://twitter.com/axccl/status/1083393735277363205 and https://github.com/openPMD/openPMD-api/pull/431/files +- if [ "${TRAVIS_OS_NAME}" == "osx" ]; then travis_wait brew upgrade --cleanup; travis_wait brew upgrade --cleanup; fi # Use new clang - if [ "${TRAVIS_OS_NAME}" == "linux" ] && [ "$CXX" = "clang++" ]; then export CXX="clang++-3.7" CC="clang-3.7"; fi # Detect number of CPU cores diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ChangeLog.txt new/ChangeLog.txt --- old/ChangeLog.txt 2018-02-05 04:16:25.000000000 +0100 +++ new/ChangeLog.txt 2019-01-21 04:39:01.000000000 +0100 @@ -1,3 +1,10 @@ +Version 0.9.10 +-------------- +Fixed bugs: +* Fixed occasional deadlock (https://github.com/cryfs/cryfs/issues/64) +* Fix for reading empty files out of bounds +* Fixed race condition (https://github.com/cryfs/cryfs/issues/224 and https://github.com/cryfs/cryfs/issues/243) + Version 0.9.9 -------------- Improvements: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/src/blobstore/implementations/onblocks/BlobOnBlocks.cpp new/src/blobstore/implementations/onblocks/BlobOnBlocks.cpp --- old/src/blobstore/implementations/onblocks/BlobOnBlocks.cpp 2018-02-05 04:16:25.000000000 +0100 +++ new/src/blobstore/implementations/onblocks/BlobOnBlocks.cpp 2019-01-21 04:39:01.000000000 +0100 @@ -26,6 +26,11 @@ } uint64_t BlobOnBlocks::size() const { + std::unique_lock<std::mutex> lock(_datatree->mutex()); + return _size(); +} + +uint64_t BlobOnBlocks::_size() const { if (_sizeCache == boost::none) { _sizeCache = _datatree->numStoredBytes(); } @@ -33,15 +38,17 @@ } void BlobOnBlocks::resize(uint64_t numBytes) { + std::unique_lock<std::mutex> lock(_datatree->mutex()); + _datatree->resizeNumBytes(numBytes); _sizeCache = numBytes; } -void BlobOnBlocks::traverseLeaves(uint64_t beginByte, uint64_t sizeBytes, function<void (uint64_t, DataLeafNode *leaf, uint32_t, uint32_t)> func) const { +void BlobOnBlocks::_traverseLeaves(uint64_t beginByte, uint64_t sizeBytes, function<void (uint64_t, DataLeafNode *leaf, uint32_t, uint32_t)> func) const { uint64_t endByte = beginByte + sizeBytes; uint32_t firstLeaf = beginByte / _datatree->maxBytesPerLeaf(); uint32_t endLeaf = utils::ceilDivision(endByte, _datatree->maxBytesPerLeaf()); - bool writingOutside = size() < endByte; // TODO Calling size() is slow because it has to traverse the tree + bool writingOutside = _size() < endByte; // TODO Calling size() is slow because it has to traverse the tree _datatree->traverseLeaves(firstLeaf, endLeaf, [&func, beginByte, endByte, endLeaf, writingOutside](DataLeafNode *leaf, uint32_t leafIndex) { uint64_t indexOfFirstLeafByte = leafIndex * leaf->maxStoreableBytes(); uint32_t dataBegin = utils::maxZeroSubtraction(beginByte, indexOfFirstLeafByte); @@ -59,41 +66,70 @@ } Data BlobOnBlocks::readAll() const { + std::unique_lock<std::mutex> lock(_datatree->mutex()); + //TODO Querying size is inefficient. Is this possible without a call to size()? - uint64_t count = size(); + uint64_t count = _size(); Data result(count); _read(result.data(), 0, count); return result; } void BlobOnBlocks::read(void *target, uint64_t offset, uint64_t count) const { - ASSERT(offset <= size() && offset + count <= size(), "BlobOnBlocks::read() read outside blob. Use BlobOnBlocks::tryRead() if this should be allowed."); - uint64_t read = tryRead(target, offset, count); - ASSERT(read == count, "BlobOnBlocks::read() couldn't read all requested bytes. Use BlobOnBlocks::tryRead() if this should be allowed."); + std::unique_lock<std::mutex> lock(_datatree->mutex()); + + if(offset > _size() || offset + count > _size()) { + throw std::runtime_error("BlobOnBlocks::read() read outside blob. Use BlobOnBlocks::tryRead() if this should be allowed."); + } + uint64_t read = _tryRead(target, offset, count); + if(read != count) { + throw std::runtime_error("BlobOnBlocks::read() couldn't read all requested bytes. Use BlobOnBlocks::tryRead() if this should be allowed."); + } } uint64_t BlobOnBlocks::tryRead(void *target, uint64_t offset, uint64_t count) const { + std::unique_lock<std::mutex> lock(_datatree->mutex()); + return _tryRead(target, offset, count); +} + +uint64_t BlobOnBlocks::_tryRead(void *target, uint64_t offset, uint64_t count) const { + if (_size() <= offset) { + return 0; + } + //TODO Quite inefficient to call size() here, because that has to traverse the tree - uint64_t realCount = std::max(UINT64_C(0), std::min(count, size()-offset)); + uint64_t realCount = std::max(INT64_C(0), std::min(static_cast<int64_t>(count), static_cast<int64_t>(_size())-static_cast<int64_t>(offset))); _read(target, offset, realCount); return realCount; } void BlobOnBlocks::_read(void *target, uint64_t offset, uint64_t count) const { - traverseLeaves(offset, count, [target, offset] (uint64_t indexOfFirstLeafByte, const DataLeafNode *leaf, uint32_t leafDataOffset, uint32_t leafDataSize) { + if (count == 0) { + return; + } + + _traverseLeaves(offset, count, [target, offset] (uint64_t indexOfFirstLeafByte, const DataLeafNode *leaf, uint32_t leafDataOffset, uint32_t leafDataSize) { //TODO Simplify formula, make it easier to understand leaf->read((uint8_t*)target + indexOfFirstLeafByte - offset + leafDataOffset, leafDataOffset, leafDataSize); }); } void BlobOnBlocks::write(const void *source, uint64_t offset, uint64_t count) { - traverseLeaves(offset, count, [source, offset] (uint64_t indexOfFirstLeafByte, DataLeafNode *leaf, uint32_t leafDataOffset, uint32_t leafDataSize) { + if (count == 0) { + return; + } + + std::unique_lock<std::mutex> lock(_datatree->mutex()); + + _traverseLeaves(offset, count, [source, offset] (uint64_t indexOfFirstLeafByte, DataLeafNode *leaf, uint32_t leafDataOffset, uint32_t leafDataSize) { //TODO Simplify formula, make it easier to understand leaf->write((uint8_t*)source + indexOfFirstLeafByte - offset + leafDataOffset, leafDataOffset, leafDataSize); }); } void BlobOnBlocks::flush() { + std::unique_lock<std::mutex> lock(_datatree->mutex()); + _datatree->flush(); } @@ -102,6 +138,8 @@ } unique_ref<DataTreeRef> BlobOnBlocks::releaseTree() { + std::unique_lock<std::mutex> lock(_datatree->mutex()); + return std::move(_datatree); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/src/blobstore/implementations/onblocks/BlobOnBlocks.h new/src/blobstore/implementations/onblocks/BlobOnBlocks.h --- old/src/blobstore/implementations/onblocks/BlobOnBlocks.h 2018-02-05 04:16:25.000000000 +0100 +++ new/src/blobstore/implementations/onblocks/BlobOnBlocks.h 2019-01-21 04:39:01.000000000 +0100 @@ -37,8 +37,11 @@ private: + uint64_t _size() const; + void _read(void *target, uint64_t offset, uint64_t count) const; - void traverseLeaves(uint64_t offsetBytes, uint64_t sizeBytes, std::function<void (uint64_t, datanodestore::DataLeafNode *, uint32_t, uint32_t)>) const; + uint64_t _tryRead(void *target, uint64_t offset, uint64_t size) const; + void _traverseLeaves(uint64_t offsetBytes, uint64_t sizeBytes, std::function<void (uint64_t, datanodestore::DataLeafNode *, uint32_t, uint32_t)>) const; cpputils::unique_ref<parallelaccessdatatreestore::DataTreeRef> _datatree; mutable boost::optional<uint64_t> _sizeCache; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/src/blobstore/implementations/onblocks/datatreestore/DataTree.cpp new/src/blobstore/implementations/onblocks/datatreestore/DataTree.cpp --- old/src/blobstore/implementations/onblocks/datatreestore/DataTree.cpp 2018-02-05 04:16:25.000000000 +0100 +++ new/src/blobstore/implementations/onblocks/datatreestore/DataTree.cpp 2019-01-21 04:39:01.000000000 +0100 @@ -158,6 +158,10 @@ } void DataTree::traverseLeaves(uint32_t beginIndex, uint32_t endIndex, function<void (DataLeafNode*, uint32_t)> func) { + if (endIndex <= beginIndex) { + return; + } + //TODO Can we traverse in parallel? unique_lock<shared_mutex> lock(_mutex); //TODO Only lock when resizing. Otherwise parallel read/write to a blob is not possible! ASSERT(beginIndex <= endIndex, "Invalid parameters"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/src/blobstore/implementations/onblocks/datatreestore/DataTree.h new/src/blobstore/implementations/onblocks/datatreestore/DataTree.h --- old/src/blobstore/implementations/onblocks/datatreestore/DataTree.h 2018-02-05 04:16:25.000000000 +0100 +++ new/src/blobstore/implementations/onblocks/datatreestore/DataTree.h 2019-01-21 04:39:01.000000000 +0100 @@ -38,7 +38,14 @@ void flush() const; + // This is a hack to fix a race condition. This is only done in the 0.9 release branch to workaround the issue, + // the develop branch and 0.10 release series have a proper fix. + std::mutex& mutex() const { + return _outerMutex; + } + private: + mutable std::mutex _outerMutex; mutable boost::shared_mutex _mutex; datanodestore::DataNodeStore *_nodeStore; cpputils::unique_ref<datanodestore::DataNode> _rootNode; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/DataTreeRef.h new/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/DataTreeRef.h --- old/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/DataTreeRef.h 2018-02-05 04:16:25.000000000 +0100 +++ new/src/blobstore/implementations/onblocks/parallelaccessdatatreestore/DataTreeRef.h 2019-01-21 04:39:01.000000000 +0100 @@ -41,6 +41,12 @@ return _baseTree->flush(); } + // This is a hack to fix a race condition. This is only done in the 0.9 release branch to workaround the issue, + // the develop branch and 0.10 release series have a proper fix. + std::mutex& mutex() const { + return _baseTree->mutex(); + } + private: datatreestore::DataTree *_baseTree; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/src/cpp-utils/random/ThreadsafeRandomDataBuffer.h new/src/cpp-utils/random/ThreadsafeRandomDataBuffer.h --- old/src/cpp-utils/random/ThreadsafeRandomDataBuffer.h 2018-02-05 04:16:25.000000000 +0100 +++ new/src/cpp-utils/random/ThreadsafeRandomDataBuffer.h 2019-01-21 04:39:01.000000000 +0100 @@ -54,7 +54,7 @@ inline size_t ThreadsafeRandomDataBuffer::_get(void *target, size_t numBytes) { boost::unique_lock<boost::mutex> lock(_mutex); - _dataAddedCv.wait(lock, [this, numBytes] { + _dataAddedCv.wait(lock, [this] { return _buffer.size() > 0; }); size_t gettableBytes = std::min(_buffer.size(), numBytes); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/src/cryfs/filesystem/fsblobstore/DirBlob.cpp new/src/cryfs/filesystem/fsblobstore/DirBlob.cpp --- old/src/cryfs/filesystem/fsblobstore/DirBlob.cpp 2018-02-05 04:16:25.000000000 +0100 +++ new/src/cryfs/filesystem/fsblobstore/DirBlob.cpp 2019-01-21 04:39:01.000000000 +0100 @@ -29,18 +29,18 @@ constexpr off_t DirBlob::DIR_LSTAT_SIZE; DirBlob::DirBlob(FsBlobStore *fsBlobStore, unique_ref<Blob> blob, std::function<off_t (const blockstore::Key&)> getLstatSize) : - FsBlob(std::move(blob)), _fsBlobStore(fsBlobStore), _getLstatSize(getLstatSize), _entries(), _mutex(), _changed(false) { + FsBlob(std::move(blob)), _fsBlobStore(fsBlobStore), _getLstatSize(getLstatSize), _getLstatSizeMutex(), _entries(), _entriesAndChangedMutex(), _changed(false) { ASSERT(baseBlob().blobType() == FsBlobView::BlobType::DIR, "Loaded blob is not a directory"); _readEntriesFromBlob(); } DirBlob::~DirBlob() { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _writeEntriesToBlob(); } void DirBlob::flush() { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _writeEntriesToBlob(); baseBlob().flush(); } @@ -66,17 +66,17 @@ } void DirBlob::AddChildDir(const std::string &name, const Key &blobKey, mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _addChild(name, blobKey, fspp::Dir::EntryType::DIR, mode, uid, gid, lastAccessTime, lastModificationTime); } void DirBlob::AddChildFile(const std::string &name, const Key &blobKey, mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _addChild(name, blobKey, fspp::Dir::EntryType::FILE, mode, uid, gid, lastAccessTime, lastModificationTime); } void DirBlob::AddChildSymlink(const std::string &name, const blockstore::Key &blobKey, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _addChild(name, blobKey, fspp::Dir::EntryType::SYMLINK, S_IFLNK | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH, uid, gid, lastAccessTime, lastModificationTime); } @@ -89,41 +89,41 @@ void DirBlob::AddOrOverwriteChild(const std::string &name, const Key &blobKey, fspp::Dir::EntryType entryType, mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime, std::function<void (const blockstore::Key &key)> onOverwritten) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _entries.addOrOverwrite(name, blobKey, entryType, mode, uid, gid, lastAccessTime, lastModificationTime, onOverwritten); _changed = true; } void DirBlob::RenameChild(const blockstore::Key &key, const std::string &newName, std::function<void (const blockstore::Key &key)> onOverwritten) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _entries.rename(key, newName, onOverwritten); _changed = true; } boost::optional<const DirEntry&> DirBlob::GetChild(const string &name) const { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); return _entries.get(name); } boost::optional<const DirEntry&> DirBlob::GetChild(const Key &key) const { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); return _entries.get(key); } void DirBlob::RemoveChild(const string &name) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _entries.remove(name); _changed = true; } void DirBlob::RemoveChild(const Key &key) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _entries.remove(key); _changed = true; } void DirBlob::AppendChildrenTo(vector<fspp::Dir::Entry> *result) const { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); result->reserve(result->size() + _entries.size()); for (const auto &entry : _entries) { result->emplace_back(entry.type(), entry.name()); @@ -135,7 +135,17 @@ } void DirBlob::statChild(const Key &key, struct ::stat *result) const { - result->st_size = _getLstatSize(key); + std::unique_lock<std::mutex> lock(_getLstatSizeMutex); + auto lstatSizeGetter = _getLstatSize; + + // The following unlock is important to avoid deadlock. + // ParallelAccessFsBlobStore::load() causes a call to DirBlob::setLstatSizeGetter, + // so their lock ordering first locks the ParallelAccessStore::_mutex, then the DirBlob::_getLstatSizeMutex. + // this requires us to free DirBlob::_getLstatSizeMutex before calling into lstatSizeGetter(), because + // lstatSizeGetter can call ParallelAccessFsBlobStore::load(). + lock.unlock(); + + result->st_size = lstatSizeGetter(key); statChildWithSizeAlreadySet(key, result); } @@ -159,43 +169,43 @@ } void DirBlob::updateAccessTimestampForChild(const Key &key) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _entries.updateAccessTimestampForChild(key); _changed = true; } void DirBlob::updateModificationTimestampForChild(const Key &key) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _entries.updateModificationTimestampForChild(key); _changed = true; } void DirBlob::chmodChild(const Key &key, mode_t mode) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _entries.setMode(key, mode); _changed = true; } void DirBlob::chownChild(const Key &key, uid_t uid, gid_t gid) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); if(_entries.setUidGid(key, uid, gid)) { _changed = true; } } void DirBlob::utimensChild(const Key &key, timespec lastAccessTime, timespec lastModificationTime) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _entries.setAccessTimes(key, lastAccessTime, lastModificationTime); _changed = true; } void DirBlob::setLstatSizeGetter(std::function<off_t(const blockstore::Key&)> getLstatSize) { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_getLstatSizeMutex); _getLstatSize = getLstatSize; } cpputils::unique_ref<blobstore::Blob> DirBlob::releaseBaseBlob() { - std::unique_lock<std::mutex> lock(_mutex); + std::unique_lock<std::mutex> lock(_entriesAndChangedMutex); _writeEntriesToBlob(); return FsBlob::releaseBaseBlob(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/src/cryfs/filesystem/fsblobstore/DirBlob.h new/src/cryfs/filesystem/fsblobstore/DirBlob.h --- old/src/cryfs/filesystem/fsblobstore/DirBlob.h 2018-02-05 04:16:25.000000000 +0100 +++ new/src/cryfs/filesystem/fsblobstore/DirBlob.h 2019-01-21 04:39:01.000000000 +0100 @@ -82,8 +82,9 @@ FsBlobStore *_fsBlobStore; std::function<off_t (const blockstore::Key&)> _getLstatSize; + mutable std::mutex _getLstatSizeMutex; DirEntryList _entries; - mutable std::mutex _mutex; + mutable std::mutex _entriesAndChangedMutex; bool _changed; DISALLOW_COPY_AND_ASSIGN(DirBlob); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/src/gitversion/_version.py new/src/gitversion/_version.py --- old/src/gitversion/_version.py 2018-02-05 04:16:25.000000000 +0100 +++ new/src/gitversion/_version.py 2019-01-21 04:39:01.000000000 +0100 @@ -23,8 +23,8 @@ # setup.py/versioneer.py will grep for the variable names, so they must # each be defined on a line of their own. _version.py will just call # get_keywords(). - git_refnames = " (HEAD -> develop, tag: 0.9.9, origin/develop, origin/HEAD)" - git_full = "fb792ec353a2fb93f7a61566e1d042fa7811f2a4" + git_refnames = " (HEAD -> release/0.9, tag: 0.9.10)" + git_full = "86600e725345b7864be2ca3c394d682996dc0814" keywords = {"refnames": git_refnames, "full": git_full} return keywords diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/blobstore/implementations/onblocks/BlobReadWriteTest.cpp new/test/blobstore/implementations/onblocks/BlobReadWriteTest.cpp --- old/test/blobstore/implementations/onblocks/BlobReadWriteTest.cpp 2018-02-05 04:16:25.000000000 +0100 +++ new/test/blobstore/implementations/onblocks/BlobReadWriteTest.cpp 2019-01-21 04:39:01.000000000 +0100 @@ -63,6 +63,92 @@ EXPECT_EQ(32780u, blob->size()); } +TEST_F(BlobReadWriteTest, givenEmptyBlob_whenTryReadInFirstLeaf_thenFails) { + Data data(5); + size_t read = blob->tryRead(data.data(), 3, 5); + EXPECT_EQ(0, read); +} + +TEST_F(BlobReadWriteTest, givenEmptyBlob_whenTryReadInLaterLeaf_thenFails) { + Data data(5); + size_t read = blob->tryRead(data.data(), 2*LAYOUT.maxBytesPerLeaf(), 5); + EXPECT_EQ(0, read); +} + +TEST_F(BlobReadWriteTest, givenEmptyBlob_whenReadInFirstLeaf_thenFails) { + Data data(5); + EXPECT_ANY_THROW( + blob->read(data.data(), 3, 5) + ); +} + +TEST_F(BlobReadWriteTest, givenEmptyBlob_whenReadInLaterLeaf_thenFails) { + Data data(5); + EXPECT_ANY_THROW( + blob->read(data.data(), 2*LAYOUT.maxBytesPerLeaf(), 5) + ); +} + +TEST_F(BlobReadWriteTest, givenEmptyBlob_whenReadAll_thenReturnsZeroSizedData) { + Data data = blob->readAll(); + EXPECT_EQ(0, data.size()); +} + +TEST_F(BlobReadWriteTest, givenEmptyBlob_whenWrite_thenGrows) { + Data data(5); + blob->write(data.data(), 4, 5); + EXPECT_EQ(9, blob->size()); +} + +TEST_F(BlobReadWriteTest, givenEmptyBlob_whenWriteZeroBytes_thenDoesntGrow) { + Data data(5); + blob->write(data.data(), 4, 0); + EXPECT_EQ(0, blob->size());; +} + +TEST_F(BlobReadWriteTest, givenBlobResizedToZero_whenTryReadInFirstLeaf_thenFails) { + Data data(5); + size_t read = blob->tryRead(data.data(), 3, 5); + EXPECT_EQ(0, read); +} + +TEST_F(BlobReadWriteTest, givenBlobResizedToZero_whenTryReadInLaterLeaf_thenFails) { + Data data(5); + size_t read = blob->tryRead(data.data(), 2*LAYOUT.maxBytesPerLeaf(), 5); + EXPECT_EQ(0, read); +} + +TEST_F(BlobReadWriteTest, givenBlobResizedToZero_whenReadInFirstLeaf_thenFails) { + Data data(5); + EXPECT_ANY_THROW( + blob->read(data.data(), 3, 5) + ); +} + +TEST_F(BlobReadWriteTest, givenBlobResizedToZero_whenReadInLaterLeaf_thenFails) { + Data data(5); + EXPECT_ANY_THROW( + blob->read(data.data(), 2*LAYOUT.maxBytesPerLeaf(), 5) + ); +} + +TEST_F(BlobReadWriteTest, givenBlobResizedToZero_whenReadAll_thenReturnsZeroSizedData) { + Data data = blob->readAll(); + EXPECT_EQ(0, data.size()); +} + +TEST_F(BlobReadWriteTest, givenBlobResizedToZero_whenWrite_thenGrows) { + Data data(5); + blob->write(data.data(), 4, 5); + EXPECT_EQ(9, blob->size()); +} + +TEST_F(BlobReadWriteTest, givenBlobResizedToZero_whenWriteZeroBytes_thenDoesntGrow) { + Data data(5); + blob->write(data.data(), 4, 0); + EXPECT_EQ(0, blob->size()); +} + struct DataRange { size_t blobsize; off_t offset;
