A DBUS API to move the files between containers 67/23767/10
authorLukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
Thu, 12 Jun 2014 08:12:41 +0000 (10:12 +0200)
committerLukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
Fri, 4 Jul 2014 14:20:43 +0000 (16:20 +0200)
[Bug/Feature]   Add an ability for a container to request a file move to another container.
                Some minor fixes here and there.
                Added missing tests for utils/fs.
                A little revamp of DbusAccessory in ut-cm tests.
[Cause]         N/A
[Solution]      Implement a DBUS API and a simple move implementation.
[Verification]  Build, install, run tests, run server.

Change-Id: I881f7b6079e38e3dd43d6fe34360457172047c2c
Signed-off-by: Lukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
51 files changed:
common/config.hpp
common/utils/fs.cpp
common/utils/fs.hpp
container-daemon/CMakeLists.txt
packaging/security-containers.spec
server/CMakeLists.txt
server/configs/containers/business.conf
server/configs/containers/private.conf
server/configs/daemon.conf
server/configs/libvirt-config/business.xml
server/configs/libvirt-config/private.xml
server/container-config.hpp
server/container-connection.cpp
server/container-connection.hpp
server/container-dbus-definitions.hpp
server/container.cpp
server/container.hpp
server/containers-manager-config.hpp
server/containers-manager.cpp
server/containers-manager.hpp
tests/unit_tests/server/configs/ut-container-admin/containers/buggy.conf.in
tests/unit_tests/server/configs/ut-container-admin/containers/missing.conf
tests/unit_tests/server/configs/ut-container-admin/containers/test-no-shutdown.conf.in
tests/unit_tests/server/configs/ut-container-admin/containers/test.conf.in
tests/unit_tests/server/configs/ut-container/containers/buggy.conf
tests/unit_tests/server/configs/ut-container/containers/test-dbus.conf
tests/unit_tests/server/configs/ut-container/containers/test.conf
tests/unit_tests/server/configs/ut-containers-manager/buggy-daemon.conf
tests/unit_tests/server/configs/ut-containers-manager/buggy-default-daemon.conf
tests/unit_tests/server/configs/ut-containers-manager/buggy-foreground-daemon.conf
tests/unit_tests/server/configs/ut-containers-manager/containers/console1-dbus.conf
tests/unit_tests/server/configs/ut-containers-manager/containers/console1.conf
tests/unit_tests/server/configs/ut-containers-manager/containers/console2-dbus.conf
tests/unit_tests/server/configs/ut-containers-manager/containers/console2.conf
tests/unit_tests/server/configs/ut-containers-manager/containers/console3-dbus.conf
tests/unit_tests/server/configs/ut-containers-manager/containers/console3.conf
tests/unit_tests/server/configs/ut-containers-manager/libvirt-config/console1-dbus.xml.in
tests/unit_tests/server/configs/ut-containers-manager/libvirt-config/console2-dbus.xml.in
tests/unit_tests/server/configs/ut-containers-manager/libvirt-config/console3-dbus.xml.in
tests/unit_tests/server/configs/ut-containers-manager/test-daemon.conf
tests/unit_tests/server/configs/ut-containers-manager/test-dbus-daemon.conf
tests/unit_tests/server/configs/ut-network-admin/containers/buggy.conf.in
tests/unit_tests/server/configs/ut-network-admin/containers/missing.conf
tests/unit_tests/server/configs/ut-network-admin/containers/test.conf.in
tests/unit_tests/server/configs/ut-server/buggy-daemon.conf
tests/unit_tests/server/configs/ut-server/containers/container1.conf
tests/unit_tests/server/configs/ut-server/containers/container2.conf
tests/unit_tests/server/configs/ut-server/containers/container3.conf
tests/unit_tests/server/configs/ut-server/test-daemon.conf
tests/unit_tests/server/ut-containers-manager.cpp
tests/unit_tests/utils/ut-fs.cpp

index 2ed11a6..0721de4 100644 (file)
 #define BOOST_PP_VARIADICS 1
 #endif
 
+// This has to be defined always when the boost has not been compiled
+// using C++11. Headers detect that you are compiling using C++11 and
+// blindly and wrongly assume that boost has been as well.
+#ifndef BOOST_NO_CXX11_SCOPED_ENUMS
+#define BOOST_NO_CXX11_SCOPED_ENUMS 1
+#endif
+
 #endif // COMMON_CONFIG_HPP
index 2bae7d6..421ff54 100644 (file)
@@ -28,6 +28,7 @@
 #include "utils/paths.hpp"
 #include "utils/exception.hpp"
 
+#include <boost/filesystem.hpp>
 #include <dirent.h>
 #include <fstream>
 #include <streambuf>
@@ -135,20 +136,66 @@ bool umount(const std::string& path)
 
 bool isMountPoint(const std::string& path, bool& result)
 {
-    struct stat stat, parentStat;
     std::string parentPath = dirName(path);
+    bool newResult;
+    bool ret = hasSameMountPoint(path, parentPath, newResult);
 
-    if (::stat(path.c_str(), &stat)) {
-        LOGD("Failed to get stat of " << path << ": " << strerror(errno));
+    result = !newResult;
+    return ret;
+}
+
+bool hasSameMountPoint(const std::string& path1, const std::string& path2, bool& result)
+{
+    struct stat s1, s2;
+
+    if (::stat(path1.c_str(), &s1)) {
+        LOGD("Failed to get stat of " << path1 << ": " << strerror(errno));
         return false;
     }
 
-    if (::stat(parentPath.c_str(), &parentStat)) {
-        LOGD("Failed to get stat of " << parentPath << ": " << strerror(errno));
+    if (::stat(path2.c_str(), &s2)) {
+        LOGD("Failed to get stat of " << path2 << ": " << strerror(errno));
         return false;
     }
 
-    result = (stat.st_dev != parentStat.st_dev);
+    result = (s1.st_dev == s2.st_dev);
+    return true;
+}
+
+bool moveFile(const std::string& src, const std::string& dst)
+{
+    bool bResult;
+
+    namespace fs = boost::filesystem;
+    boost::system::error_code error;
+
+    // The destination has to be a full path (including a file name)
+    // so it doesn't exist yet, we need to check upper level dir instead.
+    if (!hasSameMountPoint(src, dirName(dst), bResult)) {
+        LOGE("Failed to check the files' mount points");
+        return false;
+    }
+
+    if (bResult) {
+        fs::rename(src, dst, error);
+        if (error) {
+            LOGE("Failed to rename the file: " << error);
+            return false;
+        }
+    } else {
+        fs::copy_file(src, dst, error);
+        if (error) {
+            LOGE("Failed to copy the file: " << error);
+            return false;
+        }
+        fs::remove(src, error);
+        if (error) {
+            LOGE("Failed to remove the file: " << error);
+            fs::remove(dst, error);
+            return false;
+        }
+    }
+
     return true;
 }
 
index 8312393..9e69cbd 100644 (file)
@@ -73,6 +73,18 @@ bool umount(const std::string& path);
  */
 bool isMountPoint(const std::string& path, bool& result);
 
+/**
+ * Checks whether the given paths are under the same mount point
+ */
+bool hasSameMountPoint(const std::string& path1, const std::string& path2, bool& result);
+
+/**
+ * Moves the file either by rename if under the same mount point
+ * or by copy&delete if under a different one.
+ * The destination has to be a full path including file name.
+ */
+bool moveFile(const std::string& src, const std::string& dst);
+
 
 } // namespace utils
 } // namespace security_containers
