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