Finish CreateFile API 79/41579/6
authorLukasz Kostyra <l.kostyra@samsung.com>
Tue, 16 Jun 2015 10:56:18 +0000 (12:56 +0200)
committerLukasz Kostyra <l.kostyra@samsung.com>
Fri, 19 Jun 2015 06:24:36 +0000 (08:24 +0200)
[Feature]       N/A
[Cause]         Missing API
[Solution]      Add missing functions in ZonesManager and in Communication
[Verification]  Build, install, run tests

Change-Id: I417e1deeca2effcdb772189045263d0c9dd22de7

16 files changed:
common/api/messages.hpp
common/lxc/zone.cpp
common/lxc/zone.hpp
libs/config/from-gvariant-visitor.hpp
libs/config/to-gvariant-visitor.hpp
server/common-definitions.hpp
server/host-dbus-connection.cpp
server/host-dbus-definitions.hpp
server/host-ipc-connection.cpp
server/host-ipc-connection.hpp
server/zone.cpp
server/zone.hpp
server/zones-manager.cpp
server/zones-manager.hpp
tests/unit_tests/lxc/ut-zone.cpp
tests/unit_tests/server/ut-zones-manager.cpp

index a00a2dd..7299898 100644 (file)
@@ -227,11 +227,13 @@ struct GrantDeviceIn {
 struct CreateFileIn {
     std::string id;
     std::string path;
+    int32_t flags;
     int32_t mode;
     CONFIG_REGISTER
     (
         id,
         path,
+        flags,
         mode
     )
 };
index 5fd1762..845d959 100644 (file)
@@ -376,7 +376,10 @@ bool LxcZone::runInZone(Call& call)
     return status == 0;
 }
 