index d997ab7..3408f43 100644 (file)
@@ -29,7 +29,7 @@ ADD_EXECUTABLE(${CONTAINER_DAEMON_CODENAME} ${project_SRCS} ${common_SRCS})
 
 
 ## Link libraries ##############################################################
-FIND_PACKAGE (Boost COMPONENTS program_options)
+FIND_PACKAGE (Boost COMPONENTS program_options system filesystem)
 
 PKG_CHECK_MODULES(CONTAINER_DAEMON_DEPS REQUIRED gio-2.0 libsystemd-journal)
 INCLUDE_DIRECTORIES(${COMMON_FOLDER})
index d6bcc2c..3afbdd1 100644 (file)
@@ -8,6 +8,7 @@ License:       Apache-2.0
 Group:         Security/Other
 Summary:       Daemon for managing containers
 BuildRequires: cmake
+BuildRequires: boost-devel
 BuildRequires: libvirt-devel
 BuildRequires: libjson-devel
 BuildRequires: pkgconfig(glib-2.0)
@@ -123,8 +124,6 @@ Development package including the header files for the client library
 Summary:          Security Containers Containers Daemon
 Group:            Security/Other
 Requires:         security-containers = %{version}-%{release}
-BuildRequires:    pkgconfig(glib-2.0)
-BuildRequires:    pkgconfig(libsystemd-journal)
 
 %description container-daemon
 Daemon running inside every container.
@@ -144,7 +143,6 @@ Requires:         security-containers = %{version}-%{release}
 Requires:         security-containers-client = %{version}-%{release}
 Requires:         python
 Requires:         boost-test
-BuildRequires:    boost-devel
 
 %description tests
 Unit tests for both: server and client and integration tests.
index b93b010..595ec72 100644 (file)
@@ -29,6 +29,7 @@ ADD_EXECUTABLE(${SERVER_CODENAME} ${project_SRCS} ${common_SRCS})
 
 ## Link libraries ##############################################################
 FIND_PACKAGE (Boost COMPONENTS program_options system filesystem regex)
+
 PKG_CHECK_MODULES(SERVER_DEPS REQUIRED libvirt libvirt-glib-1.0 json gio-2.0 libsystemd-journal)
 INCLUDE_DIRECTORIES(${COMMON_FOLDER})
 INCLUDE_DIRECTORIES(SYSTEM ${SERVER_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
index 8468e2c..fbd1bbb 100644 (file)
@@ -4,5 +4,7 @@
     "privilege" : 1,
     "config" : "../libvirt-config/business.xml",
     "networkConfig" : "../libvirt-config/business-network.xml",
-    "runMountPoint" : "/var/run/containers/business/run"
+    "runMountPoint" : "/var/run/containers/business/run",
+    "permittedToSend" : [ "/tmp/.*" ],
+    "permittedToRecv" : [ "/tmp/.*" ]
 }
index 444638f..a12a847 100644 (file)
@@ -4,5 +4,7 @@
     "privilege" : 10,
     "config" : "../libvirt-config/private.xml",
     "networkConfig" : "../libvirt-config/private-network.xml",
-    "runMountPoint" : "/var/run/containers/private/run"
+    "runMountPoint" : "/var/run/containers/private/run",
+    "permittedToSend" : [ "/tmp/.*" ],
+    "permittedToRecv" : [ "/tmp/.*" ]
 }
