external: add functions to get storage size
[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);
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);
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);
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;
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 API int storage_get_internal_memory_size(struct statvfs *buf)
245 {
246         struct statvfs_32 temp;
247         static unsigned long reserved = 0;
248         int ret;
249
250         if (!buf) {
251                 _E("input param error");
252                 return -EINVAL;
253         }
254
255         ret = get_memory_size(tzplatform_getenv(TZ_SYS_HOME), &temp);
256         if (ret || temp.f_bsize == 0) {
257                 _E("fail to get memory size");
258                 return -errno;
259         }
260
261         if (reserved == 0) {
262                 storage_info.total_size = (double)temp.f_frsize * temp.f_blocks;
263                 storage_config_load(&storage_info);
264                 reserved = (unsigned long)storage_info.reserved_size;
265                 reserved = reserved/temp.f_bsize;
266                 _I("total %4.4lf check %4.4lf reserved %4.4lf",
267                         storage_info.total_size, storage_info.check_size, storage_info.reserved_size);
268         }
269         if (temp.f_bavail < reserved)
270                 temp.f_bavail = 0;
271         else
272                 temp.f_bavail -= reserved;
273
274         memcpy(buf, &temp, sizeof(temp));
275         return 0;
276 }
277
278 API int storage_get_internal_memory_size64(struct statvfs *buf)
279 {
280         static unsigned long reserved = 0;
281         int ret;
282
283         if (!buf) {
284                 _E("input param error");
285                 return -EINVAL;
286         }
287
288         ret = statvfs(tzplatform_getenv(TZ_SYS_HOME), buf);
289         if (ret) {
290                 _E("fail to get memory size");
291                 return -errno;
292         }
293
294         if (reserved == 0) {
295                 storage_info.total_size = (double)(buf->f_frsize * buf->f_blocks);
296                 storage_config_load(&storage_info);
297                 reserved = (unsigned long)storage_info.reserved_size;
298                 reserved = reserved/buf->f_bsize;
299                 _I("total %4.4lf check %4.4lf reserved %4.4lf",
300                         storage_info.total_size, storage_info.check_size, storage_info.reserved_size);
301         }
302         if (buf->f_bavail < reserved)
303                 buf->f_bavail = 0;
304         else
305                 buf->f_bavail -= reserved;
306         return 0;
307 }
308
309 static int mount_check(char *path)
310 {
311         int ret = false;
312         struct mntent *mnt;
313         const char *table = "/etc/mtab";
314         FILE *fp;
315
316         fp = setmntent(table, "r");
317         if (!fp)
318                 return ret;
319         while ((mnt = getmntent(fp))) {
320                 if (!strcmp(mnt->mnt_dir, path)) {
321                         ret = true;
322                         break;
323                 }
324         }
325         endmntent(fp);
326         return ret;
327 }
328
329 static int get_external_path(char *path, size_t len)
330 {
331         return storage_ext_get_primary_mmc_path(path, len);
332 }
333
334 int storage_get_external_memory_size_with_path(char *path, struct statvfs *buf)
335 {
336         struct statvfs_32 temp;
337         int ret;
338         char ext_path[32];
339
340         _D("storage_get_external_memory_size");
341         if (!buf) {
342                 _E("input param error");
343                 return -EINVAL;
344         }
345
346         if (path)
347                 snprintf(ext_path, sizeof(ext_path), "%s", path);
348         else {
349                 ret = get_external_path(ext_path, sizeof(ext_path));
350                 if (ret < 0) {
351                         _E("Failed to get external path(%d)", ret);
352                         return ret;
353                 }
354         }
355
356         if (!mount_check(ext_path)) {
357                 memset(buf, 0, sizeof(struct statvfs_32));
358                 return 0;
359         }
360
361         ret = get_memory_size(ext_path, &temp);
362         if (ret) {
363                 _E("fail to get memory size");
364                 return -errno;
365         }
366
367         memcpy(buf, &temp, sizeof(temp));
368         return 0;
369 }
370
371 int storage_get_external_memory_size64_with_path(char *path, struct statvfs *buf)
372 {
373         int ret;
374         char ext_path[32];
375
376         _D("storage_get_external_memory_size64");
377         if (!buf) {
378                 _E("input param error");
379                 return -EINVAL;
380         }
381
382         if (path)
383                 snprintf(ext_path, sizeof(ext_path), "%s", path);
384         else {
385                 ret = get_external_path(ext_path, sizeof(ext_path));
386                 if (ret < 0) {
387                         _E("Failed to get external path(%d)", ret);
388                         return ret;
389                 }
390         }
391
392         if (!mount_check(ext_path)) {
393                 memset(buf, 0, sizeof(struct statvfs));
394                 return 0;
395         }
396
397         ret = statvfs(ext_path, buf);
398         if (ret) {
399                 _E("fail to get memory size");
400                 return -errno;
401         }
402
403         return 0;
404 }
405
406 API int storage_get_external_memory_size(struct statvfs *buf)
407 {
408         return storage_get_external_memory_size_with_path(NULL, buf);
409 }
410
411 API int storage_get_external_memory_size64(struct statvfs *buf)
412 {
413         return storage_get_external_memory_size64_with_path(NULL, buf);
414 }