-int LxcZone::createFile(const std::string& path, const std::int32_t mode, int *fdPtr)
+bool LxcZone::createFile(const std::string& path,
+                         const std::int32_t flags,
+                         const std::int32_t mode,
+                         int *fdPtr)
 {
     *fdPtr = -1;
 
@@ -389,12 +392,13 @@ int LxcZone::createFile(const std::string& path, const std::int32_t mode, int *f
     lxc::LxcZone::Call call = [&]()->int{
         utils::close(sockets[1]);
 
-        int fd = ::open(path.c_str(), O_CREAT | O_EXCL, mode);
+        int fd = ::open(path.c_str(), O_CREAT | O_EXCL | flags, mode);
         if (fd < 0) {
             LOGE("Error during file creation: " << utils::getSystemErrorMessage());
             utils::close(sockets[0]);
             return -1;
         }
+        LOGT("Created file in zone with fd " << fd);
         utils::fdSend(sockets[0], fd);
         utils::close(fd);
         return 0;
index 0d34e6f..ff5bb85 100644 (file)
@@ -158,12 +158,19 @@ public:
     /**
      * Create a file inside the zone and return it's file descriptor
      *
-     * @param path path in the container
-     * @param mode mode
+     * @param[in]  path  Path in container where file shall be created
+     * @param[in]  flags Flags used when creating file. Note that the method already provides
+     *                   O_CREAT and O_EXCL flags. User must provide the function with
+     *                   O_RDWR/O_RDONLY/O_WRONLY flag explicitly.
+     * @param[in]  mode  Permissions with which file is created
+     * @param[out] fdPtr Pointer to opened file descriptor.
      *
-     * @return file descriptor of the file, opened in mode
+     * @return True on success, false on failure.
      */
-    int createFile(const std::string& path, const std::int32_t mode, int *fdPtr);
+    bool createFile(const std::string& path,
+                    const std::int32_t flags,
+                    const std::int32_t mode,
+                    int *fdPtr);
 
 private:
     lxc_container* mLxcContainer;
index 040dcae..e648625 100644 (file)
@@ -28,6 +28,7 @@
 #include "config/is-visitable.hpp"
 #include "config/exception.hpp"
 #include "config/is-union.hpp"
+#include "config/types.hpp"
 
 #include <string>
 #include <vector>
@@ -126,6 +127,12 @@ private:
         value = g_variant_get_string(object, NULL);
     }
 
+    static void fromGVariant(GVariant* object, config::FileDescriptor& value)
+    {
+        checkType(object, G_VARIANT_TYPE_INT32);
+        value.value = g_variant_get_int32(object);
+    }
+
     template<typename T>
     static void fromGVariant(GVariant* object, std::vector<T>& value)
     {
index bb94e49..047aa17 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "config/is-visitable.hpp"
 #include "config/is-union.hpp"
+#include "config/types.hpp"
 
 #include <string>
 #include <vector>
@@ -82,6 +83,7 @@ private:
     void writeInternal(bool value) { add("b", value); };
     void writeInternal(double value) { add("d", value); };
     void writeInternal(const std::string& value) { add("s", value.c_str()); };
+    void writeInternal(const config::FileDescriptor& value) { add("h", value.value); };
 
     template<typename T>
     void writeInternal(const std::vector<T>& value)
index 5e85f92..cf8bfe4 100644 (file)
@@ -36,12 +36,13 @@ namespace api {
  */
 //TODO: Errors should use exception handling mechanism
 ///@{
-const std::string ERROR_FORBIDDEN         = "org.tizen.vasum.Error.Forbidden";
-const std::string ERROR_FORWARDED         = "org.tizen.vasum.Error.Forwarded";
-const std::string ERROR_INVALID_ID        = "org.tizen.vasum.Error.InvalidId";
-const std::string ERROR_INVALID_STATE     = "org.tizen.vasum.Error.InvalidState";
-const std::string ERROR_INTERNAL          = "org.tizen.vasum.Error.Internal";
-const std::string ERROR_ZONE_NOT_RUNNING  = "org.tizen.vasum.Error.ZonesNotRunning";
+const std::string ERROR_FORBIDDEN          = "org.tizen.vasum.Error.Forbidden";
+const std::string ERROR_FORWARDED          = "org.tizen.vasum.Error.Forwarded";
+const std::string ERROR_INVALID_ID         = "org.tizen.vasum.Error.InvalidId";
+const std::string ERROR_INVALID_STATE      = "org.tizen.vasum.Error.InvalidState";
+const std::string ERROR_INTERNAL           = "org.tizen.vasum.Error.Internal";
+const std::string ERROR_ZONE_NOT_RUNNING   = "org.tizen.vasum.Error.ZonesNotRunning";
+const std::string ERROR_CREATE_FILE_FAILED = "org.tizen.vasum.Error.CreateFileFailed";
 ///@}
 
 } // namespace api
index 7714b65..ba4e9ee 100644 (file)
@@ -394,6 +394,15 @@ void HostDbusConnection::onMessageCall(const std::string& objectPath,
         mZonesManagerPtr->handleFileMoveCall(EMPTY_CALLER, data, rb);
         return;
     }
+
+    if (methodName == api::dbus::METHOD_CREATE_FILE) {
+        api::CreateFileIn data;
+        config::loadFromGVariant(parameters, data);
+
+        auto rb = std::make_shared<api::DbusMethodResultBuilder<api::CreateFileOut>>(result);
+        mZonesManagerPtr->handleCreateFileCall(data, rb);
+        return;
+    }
 }
 
 void HostDbusConnection::onSignalCall(const std::string& /* senderBusName */,
index 142a6d2..9b74ab2 100644 (file)
@@ -181,6 +181,7 @@ const std::string DEFINITION =
     "    <method name='" + METHOD_CREATE_FILE + "'>"
     "      <arg type='s' name='id' direction='in'/>"
     "      <arg type='s' name='path' direction='in'/>"
+    "      <arg type='i' name='flags' direction='in'/>"
     "      <arg type='i' name='mode' direction='in'/>"
     "      <arg type='h' name='fileDescriptor' direction='out'/>"
     "    </method>"
index e87adca..c389919 100644 (file)
@@ -125,6 +125,9 @@ HostIPCConnection::HostIPCConnection(ZonesManager* zonesManagerPtr)
 
     setFileMoveCallback(std::bind(&ZonesManager::handleFileMoveCall,
                                   mZonesManagerPtr, "", _1, _2));
+
+    setCreateFileCallback(std::bind(&ZonesManager::handleCreateFileCall,
+                                    mZonesManagerPtr, _1, _2));
 }
 
 HostIPCConnection::~HostIPCConnection()
@@ -366,6 +369,15 @@ void HostIPCConnection::setFileMoveCallback(const Method<const api::FileMoveRequ
         Method::getWrapper(callback));
 }
 
+void HostIPCConnection::setCreateFileCallback(const Method<const api::CreateFileIn,
+                                              api::CreateFileOut>::type& callback)
+{
+    typedef IPCMethodWrapper<const api::CreateFileIn, api::CreateFileOut> Method;
+    mService->setMethodHandler<Method::out, Method::in>(
+        api::ipc::METHOD_CREATE_FILE,
+        Method::getWrapper(callback));
+}
+
 void HostIPCConnection::sendNotification(const api::Notification& notification)
 {
     mService->signal(api::ipc::SIGNAL_NOTIFICATION,
index 8107612..44fe356 100644 (file)
@@ -86,6 +86,7 @@ private:
     void setSwitchToDefaultCallback(const Signal<const api::Void>::type& callback);
     void setFileMoveCallback(const Method<const api::FileMoveRequestIn,
                              api::FileMoveRequestStatus>::type& callback);
+    void setCreateFileCallback(const Method<const api::CreateFileIn, api::CreateFileOut>::type& callback);
 
     ipc::epoll::ThreadDispatcher mDispatcher;
     std::unique_ptr<ipc::Service> mService;
index 94f6873..590badd 100644 (file)
@@ -394,6 +394,15 @@ bool Zone::isSwitchToDefaultAfterTimeoutAllowed() const
     return mConfig.switchToDefaultAfterTimeout;
 }
 
+int Zone::createFile(const std::string& path, const std::int32_t flags, const std::int32_t mode)
+{
+    int fd = 0;
+    if (!mZone.createFile(path, flags, mode, &fd)) {
+        throw ZoneOperationException("Failed to create file.");
+    }
+    return fd;
+}
+
 std::string Zone::declareFile(const int32_t& type,
                               const std::string& path,
                               const int32_t& flags,
index 0829c35..dbc384d 100644 (file)
@@ -183,6 +183,17 @@ public:
     int getVT() const;
 
     /**
+     * Create file inside zone, return its fd
+     *
+     * @param path  Path where the file should be created
+     * @param flags Flags used when opening the file. See common/lxc/zone.hpp for more info.
+     * @param mode  Permissions with which file is created
+     *
+     * @return Created files fd
+     */
+    int createFile(const std::string& path, const std::int32_t flags, const std::int32_t mode);
+
+    /**
      * Declare file, directory or pipe that will be created while zone startup
      */
     std::string declareFile(const int32_t& type,
index d213669..d28c9b4 100644 (file)
@@ -625,6 +625,36 @@ void ZonesManager::handleFileMoveCall(const std::string& /*srcZoneId*/,
 
 #endif /* ZONE_CONNECTION */
 
+void ZonesManager::handleCreateFileCall(const api::CreateFileIn& request,
+                                        api::MethodResultBuilder::Pointer result)
+{
+    auto handler = [&, this] {
+        LOGI("CreateFile call");
+
+        Lock lock(mMutex);
+
+        auto srcIter = findZone(request.id);
+        if (srcIter == mZones.end()) {
+            LOGE("Zone '" << request.id << "' not found");
+            result->setError(api::ERROR_INVALID_ID, "Requested Zone was not found.");
+            return;
+        }
+        Zone& srcZone = get(srcIter);
+
+        auto retValue = std::make_shared<api::CreateFileOut>();
+        try {
+            retValue->fd = srcZone.createFile(request.path, request.flags, request.mode);
+        } catch(ZoneOperationException& e) {
+            result->setError(api::ERROR_CREATE_FILE_FAILED, "Unable to create file");
+            return;
+        }
+
+        result->set(retValue);
+    };
+
+    mWorker->addTaskAndWait(handler);
+}
+
 #ifdef DBUS_CONNECTION
 void ZonesManager::handleProxyCall(const std::string& caller,
                                    const std::string& target,
index c74f5ec..55e7023 100644 (file)
@@ -164,6 +164,8 @@ public:
                                api::MethodResultBuilder::Pointer result);
     void handleRevokeDeviceCall(const api::RevokeDeviceIn& data,
                                 api::MethodResultBuilder::Pointer result);
+    void handleCreateFileCall(const api::CreateFileIn& request,
+                              api::MethodResultBuilder::Pointer result);
 
     // Zone's handlers---------------------------------------------------------
     void handleNotifyActiveZoneCall(const std::string& caller,
index f87427e..ba839c9 100644 (file)
@@ -44,6 +44,7 @@ const std::string ZONE_PATH = "/tmp/ut-zone/";
 const std::string ZONE_NAME = "ut-zone";
 const std::string ZONE_TEMPLATE = VSM_TEST_TEMPLATES_INSTALL_DIR "/minimal.sh";
 const char* TEMPLATE_ARGS[] = {NULL};
+const std::int32_t DEFAULT_FILE_MODE = 0666;
 
 struct Fixture {
     utils::ScopedDir mLxcDirGuard;
@@ -267,15 +268,15 @@ BOOST_AUTO_TEST_CASE(CreateFile)
 
     // The test
     int fd;
-    BOOST_REQUIRE(lxc.createFile("./112.txt", O_RDWR, &fd));
+    BOOST_REQUIRE(lxc.createFile("./112.txt", O_RDWR, DEFAULT_FILE_MODE, &fd));
     BOOST_REQUIRE(::fcntl(fd, F_GETFD) != -1);
     BOOST_REQUIRE(::close(fd) != -1);
 
-    BOOST_REQUIRE(lxc.createFile("/2.txt", O_RDONLY, &fd));
+    BOOST_REQUIRE(lxc.createFile("/2.txt", O_RDONLY, DEFAULT_FILE_MODE, &fd));
     BOOST_REQUIRE(::fcntl(fd, F_GETFD) != -1);
     BOOST_REQUIRE(::close(fd) != -1);
 
-    BOOST_REQUIRE(lxc.createFile("/3.txt", O_WRONLY, &fd));
+    BOOST_REQUIRE(lxc.createFile("/3.txt", O_WRONLY, DEFAULT_FILE_MODE, &fd));
     BOOST_REQUIRE(::fcntl(fd, F_GETFD) != -1);
     BOOST_REQUIRE(::close(fd) != -1);
 
index 0283fd9..375f212 100644 (file)
@@ -58,6 +58,8 @@
 #include <mutex>
 #include <condition_variable>
 #include <boost/filesystem.hpp>
+#include <fcntl.h>
+#include <sys/stat.h>
 
 using namespace vasum;
 using namespace config;
@@ -74,6 +76,7 @@ const std::string TEST_CONFIG_PATH = CONFIG_DIR + "/test-daemon.conf";
 const std::string MISSING_CONFIG_PATH = CONFIG_DIR + "/missing-daemon.conf";
 const int EVENT_TIMEOUT = 5000;
 const int SIGNAL_PROPAGATE_TIME = 500; // ms
+const std::int32_t DEFAULT_FILE_MODE = 0666;
 //const int UNEXPECTED_EVENT_TIMEOUT = EVENT_TIMEOUT / 5;
 const std::string TEST_APP_NAME = "testapp";
 const std::string TEST_MESSAGE = "testmessage";
@@ -445,6 +448,24 @@ public:
                                                  "()");
     }
 
+    int callMethodCreateFile(const std::string& id,
+                             const std::string& path,
+                             const std::int32_t& flags,
+                             const std::int32_t& mode)
+    {
+        assert(isHost());
+        GVariant* parameters = g_variant_new("(ssii)", id.c_str(), path.c_str(), flags, mode);
+        GVariantPtr result = mClient->callMethod(api::dbus::BUS_NAME,
+                                                 api::dbus::OBJECT_PATH,
+                                                 api::dbus::INTERFACE,
+                                                 api::dbus::METHOD_CREATE_FILE,
+                                                 parameters,
+                                                 "(h)");
+        int fileFd = 0;
+        g_variant_get(result.get(), "(h)", &fileFd);
+        return fileFd;
+    }
+
 private:
     const int mId;
     DbusConnection::Pointer mClient;
@@ -599,6 +620,18 @@ public:
         return result->value;
     }
 
+    int callMethodCreateFile(const std::string& id,
+                             const std::string& path,
+                             const std::int32_t& flags,
+                             const std::int32_t& mode)
+    {
+        auto result = mClient.callSync<api::CreateFileIn, api::CreateFileOut>(
+            api::ipc::METHOD_CREATE_FILE,
+            std::make_shared<api::CreateFileIn>(api::CreateFileIn{id, path, flags, mode}),
+            EVENT_TIMEOUT*10);
+        return result->fd.value;
+    }
+
 private:
     ipc::epoll::ThreadDispatcher mDispatcher;
     ipc::Client mClient;
@@ -1294,4 +1327,54 @@ MULTI_FIXTURE_TEST_CASE(LockUnlockZone, F, ACCESSORS)
                             WhatEquals("Zone is not paused"));
 }
 
+MULTI_FIXTURE_TEST_CASE(CreateFile, F, ACCESSORS)
+{
+    ZonesManager cm(TEST_CONFIG_PATH);
+    cm.createZone("zone1", SIMPLE_TEMPLATE);
+    cm.restoreAll();
+
+    typename F::HostAccessory host;
+    int returnedFd = host.callMethodCreateFile("zone1", "/123.txt", O_RDWR, DEFAULT_FILE_MODE);
+    BOOST_REQUIRE(::fcntl(returnedFd, F_GETFD) != -1);
+    BOOST_REQUIRE(::close(returnedFd) != -1);
+
+    returnedFd = host.callMethodCreateFile("zone1", "/56.txt", O_RDONLY, DEFAULT_FILE_MODE);
+    BOOST_REQUIRE(::fcntl(returnedFd, F_GETFD) != -1);
+    BOOST_REQUIRE(::close(returnedFd) != -1);
+
+    returnedFd = host.callMethodCreateFile("zone1", "/89.txt", O_WRONLY, DEFAULT_FILE_MODE);
+    BOOST_REQUIRE(::fcntl(returnedFd, F_GETFD) != -1);
+    BOOST_REQUIRE(::close(returnedFd) != -1);
+}
+
+MULTI_FIXTURE_TEST_CASE(CreateWriteReadFile, F, ACCESSORS)
+{
+    ZonesManager cm(TEST_CONFIG_PATH);
+    cm.createZone("zone1", SIMPLE_TEMPLATE);
+    cm.restoreAll();
+
+    typename F::HostAccessory host;
+
+    // create file, make sure returned fd is correct
+    int returnedFd = host.callMethodCreateFile("zone1", "/test123.txt", O_RDWR, DEFAULT_FILE_MODE);
+    BOOST_REQUIRE(::fcntl(returnedFd, F_GETFD) != -1);
+
+    // write sth
+    BOOST_REQUIRE_MESSAGE(::write(returnedFd, FILE_CONTENT.c_str(), FILE_CONTENT.size()) > 0,
+                          "Failed to write to fd " << returnedFd << ": " << ::strerror(errno));
+
+    // go back to the beginning
+    BOOST_REQUIRE(::lseek(returnedFd, 0, SEEK_SET) != -1);
+
+    // read it back, zero-terminate it and verify
+    std::unique_ptr<char[]> retStr(new char[FILE_CONTENT.size()+1]);
+    BOOST_REQUIRE_MESSAGE(::read(returnedFd, retStr.get(), FILE_CONTENT.size()) > 0,
+                          "Failed to read from file: " << ::strerror(errno));
+    retStr[FILE_CONTENT.size()] = 0;
+    BOOST_REQUIRE(FILE_CONTENT.compare(retStr.get()) == 0);
+
+    // close
+    BOOST_REQUIRE(::close(returnedFd) != -1);
+}
+
 BOOST_AUTO_TEST_SUITE_END()