index 591e0b7..94af879 100644 (file)
@@ -1,5 +1,6 @@
 {
     "containerConfigs" : ["containers/private.conf", "containers/business.conf" ],
+    "containersPath" : "/opt/usr/containers",
     "foregroundId" : "private",
     "defaultId" : "private",
     "inputConfig" : {"enabled" : true,
index 85e9a77..deaf000 100644 (file)
       <source network='business'/>
     </interface>
     <filesystem type='mount'>
+      <!-- TODO: this directory could be generated from .conf files-->
       <source dir='/opt/usr/containers/business'/>
       <target dir='/'/>
     </filesystem>
     <!-- Enable access to the containers dbus -->
     <filesystem type='mount'>
+      <!-- TODO: this directory could be generated from .conf files-->
       <source dir='/var/run/containers/business/run'/>
       <target dir='/var/run'/>
     </filesystem>
index 7561bab..532cd76 100644 (file)
       <source network='private'/>
     </interface>
     <filesystem type='mount'>
+      <!-- TODO: this directory could be generated from .conf files-->
       <source dir='/opt/usr/containers/private'/>
       <target dir='/'/>
     </filesystem>
     <!-- Enable access to the containers dbus -->
     <filesystem type='mount'>
+      <!-- TODO: this directory could be generated from .conf files-->
       <source dir='/var/run/containers/private/run'/>
       <target dir='/var/run'/>
     </filesystem>
index 2c12363..d5a3038 100644 (file)
@@ -29,6 +29,7 @@
 #include "config/fields.hpp"
 
 #include <string>
+#include <vector>
 
 
 namespace security_containers {
@@ -67,6 +68,18 @@ struct ContainerConfig {
      */
     std::string runMountPoint;
 
+    /**
+     * When you move a file out of the container (by move request)
+     * its path must match at least one of the regexps in this vector.
+     */
+    std::vector<std::string> permittedToSend;
+
+    /**
+     * When you move a file to the container (by move request)
+     * its path must match at least one of the regexps in this vector.
+     */
+    std::vector<std::string> permittedToRecv;
+
     CONFIG_REGISTER
     (
         privilege,
@@ -74,7 +87,9 @@ struct ContainerConfig {
         networkConfig,
         cpuQuotaForeground,
         cpuQuotaBackground,
-        runMountPoint
+        runMountPoint,
+        permittedToSend,
+        permittedToRecv
     )
 };
 
index cf541f7..3ef2dd7 100644 (file)
@@ -140,6 +140,12 @@ void ContainerConnection::setDisplayOffCallback(const DisplayOffCallback& callba
     mDisplayOffCallback = callback;
 }
 
+void ContainerConnection::setFileMoveRequestCallback(
+    const FileMoveRequestCallback& callback)
+{
+    mFileMoveRequestCallback = callback;
+}
+
 void ContainerConnection::onMessageCall(const std::string& objectPath,
                                         const std::string& interface,
                                         const std::string& methodName,
@@ -159,6 +165,15 @@ void ContainerConnection::onMessageCall(const std::string& objectPath,
             result.setVoid();
         }
     }
+
+    if (methodName == api::METHOD_FILE_MOVE_REQUEST) {
+        const gchar* destination = NULL;
+        const gchar* path = NULL;
+        g_variant_get(parameters, "(&s&s)", &destination, &path);
+        if (mFileMoveRequestCallback) {
+            mFileMoveRequestCallback(destination, path, result);
+        }
+    }
 }
 
 void ContainerConnection::onSignalReceived(const std::string& senderBusName,
index e0b2467..2f429ce 100644 (file)
@@ -50,6 +50,11 @@ public:
                                const std::string& message
                               )> NotifyActiveContainerCallback;
 
+    typedef std::function<void(const std::string& destination,
+                               const std::string& path,
+                               dbus::MethodResultBuilder& result
+                              )> FileMoveRequestCallback;
+
     /**
      * Register notification request callback
      */
@@ -60,6 +65,11 @@ public:
      */
     void setDisplayOffCallback(const DisplayOffCallback& callback);
 
+    /*
+     * Register file move request callback
+     */
+    void setFileMoveRequestCallback(const FileMoveRequestCallback& callback);
+
     /**
      * Send notification signal to this container
      */
@@ -76,6 +86,7 @@ private:
     OnNameLostCallback mOnNameLostCallback;
     NotifyActiveContainerCallback mNotifyActiveContainerCallback;
     DisplayOffCallback mDisplayOffCallback;
+    FileMoveRequestCallback mFileMoveRequestCallback;
 
     void onNameAcquired();
     void onNameLost();
index 7fec0db..118a348 100644 (file)
@@ -37,8 +37,17 @@ const std::string OBJECT_PATH                       = "/org/tizen/containers";
 const std::string INTERFACE                         = "org.tizen.containers.manager";
 
 const std::string METHOD_NOTIFY_ACTIVE_CONTAINER    = "NotifyActiveContainer";
+const std::string METHOD_FILE_MOVE_REQUEST          = "FileMoveRequest";
 const std::string SIGNAL_NOTIFICATION               = "Notification";
 
+const std::string FILE_MOVE_DESTINATION_NOT_FOUND   = "FILE_MOVE_DESTINATION_NOT_FOUND";
+const std::string FILE_MOVE_WRONG_DESTINATION       = "FILE_MOVE_WRONG_DESTINATION";
+const std::string FILE_MOVE_NO_PERMISSIONS_SEND     = "FILE_MOVE_NO_PERMISSIONS_SEND";
+const std::string FILE_MOVE_NO_PERMISSIONS_RECEIVE  = "FILE_MOVE_NO_PERMISSIONS_RECEIVE";
+const std::string FILE_MOVE_FAILED                  = "FILE_MOVE_FAILED";
+const std::string FILE_MOVE_SUCCEEDED               = "FILE_MOVE_SUCCEEDED";
+
+
 const std::string DEFINITION =
     "<node>"
     "  <interface name='" + INTERFACE + "'>"
@@ -46,6 +55,11 @@ const std::string DEFINITION =
     "      <arg type='s' name='application' direction='in'/>"
     "      <arg type='s' name='message' direction='in'/>"
     "    </method>"
+    "    <method name='" + METHOD_FILE_MOVE_REQUEST + "'>"
+    "      <arg type='s' name='destination' direction='in'/>"
+    "      <arg type='s' name='path' direction='in'/>"
+    "      <arg type='s' name='result' direction='out'/>"
+    "    </method>"
     "    <signal name='" + SIGNAL_NOTIFICATION + "'>"
     "      <arg type='s' name='container'/>"
     "      <arg type='s' name='application'/>"
index 2d7114f..ea45b39 100644 (file)
@@ -54,6 +54,13 @@ Container::Container(const std::string& containerConfigPath)
 {
     config::loadFromFile(containerConfigPath, mConfig);
 
+    for (std::string r: mConfig.permittedToSend) {
+        mPermittedToSend.push_back(boost::regex(r));
+    }
+    for (std::string r: mConfig.permittedToRecv) {
+        mPermittedToRecv.push_back(boost::regex(r));
+    }
+
     const std::string baseConfigPath = utils::dirName(containerConfigPath);
     mConfig.config = fs::absolute(mConfig.config, baseConfigPath).string();
     mConfig.networkConfig = fs::absolute(mConfig.networkConfig, baseConfigPath).string();
@@ -79,6 +86,16 @@ Container::~Container()
     }
 }
 
+const std::vector<boost::regex>& Container::getPermittedToSend() const
+{
+    return mPermittedToSend;
+}
+
+const std::vector<boost::regex>& Container::getPermittedToRecv() const
+{
+    return mPermittedToRecv;
+}
+
 const std::string& Container::getId() const
 {
     Lock lock(mReconnectMutex);
@@ -104,6 +121,9 @@ void Container::start()
     if (mDisplayOffCallback) {
         mConnection->setDisplayOffCallback(mDisplayOffCallback);
     }
+    if (mFileMoveCallback) {
+        mConnection->setFileMoveRequestCallback(mFileMoveCallback);
+    }
 
     // Send to the background only after we're connected,
     // otherwise it'd take ages.
@@ -207,7 +227,6 @@ void Container::setNotifyActiveContainerCallback(const NotifyActiveContainerCall
     if (mConnection) {
         mConnection->setNotifyActiveContainerCallback(mNotifyCallback);
     }
-
 }
 
 void Container::sendNotification(const std::string& container,
@@ -232,5 +251,15 @@ void Container::setDisplayOffCallback(const DisplayOffCallback& callback)
     }
 }
 
+void Container::setFileMoveRequestCallback(const FileMoveRequestCallback& callback)
+{
+    Lock lock(mReconnectMutex);
+
+    mFileMoveCallback = callback;
+    if (mConnection) {
+        mConnection->setFileMoveRequestCallback(callback);
+    }
+}
+
 
 } // namespace security_containers
index 5dbccad..7cc0c5b 100644 (file)
@@ -35,6 +35,7 @@
 #include <string>
 #include <memory>
 #include <thread>
+#include <boost/regex.hpp>
 
 
 namespace security_containers {
@@ -49,6 +50,21 @@ public:
 
     typedef ContainerConnection::NotifyActiveContainerCallback NotifyActiveContainerCallback;
     typedef ContainerConnection::DisplayOffCallback DisplayOffCallback;
+    typedef ContainerConnection::FileMoveRequestCallback FileMoveRequestCallback;
+
+    /**
+     * Returns a vector of regexps defining files permitted to be
+     * send to other containers using file move functionality
+     */
+    const std::vector<boost::regex>& getPermittedToSend() const;
+
+    /**
+     * Returns a vector of regexps defining files permitted to be
+     * send to other containers using file move functionality
+     */
+    const std::vector<boost::regex>& getPermittedToRecv() const;
+
+    // ContainerAdmin API
 
     /**
      * Get the container id
@@ -109,6 +125,8 @@ public:
      */
     bool isPaused();
 
+    // ContainerConnection API
+
     /**
      * Register notification request callback
      */
@@ -130,8 +148,15 @@ public:
                           const std::string& application,
                           const std::string& message);
 
+    /**
+     * Register file move request callback
+     */
+    void setFileMoveRequestCallback(const FileMoveRequestCallback& callback);
+
 private:
     ContainerConfig mConfig;
+    std::vector<boost::regex> mPermittedToSend;
+    std::vector<boost::regex> mPermittedToRecv;
     std::unique_ptr<ContainerConnectionTransport> mConnectionTransport;
     std::unique_ptr<NetworkAdmin> mNetworkAdmin;
     std::unique_ptr<ContainerAdmin> mAdmin;
@@ -140,6 +165,7 @@ private:
     mutable std::recursive_mutex mReconnectMutex;
     NotifyActiveContainerCallback mNotifyCallback;
     DisplayOffCallback mDisplayOffCallback;
+    FileMoveRequestCallback mFileMoveCallback;
 
     void onNameLostCallback();
     void reconnectHandler();
index 9f881c1..c890880 100644 (file)
@@ -41,11 +41,6 @@ const std::string CONTAINERS_MANAGER_CONFIG_PATH = "/etc/security-containers/con
 struct ContainersManagerConfig {
 
     /**
-     * Parameters describing input device used to switch between containers
-     */
-    InputConfig inputConfig;
-
-    /**
      * List of containers' configs that we manage.
      * File paths can be relative to the ContainerManager config file.
      */
@@ -61,11 +56,22 @@ struct ContainersManagerConfig {
      */
     std::string defaultId;
 
+    /**
+     * A path where the containers mount points reside.
+     */
+    std::string containersPath;
+
+    /*
+     * Parameters describing input device used to switch between containers
+     */
+    InputConfig inputConfig;
+
     CONFIG_REGISTER
     (
         containerConfigs,
         foregroundId,
         defaultId,
+        containersPath,
         inputConfig
     )
 };
index 13ee3d4..44539da 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "config.hpp"
 
+#include "container-dbus-definitions.hpp"
 #include "containers-manager.hpp"
 #include "container-admin.hpp"
 #include "exception.hpp"
@@ -32,6 +33,8 @@
 #include "log/logger.hpp"
 #include "config/manager.hpp"
 
+#include <boost/filesystem.hpp>
+#include <boost/regex.hpp>
 #include <cassert>
 #include <string>
 #include <climits>
 namespace security_containers {
 
 
+namespace {
+
+bool regexMatchVector(const std::string& str, const std::vector<boost::regex>& v)
+{
+    for (const boost::regex& toMatch: v) {
+        if (boost::regex_match(str, toMatch)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+} // namespace
+
 ContainersManager::ContainersManager(const std::string& managerConfigPath): mDetachOnExit(false)
 {
     LOGD("Instantiating ContainersManager object...");
@@ -60,14 +78,13 @@ ContainersManager::ContainersManager(const std::string& managerConfigPath): mDet
         std::string id = c->getId();
         using namespace std::placeholders;
         c->setNotifyActiveContainerCallback(bind(&ContainersManager::notifyActiveContainerHandler,
-                                                 this,
-                                                 id,
-                                                 _1,
-                                                 _2));
+                                                 this, id, _1, _2));
 
         c->setDisplayOffCallback(bind(&ContainersManager::displayOffHandler,
-                                      this,
-                                      id));
+                                      this, id));
+
+        c->setFileMoveRequestCallback(std::bind(&ContainersManager::handleContainerMoveFileRequest,
+                                                this, id, _1, _2, _3));
 
         mContainers.insert(ContainerMap::value_type(id, std::move(c)));
     }
@@ -210,4 +227,84 @@ void ContainersManager::displayOffHandler(const std::string& /*caller*/)
     focus(mConfig.defaultId);
 }
 
+void ContainersManager::handleContainerMoveFileRequest(const std::string& srcContainerId,
+                                                       const std::string& dstContainerId,
+                                                       const std::string& path,
+                                                       dbus::MethodResultBuilder& result)
+{
+    // TODO: this implementation is only a placeholder.
+    // There are too many unanswered questions and security concerns:
+    // 1. What about mount namespace, host might not see the source/destination
+    //    file. The file might be a different file from a host perspective.
+    // 2. Copy vs move (speed and security concerns over already opened FDs)
+    // 3. Access to source and destination files - DAC, uid/gig
+    // 4. Access to source and destintation files - MAC, smack
+    // 5. Destination file uid/gid assignment
+    // 6. Destination file smack label assignment
+    // 7. Verifiability of the source path
+
+    // NOTE: other possible implementations include:
+    // 1. Sending file descriptors opened directly in each container through DBUS
+    //    using something like g_dbus_message_set_unix_fd_list()
+    // 2. SCS forking and calling setns(MNT) in each container and opening files
+    //    by itself, then passing FDs to the main process
+    // Now when the main process has obtained FDs (by either of those methods)
+    // it can do the copying by itself.
+
+    LOGI("File move requested\n"
+         << "src: " << srcContainerId << "\n"
+         << "dst: " << dstContainerId << "\n"
+         << "path: " << path);
+
+    ContainerMap::const_iterator srcIter = mContainers.find(srcContainerId);
+    if (srcIter == mContainers.end()) {
+        LOGE("Source container '" << srcContainerId << "' not found");
+        return;
+    }
+    Container& srcContainer = *srcIter->second;
+
+    ContainerMap::const_iterator dstIter = mContainers.find(dstContainerId);
+    if (dstIter == mContainers.end()) {
+        LOGE("Destination container '" << dstContainerId << "' not found");
+        result.set(g_variant_new("(s)", api::FILE_MOVE_DESTINATION_NOT_FOUND.c_str()));
+        return;
+    }
+    Container& dstContanier = *dstIter->second;
+
+    if (srcContainerId == dstContainerId) {
+        LOGE("Cannot send a file to yourself");
+        result.set(g_variant_new("(s)", api::FILE_MOVE_WRONG_DESTINATION.c_str()));
+        return;
+    }
+
+    if (!regexMatchVector(path, srcContainer.getPermittedToSend())) {
+        LOGE("Source container has no permissions to send the file: " << path);
+        result.set(g_variant_new("(s)", api::FILE_MOVE_NO_PERMISSIONS_SEND.c_str()));
+        return;
+    }
+
+    if (!regexMatchVector(path, dstContanier.getPermittedToRecv())) {
+        LOGE("Destination container has no permissions to receive the file: " << path);
+        result.set(g_variant_new("(s)", api::FILE_MOVE_NO_PERMISSIONS_RECEIVE.c_str()));
+        return;
+    }
+
+    namespace fs = boost::filesystem;
+    std::string srcPath = fs::absolute(srcContainerId, mConfig.containersPath).string() + path;
+    std::string dstPath = fs::absolute(dstContainerId, mConfig.containersPath).string() + path;
+
+    if (!utils::moveFile(srcPath, dstPath)) {
+        LOGE("Failed to move the file: " << path);
+        result.set(g_variant_new("(s)", api::FILE_MOVE_FAILED.c_str()));
+    } else {
+        result.set(g_variant_new("(s)", api::FILE_MOVE_SUCCEEDED.c_str()));
+        try {
+            dstContanier.sendNotification(srcContainerId, path, api::FILE_MOVE_SUCCEEDED);
+        } catch (ServerException&) {
+            LOGE("Notification to '" << dstContainerId << "' has not been sent");
+        }
+    }
+}
+
+
 } // namespace security_containers
