Check if it is mounted before trying mount
[platform/core/appfw/app-installers.git] / src / common / tzip_interface.cc
1 // Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by an apache 2.0 license that can be
3 // found in the LICENSE file.
4
5 #include "common/tzip_interface.h"
6
7 #include <manifest_parser/utils/logging.h>
8
9 #include <boost/filesystem.hpp>
10
11 #include <gio/gio.h>
12 #include <glib.h>
13
14 #include <cstdlib>
15 #include <functional>
16
17 namespace bf = boost::filesystem;
18
19 namespace {
20
21 const char kTzipBusName[] = "org.tizen.system.deviced";
22 const char kTzipObjectPath[] = "/Org/Tizen/System/DeviceD/Tzip";
23 const char kTzipInterfaceName[] = "org.tizen.system.deviced.Tzip";
24 const char kTzipMountMethod[] = "Mount";
25 const char kTzipUnmountMethod[] = "Unmount";
26 const char kTzipIsMountedMethod[] = "IsMounted";
27 const char kTzipSmackRule[] = "User::Home";
28 const int kTzipMountMaximumRetryCount = 15;
29
30 }  // namespace
31
32 namespace common_installer {
33
34 class DBusProxy {
35  public:
36   DBusProxy() : conn_(nullptr), proxy_(nullptr) {
37     if (!Init())
38       LOG(ERROR) << "Failed to initialize DBus proxy";
39   }
40
41   ~DBusProxy() {
42     if (proxy_)
43       g_object_unref(proxy_);
44     if (conn_) {
45       g_dbus_connection_flush_sync(conn_, nullptr, nullptr);
46       g_object_unref(conn_);
47     }
48   }
49
50   bool Init() {
51     GError* error = nullptr;
52     conn_ = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &error);
53     if (!conn_ || error) {
54       LOG(ERROR) << "g_bus_get_sync() failed: " << error->message;
55       g_error_free(error);
56       return false;
57     }
58     proxy_ = g_dbus_proxy_new_sync(conn_, G_DBUS_PROXY_FLAGS_NONE, nullptr,
59         kTzipBusName, kTzipObjectPath, kTzipInterfaceName, nullptr, &error);
60     if (!proxy_ || error) {
61       LOG(ERROR) << "g_dbus_proxy_new_sync() failed: " << error->message;
62       g_error_free(error);
63       return false;
64     }
65     return true;
66   }
67
68   GVariant* ProxyCallSync(const char* method, GVariant* params) {
69     if (!proxy_) {
70       LOG(ERROR) << "DBus proxy is not initialized";
71       return nullptr;
72     }
73     GError* error = nullptr;
74     GVariant* ret = g_dbus_proxy_call_sync(proxy_, method,
75         params, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error);
76     if (error) {
77       LOG(ERROR) << "g_dbus_proxy_call_sync() of " << method << " failed: "
78                  << error->message;
79       g_error_free(error);
80     }
81     return ret;
82   }
83
84  private:
85   GDBusConnection* conn_;
86   GDBusProxy* proxy_;
87 };
88
89 class TzipInterface::Pimpl {
90  public:
91   explicit Pimpl(const boost::filesystem::path& mount_path) :
92     mount_path_(mount_path), dbus_proxy_(new DBusProxy()) { }
93
94   bool MountZip(const boost::filesystem::path& zip_path) {
95     zip_path_ = zip_path;
96
97     if (IsMounted()) {
98       LOG(WARNING) << "Mount path(" << mount_path_
99           << ") already mounted, try to unmount";
100       UnmountZip();
101     }
102
103     if (bf::exists(mount_path_)) {
104       LOG(WARNING) << "Mount path(" << mount_path_ << ") already exists! "
105                    << "We will remove it...";
106       bf::remove(mount_path_);
107     }
108
109     if (!bf::exists(zip_path)) {
110       LOG(WARNING) << "zip path(" << zip_path << ") doesn't exist!";
111       return false;
112     }
113
114     const char* mount_path_str = mount_path_.string().c_str();
115     const char* zip_path_str = zip_path_.string().c_str();
116     const char* tzip_smack_rule = kTzipSmackRule;
117
118     GVariant* r = dbus_proxy_->ProxyCallSync(kTzipMountMethod,
119         g_variant_new("(sss)", mount_path_str, zip_path_str, tzip_smack_rule));
120     if (!r) {
121       LOG(ERROR) << "Could not send DBUS message when mounting zip file";
122       return false;
123     }
124     g_variant_unref(r);
125
126     return WaitForCondition([=]()->bool {
127       return IsMounted();
128     });
129   }
130
131   bool UnmountZip() {
132     const char* mount_path_str = mount_path_.string().c_str();
133
134     GVariant* r = dbus_proxy_->ProxyCallSync(kTzipUnmountMethod,
135         g_variant_new("(s)", mount_path_str));
136     if (!r) {
137       LOG(ERROR) << "Could not send DBUS message when unmounting zip file";
138       return false;
139     }
140     g_variant_unref(r);
141
142     return WaitForCondition([=]()->bool {
143       return IsUnmounted();
144     });
145   }
146
147  private:
148   bool IsMounted() {
149     const char* mount_path_str = mount_path_.string().c_str();
150     GVariant* r = dbus_proxy_->ProxyCallSync(kTzipIsMountedMethod,
151         g_variant_new("(s)", mount_path_str));
152     if (!r)
153       return false;
154
155     gint ret;
156     g_variant_get(r, "(i)", &ret);
157     g_variant_unref(r);
158     return ret != 0;
159   }
160
161   bool IsUnmounted() {
162     return !IsMounted();
163   }
164
165   bool WaitForCondition(std::function<bool()> condition) {
166     if (!mount_path_.empty()) {
167       bool rv = false;
168       int cnt = 0;
169       while (cnt < kTzipMountMaximumRetryCount) {
170         rv = condition();
171         if (rv)
172           break;
173         sleep(1);
174         cnt++;
175       }
176
177       if (!rv) {
178         return false;
179       }
180     }
181     return true;
182   }
183
184   boost::filesystem::path mount_path_;
185   boost::filesystem::path zip_path_;
186   std::unique_ptr<DBusProxy> dbus_proxy_;
187 };
188
189 TzipInterface::TzipInterface(const boost::filesystem::path& mount_path)
190   : impl_(new Pimpl(mount_path)) {}
191
192 TzipInterface::~TzipInterface() { }
193
194 bool TzipInterface::MountZip(const boost::filesystem::path& zip_path) {
195   return impl_->MountZip(zip_path);
196 }
197
198 bool TzipInterface::UnmountZip() {
199   return impl_->UnmountZip();
200 }
201
202 }  // namespace common_installer