struct CreateFileIn {
std::string id;
std::string path;
+ int32_t flags;
int32_t mode;
CONFIG_REGISTER
(
id,
path,
+ flags,
mode
)
};
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;
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;
/**
* 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;
#include "config/is-visitable.hpp"
#include "config/exception.hpp"
#include "config/is-union.hpp"
+#include "config/types.hpp"
#include <string>
#include <vector>
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)
{
#include "config/is-visitable.hpp"
#include "config/is-union.hpp"
+#include "config/types.hpp"
#include <string>
#include <vector>
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)
*/
//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
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 */,
" <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>"
setFileMoveCallback(std::bind(&ZonesManager::handleFileMoveCall,
mZonesManagerPtr, "", _1, _2));
+
+ setCreateFileCallback(std::bind(&ZonesManager::handleCreateFileCall,
+ mZonesManagerPtr, _1, _2));
}
HostIPCConnection::~HostIPCConnection()
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,
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;
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,
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,
#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,
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,
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;
// 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);
#include <mutex>
#include <condition_variable>
#include <boost/filesystem.hpp>
+#include <fcntl.h>
+#include <sys/stat.h>
using namespace vasum;
using namespace config;
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";
"()");
}
+ 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;
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;
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()