index 316e7bb..1a1af88 100644 (file)
@@ -86,6 +86,10 @@ private:
                                       const std::string& appliaction,
                                       const std::string& message);
     void displayOffHandler(const std::string& caller);
+    void handleContainerMoveFileRequest(const std::string& srcContainerId,
+                                        const std::string& dstContainerId,
+                                        const std::string& path,
+                                        dbus::MethodResultBuilder& result);
 };
 
 
index 8617125..e06bf63 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index 9add4c3..55d6852 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index a7bccd0..6d01a50 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index 0ad824e..518aa4c 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index fdb3ce8..d5ccd1e 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "../libvirt-config/network.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index f4b38f9..fde05d8 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "../libvirt-config/network.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : "/tmp/ut-container"
+    "runMountPoint" : "/tmp/ut-container",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index 458bca4..e1769c3 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "../libvirt-config/network.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index a403aa8..8e89f50 100644 (file)
@@ -2,6 +2,7 @@
     "containerConfigs" : ["containers/console1.conf", "missing/file/path/missing.conf", "containers/console3.conf"],
     "foregroundId" : "ut-containers-manager-console1",
     "defaultId" : "ut-containers-manager-console1",
+    "containersPath" : "/tmp",
     "inputConfig" : {"enabled" : false,
                      "device" : "/dev/doesnotexist",
                      "code" : 139,
index 39d56df..51b2bbd 100644 (file)
@@ -1,7 +1,8 @@
 {
-    "containerConfigs" : ["containers/console1-dbus.conf", "containers/console2-dbus.conf", "containers/console3-dbus.conf"],
+    "containerConfigs" : ["containers/console1.conf", "containers/console2.conf", "containers/console3.conf"],
     "foregroundId" : "ut-containers-manager-console1",
     "defaultId" : "in_no_way_there_is_a_valid_id_here",
+    "containersPath" : "/tmp",
     "inputConfig" : {"enabled" : false,
                      "device" : "/dev/doesnotexist",
                      "code" : 139,
index e12964a..c62daad 100644 (file)
@@ -2,6 +2,7 @@
     "containerConfigs" : ["containers/console1.conf", "containers/console2.conf", "containers/console3.conf"],
     "foregroundId" : "this_id_does_not_exist",
     "defaultId" : "ut-containers-manager-console1",
+    "containersPath" : "/tmp",
     "inputConfig" : {"enabled" : false,
                      "device" : "/dev/doesnotexist",
                      "code" : 139,
index fc12fec..55dfeb5 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "../libvirt-config/network1.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : "/tmp/ut-containers-manager/console1-dbus"
+    "runMountPoint" : "/tmp/ut-containers-manager/console1-dbus",
+    "permittedToSend" : [ "/tmp/.*", "/etc/secret2" ],
+    "permittedToRecv" : [ "/tmp/.*" ]
 }
index 47bbf5c..61668c8 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "../libvirt-config/network1.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [ "/tmp/.*" ],
+    "permittedToRecv" : [ "/tmp/.*" ]
 }
index 41988e6..713cb02 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "../libvirt-config/network2.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : "/tmp/ut-containers-manager/console2-dbus"
+    "runMountPoint" : "/tmp/ut-containers-manager/console2-dbus",
+    "permittedToSend" : [ "/tmp/.*" ],
+    "permittedToRecv" : [ "/tmp/.*", "/etc/secret1" ]
 }
index 3bf1623..10e5cef 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "../libvirt-config/network2.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [ "/tmp/.*" ],
+    "permittedToRecv" : [ "/tmp/.*" ]
 }
