api: add new api to register insert/remove callback
[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_ADDED:
42                 *state = STORAGE_STATE_UNMOUNTABLE;
43                 return 0;
44         case STORAGE_EXT_REMOVED:
45                 *state = STORAGE_STATE_REMOVED;
46                 return 0;
47         case STORAGE_EXT_CHANGED:
48                 switch (dev->state) {
49                 case STORAGE_EXT_UNMOUNTED:
50                         *state = STORAGE_STATE_UNMOUNTABLE;
51                         return 0;
52                 case STORAGE_EXT_MOUNTED:
53                         if (dev->flags & MOUNT_READONLY)
54                                 *state = STORAGE_STATE_MOUNTED_READ_ONLY;
55                         else
56                                 *state = STORAGE_STATE_MOUNTED;
57                         return 0;
58                 default:
59                         return -EINVAL;
60                 }
61         default:
62                 return -EINVAL;
63         }
64 }
65
66 int storage_ext_get_space(int storage_id,
67                 unsigned long long *total, unsigned long long *available)
68 {
69         storage_state_e state;
70         struct statvfs s;
71         int ret;
72         unsigned long long t = 0, a = 0;
73         storage_ext_device *dev;
74
75         if (storage_id < 0)
76                 return -ENOTSUP;
77
78         dev = calloc(1, sizeof(storage_ext_device));
79         if (!dev) {
80 //LCOV_EXCL_START System Error
81                 _E("calloc failed");
82                 return -ENOMEM;
83 //LCOV_EXCL_STOP
84         }
85
86         ret = storage_ext_get_device_info(storage_id, dev);
87         if (ret < 0) {
88                 _E("Cannot get the storage with id (%d, ret:%d)", storage_id, ret); //LCOV_EXCL_LINE
89                 goto out;
90         }
91
92         ret = storage_ext_get_dev_state(dev, STORAGE_EXT_CHANGED, &state);
93         if (ret < 0) {
94                 _E("Failed to get state of storage (id:%d, ret:%d)", storage_id, ret); //LCOV_EXCL_LINE
95                 goto out;
96         }
97
98         if (state >= STORAGE_STATE_MOUNTED) {
99 #ifndef __USE_FILE_OFFSET64
100                 ret = storage_get_external_memory_size_with_path(dev->mount_point, &s);
101 #else
102                 ret = storage_get_external_memory_size64_with_path(dev->mount_point, &s);
103 #endif
104                 if (ret < 0) {
105                         _E("Failed to get external memory size of (%s)(ret:%d)", dev->mount_point, ret); //LCOV_EXCL_LINE
106                         goto out;
107                 }
108
109                 t = (unsigned long long)s.f_frsize*s.f_blocks;
110                 a = (unsigned long long)s.f_bsize*s.f_bavail;
111         }
112
113         if (total)
114                 *total = t;
115         if (available)
116                 *available = a;
117
118         ret = 0;
119 out:
120         storage_ext_release_device(&dev);
121         return ret;
122 }
123
124 int storage_ext_foreach_device_list(storage_device_supported_cb callback, void *user_data)
125 {
126         int ret;
127         bool ret_cb;
128         dd_list *list = NULL, *elem;
129         storage_ext_device *dev;
130         storage_state_e state;
131
132         if (!callback)
133                 return -EINVAL;
134
135         ret = storage_ext_get_list(&list);
136         if (ret < 0) {
137                 _E("Failed to get external storage list from deviced (%d)", errno); //LCOV_EXCL_LINE
138                 return ret;
139         }
140
141         DD_LIST_FOREACH(list, elem, dev) {
142                 ret = storage_ext_get_dev_state(dev, STORAGE_EXT_CHANGED, &state);
143                 if (ret < 0) {
144                         _E("Failed to get storage state (devnode:%s, ret:%d)", dev->devnode, ret); //LCOV_EXCL_LINE
145                         continue;
146                 }
147
148                 ret_cb = callback(dev->storage_id, STORAGE_TYPE_EXTERNAL,
149                                 state, dev->mount_point, user_data);
150                 if (!ret_cb)
151                         break;
152         }
153
154         if (list)
155                 storage_ext_release_list(&list);
156         return 0;
157 }
158
159 //LCOV_EXCL_START Not called Callback
160 static int storage_ext_id_changed(storage_ext_device *dev, enum storage_ext_state blk_state, void *data)
161 {
162         enum storage_cb_type type = (enum storage_cb_type)data;
163         struct storage_cb_info *cb_info;
164         dd_list *elem;
165         storage_state_e state;
166         int ret;
167
168         if (!dev)
169                 return -EINVAL;
170
171         if (type != STORAGE_CALLBACK_ID)
172                 return 0;
173
174         ret = storage_ext_get_dev_state(dev, blk_state, &state);
175         if (ret < 0) {
176                 _E("Failed to get storage state (devnode:%s, ret:%d)", dev->devnode, ret);
177                 return ret;
178         }
179
180         DD_LIST_FOREACH(cb_list[STORAGE_CALLBACK_ID], elem, cb_info)
181                 cb_info->state_cb(cb_info->id, state, cb_info->user_data);
182
183         return 0;
184 }
185
186 static int storage_ext_type_changed(storage_ext_device *dev, enum storage_ext_state blk_state, void *data)
187 {
188         enum storage_cb_type type = (enum storage_cb_type)data;
189         struct storage_cb_info *cb_info;
190         dd_list *elem;
191         storage_state_e state;
192         int ret;
193         storage_dev_e strdev;
194         const char *fstype, *fsuuid, *mountpath;
195
196         if (!dev)
197                 return -EINVAL;
198
199         if (type != STORAGE_CALLBACK_TYPE)
200                 return -EINVAL;
201
202         ret = storage_ext_get_dev_state(dev, blk_state, &state);
203         if (ret < 0) {
204                 _E("Failed to get storage state (devnode:%s, ret:%d)", dev->devnode, ret);
205                 return ret;
206         }
207
208         if (dev->type == STORAGE_EXT_SCSI)
209                 strdev = STORAGE_DEV_EXT_USB_MASS_STORAGE;
210         else if (dev->type == STORAGE_EXT_MMC)
211                 strdev = STORAGE_DEV_EXT_SDCARD;
212         else {
213                 _E("Invalid dev type (%d)", dev->type);
214                 return -EINVAL;
215         }
216
217         fstype = (dev->fs_type ? (const char *)dev->fs_type : "");
218         fsuuid = (dev->fs_uuid ? (const char *)dev->fs_uuid : "");
219         mountpath = (dev->mount_point ? (const char *)dev->mount_point : "");
220
221         DD_LIST_FOREACH(cb_list[STORAGE_CALLBACK_TYPE], elem, cb_info)
222                 if (cb_info->type_cb)
223                         cb_info->type_cb(dev->storage_id, strdev, state,
224                                         fstype, fsuuid, mountpath, dev->primary,
225                                         dev->flags, cb_info->user_data);
226
227         return 0;
228 }
229
230 //LCOV_EXCL_STOP
231
232 static bool check_if_callback_exist(enum storage_cb_type type,
233                 struct storage_cb_info *info, struct storage_cb_info **cb_data)
234 {
235         struct storage_cb_info *cb_info;
236         dd_list *elem;
237
238         if (!info)
239                 return false;
240
241         if (type == STORAGE_CALLBACK_ID) {
242                 DD_LIST_FOREACH(cb_list[type], elem, cb_info) {
243                         if (cb_info->id == info->id &&
244                             cb_info->state_cb == info->state_cb) {
245                                 goto out;
246                         }
247                 }
248         }
249
250         if (type == STORAGE_CALLBACK_TYPE) {
251                 DD_LIST_FOREACH(cb_list[type], elem, cb_info) {
252                         if (cb_info->type == info->type &&
253                             cb_info->type_cb == info->type_cb)
254                                 goto out;
255                 }
256         }
257
258         return false;
259
260 out:
261         if (cb_data)
262                 *cb_data = cb_info;
263
264         return true;
265 }
266
267 int storage_ext_register_cb(enum storage_cb_type type, struct storage_cb_info *info)
268 {
269         struct storage_cb_info *cb_info;
270         int n, ret;
271         storage_ext_changed_cb callback;
272
273         if (!info)
274                 return -EINVAL;
275
276         switch (type) {
277         case STORAGE_CALLBACK_ID:
278                 callback = storage_ext_id_changed;
279                 break;
280         case STORAGE_CALLBACK_TYPE:
281                 callback = storage_ext_type_changed;
282                 break;
283         default:
284                 _E("Invalid callback type (%d)", type);
285                 return -EINVAL;
286         }
287
288         n = DD_LIST_LENGTH(cb_list[type]);
289         if (n == 0) {
290                 ret = storage_ext_register_device_change(callback, (void *)type);
291                 if (ret < 0)
292                         return -EPERM;
293         }
294
295         if (check_if_callback_exist(type, info, NULL)) {
296                 _E("The callback is already registered");
297                 return 0;
298         }
299
300         /* add device changed callback to list (local) */
301         cb_info = malloc(sizeof(struct storage_cb_info));
302         if (!cb_info)
303                 return -errno;
304
305         memcpy(cb_info, info, sizeof(struct storage_cb_info));
306         DD_LIST_APPEND(cb_list[type], cb_info);
307
308         return 0;
309 }
310
311 int storage_ext_unregister_cb(enum storage_cb_type type, struct storage_cb_info *info)
312 {
313         struct storage_cb_info *cb_info;
314         int n;
315         storage_ext_changed_cb callback;
316
317         if (!info)
318                 return -EINVAL;
319
320         switch (type) {
321         case STORAGE_CALLBACK_ID:
322                 callback = storage_ext_id_changed;
323                 break;
324         case STORAGE_CALLBACK_TYPE:
325                 callback = storage_ext_type_changed;
326                 break;
327         default:
328                 _E("Invalid callback type (%d)", type);
329                 return -EINVAL;
330         }
331
332         if (!check_if_callback_exist(type, info, &cb_info)) {
333                 _E("The callback is not registered");
334                 return 0;
335         }
336
337         /* remove device callback from list (local) */
338         if (cb_info) {
339                 DD_LIST_REMOVE(cb_list[type], cb_info);
340                 free(cb_info);
341         }
342
343         /* check if this callback is last element */
344         n = DD_LIST_LENGTH(cb_list[type]);
345         if (n == 0)
346                 storage_ext_unregister_device_change(callback);
347
348         return 0;
349 }
350
351 int storage_ext_get_root(int storage_id, char *path, size_t len)
352 {
353         storage_ext_device *dev;
354         int ret;
355
356         if (storage_id < 0)
357                 return -ENOTSUP;
358
359         if (!path)
360                 return -EINVAL;
361
362         dev = calloc(1, sizeof(storage_ext_device));
363         if (!dev) {
364 //LCOV_EXCL_START System Error
365                 _E("calloc failed");
366                 return -ENOMEM;
367 //LCOV_EXCL_STOP
368         }
369
370         ret = storage_ext_get_device_info(storage_id, dev);
371         if (ret < 0) {
372                 _E("Cannot get the storage with id (%d, ret:%d)", storage_id, ret); //LCOV_EXCL_LINE
373                 goto out;
374         }
375
376         snprintf(path, len, "%s", dev->mount_point);
377         ret = 0;
378
379 out:
380         storage_ext_release_device(&dev);
381         return ret;
382 }
383
384 int storage_ext_get_state(int storage_id, storage_state_e *state)
385 {
386         storage_ext_device *dev;
387         int ret;
388
389         if (storage_id < 0)
390                 return -ENOTSUP;
391
392         if (!state)
393                 return -EINVAL;
394
395         dev = calloc(1, sizeof(storage_ext_device));
396         if (!dev) {
397 //LCOV_EXCL_START System Error
398                 _E("calloc failed");
399                 return -ENOMEM;
400 //LCOV_EXCL_STOP
401         }
402
403         ret = storage_ext_get_device_info(storage_id, dev);
404         if (ret < 0) {
405                 _E("Cannot get the storage with id (%d, ret:%d)", storage_id, ret); //LCOV_EXCL_LINE
406                 goto out;
407         }
408
409         ret = storage_ext_get_dev_state(dev, STORAGE_EXT_CHANGED, state);
410         if (ret < 0)
411                 _E("Failed to get state of storage id (%d, ret:%d)", storage_id, ret); //LCOV_EXCL_LINE
412
413 out:
414         storage_ext_release_device(&dev);
415         return ret;
416 }
417
418 int storage_ext_get_primary_mmc_path(char *path, size_t len)
419 {
420         dd_list *list = NULL, *elem;
421         storage_ext_device *dev;
422         int ret;
423
424         ret = storage_ext_get_list(&list);
425         if (ret < 0) {
426                 _E("Failed to get external storage list from deviced (%d)", errno); //LCOV_EXCL_LINE
427                 return ret;
428         }
429
430         DD_LIST_FOREACH(list, elem, dev) {
431                 if (dev->primary) {
432                         snprintf(path, len, "%s", dev->mount_point);
433                         ret = 0;
434                         goto out;
435                 }
436         }
437
438         ret = -ENODEV;
439
440 out:
441         if (list)
442                 storage_ext_release_list(&list);
443         return ret;
444 }