diff --git a/libs/middletier/MockMiddleTierUser.cpp b/libs/middletier/MockMiddleTierUser.cpp index a061890be028fbb3f54f216d7adde4fc0a91bbc5..c2b37531d8e9fa8c6601f129afb3af0fedb34616 100644 --- a/libs/middletier/MockMiddleTierUser.cpp +++ b/libs/middletier/MockMiddleTierUser.cpp @@ -256,6 +256,37 @@ void cta::MockMiddleTierUser::archiveToDirectory( FileSystemNode &dstDirNode = getFileSystemNode(dstDir); checkUserIsAuthorisedToArchive(requester, dstDirNode); + + const std::string inheritedStorageClassName = dstDirNode.getFileSystemEntry(). + getEntry().getStorageClassName(); + const std::list<std::string> dstFileNames = Utils::getEnclosedNames(srcUrls); + checkDirNodeDoesNotContainFiles(dstDir, dstDirNode, dstFileNames); + + for(std::list<std::string>::const_iterator itor = dstFileNames.begin(); + itor != dstFileNames.end(); itor++) { + const std::string &dstFileName = *itor; + DirectoryEntry dirEntry(DirectoryEntry::ENTRYTYPE_FILE, dstFileName, + inheritedStorageClassName); + dstDirNode.addChild(new FileSystemNode(m_db.storageClasses, dirEntry)); + } +} + +//------------------------------------------------------------------------------ +// checkDirNodeDoesNotContainFiles +//------------------------------------------------------------------------------ +void cta::MockMiddleTierUser::checkDirNodeDoesNotContainFiles( + const std::string &dirPath, + const FileSystemNode &dirNode, + const std::list<std::string> &fileNames) { + for(std::list<std::string>::const_iterator itor = fileNames.begin(); + itor != fileNames.end(); itor++) { + const std::string &fileName = *itor; + if(dirNode.childExists(fileName)) { + std::ostringstream message; + message << dirPath << fileName << " already exists"; + throw(Exception(message.str())); + } + } } //------------------------------------------------------------------------------ diff --git a/libs/middletier/MockMiddleTierUser.hpp b/libs/middletier/MockMiddleTierUser.hpp index f355cf7d856922ef7dfa42803346b6a20c2453a7..12ce3a08ec6ae5c75cc321627df74319db2913c5 100644 --- a/libs/middletier/MockMiddleTierUser.hpp +++ b/libs/middletier/MockMiddleTierUser.hpp @@ -305,6 +305,19 @@ private: const SecurityIdentity &user, const FileSystemNode &dstDir); + /** + * Throws an exception if at least one of the the specified file names are + * contained within the specified directory. + * + * @param dirPath The absolute path of the directory. + * @param dirNode The file-system node representing the directory. + * @param fileNames The file names to be searched for. + */ +void checkDirNodeDoesNotContainFiles( + const std::string &dirPath, + const FileSystemNode &dirNode, + const std::list<std::string> &fileNames); + }; // class MockMiddleTierUser } // namespace cta diff --git a/libs/middletier/MockMiddleTierUserTest.cpp b/libs/middletier/MockMiddleTierUserTest.cpp index 84ac883f90415c13f6d316c7b8fb28c24a858242..c5b728f4839cd5fdea2de0cb490d2608c1fa4495 100644 --- a/libs/middletier/MockMiddleTierUserTest.cpp +++ b/libs/middletier/MockMiddleTierUserTest.cpp @@ -2,6 +2,7 @@ #include "MockMiddleTierUser.hpp" #include <gtest/gtest.h> +#include <set> namespace unitTests { @@ -477,7 +478,7 @@ TEST_F(cta_client_MockMiddleTierUserTest, ASSERT_NO_THROW(adminApi.deleteStorageClass(requester, storageClassName)); } -TEST_F(cta_client_MockMiddleTierUserTest, archive_new_file) { +TEST_F(cta_client_MockMiddleTierUserTest, archive_to_new_file) { using namespace cta; MockDatabase db; @@ -543,4 +544,70 @@ TEST_F(cta_client_MockMiddleTierUserTest, archive_new_file) { } } +TEST_F(cta_client_MockMiddleTierUserTest, archive_to_directory) { + using namespace cta; + + MockDatabase db; + MockMiddleTierAdmin adminApi(db); + MockMiddleTierUser userApi(db); + const SecurityIdentity requester; + + const std::string storageClassName = "TestStorageClass"; + const uint8_t nbCopies = 1; + const std::string storageClassComment = "Storage-class omment"; + ASSERT_NO_THROW(adminApi.createStorageClass(requester, storageClassName, + nbCopies, storageClassComment)); + + const std::string dirPath = "/grandparent"; + ASSERT_NO_THROW(userApi.createDirectory(requester, dirPath)); + ASSERT_NO_THROW(userApi.setDirectoryStorageClass(requester, dirPath, + storageClassName)); + + const std::string tapePoolName = "TestTapePool"; + const uint16_t nbDrives = 1; + const uint16_t nbPartialTapes = 1; + const std::string tapePoolComment = "Tape-pool comment"; + ASSERT_NO_THROW(adminApi.createTapePool(requester, tapePoolName, nbDrives, + nbPartialTapes, tapePoolComment)); + + const uint8_t copyNb = 1; + const std::string migrationRouteComment = "Migration-route comment"; + ASSERT_NO_THROW(adminApi.createMigrationRoute(requester, storageClassName, + copyNb, tapePoolName, migrationRouteComment)); + + std::list<std::string> srcUrls; + srcUrls.push_back("diskUrl1"); + srcUrls.push_back("diskUrl2"); + srcUrls.push_back("diskUrl3"); + srcUrls.push_back("diskUrl4"); + const std::string dstPath = "/grandparent"; + ASSERT_NO_THROW(userApi.archive(requester, srcUrls, dstPath)); + + { + DirectoryIterator itor; + ASSERT_NO_THROW(itor = userApi.getDirectoryContents(requester, "/")); + ASSERT_TRUE(itor.hasMore()); + DirectoryEntry entry; + ASSERT_NO_THROW(entry = itor.next()); + ASSERT_EQ(std::string("grandparent"), entry.getName()); + ASSERT_EQ(DirectoryEntry::ENTRYTYPE_DIRECTORY, entry.getType()); + ASSERT_EQ(storageClassName, entry.getStorageClassName()); + } + + { + std::set<std::string> archiveFileNames; + DirectoryIterator itor; + ASSERT_NO_THROW(itor = userApi.getDirectoryContents(requester, "/grandparent")); + while(itor.hasMore()) { + const DirectoryEntry entry = itor.next(); + archiveFileNames.insert(entry.getName()); + } + ASSERT_EQ(4, archiveFileNames.size()); + ASSERT_TRUE(archiveFileNames.find("diskUrl1") != archiveFileNames.end()); + ASSERT_TRUE(archiveFileNames.find("diskUrl2") != archiveFileNames.end()); + ASSERT_TRUE(archiveFileNames.find("diskUrl3") != archiveFileNames.end()); + ASSERT_TRUE(archiveFileNames.find("diskUrl4") != archiveFileNames.end()); + } +} + } // namespace unitTests diff --git a/libs/middletier/Utils.cpp b/libs/middletier/Utils.cpp index e992aebcee409d511499a154ddfd659d21b25162..fec9cece5e1f44be268721c7b3edbbc818e1f04d 100644 --- a/libs/middletier/Utils.cpp +++ b/libs/middletier/Utils.cpp @@ -112,6 +112,21 @@ std::string cta::Utils::getEnclosedName(const std::string &path) { } } +//----------------------------------------------------------------------------- +// getEnclosedNames +//----------------------------------------------------------------------------- +std::list<std::string> cta::Utils::getEnclosedNames( + const std::list<std::string> &paths) { + std::list<std::string> names; + + for(std::list<std::string>::const_iterator itor = paths.begin(); + itor != paths.end(); itor++) { + names.push_back(getEnclosedName(*itor)); + } + + return names; +} + //----------------------------------------------------------------------------- // trimSlashes //----------------------------------------------------------------------------- diff --git a/libs/middletier/Utils.hpp b/libs/middletier/Utils.hpp index eca7a062f451da7c159d3abd8d8876244bbf6483..6b229b74e27695ab0840eec4a438eaaf506072bf 100644 --- a/libs/middletier/Utils.hpp +++ b/libs/middletier/Utils.hpp @@ -1,5 +1,6 @@ #pragma once +#include <list> #include <string> #include <vector> @@ -41,6 +42,17 @@ public: */ static std::string getEnclosedName(const std::string &path); + /** + * Returns the names of the enclosed file or directory of each of the + * specified paths. + * + * @param paths The path + * @return The names of the enclosed file or directory of each of the + * specified paths. + */ + static std::list<std::string> getEnclosedNames( + const std::list<std::string> &paths); + /** * Returns the result of trimming both left and right slashes from the * specified string. diff --git a/libs/middletier/UtilsTest.cpp b/libs/middletier/UtilsTest.cpp index 700fb3304a11e8a4e63b3ea069a2e6e83649a518..37ca0c54f8183cb6e96bf55f37de38791a8d49a3 100644 --- a/libs/middletier/UtilsTest.cpp +++ b/libs/middletier/UtilsTest.cpp @@ -130,6 +130,45 @@ TEST_F(cta_client_UtilsTest, ASSERT_EQ(std::string("/grandparent/parent/"), enclosingDirPath); } +TEST_F(cta_client_UtilsTest, getEnclosedName) { + using namespace cta; + + const std::string enclosingDirPath = "/grandparent/parent/"; + const std::string enclosedName = "child"; + const std::string absoluteFilePath = enclosingDirPath + enclosedName; + std::string result; + ASSERT_NO_THROW(result = Utils::getEnclosedName(absoluteFilePath)); + ASSERT_EQ(enclosedName, result); +} + +TEST_F(cta_client_UtilsTest, getEnclosedNames) { + using namespace cta; + + const std::string enclosingDirPath = "/grandparent/parent/"; + const std::string enclosedName1 = "child1"; + const std::string enclosedName2 = "child2"; + const std::string enclosedName3 = "child3"; + const std::string enclosedName4 = "child4"; + std::list<std::string> absoluteFilePaths; + absoluteFilePaths.push_back(enclosingDirPath + enclosedName1); + absoluteFilePaths.push_back(enclosingDirPath + enclosedName2); + absoluteFilePaths.push_back(enclosingDirPath + enclosedName3); + absoluteFilePaths.push_back(enclosingDirPath + enclosedName4); + std::list<std::string> results; + ASSERT_NO_THROW(results = Utils::getEnclosedNames(absoluteFilePaths)); + ASSERT_EQ(4, results.size()); + std::set<std::string> resultSet; + for(std::list<std::string>::const_iterator itor = results.begin(); + itor != results.end(); itor++) { + resultSet.insert(*itor); + } + ASSERT_EQ(4, resultSet.size()); + ASSERT_FALSE(resultSet.find(enclosedName1) == resultSet.end()); + ASSERT_FALSE(resultSet.find(enclosedName2) == resultSet.end()); + ASSERT_FALSE(resultSet.find(enclosedName3) == resultSet.end()); + ASSERT_FALSE(resultSet.find(enclosedName4) == resultSet.end()); +} + TEST_F(cta_client_UtilsTest, splitString_goodDay) { using namespace cta; const std::string line("col0 col1 col2 col3 col4 col5 col6 col7");