index 8ebdfaa..70ff251 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "../libvirt-config/network3.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : "/tmp/ut-containers-manager/console3-dbus"
+    "runMountPoint" : "/tmp/ut-containers-manager/console3-dbus",
+    "permittedToSend" : [ "/tmp/.*" ],
+    "permittedToRecv" : [ "/tmp/.*" ]
 }
index 0a90aff..8ccf55b 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "../libvirt-config/network3.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [ "/tmp/.*" ],
+    "permittedToRecv" : [ "/tmp/.*" ]
 }
index 7aea75f..6d24978 100644 (file)
@@ -1,5 +1,5 @@
 <domain type="lxc">
-    <name>ut-containers-manager-console1</name>
+    <name>ut-containers-manager-console1-dbus</name>
     <uuid>58184009-b278-4d01-975d-708393690084</uuid>
     <memory>102400</memory>
     <os>
index 0336d3d..5fe2996 100644 (file)
@@ -1,5 +1,5 @@
 <domain type="lxc">
-    <name>ut-containers-manager-console2</name>
+    <name>ut-containers-manager-console2-dbus</name>
     <uuid>3d18323e-4ada-4a1b-a907-836701891306</uuid>
     <memory>102400</memory>
     <os>
index 0b65ad4..be4d618 100644 (file)
@@ -1,5 +1,5 @@
 <domain type="lxc">
-    <name>ut-containers-manager-console3</name>
+    <name>ut-containers-manager-console3-dbus</name>
     <uuid>71cb8511-7474-4e90-865a-3360b7f77254</uuid>
     <memory>102400</memory>
     <os>
index 4ffe017..7908dea 100644 (file)
@@ -2,6 +2,7 @@
     "containerConfigs" : ["containers/console1.conf", "containers/console2.conf", "containers/console3.conf"],
     "foregroundId" : "ut-containers-manager-console1",
     "defaultId" : "ut-containers-manager-console1",
