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