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