+    "containersPath" : "/tmp",
     "inputConfig" : {"enabled" : false,
                      "device" : "/dev/doesnotexist",
                      "code" : 139,
index ae59483..5466f3d 100644 (file)
@@ -2,8 +2,9 @@
     "containerConfigs" : ["containers/console1-dbus.conf",
                           "containers/console2-dbus.conf",
                           "containers/console3-dbus.conf"],
-    "foregroundId" : "ut-containers-manager-console1",
-    "defaultId" : "ut-containers-manager-console1",
+    "foregroundId" : "ut-containers-manager-console1-dbus",
+    "defaultId" : "ut-containers-manager-console1-dbus",
+    "containersPath" : "/tmp",
     "inputConfig" : {"enabled" : false,
                      "device" : "/dev/doesnotexist",
                      "code" : 139,
index 1078859..be3f7c1 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-network-admin/libvirt-config/buggy-network.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index cc31322..f418503 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index f680989..771a93b 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-network-admin/libvirt-config/network.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index 47b4450..41cfc2f 100644 (file)
@@ -1,5 +1,6 @@
 {
     "containerConfigs" : ["containers/container1.conf", "missing/file/path/missing.conf", "containers/container3.conf"],
+    "containersPath" : "/tmp",
     "foregroundId" : "ut-server-container1",
     "defaultId" : "ut-server-container1",
     "inputConfig" : {"enabled" : false,
index 0b18659..952decf 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "../libvirt-config/network1.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index b5bab4d..4e6de09 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "../libvirt-config/network2.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index 3a1ae36..9170c54 100644 (file)
@@ -4,5 +4,7 @@
     "networkConfig" : "../libvirt-config/network3.xml",
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 1000,
-    "runMountPoint" : ""
+    "runMountPoint" : "",
+    "permittedToSend" : [],
+    "permittedToRecv" : []
 }
index 360bab7..8935030 100644 (file)
@@ -1,5 +1,6 @@
 {
     "containerConfigs" : ["containers/container1.conf", "containers/container2.conf", "containers/container3.conf"],
+    "containersPath" : "/tmp",
     "foregroundId" : "ut-server-container1",
     "defaultId" : "ut-server-container1",
     "inputConfig" : {"enabled" : false,
index 61eea40..0182cb8 100644 (file)
 #include "utils/glib-loop.hpp"
 #include "config/exception.hpp"
 #include "utils/latch.hpp"
+#include "utils/fs.hpp"
 
+#include <vector>
+#include <map>
 #include <memory>
 #include <string>
 #include <algorithm>
 #include <functional>
 #include <mutex>
 #include <condition_variable>
-
+#include <boost/filesystem.hpp>
 
 using namespace security_containers;
 using namespace security_containers::config;
@@ -63,12 +66,13 @@ const int EVENT_TIMEOUT = 5000;
 const int TEST_DBUS_CONNECTION_CONTAINERS_COUNT = 3;
 const std::string PREFIX_CONSOLE_NAME = "ut-containers-manager-console";
 const std::string TEST_APP_NAME = "testapp";
-const std::string TEST_MESSGAE = "testmessage";
+const std::string TEST_MESSAGE = "testmessage";
+const std::string FILE_CONTENT = "File content\n"
+                                 "Line 1\n"
+                                 "Line 2\n";
 
 class DbusAccessory {
 public:
-    std::vector<std::string> mReceivedSignalsSource;
-
     DbusAccessory(int id)
         : mId(id),
           mClient(DbusConnection::create(acquireAddress())),
@@ -110,12 +114,9 @@ public:
         mNameCondition.notify_one();
     }
 
-    void signalSubscribe(Latch& referenceLatch)
+    void signalSubscribe(const DbusConnection::SignalCallback& callback)
     {
-        using namespace std::placeholders;
-        mClient->signalSubscribe(std::bind(&DbusAccessory::handler,
-                                 this, std::ref(referenceLatch), _1, _2, _3, _4, _5),
-                                 api::BUS_NAME);
+        mClient->signalSubscribe(callback, api::BUS_NAME);
     }
 
     void emitSignal(const std::string& objectPath,
@@ -126,9 +127,9 @@ public:
         mClient->emitSignal(objectPath, interface, name, parameters);
     }
 
-    void callMethod()
+    void callMethodNotify()
     {
-        GVariant* parameters = g_variant_new("(ss)", TEST_APP_NAME.c_str(), TEST_MESSGAE.c_str());
+        GVariant* parameters = g_variant_new("(ss)", TEST_APP_NAME.c_str(), TEST_MESSAGE.c_str());
         mClient->callMethod(api::BUS_NAME,
                             api::OBJECT_PATH,
                             api::INTERFACE,
@@ -137,9 +138,19 @@ public:
                             "()");
     }
 
-    std::string getContainerName() const
+    std::string callMethodMove(const std::string& dest, const std::string& path)
     {
-        return PREFIX_CONSOLE_NAME + std::to_string(mId);
+        GVariant* parameters = g_variant_new("(ss)", dest.c_str(), path.c_str());
+        GVariantPtr result = mClient->callMethod(api::BUS_NAME,
+                                                 api::OBJECT_PATH,
+                                                 api::INTERFACE,
+                                                 api::METHOD_FILE_MOVE_REQUEST,
+                                                 parameters,
+                                                 "(s)");
+
+        const gchar* retcode = NULL;
+        g_variant_get(result.get(), "(&s)", &retcode);
+        return std::string(retcode);
     }
 
 private:
@@ -155,29 +166,6 @@ private:
         return "unix:path=/tmp/ut-containers-manager/console" + std::to_string(mId) +
                "-dbus/dbus/system_bus_socket";
     }
-
-    void handler(Latch& referenceLatch,
-                 const std::string& /*senderBusName*/,
-                 const std::string& objectPath,
-                 const std::string& interface,
-                 const std::string& signalName,
-                 GVariant* parameters)
-    {
-        if (objectPath == api::OBJECT_PATH &&
-            interface == api::INTERFACE &&
-            signalName == api::SIGNAL_NOTIFICATION &&
-            g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) {
-
-            const gchar* container = NULL;
-            const gchar* application = NULL;
-            const gchar* message = NULL;
-            g_variant_get(parameters, "(&s&s&s)", &container, &application, &message);
-            mReceivedSignalsSource.push_back(container);
-            if (application == TEST_APP_NAME && message == TEST_MESSGAE) {
-                referenceLatch.set();
-            }
-        }
-    }
 };
 
 
@@ -262,36 +250,65 @@ BOOST_AUTO_TEST_CASE(FocusTest)
 
 BOOST_AUTO_TEST_CASE(NotifyActiveContainerTest)
 {
-    Latch signalReceivedLatch;
-
     ContainersManager cm(TEST_DBUS_CONFIG_PATH);
     cm.startAll();
 
-    std::vector< std::unique_ptr<DbusAccessory> > dbuses;
+    std::map<int, std::unique_ptr<DbusAccessory>> dbuses;
     for (int i = 1; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
-        dbuses.push_back(std::unique_ptr<DbusAccessory>(new DbusAccessory(i)));
+        dbuses[i] = std::unique_ptr<DbusAccessory>(new DbusAccessory(i));
     }
-    for (auto& dbus : dbuses) {
-        dbus->signalSubscribe(signalReceivedLatch);
+
+    Latch signalReceivedLatch;
+    std::map<int, std::vector<std::string>> signalReceivedSourcesMap;
+    auto handler = [](Latch& latch,
+                      std::vector<std::string>& receivedSignalSources,
+                      const std::string& /*senderBusName*/,
+                      const std::string& objectPath,
+                      const std::string& interface,
+                      const std::string& signalName,
+                      GVariant* parameters)
+        {
+            if (objectPath == api::OBJECT_PATH &&
+                interface == api::INTERFACE &&
+                signalName == api::SIGNAL_NOTIFICATION &&
+                g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) {
+
+                const gchar* container = NULL;
+                const gchar* application = NULL;
+                const gchar* message = NULL;
+                g_variant_get(parameters, "(&s&s&s)", &container, &application, &message);
+                receivedSignalSources.push_back(container);
+                if (application == TEST_APP_NAME && message == TEST_MESSAGE) {
+                    latch.set();
+                }
+            }
+        };
+
+    using namespace std::placeholders;
+    for (int i = 1; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
+        dbuses[i]->signalSubscribe(std::bind(handler,
+                                             std::ref(signalReceivedLatch),
+                                             std::ref(signalReceivedSourcesMap[i]),
+                                             _1, _2, _3, _4, _5));
     }
     for (auto& dbus : dbuses) {
-        dbus->callMethod();
+        dbus.second->callMethodNotify();
     }
 
     BOOST_CHECK(signalReceivedLatch.waitForN(dbuses.size() - 1, EVENT_TIMEOUT));
     BOOST_CHECK(signalReceivedLatch.empty());
 
     //check if there are no signals that was received more than once
-    for (const auto& source : dbuses[0]->mReceivedSignalsSource) {
-        BOOST_CHECK_EQUAL(std::count(dbuses[0]->mReceivedSignalsSource.begin(),
-                               dbuses[0]->mReceivedSignalsSource.end(),
-                               source), 1);
+    for (const auto& source : signalReceivedSourcesMap[1]) {
+        BOOST_CHECK_EQUAL(std::count(signalReceivedSourcesMap[1].begin(),
+                                     signalReceivedSourcesMap[1].end(),
+                                     source), 1);
     }
     //check if all signals was received by active container
-    BOOST_CHECK_EQUAL(dbuses[0]->mReceivedSignalsSource.size(), dbuses.size() - 1);
+    BOOST_CHECK_EQUAL(signalReceivedSourcesMap[1].size(), dbuses.size() - 1);
     //check if no signals was received by inactive container
-    for (size_t i = 1; i < dbuses.size(); ++i) {
-        BOOST_CHECK(dbuses[i]->mReceivedSignalsSource.empty());
+    for (size_t i = 2; i <= dbuses.size(); ++i) {
+        BOOST_CHECK(signalReceivedSourcesMap[i].empty());
     }
 
     dbuses.clear();
@@ -315,13 +332,13 @@ BOOST_AUTO_TEST_CASE(DisplayOffTest)
     std::unique_lock<std::mutex> Lock(Mutex);
     std::condition_variable Condition;
     auto cond = [&cm]() -> bool {
-        return cm.getRunningForegroundContainerId() == "ut-containers-manager-console1";
+        return cm.getRunningForegroundContainerId() == "ut-containers-manager-console1-dbus";
     };
 
     for (auto& client : clients) {
         // TEST SWITCHING TO DEFAULT CONTAINER
         // focus non-default container
-        BOOST_REQUIRE_NO_THROW(cm.focus("ut-containers-manager-console3"));
+        BOOST_REQUIRE_NO_THROW(cm.focus("ut-containers-manager-console3-dbus"));
 
         // emit signal from dbus connection
         BOOST_REQUIRE_NO_THROW(client->emitSignal(fake_power_manager_api::OBJECT_PATH,
@@ -334,4 +351,102 @@ BOOST_AUTO_TEST_CASE(DisplayOffTest)
     }
 }
 
+BOOST_AUTO_TEST_CASE(MoveFileTest)
+{
+    ContainersManager cm(TEST_DBUS_CONFIG_PATH);
+    cm.startAll();
+
+    std::map<int, std::unique_ptr<DbusAccessory>> dbuses;
+    for (int i = 1; i <= 2; ++i) {
+        dbuses[i] = std::unique_ptr<DbusAccessory>(new DbusAccessory(i));
+    }
+
+    Latch notificationLatch;
+    std::string notificationSource;
+    std::string notificationPath;
+    std::string notificationRetcode;
+    auto handler = [&](const std::string& /*senderBusName*/,
+                       const std::string& objectPath,
+                       const std::string& interface,
+                       const std::string& signalName,
+                       GVariant* parameters)
+        {
+            if (objectPath == api::OBJECT_PATH &&
+                interface == api::INTERFACE &&
+                signalName == api::SIGNAL_NOTIFICATION &&
+                g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) {
+
+                const gchar* source = NULL;
+                const gchar* path = NULL;
+                const gchar* retcode = NULL;
+                g_variant_get(parameters, "(&s&s&s)", &source, &path, &retcode);
+
+                notificationSource = source;
+                notificationPath = path;
+                notificationRetcode = retcode;
+                notificationLatch.set();
+            }
+        };
+
+    // subscribe the second (destination) container for notifications
+    dbuses.at(2)->signalSubscribe(handler);
+
+    const std::string TMP = "/tmp";
+    const std::string NO_PATH = "path_doesnt_matter_here";
+    const std::string BUGGY_PATH = TMP + "/this_file_does_not_exist";
+    const std::string BUGGY_CONTAINER = "this-container-does-not-exist";
+    const std::string CONTAINER1 = "ut-containers-manager-console1-dbus";
+    const std::string CONTAINER2 = "ut-containers-manager-console2-dbus";
+    const std::string CONTAINER1PATH = TMP + "/" + CONTAINER1 + TMP;
+    const std::string CONTAINER2PATH = TMP + "/" + CONTAINER2 + TMP;
+
+    // sending to a non existing container
+    BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(BUGGY_CONTAINER, NO_PATH),
+                      api::FILE_MOVE_DESTINATION_NOT_FOUND);
+    BOOST_CHECK(notificationLatch.empty());
+
+    // sending to self
+    BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER1, NO_PATH),
+                      api::FILE_MOVE_WRONG_DESTINATION);
+    BOOST_CHECK(notificationLatch.empty());
+
+    // no permission to send
+    BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER2, "/etc/secret1"),
+                      api::FILE_MOVE_NO_PERMISSIONS_SEND);
+    BOOST_CHECK(notificationLatch.empty());
+
+    // no permission to receive
+    BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER2, "/etc/secret2"),
+                      api::FILE_MOVE_NO_PERMISSIONS_RECEIVE);
+    BOOST_CHECK(notificationLatch.empty());
+
+    // non existing file
+    BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER2, BUGGY_PATH),
+                      api::FILE_MOVE_FAILED);
+    BOOST_CHECK(notificationLatch.empty());
+
+    // a working scenario
+    namespace fs = boost::filesystem;
+    boost::system::error_code ec;
+    fs::remove_all(CONTAINER1PATH, ec);
+    fs::remove_all(CONTAINER2PATH, ec);
+    BOOST_REQUIRE(fs::create_directories(CONTAINER1PATH, ec));
+    BOOST_REQUIRE(fs::create_directories(CONTAINER2PATH, ec));
+    BOOST_REQUIRE(utils::saveFileContent(CONTAINER1PATH + "/file", FILE_CONTENT));
+
+    BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER2, TMP + "/file"),
+                      api::FILE_MOVE_SUCCEEDED);
+    BOOST_CHECK(notificationLatch.wait(EVENT_TIMEOUT));
+    BOOST_CHECK(notificationLatch.empty());
+    BOOST_CHECK_EQUAL(notificationSource, CONTAINER1);
+    BOOST_CHECK_EQUAL(notificationPath, TMP + "/file");
+    BOOST_CHECK_EQUAL(notificationRetcode, api::FILE_MOVE_SUCCEEDED);
+    BOOST_CHECK(!fs::exists(CONTAINER1PATH + "/file"));
+    BOOST_CHECK_EQUAL(utils::readFileContent(CONTAINER2PATH + "/file"), FILE_CONTENT);
+
+    fs::remove_all(CONTAINER1PATH, ec);
+    fs::remove_all(CONTAINER2PATH, ec);
+}
+
+
 BOOST_AUTO_TEST_SUITE_END()
