libstorage: Add memory size check routine
[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
29 #include "log.h"
30 #include "common.h"
31
32 #define MEMORY_GIGABYTE_VALUE  1073741824
33 #define MEMORY_MEGABYTE_VALUE  1048576
34
35 #define MEMORY_STATUS_USR_PATH "/opt/usr"
36 #define EXTERNAL_MEMORY_PATH   "/opt/storage/sdcard"
37 #define STORAGE_CONF_FILE      "/etc/storage/libstorage.conf"
38
39 /* it's for 32bit file offset */
40 struct statvfs_32 {
41         unsigned long int f_bsize;
42         unsigned long int f_frsize;
43         unsigned long int f_blocks;
44         unsigned long int f_bfree;
45         unsigned long int f_bavail;
46         unsigned long int f_files;
47         unsigned long int f_ffree;
48         unsigned long int f_favail;
49         unsigned long int f_fsid;
50 #ifdef _STATVFSBUF_F_UNUSED
51         int __f_unused;
52 #endif
53         unsigned long int f_flag;
54         unsigned long int f_namemax;
55         int __f_spare[6];
56 };
57
58 #define MAX_LINE    128
59 #define MAX_SECTION 64
60 #define WHITESPACE  " \t"
61 #define NEWLINE     "\n\r"
62 #define COMMENT     '#'
63
64 #define MATCH(a, b)    (!strncmp(a, b, strlen(a)))
65 #define SET_CONF(a, b) (a = (b > 0.0 ? b : a))
66
67 struct parse_result {
68         char *section;
69         char *name;
70         char *value;
71 };
72
73 struct storage_config_info {
74         double total_size;
75         double check_size;
76         double reserved_size;
77 };
78
79 static struct storage_config_info storage_info;
80
81 static inline char *trim_str(char *s)
82 {
83         char *t;
84         /* left trim */
85         s += strspn(s, WHITESPACE);
86
87         /* right trim */
88         for (t = strchr(s, 0); t > s; t--)
89                 if (!strchr(WHITESPACE, t[-1]))
90                         break;
91         *t = 0;
92         return s;
93 }
94
95 static int config_parse(const char *file_name, int cb(struct parse_result *result,
96     void *user_data), void *user_data)
97 {
98         FILE *f = NULL;
99         struct parse_result result;
100         /* use stack for parsing */
101         char line[MAX_LINE];
102         char section[MAX_SECTION];
103         char *start, *end, *name, *value;
104         int lineno = 0, ret = 0;
105
106         if (!file_name || !cb) {
107                 ret = -EINVAL;
108                 goto error;
109         }
110
111         /* open conf file */
112         f = fopen(file_name, "r");
113         if (!f) {
114                 _E("Failed to open file %s", file_name);
115                 ret = -EIO;
116                 goto error;
117         }
118
119         /* parsing line by line */
120         while (fgets(line, MAX_LINE, f) != NULL) {
121                 lineno++;
122
123                 start = line;
124                 start[strcspn(start, NEWLINE)] = '\0';
125                 start = trim_str(start);
126
127                 if (*start == COMMENT) {
128                         continue;
129                 } else if (*start == '[') {
130                         /* parse section */
131                         end = strchr(start, ']');
132                         if (!end || *end != ']') {
133                                 ret = -EBADMSG;
134                                 goto error;
135                         }
136
137                         *end = '\0';
138                         strncpy(section, start + 1, sizeof(section));
139                         section[MAX_SECTION-1] = '\0';
140                 } else if (*start) {
141                         /* parse name & value */
142                         end = strchr(start, '=');
143                         if (!end || *end != '=') {
144                                 ret = -EBADMSG;
145                                 goto error;
146                         }
147                         *end = '\0';
148                         name = trim_str(start);
149                         value = trim_str(end + 1);
150                         end = strchr(value, COMMENT);
151                         if (end && *end == COMMENT) {
152                                 *end = '\0';
153                                 value = trim_str(value);
154                         }
155
156                         result.section = section;
157                         result.name = name;
158                         result.value = value;
159                         /* callback with parse result */
160                         ret = cb(&result, user_data);
161                         if (ret < 0) {
162                                 ret = -EBADMSG;
163                                 goto error;
164                         }
165                 }
166         }
167         _D("Success to load %s", file_name);
168         fclose(f);
169         return 0;
170
171 error:
172         if (f)
173                 fclose(f);
174         _E("Failed to read %s:%d!", file_name, lineno);
175         return ret;
176 }
177
178 static int load_config(struct parse_result *result, void *user_data)
179 {
180         static int check_size = -1;
181         struct storage_config_info *info = (struct storage_config_info *)user_data;
182         char *name;
183         char *value;
184
185         if (!info)
186                 return -EINVAL;
187
188         if (!MATCH(result->section, "STORAGE"))
189                 return -EINVAL;
190
191         name = result->name;
192         value = result->value;
193
194         if (info->check_size > 0 && check_size < 0)
195                 check_size = (storage_info.total_size < info->check_size)? 1 : 0;
196         if (MATCH(name, "CHECK_SIZE"))
197                 info->check_size = atoi(value);
198         else if (check_size == 0 && MATCH(name, "RESERVE"))
199                 info->reserved_size = atoi(value);
200         else if (check_size == 1 && MATCH(name, "RESERVE_LITE"))
201                 info->reserved_size = atoi(value);
202
203         return 0;
204 }
205
206 static void storage_config_load(struct storage_config_info *info)
207 {
208         int ret;
209
210         ret = config_parse(STORAGE_CONF_FILE, load_config, info);
211         if (ret < 0)
212                 _E("Failed to load %s, %d Use default value!", STORAGE_CONF_FILE, ret);
213 }
214
215 static int get_memory_size(const char *path, struct statvfs_32 *buf)
216 {
217         struct statvfs s;
218         int ret;
219
220         assert(buf);
221
222         ret = statvfs(path, &s);
223         if (ret)
224                 return -errno;
225
226         buf->f_bsize  = s.f_bsize;
227         buf->f_frsize = s.f_frsize;
228         buf->f_blocks = (unsigned long)s.f_blocks;
229         buf->f_bfree  = (unsigned long)s.f_bfree;
230         buf->f_bavail = (unsigned long)s.f_bavail;
231         buf->f_files  = (unsigned long)s.f_files;
232         buf->f_ffree  = (unsigned long)s.f_ffree;
233         buf->f_favail = (unsigned long)s.f_favail;
234         buf->f_fsid = s.f_fsid;
235         buf->f_flag = s.f_flag;
236         buf->f_namemax = s.f_namemax;
237
238         return 0;
239 }
240
241 API int storage_get_internal_memory_size(struct statvfs *buf)
242 {
243         struct statvfs_32 temp;
244         static unsigned long reserved = 0;
245         int ret;
246
247         if (!buf) {
248                 _E("input param error");
249                 return -EINVAL;
250         }
251
252         ret = get_memory_size(MEMORY_STATUS_USR_PATH, &temp);
253         if (ret || temp.f_bsize == 0) {
254                 _E("fail to get memory size");
255                 return -errno;
256         }
257
258         if (reserved == 0) {
259                 storage_info.total_size = (double)temp.f_frsize * temp.f_blocks;
260                 storage_config_load(&storage_info);
261                 reserved = (unsigned long)storage_info.reserved_size;
262                 reserved = reserved/temp.f_bsize;
263                 _I("total %4.4lf check %4.4lf reserved %4.4lf",
264                         storage_info.total_size, storage_info.check_size, storage_info.reserved_size);
265         }
266         if (temp.f_bavail < reserved)
267                 temp.f_bavail = 0;
268         else
269                 temp.f_bavail -= reserved;
270
271         memcpy(buf, &temp, sizeof(temp));
272         return 0;
273 }
274
275 API int storage_get_internal_memory_size64(struct statvfs *buf)
276 {
277         static unsigned long reserved = 0;
278         int ret;
279
280         if (!buf) {
281                 _E("input param error");
282                 return -EINVAL;
283         }
284
285         ret = statvfs(MEMORY_STATUS_USR_PATH, buf);
286         if (ret) {
287                 _E("fail to get memory size");
288                 return -errno;
289         }
290
291         if (reserved == 0) {
292                 storage_info.total_size = (double)(buf->f_frsize * buf->f_blocks);
293                 storage_config_load(&storage_info);
294                 reserved = (unsigned long)storage_info.reserved_size;
295                 reserved = reserved/buf->f_bsize;
296                 _I("total %4.4lf check %4.4lf reserved %4.4lf",
297                         storage_info.total_size, storage_info.check_size, storage_info.reserved_size);
298         }
299         if (buf->f_bavail < reserved)
300                 buf->f_bavail = 0;
301         else
302                 buf->f_bavail -= reserved;
303         return 0;
304 }
305
306 static int mount_check(const char* path)
307 {
308         int ret = false;
309         struct mntent* mnt;
310         const char* table = "/etc/mtab";
311         FILE* fp;
312
313         fp = setmntent(table, "r");
314         if (!fp)
315                 return ret;
316         while ((mnt=getmntent(fp))) {
317                 if (!strcmp(mnt->mnt_dir, path)) {
318                         ret = true;
319                         break;
320                 }
321         }
322         endmntent(fp);
323         return ret;
324 }
325
326 API int storage_get_external_memory_size(struct statvfs *buf)
327 {
328         struct statvfs_32 temp;
329         int ret;
330
331         _D("storage_get_external_memory_size");
332         if (!buf) {
333                 _E("input param error");
334                 return -EINVAL;
335         }
336
337         if (!mount_check(EXTERNAL_MEMORY_PATH)) {
338                 memset(buf, 0, sizeof(struct statvfs_32));
339                 return 0;
340         }
341
342         ret = get_memory_size(EXTERNAL_MEMORY_PATH, &temp);
343         if (ret) {
344                 _E("fail to get memory size");
345                 return -errno;
346         }
347
348         memcpy(buf, &temp, sizeof(temp));
349         return 0;
350 }
351
352 API int storage_get_external_memory_size64(struct statvfs *buf)
353 {
354         int ret;
355
356         _D("storage_get_external_memory_size64");
357         if (!buf) {
358                 _E("input param error");
359                 return -EINVAL;
360         }
361
362         if (!mount_check(EXTERNAL_MEMORY_PATH)) {
363                 memset(buf, 0, sizeof(struct statvfs));
364                 return 0;
365         }
366
367         ret = statvfs(EXTERNAL_MEMORY_PATH, buf);
368         if (ret) {
369                 _E("fail to get memory size");
370                 return -errno;
371         }
372
373         return 0;
374 }