diff --git a/dune/fufem/hdf5/file.hh b/dune/fufem/hdf5/file.hh index a6dc21c454beb18534632d2c758e6a42f8b76312..c7c1b62e4ab4bc8c9b8241fbcaea5b7b4d4f603d 100644 --- a/dune/fufem/hdf5/file.hh +++ b/dune/fufem/hdf5/file.hh @@ -27,11 +27,15 @@ public: } virtual hid_t c_obj() = 0; + virtual bool isReadOnly() const = 0; }; +enum class Access { READONLY, READWRITE }; + class File : public Grouplike { public: - File(std::string filename, bool swmr = false) { + File(std::string filename, Access access = Access::READWRITE) + : readOnly_(access == Access::READONLY) { struct stat buffer; /* Use the latest HDF5 format. @@ -44,29 +48,27 @@ public: H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST); bool const fileExists = stat(filename.c_str(), &buffer) == 0; - - /* - Files are currently opened for reading and writing. This seems - reasonable for now because C++ is far more likely to be involved - in the generation of HDF5 data than in their processing. - - Given how easy it is to corrupt HDF5 data through unexpected - process termination, however, it might make sense to allow - read-only access in the future - */ - unsigned int flags = fileExists ? H5F_ACC_RDWR : H5F_ACC_EXCL; - if (swmr) { + bool const createFile = !fileExists; + if (createFile && readOnly_) + DUNE_THROW( + Dune::Exception, + "Tried to open non-existing file in read-only mode: " << filename); + + // If createFile is true: We already know the file does not exist, + // so H5F_ACC_EXCL and H5F_ACC_TRUNC are essentially equivalent + unsigned int flags = + createFile ? H5F_ACC_EXCL : (readOnly_ ? H5F_ACC_RDONLY : H5F_ACC_RDWR); #if H5_VERSION_GE(1, 10, 0) - flags |= H5F_ACC_SWMR_WRITE; -#else - std::cerr - << "WARNING: SWMR requested but not supported by this version of HDF5" - << std::endl; + // Enabling SWMR reading by default on HDF-1.10 does not hurt + // (except maybe performance). In contrast, enabling SWMR writing + // produces a file that is not readable by HDF5-1.8. The user can + // still enable SWMR writing later through swmrEnable(). + if (readOnly_) + flags |= H5F_ACC_SWMR_READ; #endif - } - file_ = fileExists ? H5Fopen(filename.c_str(), flags, fapl) - : H5Fcreate(filename.c_str(), flags, H5P_DEFAULT, fapl); + file_ = createFile ? H5Fcreate(filename.c_str(), flags, H5P_DEFAULT, fapl) + : H5Fopen(filename.c_str(), flags, fapl); if (file_ < 0) DUNE_THROW(Dune::Exception, "Unable to open file: " << filename); } @@ -76,15 +78,20 @@ public: ~File() { H5Fclose(file_); } hid_t c_obj() override { return file_; } - + bool isReadOnly() const override { return readOnly_; } bool swmrEnabled() { return swmrEnabled_; } void swmrEnable() { if (swmrEnabled_) return; #if H5_VERSION_GE(1, 10, 0) - swmrEnabled_ = true; - H5Fstart_swmr_write(file_); + if (!readOnly_) { + swmrEnabled_ = true; + H5Fstart_swmr_write(file_); + } else { + std::cerr << "WARNING: SWMR writing requested for a read-only file" + << std::endl; + } #else std::cerr << "WARNING: SWMR requested but not supported by this version of HDF5" @@ -94,6 +101,7 @@ public: private: hid_t file_; + bool const readOnly_; bool swmrEnabled_ = false; }; @@ -109,6 +117,7 @@ public: ~Group() { H5Gclose(group_); } hid_t c_obj() override { return group_; } + bool isReadOnly() const override { return parent_.isReadOnly(); } private: Grouplike &parent_; diff --git a/dune/fufem/hdf5/sequenceio.hh b/dune/fufem/hdf5/sequenceio.hh index f96341fb012c27aa96314b5f36dc27011ee7ee11..dc8e436f538782d7759495492f9bc96ae7c202d0 100644 --- a/dune/fufem/hdf5/sequenceio.hh +++ b/dune/fufem/hdf5/sequenceio.hh @@ -36,6 +36,9 @@ public: checkDimensions(); checkChunkSize(); } else { + if (file_.isReadOnly()) + DUNE_THROW(Dune::Exception, "Dataset not found: " << datasetname); + auto const initial_dims = maxExtent(0); auto const max_dims = maxExtent(H5S_UNLIMITED); hid_t file_space = diff --git a/dune/fufem/hdf5/singletonwriter.hh b/dune/fufem/hdf5/singletonwriter.hh index c82c6a0405bf4930fe8e015a387e4266e30acd11..b1533e80a6fb7c4350f4baccca4e4179ca1e2400 100644 --- a/dune/fufem/hdf5/singletonwriter.hh +++ b/dune/fufem/hdf5/singletonwriter.hh @@ -25,6 +25,8 @@ public: : file_(file), capacity_{{T(args)...}} { static_assert(sizeof...(args) == spatialDimensions, "wrong number of arguments"); + if (file_.isReadOnly()) + DUNE_THROW(Dune::Exception, "Cannot handle read-only file"); if (file_.hasDataset(datasetname)) { dset_ = file_.openDataset(datasetname); diff --git a/dune/fufem/test/test-hdf5.cc b/dune/fufem/test/test-hdf5.cc index 6f9a36557cdf4816ad14b003049104595df1456e..66746939668b28e37becf1060f5a25b60c866362 100644 --- a/dune/fufem/test/test-hdf5.cc +++ b/dune/fufem/test/test-hdf5.cc @@ -23,6 +23,44 @@ void deleteIfNecessary(std::string filename) { } } +template <class Vector> +bool checkMissingFile(Vector const &written0) { + bool passed = true; + using ft = typename Vector::block_type::field_type; + std::string const filename("no-such-file.h5"); + try { + deleteIfNecessary(filename); + HDF5::File file(filename.c_str(), HDF5::Access::READONLY); + HDF5::SequenceIO<2, ft> h5writer(file, "data", 2, 3); + addEntry(h5writer, 0, written0); + } catch (Dune::Exception e) { + } // Expecting an exception here! + return passed; +} + +template <class Vector> +bool checkMissingDataset(Vector const &written0) { + bool passed = true; + using ft = typename Vector::block_type::field_type; + std::string const filename("out-missing.h5"); + { + deleteIfNecessary(filename); + HDF5::File file(filename.c_str()); + HDF5::SequenceIO<2, ft> h5writer(file, "data", 2, 3); + addEntry(h5writer, 0, written0); + } + + Vector read0; + try { + HDF5::File file(filename.c_str(), HDF5::Access::READONLY); + HDF5::SequenceIO<2, ft> h5reader(file, "dataX", 2, 3); + readEntry(h5reader, 0, read0); + passed = false; + } catch (Dune::Exception e) { + } // Expecting an exception here! + return passed; +} + template <class Vector> bool checkAddSequence(Vector const &written0, Vector const &written1) { bool passed = true; @@ -42,7 +80,7 @@ bool checkAddSequence(Vector const &written0, Vector const &written1) { Vector read0; Vector read1; { - HDF5::File file(filename.c_str()); + HDF5::File file(filename.c_str(), HDF5::Access::READONLY); HDF5::SequenceIO<2, ft> h5reader(file, "data", 2, 3); readEntry(h5reader, 0, read0); readEntry(h5reader, 1, read1); @@ -122,10 +160,13 @@ bool testParameters() { deleteIfNecessary(filename); HDF5::File file(filename); + std::cout << "Content of dataset:" << std::endl + << "===============" << std::endl; Dune::ParameterTree parset; parset["a"] = "3"; parset["c.d"] = "something"; parset.report(); + std::cout << "===============" << std::endl; traverseParset(parset, "", [&](std::string key, std::string value, std::string prefix) { @@ -153,9 +194,10 @@ int main(int argc, char *argv[]) { }; Vector b1 = {{0, 10, 20}, {30, 40, 50}}; - bool passed = true; passed = passed and testParameters(); + passed = passed and checkMissingFile(b0); + passed = passed and checkMissingDataset(b0); passed = passed and checkAddSequence(b0, b1); #if H5_VERSION_GE(1, 10, 0) passed = passed and checkAppendSequence(b0, b1);