external: add storage change handler
[platform/core/system/libstorage.git] / src / storage-external.c
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <sys/statvfs.h>
23 #include <vconf.h>
24 #include <tzplatform_config.h>
25
26 #include "common.h"
27 #include "list.h"
28 #include "log.h"
29 #include "storage-external-dbus.h"
30
31 static dd_list *cb_list[STORAGE_CALLBACK_MAX];
32
33 static int storage_ext_get_dev_state(storage_ext_device *dev,
34                 enum storage_ext_state blk_state,
35                 storage_state_e *state)
36 {
37         if (!dev || !state)
38                 return -EINVAL;
39
40         switch (blk_state) {
41         case STORAGE_EXT_REMOVED:
42                 *state = STORAGE_STATE_REMOVED;
43                 return 0;
44         case STORAGE_EXT_CHANGED:
45                 switch (dev->state) {
46                 case STORAGE_EXT_UNMOUNTED:
47                         *state = STORAGE_STATE_UNMOUNTABLE;
48                         return 0;
49                 case STORAGE_EXT_MOUNTED:
50                         if (dev->flags & MOUNT_READONLY)
51                                 *state = STORAGE_STATE_MOUNTED_READ_ONLY;
52                         else
53                                 *state = STORAGE_STATE_MOUNTED;
54                         return 0;
55                 default:
56                         return -EINVAL;
57                 }
58         default:
59                 return -EINVAL;
60         }
61 }
62
63 int storage_ext_foreach_device_list(storage_device_supported_cb callback, void *user_data)
64 {
65         int ret;
66         bool ret_cb;
67         dd_list *list = NULL, *elem;
68         storage_ext_device *dev;
69         storage_state_e state;
70
71         if (!callback)
72                 return -EINVAL;
73
74         ret = storage_ext_get_list(&list);
75         if (ret < 0) {
76                 _E("Failed to get external storage list from deviced (%d)", errno);
77                 return ret;
78         }
79
80         DD_LIST_FOREACH(list, elem, dev) {
81                 ret = storage_ext_get_dev_state(dev, STORAGE_EXT_CHANGED, &state);
82                 if (ret < 0) {
83                         _E("Failed to get storage state (devnode:%s, ret:%d)", dev->devnode, ret);
84                         continue;
85                 }
86
87                 ret_cb = callback(dev->storage_id, STORAGE_TYPE_EXTERNAL,
88                                 state, dev->mount_point, user_data);
89                 if (!ret_cb)
90                         break;
91         }
92
93         if (list)
94                 storage_ext_release_list(&list);
95         return 0;
96 }
97
98 static int storage_ext_state_changed(storage_ext_device *dev, enum storage_ext_state blk_state, void *data)
99 {
100         enum storage_cb_type type = (enum storage_cb_type)data;
101         struct storage_cb_info *cb_info;
102         dd_list *elem;
103         storage_state_e state;
104         int ret;
105
106         if (!dev)
107                 return -EINVAL;
108
109         if (type != STORAGE_CALLBACK_STATE)
110                 return 0;
111
112         ret = storage_ext_get_dev_state(dev, blk_state, &state);
113         if (ret < 0) {
114                 _E("Failed to get storage state (devnode:%s, ret:%d)", dev->devnode, ret);
115                 return ret;
116         }
117
118         DD_LIST_FOREACH(cb_list[STORAGE_CALLBACK_STATE], elem, cb_info)
119                 cb_info->state_cb(cb_info->id, state, cb_info->user_data);
120
121         return 0;
122 }
123
124 int storage_ext_register_cb(enum storage_cb_type type, struct storage_cb_info *info)
125 {
126         struct storage_cb_info *cb_info;
127         dd_list *elem;
128         int ret, n;
129
130         if (type < 0 || type >= STORAGE_CALLBACK_MAX)
131                 return -EINVAL;
132
133         if (!info)
134                 return -EINVAL;
135
136         /* check if it is the first request */
137         n = DD_LIST_LENGTH(cb_list[type]);
138         if (n == 0) {
139                 ret = storage_ext_register_device_change(storage_ext_state_changed, (void *)type);
140                 if (ret < 0)
141                         return -EPERM;
142         }
143
144         /* check for the same request */
145         DD_LIST_FOREACH(cb_list[type], elem, cb_info) {
146                 if (cb_info->id == info->id &&
147                     cb_info->state_cb == info->state_cb)
148                         return -EEXIST;
149         }
150
151         /* add device changed callback to list (local) */
152         cb_info = malloc(sizeof(struct storage_cb_info));
153         if (!cb_info)
154                 return -errno;
155
156         memcpy(cb_info, info, sizeof(struct storage_cb_info));
157         DD_LIST_APPEND(cb_list[type], cb_info);
158
159         return 0;
160 }
161
162 int storage_ext_unregister_cb(enum storage_cb_type type, struct storage_cb_info *info)
163 {
164         struct storage_cb_info *cb_info;
165         dd_list *elem;
166         int n;
167
168         if (type < 0 || type >= STORAGE_CALLBACK_MAX)
169                 return -EINVAL;
170
171         if (!info)
172                 return -EINVAL;
173
174         /* search for the same element with callback */
175         DD_LIST_FOREACH(cb_list[type], elem, cb_info) {
176                 if (cb_info->id == info->id &&
177                     cb_info->state_cb == info->state_cb)
178                         break;
179         }
180
181         if (!cb_info)
182                 return -EINVAL;
183
184         /* remove device callback from list (local) */
185         DD_LIST_REMOVE(cb_list[type], cb_info);
186         free(cb_info);
187
188         /* check if this callback is last element */
189         n = DD_LIST_LENGTH(cb_list[type]);
190         if (n == 0)
191                 storage_ext_unregister_device_change(storage_ext_state_changed);
192
193         return 0;
194 }