index adc8797..8f7c965 100644 (file)
 #include "utils/exception.hpp"
 
 #include <memory>
+#include <sys/mount.h>
+#include <boost/filesystem.hpp>
 
 BOOST_AUTO_TEST_SUITE(UtilsFSSuite)
 
 using namespace security_containers;
 using namespace security_containers::utils;
 
+namespace {
+
 const std::string FILE_PATH = SC_TEST_CONFIG_INSTALL_DIR "/utils/ut-fs/file.txt";
 const std::string FILE_CONTENT = "File content\n"
                                  "Line 1\n"
                                  "Line 2\n";
 const std::string BUGGY_FILE_PATH = "/some/missing/file/path/file.txt";
+const std::string TMP_PATH = "/tmp";
+const std::string FILE_PATH_RANDOM =
+    boost::filesystem::unique_path("/tmp/testFile-%%%%").string();
+const std::string MOUNT_POINT_RANDOM_1 =
+    boost::filesystem::unique_path("/tmp/mountPoint-%%%%").string();
+const std::string MOUNT_POINT_RANDOM_2 =
+    boost::filesystem::unique_path("/tmp/mountPoint-%%%%").string();
+const std::string FILE_NAME_RANDOM_1 =
+    boost::filesystem::unique_path("testFile-%%%%").string();
+const std::string FILE_NAME_RANDOM_2 =
+    boost::filesystem::unique_path("testFile-%%%%").string();
+
+} // namespace
 
 BOOST_AUTO_TEST_CASE(ReadFileContentTest)
 {
@@ -48,4 +65,70 @@ BOOST_AUTO_TEST_CASE(ReadFileContentTest)
     BOOST_CHECK_THROW(readFileContent(BUGGY_FILE_PATH), UtilsException);
 }
 
