diff --git a/include/h5pp/details/h5ppFile.h b/include/h5pp/details/h5ppFile.h index 0c6b1ae4..6abb7258 100644 --- a/include/h5pp/details/h5ppFile.h +++ b/include/h5pp/details/h5ppFile.h @@ -730,10 +730,6 @@ namespace h5pp { return dsetInfo; } - void writeSymbolicLink(std::string_view src_path, std::string_view tgt_path) { - h5pp::hdf5::writeSymbolicLink(openFileHandle(), src_path, tgt_path, std::nullopt, plists); - } - template>> void readDataset(DataType &data, DataInfo &dataInfo, const DsetInfo &dsetInfo) const { h5pp::hdf5::resizeData(data, dataInfo, dsetInfo); @@ -1372,6 +1368,39 @@ namespace h5pp { return data; } + /* + * + * + * Functions for creating hard/soft/external links + * + * + */ + + void createSoftLink(std::string_view targetLinkPath, std::string_view softLinkPath) { + h5pp::hdf5::createSoftLink(targetLinkPath, openFileHandle(), softLinkPath, plists); + } + + void createHardLink(std::string_view targetLinkPath, std::string_view hardLinkPath) { + h5pp::hdf5::createHardLink(openFileHandle(), targetLinkPath, openFileHandle(), hardLinkPath, plists); + } + + void createExternalLink(std::string_view targetFilePath, /*!< Path to an external hdf5 file with the desired link. If relative, it is relative to the current file */ + std::string_view targetLinkPath, /*!< Full path to link within the external file */ + std::string_view softLinkPath /*!< Full path to the new soft link created within this file */ + ) { + // The given targetFilePath is written as-is into the TARGETFILE property of the new esternal link. + // Therefore it is important that it is written either as: + // 1: a path relative to the current file, and not relative to the current process, or + // 2: a full path + if(fs::path(targetFilePath).is_relative()){ + auto prox = fs::proximate(targetFilePath, filePath); + if(prox != targetFilePath) h5pp::logger::log->debug("External link [{}] is not relative to the current file [{}]." + "This can cause a dangling soft link"); + } + h5pp::hdf5::createExternalLink(targetFilePath, targetLinkPath, openFileHandle(), softLinkPath, plists); + } + + /* * * diff --git a/include/h5pp/details/h5ppHdf5.h b/include/h5pp/details/h5ppHdf5.h index 8dfc11f8..f6196f56 100644 --- a/include/h5pp/details/h5ppHdf5.h +++ b/include/h5pp/details/h5ppHdf5.h @@ -1,4 +1,5 @@ #pragma once +#include "h5ppDebug.h" #include "h5ppEigen.h" #include "h5ppEnums.h" #include "h5ppFilesystem.h" @@ -28,7 +29,7 @@ namespace h5pp::hdf5 { // [3]: this/is/a/long // [4]: this/is/a/long/path - // It is very important to note that the resulting views are not null terminate. Therefore, these vector elements + // It is very important to note that the resulting views are not null terminated. Therefore, these vector elements // **must not** be used as c-style arrays using their .data() member functions. std::vector output; size_t currentPosition = 0; @@ -970,25 +971,78 @@ namespace h5pp::hdf5 { } template - inline void writeSymbolicLink(const h5x &loc, - std::string_view srcPath, - std::string_view tgtPath, - std::optional linkExists = std::nullopt, - const PropertyLists &plists = PropertyLists()) { + inline void createSoftLink(std::string_view targetLinkPath, + const h5x &loc, + std::string_view softLinkPath, + const PropertyLists &plists = PropertyLists()) { static_assert(h5pp::type::sfinae::is_h5_loc_or_hid_v, - "Template function [h5pp::hdf5::writeSymbolicLink(const h5x & loc, ...)] requires type h5x to be: " + "Template function [h5pp::hdf5::createSoftLink(const h5x & loc, ...)] requires type h5x to be: " "[h5pp::hid::h5f], [h5pp::hid::h5g], [h5pp::hid::h5o] or [hid_t]"); - if(not linkExists) linkExists = checkIfLinkExists(loc, srcPath, plists.linkAccess); - if(not linkExists.value()) { - h5pp::logger::log->trace("Creating symbolic link [{}] --> [{}]", srcPath, tgtPath); - herr_t retval = - H5Lcreate_soft(util::safe_str(srcPath).c_str(), loc, util::safe_str(tgtPath).c_str(), plists.linkCreate, plists.linkAccess); - if(retval < 0) { - H5Eprint(H5E_DEFAULT, stderr); - throw std::runtime_error(h5pp::format("Failed to write symbolic link [{}] ", srcPath)); - } - } else { - throw std::runtime_error(h5pp::format("Tried to write soft link to non-existing path [{}]", srcPath)); + if constexpr(not h5pp::ndebug) { + if(not checkIfLinkExists(loc, targetLinkPath, plists.linkAccess)) + throw std::runtime_error(h5pp::format("Tried to create soft link to a path that does not exist [{}]", targetLinkPath)); + } + + h5pp::logger::log->trace("Creating soft link [{}] --> [{}]", targetLinkPath, softLinkPath); + herr_t retval = H5Lcreate_soft(util::safe_str(targetLinkPath).c_str(), + loc, + util::safe_str(softLinkPath).c_str(), + plists.linkCreate, + plists.linkAccess); + if(retval < 0) { + H5Eprint(H5E_DEFAULT, stderr); + throw std::runtime_error(h5pp::format("Failed to create soft link [{}] ", targetLinkPath)); + } + } + + template + inline void createHardLink( + const h5x &targetLinkLoc, + std::string_view targetLinkPath, + const h5x &hardLinkLoc, + std::string_view hardLinkPath, + const PropertyLists &plists = PropertyLists()) { + static_assert(h5pp::type::sfinae::is_h5_loc_or_hid_v, + "Template function [h5pp::hdf5::createHardLink(const h5x & loc, ...)] requires type h5x to be: " + "[h5pp::hid::h5f], [h5pp::hid::h5g], [h5pp::hid::h5o] or [hid_t]"); + if constexpr(not h5pp::ndebug) { + if(not checkIfLinkExists(targetLinkLoc, targetLinkPath, plists.linkAccess)) + throw std::runtime_error(h5pp::format("Tried to create a hard link to a path that does not exist [{}]", targetLinkPath)); + } + h5pp::logger::log->trace("Creating hard link [{}] --> [{}]", targetLinkPath, hardLinkPath); + herr_t retval = H5Lcreate_hard(targetLinkLoc, + util::safe_str(targetLinkPath).c_str(), + hardLinkLoc, + util::safe_str(hardLinkPath).c_str(), + plists.linkCreate, + plists.linkAccess); + if(retval < 0) { + H5Eprint(H5E_DEFAULT, stderr); + throw std::runtime_error(h5pp::format("Failed to create hard link [{}] -> [{}] ", targetLinkPath, hardLinkPath)); + } + } + + template + void createExternalLink(std::string_view targetFilePath, + std::string_view targetLinkPath, + const h5x &loc, + std::string_view softLinkPath, + const PropertyLists &plists = PropertyLists()) { + static_assert(h5pp::type::sfinae::is_h5_loc_or_hid_v, + "Template function [h5pp::hdf5::createExternalLink(const h5x & loc, ...)] requires type h5x to be: " + "[h5pp::hid::h5f], [h5pp::hid::h5g], [h5pp::hid::h5o] or [hid_t]"); + h5pp::logger::log->trace("Creating external link [{}] from file [{}] : [{}]", softLinkPath, targetFilePath, targetLinkPath); + + herr_t retval = H5Lcreate_external(util::safe_str(targetFilePath).c_str(), + util::safe_str(targetLinkPath).c_str(), + loc, + util::safe_str(softLinkPath).c_str(), + plists.linkCreate, + plists.linkAccess); + + if(retval < 0) { + H5Eprint(H5E_DEFAULT, stderr); + throw std::runtime_error(h5pp::format("Failed to create external link [{}] --> [{}]", targetLinkPath, softLinkPath)); } } @@ -1808,7 +1862,7 @@ namespace h5pp::hdf5 { maxHits, maxDepth); - if (not checkIfLinkExists(loc,searchRoot,linkAccess)){ + if(not checkIfLinkExists(loc, searchRoot, linkAccess)) { H5Eprint(H5E_DEFAULT, stderr); throw std::runtime_error(h5pp::format("Cannot find links inside group [{}]: it does not exist", searchRoot)); } @@ -1820,10 +1874,11 @@ namespace h5pp::hdf5 { herr_t err = internal::visit_by_name(loc, searchRoot, matchList, linkAccess); if(err < 0) { H5Eprint(H5E_DEFAULT, stderr); - throw std::runtime_error(h5pp::format("Error occurred when trying to find links of type [{}] containing [{}] while iterating from root [{}]", - internal::getObjTypeName(), - searchKey, - searchRoot)); + throw std::runtime_error( + h5pp::format("Error occurred when trying to find links of type [{}] containing [{}] while iterating from root [{}]", + internal::getObjTypeName(), + searchKey, + searchRoot)); } return matchList; } diff --git a/tests/test-externalLinks.cpp b/tests/test-externalLinks.cpp new file mode 100644 index 00000000..88c49845 --- /dev/null +++ b/tests/test-externalLinks.cpp @@ -0,0 +1,31 @@ +#define CATCH_CONFIG_RUNNER +#include "catch.hpp" +#include + +TEST_CASE("Test adding external links from other another file", "[External Link]") { + SECTION("Create an external file A"){ + h5pp::File fileA("output/externalLink-A.h5", h5pp::FilePermission::REPLACE, 2); + fileA.writeDataset(42.0, "dsetA"); + } + SECTION("Add external link to file B") { + h5pp::File fileB("output/externalLink-B.h5", h5pp::FilePermission::REPLACE, 2); + fileB.createExternalLink("externalLink-A.h5", "dsetA", "dsetA"); + REQUIRE(fileB.readDataset("dsetA") == 42.0); + } +} + + + + + + +int main(int argc, char *argv[]) { + + Catch::Session session; // There must be exactly one instance + int returnCode = session.applyCommandLine(argc, argv); + if(returnCode != 0) // Indicates a command line error + return returnCode; + // session.configData().showSuccessfulTests = true; + // session.configData().reporterName = "compact"; + return session.run(); +} \ No newline at end of file