Fix documentation for storage_get_internal_memory_size and storage_get_external_memor...
[platform/core/system/libstorage.git] / src / statvfs.c
1 /*
2  * storage
3  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
4  *
5  * Licensed under the Apache License, Version 2.0 (the License);
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <stdbool.h>
22 #include <string.h>
23 #include <sys/statvfs.h>
24 #include <sys/stat.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <mntent.h>
28 #include <tzplatform_config.h>
29
30 #include "log.h"
31 #include "common.h"
32 #include "storage-external.h"
33
34 #define MEMORY_GIGABYTE_VALUE  1073741824
35 #define MEMORY_MEGABYTE_VALUE  1048576
36
37 #define EXTERNAL_MEMORY_NODE   "sdcard"
38 #define STORAGE_CONF_FILE      "/etc/storage/libstorage.conf"
39
40 /* it's for 32bit file offset */
41 struct statvfs_32 {
42         unsigned long int f_bsize;
43         unsigned long int f_frsize;
44         unsigned long int f_blocks;
45         unsigned long int f_bfree;
46         unsigned long int f_bavail;
47         unsigned long int f_files;
48         unsigned long int f_ffree;
49         unsigned long int f_favail;
50         unsigned long int f_fsid;
51 #ifdef _STATVFSBUF_F_UNUSED
52         int __f_unused;
53 #endif
54         unsigned long int f_flag;
55         unsigned long int f_namemax;
56         int __f_spare[6];
57 };
58
59 #define MAX_LINE    128
60 #define MAX_SECTION 64
61 #define WHITESPACE  " \t"
62 #define NEWLINE     "\n\r"
63 #define COMMENT     '#'
64
65 #define MATCH(a, b)    (!strncmp(a, b, strlen(a)))
66 #define SET_CONF(a, b) (a = (b > 0.0 ? b : a))
67
68 struct parse_result {
69         char *section;
70         char *name;
71         char *value;
72 };
73
74 struct storage_config_info {
75         double total_size;
76         double check_size;
77         double reserved_size;
78 };
79
80 static struct storage_config_info storage_info;
81
82 static inline char *trim_str(char *s)
83 {
84         char *t;
85         /* left trim */
86         s += strspn(s, WHITESPACE);
87
88         /* right trim */
89         for (t = strchr(s, 0); t > s; t--)
90                 if (!strchr(WHITESPACE, t[-1]))
91                         break;
92         *t = 0;
93         return s;
94 }
95
96 static int config_parse(const char *file_name, int cb(struct parse_result *result,
97                         void *user_data), void *user_data)
98 {
99         FILE *f = NULL;
100         struct parse_result result;
101         /* use stack for parsing */
102         char line[MAX_LINE];
103         char section[MAX_SECTION];
104         char *start, *end, *name, *value;
105         int lineno = 0, ret = 0;
106
107         if (!file_name || !cb) {
108                 ret = -EINVAL;
109                 goto error;
110         }
111
112         /* open conf file */
113         f = fopen(file_name, "r");
114         if (!f) {
115                 _E("Failed to open file %s", file_name); //LCOV_EXCL_LINE
116                 ret = -EIO;
117                 goto error;
118         }
119
120         /* parsing line by line */
121         while (fgets(line, MAX_LINE, f) != NULL) {
122                 lineno++;
123
124                 start = line;
125                 start[strcspn(start, NEWLINE)] = '\0';
126                 start = trim_str(start);
127
128                 if (*start == COMMENT) {
129                         continue;
130                 } else if (*start == '[') {
131                         /* parse section */
132                         end = strchr(start, ']');
133                         if (!end || *end != ']') {
134                                 ret = -EBADMSG;
135                                 goto error;
136                         }
137
138                         *end = '\0';
139                         strncpy(section, start + 1, sizeof(section));
140                         section[MAX_SECTION-1] = '\0';
141                 } else if (*start) {
142                         /* parse name & value */
143                         end = strchr(start, '=');
144                         if (!end || *end != '=') {
145                                 ret = -EBADMSG;
146                                 goto error;
147                         }
148                         *end = '\0';
149                         name = trim_str(start);
150                         value = trim_str(end + 1);
151                         end = strchr(value, COMMENT);
152                         if (end && *end == COMMENT) {
153                                 *end = '\0';
154                                 value = trim_str(value);
155                         }
156
157                         result.section = section;
158                         result.name = name;
159                         result.value = value;
160                         /* callback with parse result */
161                         ret = cb(&result, user_data);
162                         if (ret < 0) {
163                                 ret = -EBADMSG;
164                                 goto error;
165                         }
166                 }
167         }
168         _D("Success to load %s", file_name);
169         fclose(f);
170         return 0;
171
172 error:
173         if (f)
174                 fclose(f);
175         _E("Failed to read %s:%d!", file_name, lineno); //LCOV_EXCL_LINE
176         return ret;
177 }
178
179 static int load_config(struct parse_result *result, void *user_data)
180 {
181         static int check_size = -1;
182         struct storage_config_info *info = (struct storage_config_info *)user_data;
183         char *name;
184         char *value;
185
186         if (!info)
187                 return -EINVAL;
188
189         if (!MATCH(result->section, "STORAGE"))
190                 return -EINVAL;
191
192         name = result->name;
193         value = result->value;
194
195         if (info->check_size > 0 && check_size < 0)
196                 check_size = (storage_info.total_size < info->check_size) ? 1 : 0;
197         if (MATCH(name, "CHECK_SIZE"))
198                 info->check_size = atoi(value);
199         else if (check_size == 0 && MATCH(name, "RESERVE"))
200                 info->reserved_size = atoi(value);
201         else if (check_size == 1 && MATCH(name, "RESERVE_LITE"))
202                 info->reserved_size = atoi(value);
203
204         return 0;
205 }
206
207 static void storage_config_load(struct storage_config_info *info)
208 {
209         int ret;
210
211         ret = config_parse(STORAGE_CONF_FILE, load_config, info);
212         if (ret < 0)
213                 _E("Failed to load %s, %d Use default value!", STORAGE_CONF_FILE, ret); //LCOV_EXCL_LINE
214 }
215
216 static int get_memory_size(const char *path, struct statvfs_32 *buf)
217 {
218         struct statvfs s;
219         int ret;
220
221         assert(buf);
222
223         ret = statvfs(path, &s);
224         if (ret)
225                 return -errno; //LCOV_EXCL_LINE System Error
226
227         memset(buf, 0, sizeof(struct statvfs_32));
228
229         buf->f_bsize  = s.f_bsize;
230         buf->f_frsize = s.f_frsize;
231         buf->f_blocks = (unsigned long)s.f_blocks;
232         buf->f_bfree  = (unsigned long)s.f_bfree;
233         buf->f_bavail = (unsigned long)s.f_bavail;
234         buf->f_files  = (unsigned long)s.f_files;
235         buf->f_ffree  = (unsigned long)s.f_ffree;
236         buf->f_favail = (unsigned long)s.f_favail;
237         buf->f_fsid = s.f_fsid;
238         buf->f_flag = s.f_flag;
239         buf->f_namemax = s.f_namemax;
240
241         return 0;
242 }
243
244 /* This api is intended for binaries built with _FILE_OFFSET_BITS=32 */
245 API int storage_get_internal_memory_size(struct statvfs *buf)
246 {
247         struct statvfs_32 temp;
248         static unsigned long reserved = 0;
249         int ret;
250
251         if (!buf) {
252                 _E("input param error");
253                 return STORAGE_ERROR_INVALID_PARAMETER;
254         }
255
256         ret = get_memory_size(tzplatform_getenv(TZ_SYS_HOME), &temp);
257         if (ret || temp.f_bsize == 0) {
258                 _E("fail to get memory size %d", ret); //LCOV_EXCL_LINE
259                 return STORAGE_ERROR_OPERATION_FAILED; //LCOV_EXCL_LINE
260         }
261
262         if (reserved == 0) {
263                 storage_info.total_size = (double)temp.f_frsize * temp.f_blocks;
264                 storage_config_load(&storage_info);
265                 reserved = (unsigned long)storage_info.reserved_size;
266                 reserved = reserved/temp.f_bsize;
267                 _I("total %4.4lf check %4.4lf reserved %4.4lf",
268                         storage_info.total_size, storage_info.check_size, storage_info.reserved_size);
269         }
270         if (temp.f_bavail < reserved)
271                 temp.f_bavail = 0;
272         else
273                 temp.f_bavail -= reserved;
274
275         memcpy(buf, &temp, sizeof(temp));
276         return STORAGE_ERROR_NONE;
277 }
278
279 /* This api is intended for binaries built with __USE_FILE_OFFSET64(_FILE_OFFSET_BITS=64) */
280 API int storage_get_internal_memory_size64(struct statvfs *buf)
281 {
282         static unsigned long reserved = 0;
283         int ret;
284
285         if (!buf) {
286                 _E("input param error"); //LCOV_EXCL_LINE
287                 return STORAGE_ERROR_INVALID_PARAMETER;
288         }
289
290         ret = statvfs(tzplatform_getenv(TZ_SYS_HOME), buf);
291         if (ret) {
292                 _E("fail to get memory size"); //LCOV_EXCL_LINE
293                 return STORAGE_ERROR_OPERATION_FAILED; //LCOV_EXCL_LINE
294         }
295
296         if (reserved == 0) {
297                 storage_info.total_size = (double)(buf->f_frsize * buf->f_blocks);
298                 storage_config_load(&storage_info);
299                 reserved = (unsigned long)storage_info.reserved_size;
300                 reserved = reserved/buf->f_bsize;
301                 _I("total %4.4lf check %4.4lf reserved %4.4lf",
302                         storage_info.total_size, storage_info.check_size, storage_info.reserved_size);
303         }
304         if (buf->f_bavail < reserved)
305                 buf->f_bavail = 0;
306         else
307                 buf->f_bavail -= reserved;
308         return STORAGE_ERROR_NONE;
309 }
310
311 static int mount_check(char *path)
312 {
313         int ret = false;
314         struct mntent *mnt;
315         const char *table = "/etc/mtab";
316         FILE *fp;
317
318         fp = setmntent(table, "r");
319         if (!fp)
320                 return ret;
321         while ((mnt = getmntent(fp))) {
322                 if (!strcmp(mnt->mnt_dir, path)) {
323                         ret = true;
324                         break;
325                 }
326         }
327         endmntent(fp);
328         return ret;
329 }
330
331 static int get_external_path(char *path, size_t len)
332 {
333         return storage_ext_get_primary_mmc_path(path, len);
334 }
335
336 /* This api is intended for binaries built with _FILE_OFFSET_BITS=32 */
337 int storage_get_external_memory_size_with_path(char *path, struct statvfs *buf)
338 {
339         struct statvfs_32 temp;
340         int ret;
341         char ext_path[32];
342
343         _D("storage_get_external_memory_size");
344         if (!buf) {
345                 _E("input param error");
346                 return STORAGE_ERROR_INVALID_PARAMETER;
347         }
348
349         if (path)
350                 snprintf(ext_path, sizeof(ext_path), "%s", path);
351         else {
352                 if (!storage_ext_is_supported()) {
353                         _D("Block module is not enabled");
354                         goto out_nodev;
355                 }
356                 ret = get_external_path(ext_path, sizeof(ext_path));
357                 if (ret == -ENODEV)
358                         goto out_nodev;
359                 if (ret < 0) {
360                         _E("Failed to get external path(%d)", ret); //LCOV_EXCL_LINE
361                         return STORAGE_ERROR_OPERATION_FAILED; //LCOV_EXCL_LINE
362                 }
363         }
364
365         if (!mount_check(ext_path))
366                 goto out_nodev;
367
368         ret = get_memory_size(ext_path, &temp);
369         if (ret) {
370                 _E("fail to get memory size"); //LCOV_EXCL_LINE
371                 return STORAGE_ERROR_OPERATION_FAILED; //LCOV_EXCL_LINE
372         }
373
374         memcpy(buf, &temp, sizeof(temp));
375         return 0;
376
377 out_nodev:
378         memset(buf, 0, sizeof(struct statvfs_32));
379         return STORAGE_ERROR_NONE;
380 }
381
382 /* This api is intended for binaries built with __USE_FILE_OFFSET64(_FILE_OFFSET_BITS=64) */
383 int storage_get_external_memory_size64_with_path(char *path, struct statvfs *buf)
384 {
385         int ret;
386         char ext_path[32];
387
388         _D("storage_get_external_memory_size64");
389         if (!buf) {
390                 _E("input param error");
391                 return STORAGE_ERROR_INVALID_PARAMETER;
392         }
393
394         if (path)
395                 snprintf(ext_path, sizeof(ext_path), "%s", path);
396         else {
397                 if (!storage_ext_is_supported()) {
398                         _D("Block module is not enabled");
399                         goto out_nodev;
400                 }
401                 ret = get_external_path(ext_path, sizeof(ext_path));
402                 if (ret == -ENODEV)
403                         goto out_nodev;
404                 if (ret < 0) {
405                         _E("Failed to get external path(%d)", ret);
406                         return STORAGE_ERROR_OPERATION_FAILED; //LCOV_EXCL_LINE
407                 }
408         }
409
410         if (!mount_check(ext_path))
411                 goto out_nodev;
412
413         ret = statvfs(ext_path, buf);
414         if (ret) {
415         //LCOV_EXCL_START System Error
416                 _E("fail to get memory size");
417                 return STORAGE_ERROR_OPERATION_FAILED; //LCOV_EXCL_LINE
418         //LCOV_EXCL_STOP
419         }
420
421         return STORAGE_ERROR_NONE;
422
423 out_nodev:
424         memset(buf, 0, sizeof(struct statvfs));
425         return 0;
426 }
427
428 API int storage_get_external_memory_size(struct statvfs *buf)
429 {
430         return storage_get_external_memory_size_with_path(NULL, buf);
431 }
432
433 API int storage_get_external_memory_size64(struct statvfs *buf)
434 {
435         return storage_get_external_memory_size64_with_path(NULL, buf);
436 }