+BOOST_AUTO_TEST_CASE(SaveFileContentTest)
+{
+    BOOST_REQUIRE(saveFileContent(FILE_PATH_RANDOM, FILE_CONTENT));
+    BOOST_CHECK_EQUAL(FILE_CONTENT, readFileContent(FILE_PATH));
+
+    boost::system::error_code ec;
+    boost::filesystem::remove(FILE_PATH_RANDOM, ec);
+}
+
+BOOST_AUTO_TEST_CASE(MountPointTest)
+{
+    bool result;
+    namespace fs = boost::filesystem;
+    boost::system::error_code ec;
+
+    BOOST_REQUIRE(fs::create_directory(MOUNT_POINT_RANDOM_1, ec));
+    BOOST_REQUIRE(isMountPoint(MOUNT_POINT_RANDOM_1, result));
+    BOOST_CHECK_EQUAL(result, false);
+    BOOST_REQUIRE(hasSameMountPoint(TMP_PATH, MOUNT_POINT_RANDOM_1, result));
+    BOOST_CHECK_EQUAL(result, true);
+
+    BOOST_REQUIRE(mountRun(MOUNT_POINT_RANDOM_1));
+    BOOST_REQUIRE(isMountPoint(MOUNT_POINT_RANDOM_1, result));
+    BOOST_CHECK_EQUAL(result, true);
+    BOOST_REQUIRE(hasSameMountPoint(TMP_PATH, MOUNT_POINT_RANDOM_1, result));
+    BOOST_CHECK_EQUAL(result, false);
+
+    BOOST_REQUIRE(umount(MOUNT_POINT_RANDOM_1));
+    BOOST_REQUIRE(fs::remove(MOUNT_POINT_RANDOM_1, ec));
+}
+
+BOOST_AUTO_TEST_CASE(MoveFileTest)
+{
+    namespace fs = boost::filesystem;
+    boost::system::error_code ec;
+    std::string src, dst;
+
+    // same mount point
+    src = TMP_PATH + "/" + FILE_NAME_RANDOM_1;
+    dst = TMP_PATH + "/" + FILE_NAME_RANDOM_2;
+
+    BOOST_REQUIRE(saveFileContent(src, FILE_CONTENT));
+
+    BOOST_CHECK(moveFile(src, dst));
+    BOOST_CHECK(!fs::exists(src));
+    BOOST_CHECK_EQUAL(readFileContent(dst), FILE_CONTENT);
+
+    BOOST_REQUIRE(fs::remove(dst));
+
+    // different mount point
+    src = TMP_PATH + "/" + FILE_NAME_RANDOM_1;
+    dst = MOUNT_POINT_RANDOM_2 + "/" + FILE_NAME_RANDOM_2;
+
+    BOOST_REQUIRE(fs::create_directory(MOUNT_POINT_RANDOM_2, ec));
+    BOOST_REQUIRE(mountRun(MOUNT_POINT_RANDOM_2));
+    BOOST_REQUIRE(saveFileContent(src, FILE_CONTENT));
+
+    BOOST_CHECK(moveFile(src, dst));
+    BOOST_CHECK(!fs::exists(src));
+    BOOST_CHECK_EQUAL(readFileContent(dst), FILE_CONTENT);
+
+    BOOST_REQUIRE(fs::remove(dst));
+    BOOST_REQUIRE(umount(MOUNT_POINT_RANDOM_2));
+    BOOST_REQUIRE(fs::remove(MOUNT_POINT_RANDOM_2, ec));
+}
+
 BOOST_AUTO_TEST_SUITE_END()