#include <errno.h>
#include <gio/gio.h>
#include <glib.h>
+#include <assert.h>
#include <limits.h>
+#include <sys/statvfs.h>
+#include <tzplatform_config.h>
#include "log.h"
#include "storage-external-dbus.h"
#define CHECK_STR(a) (a ? a : "")
-#define STORAGE_EXT_GET_LIST "GetDeviceList"
+#define STORAGE_EXT_GET_LIST "GetDeviceList"
+#define STORAGE_EXT_GET_STATVFS "GetStatvfs"
+#define STORAGE_EXT_GET_STORAGE_LEVEL "GetStorageLevel"
+
+#define STORAGE_EXT_DEVICE_CHANGED "DeviceChanged"
+#define STORAGE_EXT_DEVICE_ADDED "DeviceAdded"
+#define STORAGE_EXT_DEVICE_REMOVED "DeviceRemoved"
+#define STORAGE_EXT_DEVICE_BLOCKED "DeviceBlocked"
#define DBUS_REPLY_TIMEOUT (-1)
+struct storage_ext_callback {
+ storage_ext_changed_cb func;
+ void *data;
+ guint block_id;
+};
+
+typedef struct {
+ dbus_pending_cb func;
+ void *data;
+} pending_call_data;
+
+static dd_list *changed_list;
+
static void storage_ext_release_internal(storage_ext_device *dev)
{
if (!dev)
conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
if (!conn) {
+//LCOV_EXCL_START System Error
if (err) {
_E("fail to get dbus connection : %s", err->message);
g_clear_error(&err);
} else
_E("fail to get dbus connection");
return NULL;
+//LCOV_EXCL_STOP
}
return conn;
}
-static GVariant *dbus_method_call_sync(const gchar *dest, const gchar *path,
+static void _cb_pending(GDBusConnection *conn,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GVariant *reply = NULL;
+ GError *err = NULL;
+ pending_call_data *data = (pending_call_data *)user_data;
+
+ reply = g_dbus_connection_call_finish(conn, res, &err);
+ if (!reply || err) {
+ if (!err)
+ g_set_error(&err, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Error during g_dbus_connection_call");
+
+ if (data && data->func)
+ data->func(NULL, data->data, err);
+ goto out;
+ }
+
+ if (data && data->func)
+ data->func(reply, data->data, err);
+out:
+ if (err)
+ g_error_free(err);
+ if (data)
+ free(data);
+}
+
+int dbus_method_async_with_reply_var(const char *dest, const char *path,
+ const char *iface, const char *method, GVariant *param,
+ dbus_pending_cb cb, int timeout, void *data)
+{
+ GDBusConnection *conn;
+ pending_call_data *pdata = NULL;
+ int ret = 0;
+
+ if (!dest || !path || !iface || !method)
+ return -EINVAL;
+
+ if (timeout < -1) {
+ _E("wrong timeout %d", timeout);
+ return -EINVAL;
+ }
+
+ conn = get_dbus_connection();
+ if (!conn) {
+ _E("fail to get dbus connection"); //LCOV_EXCL_LINE
+ return -1;
+ }
+
+ if (cb) {
+ pdata = (pending_call_data*)malloc(sizeof(pending_call_data));
+ if (!pdata) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ pdata->func = cb;
+ pdata->data = data;
+ }
+
+ g_dbus_connection_call(conn, dest, path, iface, method,
+ param, NULL, G_DBUS_CALL_FLAGS_NONE, timeout, NULL,
+ (GAsyncReadyCallback)_cb_pending,
+ pdata);
+
+ return ret;
+err:
+ if (param)
+ g_variant_unref(param);
+ return ret;
+}
+
+GVariant *dbus_method_call_sync(const gchar *dest, const gchar *path,
const gchar *iface, const gchar *method, GVariant *param)
{
GDBusConnection *conn;
GError *err = NULL;
GVariant *ret;
- if (!dest || !path || !iface || !method || !param)
+ if (!dest || !path || !iface || !method)
return NULL;
conn = get_dbus_connection();
if (!conn) {
- _E("fail to get dbus connection");
+ _E("fail to get dbus connection"); //LCOV_EXCL_LINE
return NULL;
}
param, NULL, G_DBUS_CALL_FLAGS_NONE,
-1, NULL, &err);
if (!ret) {
+//LCOV_EXCL_START System Error
if (err) {
_E("dbus method sync call failed(%s)", err->message);
g_clear_error(&err);
} else
_E("g_dbus_connection_call_sync() failed");
return NULL;
+//LCOV_EXCL_STOP
}
return ret;
STORAGE_EXT_GET_LIST,
g_variant_new("(s)", "all"));
if (!result) {
- _E("Failed to get storage_ext device info");
+ _E("Failed to get storage_ext device info"); //LCOV_EXCL_LINE
return -EIO;
}
elem = (storage_ext_device *)malloc(sizeof(storage_ext_device));
if (!elem) {
- _E("malloc() failed");
+ _E("malloc() failed"); //LCOV_EXCL_LINE
ret = -ENOMEM;
goto out;
}
out:
if (ret < 0)
- storage_ext_release_list(list);
+ storage_ext_release_list(list); //LCOV_EXCL_LINE System Error
g_variant_iter_free(iter);
g_variant_unref(result);
return ret;
}
+
+int storage_ext_get_statvfs(char *path, struct statvfs_32 *buf)
+{
+ GVariant *result;
+ guint64 bsize, frsize, blocks, bfree, bavail, files, ffree, favail, fsid, flag, namemax;
+
+ assert(buf);
+
+ memset(buf, 0, sizeof(struct statvfs_32));
+
+ result = dbus_method_call_sync(STORAGE_EXT_BUS_NAME,
+ STORAGE_EXT_PATH_STORAGE,
+ STORAGE_EXT_IFACE_STORAGE,
+ STORAGE_EXT_GET_STATVFS,
+ g_variant_new("(s)", path));
+ if (!result) {
+ _E("Failed to get storage_ext device info"); //LCOV_EXCL_LINE
+ return -EIO;
+ }
+
+ g_variant_get(result, "(ttttttttttt)",
+ &bsize, &frsize, &blocks,
+ &bfree, &bavail, &files,
+ &ffree, &favail, &fsid,
+ &flag, &namemax);
+// %llu bsize, frsize, blocks, bfree, bavail, files, ffree, favail, fsid, flag, namemax
+
+ buf->f_bsize = (unsigned long)bsize;
+ buf->f_frsize = (unsigned long)frsize;
+ buf->f_blocks = (unsigned long)blocks;
+ buf->f_bfree = (unsigned long)bfree;
+ buf->f_bavail = (unsigned long)bavail;
+ buf->f_files = (unsigned long)files;
+ buf->f_ffree = (unsigned long)ffree;
+ buf->f_favail = (unsigned long)favail;
+ buf->f_fsid = (unsigned long)fsid;
+ buf->f_flag = (unsigned long)flag;
+ buf->f_namemax = (unsigned long)namemax;
+
+// %lu buf->f_bsize, buf->f_frsize, buf->f_blocks, buf->f_bfree, buf->f_bavail, buf->f_files, buf->f_ffree, buf->f_favail, buf->f_fsid, buf->f_flag, buf->f_namemax
+ return 0;
+}
+
+int storage_ext_get_statvfs_size64(char *path, struct statvfs *buf)
+{
+ GVariant *result;
+
+ assert(buf);
+
+ memset(buf, 0, sizeof(struct statvfs));
+
+ result = dbus_method_call_sync(STORAGE_EXT_BUS_NAME,
+ STORAGE_EXT_PATH_STORAGE,
+ STORAGE_EXT_IFACE_STORAGE,
+ STORAGE_EXT_GET_STATVFS,
+ g_variant_new("(s)", path));
+ if (!result) {
+ _E("Failed to get storage_ext device info"); //LCOV_EXCL_LINE
+ return -EIO;
+ }
+
+ g_variant_get(result, "(ttttttttttt)",
+ &(buf->f_bsize), &(buf->f_frsize), &(buf->f_blocks),
+ &(buf->f_bfree), &(buf->f_bavail), &(buf->f_files),
+ &(buf->f_ffree), &(buf->f_favail), &(buf->f_fsid),
+ &(buf->f_flag), &(buf->f_namemax));
+
+// %lu buf->f_bsize, buf->f_frsize, buf->f_fsid, buf->f_flag, buf->f_namemax
+// %llu buf->f_blocks, buf->f_bfree, buf->f_bavail, buf->f_files, buf->f_ffree, buf->f_favail
+
+ return 0;
+}
+
+int storage_ext_get_storage_level(const char *path, char **level)
+{
+ GVariant *result;
+ char *tmp;
+ enum tzplatform_variable id;
+
+ if (!strcmp(path, tzplatform_getenv(TZ_SYS_USER)))
+ id = TZ_SYS_USER;
+ else if (!strcmp(path, tzplatform_getenv(TZ_SYS_TMP)))
+ id = TZ_SYS_TMP;
+ else if (!strcmp(path, tzplatform_getenv(TZ_SYS_OPT)))
+ id = TZ_SYS_OPT;
+ else {
+ _E("Invalid path");
+ return -EINVAL;
+ }
+
+ result = dbus_method_call_sync(STORAGE_EXT_BUS_NAME,
+ STORAGE_EXT_PATH_STORAGE,
+ STORAGE_EXT_IFACE_STORAGE,
+ STORAGE_EXT_GET_STORAGE_LEVEL,
+ g_variant_new("(i)", id));
+ if (!result) {
+ _E("Failed to get %d level", id);
+ return -EIO;
+ }
+
+ g_variant_get(result, "(s)", &tmp);
+ *level = strdup(tmp);
+ if (*level == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+//LCOV_EXCL_START Not called Callback
+static void storage_ext_device_changed(GVariant *params, enum storage_ext_state state, gpointer user_data)
+{
+ storage_ext_device *dev;
+ dd_list *elem;
+ struct storage_ext_callback *callback;
+ int ret;
+
+ if (!params)
+ return;
+
+ dev = calloc(1, sizeof(storage_ext_device));
+ if (!dev)
+ return;
+
+ g_variant_get(params, "(issssssisibii)",
+ &dev->type,
+ &dev->devnode,
+ &dev->syspath,
+ &dev->fs_usage,
+ &dev->fs_type,
+ &dev->fs_version,
+ &dev->fs_uuid,
+ &dev->readonly,
+ &dev->mount_point,
+ &dev->state,
+ &dev->primary,
+ &dev->flags,
+ &dev->storage_id);
+
+ /* Callback is called when unmount is started(DeviceBlocked signal) */
+ if (state == STORAGE_EXT_CHANGED && dev->state == STORAGE_EXT_UNMOUNTED) {
+ storage_ext_release_device(&dev);
+ return;
+ }
+
+ DD_LIST_FOREACH(changed_list, elem, callback) {
+ if (!callback->func)
+ continue;
+ ret = callback->func(dev, state, callback->data);
+ if (ret < 0)
+ _E("Failed to call callback for devnode(%s, %d)", dev->devnode, ret);
+ }
+
+ storage_ext_release_device(&dev);
+}
+//LCOV_EXCL_STOP
+
+//LCOV_EXCL_START Not called Callback
+static void storage_ext_changed(GDBusConnection *conn,
+ const gchar *sender,
+ const gchar *path,
+ const gchar *iface,
+ const gchar *signal,
+ GVariant *params,
+ gpointer user_data)
+{
+ size_t iface_len, signal_len;
+ enum storage_ext_state state;
+
+ if (!params || !sender || !path || !iface || !signal)
+ return;
+
+ iface_len = strlen(iface) + 1;
+ signal_len = strlen(signal) + 1;
+
+ if (strncmp(iface, STORAGE_EXT_IFACE_MANAGER, iface_len))
+ return;
+
+ if (!strncmp(signal, STORAGE_EXT_DEVICE_CHANGED, signal_len))
+ state = STORAGE_EXT_CHANGED;
+
+ else if (!strncmp(signal, STORAGE_EXT_DEVICE_ADDED, signal_len))
+ state = STORAGE_EXT_ADDED;
+
+ else if (!strncmp(signal, STORAGE_EXT_DEVICE_REMOVED, signal_len))
+ state = STORAGE_EXT_REMOVED;
+
+ else if (!strncmp(signal, STORAGE_EXT_DEVICE_BLOCKED, signal_len))
+ state = STORAGE_EXT_BLOCKED;
+
+ else
+ return;
+
+ storage_ext_device_changed(params, state, user_data);
+}
+//LCOV_EXCL_STOP
+
+int storage_ext_register_device_change(storage_ext_changed_cb func, void *data)
+{
+ GDBusConnection *conn;
+ guint block_id = 0;
+ struct storage_ext_callback *callback;
+ dd_list *elem;
+
+ if (!func)
+ return -EINVAL;
+
+ DD_LIST_FOREACH(changed_list, elem, callback) {
+ if (callback->func != func)
+ continue;
+ if (callback->block_id == 0)
+ continue;
+
+ return -EEXIST;
+ }
+
+ callback = (struct storage_ext_callback *)malloc(sizeof(struct storage_ext_callback));
+ if (!callback) {
+ //LCOV_EXCL_START System Error
+ _E("malloc() failed");
+ return -ENOMEM;
+ //LCOV_EXCL_STOP
+ }
+
+ conn = get_dbus_connection();
+ if (!conn) {
+ //LCOV_EXCL_START System Error
+ free(callback);
+ _E("Failed to get dbus connection");
+ return -EPERM;
+ //LCOV_EXCL_STOP
+ }
+
+ block_id = g_dbus_connection_signal_subscribe(conn,
+ NULL,
+ STORAGE_EXT_IFACE_MANAGER,
+ NULL,
+ NULL,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ storage_ext_changed,
+ NULL,
+ NULL);
+ if (block_id == 0) {
+ //LCOV_EXCL_START System Error
+ free(callback);
+ _E("Failed to subscrive bus signal");
+ return -EPERM;
+ //LCOV_EXCL_STOP
+ }
+
+ callback->func = func;
+ callback->data = data;
+ callback->block_id = block_id;
+
+ DD_LIST_APPEND(changed_list, callback);
+
+ return 0;
+}
+
+void storage_ext_unregister_device_change(storage_ext_changed_cb func)
+{
+ GDBusConnection *conn;
+ struct storage_ext_callback *callback;
+ dd_list *elem;
+ dd_list *elem_n;
+
+ if (!func)
+ return;
+
+ conn = get_dbus_connection();
+ if (!conn) {
+//LCOV_EXCL_START System Error
+ _E("fail to get dbus connection");
+ return;
+//LCOV_EXCL_STOP
+ }
+
+ DD_LIST_FOREACH_SAFE(changed_list, elem, elem_n, callback) {
+ if (callback->func != func)
+ continue;
+ if (callback->block_id > 0)
+ g_dbus_connection_signal_unsubscribe(conn, callback->block_id);
+
+ DD_LIST_REMOVE(changed_list, callback);
+ free(callback);
+ }
+}
+
+int storage_ext_get_device_info(int storage_id, storage_ext_device *info)
+{
+ GVariant *result;
+
+ result = dbus_method_call_sync(STORAGE_EXT_BUS_NAME,
+ STORAGE_EXT_PATH_MANAGER,
+ STORAGE_EXT_IFACE_MANAGER,
+ "GetDeviceInfo",
+ g_variant_new("(i)", storage_id));
+ if (!result) {
+ _E("There is no storage with the storage id (%d)", storage_id); //LCOV_EXCL_LINE
+ return -ENODEV;
+ }
+
+ if (g_variant_check_format_string(result, "(issssssisibii)", true)) {
+ g_variant_get(result, "(issssssisibii)",
+ &info->type, &info->devnode, &info->syspath,
+ &info->fs_usage, &info->fs_type,
+ &info->fs_version, &info->fs_uuid,
+ &info->readonly, &info->mount_point,
+ &info->state, &info->primary,
+ &info->flags, &info->storage_id);
+ } else {
+ _E("No storage with the storage id (%d)", storage_id); //LCOV_EXCL_LINE
+ return -ENODEV;
+ }
+
+ if (info->storage_id < 0) {
+ _E("No storage with the storage id (%d)", storage_id); //LCOV_EXCL_LINE
+ return -ENODEV;
+ }
+
+ g_variant_unref(result);
+
+ return 0;
+}