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