blob: cf35b3f4277703fb558ea8616ef3644db4bcf536 [file] [log] [blame]
Colin Cross5edee2a2014-01-23 14:22:48 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
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#include <ctype.h>
Iliyan Malchev3ea902f2015-05-01 14:05:04 -070018#include <errno.h>
Colin Cross5edee2a2014-01-23 14:22:48 -080019#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sys/mount.h>
Iliyan Malchev3ea902f2015-05-01 14:05:04 -070023#include <unistd.h>
Colin Cross5edee2a2014-01-23 14:22:48 -080024
25#include "fs_mgr_priv.h"
26
27struct fs_mgr_flag_values {
28 char *key_loc;
Sami Tolvanen946a0f32015-03-22 12:40:05 +000029 char *verity_loc;
Colin Cross5edee2a2014-01-23 14:22:48 -080030 long long part_length;
31 char *label;
32 int partnum;
33 int swap_prio;
34 unsigned int zram_size;
35};
36
37struct flag_list {
38 const char *name;
39 unsigned flag;
40};
41
42static struct flag_list mount_flags[] = {
43 { "noatime", MS_NOATIME },
44 { "noexec", MS_NOEXEC },
45 { "nosuid", MS_NOSUID },
46 { "nodev", MS_NODEV },
47 { "nodiratime", MS_NODIRATIME },
48 { "ro", MS_RDONLY },
49 { "rw", 0 },
50 { "remount", MS_REMOUNT },
51 { "bind", MS_BIND },
52 { "rec", MS_REC },
53 { "unbindable", MS_UNBINDABLE },
54 { "private", MS_PRIVATE },
55 { "slave", MS_SLAVE },
56 { "shared", MS_SHARED },
57 { "defaults", 0 },
58 { 0, 0 },
59};
60
61static struct flag_list fs_mgr_flags[] = {
62 { "wait", MF_WAIT },
63 { "check", MF_CHECK },
64 { "encryptable=",MF_CRYPT },
Paul Lawrence2e5ae0a2014-04-04 09:34:19 -070065 { "forceencrypt=",MF_FORCECRYPT },
Paul Lawrencec410b3b2015-03-26 15:49:42 +000066 { "fileencryption",MF_FILEENCRYPTION },
Colin Cross5edee2a2014-01-23 14:22:48 -080067 { "nonremovable",MF_NONREMOVABLE },
68 { "voldmanaged=",MF_VOLDMANAGED},
69 { "length=", MF_LENGTH },
70 { "recoveryonly",MF_RECOVERYONLY },
71 { "swapprio=", MF_SWAPPRIO },
72 { "zramsize=", MF_ZRAMSIZE },
73 { "verify", MF_VERIFY },
74 { "noemulatedsd", MF_NOEMULATEDSD },
JP Abgrallfe6fcc42015-03-05 17:30:53 -080075 { "notrim", MF_NOTRIM },
Chris Fries79f33842013-09-05 13:19:21 -050076 { "formattable", MF_FORMATTABLE },
Daniel Rosenberg8bb2f362015-08-27 13:42:20 -070077 { "slotselect", MF_SLOTSELECT },
Colin Cross5edee2a2014-01-23 14:22:48 -080078 { "defaults", 0 },
79 { 0, 0 },
80};
81
Iliyan Malchev3ea902f2015-05-01 14:05:04 -070082static uint64_t calculate_zram_size(unsigned int percentage)
83{
84 uint64_t total;
85
86 total = sysconf(_SC_PHYS_PAGES);
87 total *= percentage;
88 total /= 100;
89
90 total *= sysconf(_SC_PAGESIZE);
91
92 return total;
93}
94
Colin Cross5edee2a2014-01-23 14:22:48 -080095static int parse_flags(char *flags, struct flag_list *fl,
96 struct fs_mgr_flag_values *flag_vals,
97 char *fs_options, int fs_options_len)
98{
99 int f = 0;
100 int i;
101 char *p;
102 char *savep;
103
104 /* initialize flag values. If we find a relevant flag, we'll
105 * update the value */
106 if (flag_vals) {
107 memset(flag_vals, 0, sizeof(*flag_vals));
108 flag_vals->partnum = -1;
109 flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
110 }
111
112 /* initialize fs_options to the null string */
113 if (fs_options && (fs_options_len > 0)) {
114 fs_options[0] = '\0';
115 }
116
117 p = strtok_r(flags, ",", &savep);
118 while (p) {
119 /* Look for the flag "p" in the flag list "fl"
120 * If not found, the loop exits with fl[i].name being null.
121 */
122 for (i = 0; fl[i].name; i++) {
123 if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
124 f |= fl[i].flag;
125 if ((fl[i].flag == MF_CRYPT) && flag_vals) {
126 /* The encryptable flag is followed by an = and the
127 * location of the keys. Get it and return it.
128 */
129 flag_vals->key_loc = strdup(strchr(p, '=') + 1);
Sami Tolvanen946a0f32015-03-22 12:40:05 +0000130 } else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
131 /* If the verify flag is followed by an = and the
132 * location for the verity state, get it and return it.
133 */
134 char *start = strchr(p, '=');
135 if (start) {
136 flag_vals->verity_loc = strdup(start + 1);
137 }
Paul Lawrence2e5ae0a2014-04-04 09:34:19 -0700138 } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
139 /* The forceencrypt flag is followed by an = and the
140 * location of the keys. Get it and return it.
141 */
142 flag_vals->key_loc = strdup(strchr(p, '=') + 1);
Colin Cross5edee2a2014-01-23 14:22:48 -0800143 } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
144 /* The length flag is followed by an = and the
145 * size of the partition. Get it and return it.
146 */
147 flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
148 } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
149 /* The voldmanaged flag is followed by an = and the
150 * label, a colon and the partition number or the
151 * word "auto", e.g.
152 * voldmanaged=sdcard:3
153 * Get and return them.
154 */
155 char *label_start;
156 char *label_end;
157 char *part_start;
158
159 label_start = strchr(p, '=') + 1;
160 label_end = strchr(p, ':');
161 if (label_end) {
162 flag_vals->label = strndup(label_start,
163 (int) (label_end - label_start));
164 part_start = strchr(p, ':') + 1;
165 if (!strcmp(part_start, "auto")) {
166 flag_vals->partnum = -1;
167 } else {
168 flag_vals->partnum = strtol(part_start, NULL, 0);
169 }
170 } else {
171 ERROR("Warning: voldmanaged= flag malformed\n");
172 }
173 } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
174 flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
175 } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
Iliyan Malchev3ea902f2015-05-01 14:05:04 -0700176 int is_percent = !!strrchr(p, '%');
177 unsigned int val = strtoll(strchr(p, '=') + 1, NULL, 0);
178 if (is_percent)
179 flag_vals->zram_size = calculate_zram_size(val);
180 else
181 flag_vals->zram_size = val;
Colin Cross5edee2a2014-01-23 14:22:48 -0800182 }
183 break;
184 }
185 }
186
187 if (!fl[i].name) {
188 if (fs_options) {
189 /* It's not a known flag, so it must be a filesystem specific
190 * option. Add it to fs_options if it was passed in.
191 */
192 strlcat(fs_options, p, fs_options_len);
193 strlcat(fs_options, ",", fs_options_len);
194 } else {
195 /* fs_options was not passed in, so if the flag is unknown
196 * it's an error.
197 */
198 ERROR("Warning: unknown flag %s\n", p);
199 }
200 }
201 p = strtok_r(NULL, ",", &savep);
202 }
203
Colin Cross5edee2a2014-01-23 14:22:48 -0800204 if (fs_options && fs_options[0]) {
205 /* remove the last trailing comma from the list of options */
206 fs_options[strlen(fs_options) - 1] = '\0';
207 }
208
209 return f;
210}
211
212struct fstab *fs_mgr_read_fstab(const char *fstab_path)
213{
214 FILE *fstab_file;
215 int cnt, entries;
216 ssize_t len;
217 size_t alloc_len = 0;
218 char *line = NULL;
219 const char *delim = " \t";
220 char *save_ptr, *p;
221 struct fstab *fstab = NULL;
Colin Cross5edee2a2014-01-23 14:22:48 -0800222 struct fs_mgr_flag_values flag_vals;
223#define FS_OPTIONS_LEN 1024
224 char tmp_fs_options[FS_OPTIONS_LEN];
225
226 fstab_file = fopen(fstab_path, "r");
227 if (!fstab_file) {
228 ERROR("Cannot open file %s\n", fstab_path);
229 return 0;
230 }
231
232 entries = 0;
233 while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
234 /* if the last character is a newline, shorten the string by 1 byte */
235 if (line[len - 1] == '\n') {
236 line[len - 1] = '\0';
237 }
238 /* Skip any leading whitespace */
239 p = line;
240 while (isspace(*p)) {
241 p++;
242 }
243 /* ignore comments or empty lines */
244 if (*p == '#' || *p == '\0')
245 continue;
246 entries++;
247 }
248
249 if (!entries) {
250 ERROR("No entries found in fstab\n");
251 goto err;
252 }
253
254 /* Allocate and init the fstab structure */
255 fstab = calloc(1, sizeof(struct fstab));
256 fstab->num_entries = entries;
257 fstab->fstab_filename = strdup(fstab_path);
258 fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));
259
260 fseek(fstab_file, 0, SEEK_SET);
261
262 cnt = 0;
263 while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
264 /* if the last character is a newline, shorten the string by 1 byte */
265 if (line[len - 1] == '\n') {
266 line[len - 1] = '\0';
267 }
268
269 /* Skip any leading whitespace */
270 p = line;
271 while (isspace(*p)) {
272 p++;
273 }
274 /* ignore comments or empty lines */
275 if (*p == '#' || *p == '\0')
276 continue;
277
278 /* If a non-comment entry is greater than the size we allocated, give an
279 * error and quit. This can happen in the unlikely case the file changes
280 * between the two reads.
281 */
282 if (cnt >= entries) {
283 ERROR("Tried to process more entries than counted\n");
284 break;
285 }
286
287 if (!(p = strtok_r(line, delim, &save_ptr))) {
288 ERROR("Error parsing mount source\n");
289 goto err;
290 }
291 fstab->recs[cnt].blk_device = strdup(p);
292
293 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
294 ERROR("Error parsing mount_point\n");
295 goto err;
296 }
297 fstab->recs[cnt].mount_point = strdup(p);
298
299 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
300 ERROR("Error parsing fs_type\n");
301 goto err;
302 }
303 fstab->recs[cnt].fs_type = strdup(p);
304
305 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
306 ERROR("Error parsing mount_flags\n");
307 goto err;
308 }
309 tmp_fs_options[0] = '\0';
310 fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
311 tmp_fs_options, FS_OPTIONS_LEN);
312
313 /* fs_options are optional */
314 if (tmp_fs_options[0]) {
315 fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
316 } else {
317 fstab->recs[cnt].fs_options = NULL;
318 }
319
320 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
321 ERROR("Error parsing fs_mgr_options\n");
322 goto err;
323 }
324 fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
325 &flag_vals, NULL, 0);
326 fstab->recs[cnt].key_loc = flag_vals.key_loc;
Sami Tolvanen946a0f32015-03-22 12:40:05 +0000327 fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
Colin Cross5edee2a2014-01-23 14:22:48 -0800328 fstab->recs[cnt].length = flag_vals.part_length;
329 fstab->recs[cnt].label = flag_vals.label;
330 fstab->recs[cnt].partnum = flag_vals.partnum;
331 fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
332 fstab->recs[cnt].zram_size = flag_vals.zram_size;
333 cnt++;
334 }
David Zeuthen227ef3c2015-09-03 12:23:12 -0400335 /* If an A/B partition, modify block device to be the real block device */
336 if (fs_mgr_update_for_slotselect(fstab) != 0) {
337 ERROR("Error updating for slotselect\n");
David Zeuthen744a8f82015-09-09 18:03:13 -0400338 goto err;
David Zeuthen227ef3c2015-09-03 12:23:12 -0400339 }
Colin Cross5edee2a2014-01-23 14:22:48 -0800340 fclose(fstab_file);
341 free(line);
342 return fstab;
343
344err:
345 fclose(fstab_file);
346 free(line);
347 if (fstab)
348 fs_mgr_free_fstab(fstab);
349 return NULL;
350}
351
352void fs_mgr_free_fstab(struct fstab *fstab)
353{
354 int i;
355
356 if (!fstab) {
357 return;
358 }
359
360 for (i = 0; i < fstab->num_entries; i++) {
361 /* Free the pointers return by strdup(3) */
362 free(fstab->recs[i].blk_device);
363 free(fstab->recs[i].mount_point);
364 free(fstab->recs[i].fs_type);
365 free(fstab->recs[i].fs_options);
366 free(fstab->recs[i].key_loc);
367 free(fstab->recs[i].label);
368 }
369
370 /* Free the fstab_recs array created by calloc(3) */
371 free(fstab->recs);
372
373 /* Free the fstab filename */
374 free(fstab->fstab_filename);
375
376 /* Free fstab */
377 free(fstab);
378}
379
380/* Add an entry to the fstab, and return 0 on success or -1 on error */
381int fs_mgr_add_entry(struct fstab *fstab,
382 const char *mount_point, const char *fs_type,
Sasha Levitskiycdc1cfb2014-04-10 17:10:21 -0700383 const char *blk_device)
Colin Cross5edee2a2014-01-23 14:22:48 -0800384{
385 struct fstab_rec *new_fstab_recs;
386 int n = fstab->num_entries;
387
388 new_fstab_recs = (struct fstab_rec *)
389 realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
390
391 if (!new_fstab_recs) {
392 return -1;
393 }
394
395 /* A new entry was added, so initialize it */
396 memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
397 new_fstab_recs[n].mount_point = strdup(mount_point);
398 new_fstab_recs[n].fs_type = strdup(fs_type);
399 new_fstab_recs[n].blk_device = strdup(blk_device);
400 new_fstab_recs[n].length = 0;
401
402 /* Update the fstab struct */
403 fstab->recs = new_fstab_recs;
404 fstab->num_entries++;
405
406 return 0;
407}
408
JP Abgrall5c01dac2014-06-18 14:54:37 -0700409/*
410 * Returns the 1st matching fstab_rec that follows the start_rec.
411 * start_rec is the result of a previous search or NULL.
412 */
413struct fstab_rec *fs_mgr_get_entry_for_mount_point_after(struct fstab_rec *start_rec, struct fstab *fstab, const char *path)
Colin Cross5edee2a2014-01-23 14:22:48 -0800414{
415 int i;
Colin Cross5edee2a2014-01-23 14:22:48 -0800416 if (!fstab) {
417 return NULL;
418 }
419
JP Abgrall5c01dac2014-06-18 14:54:37 -0700420 if (start_rec) {
421 for (i = 0; i < fstab->num_entries; i++) {
422 if (&fstab->recs[i] == start_rec) {
423 i++;
424 break;
425 }
426 }
427 } else {
428 i = 0;
429 }
430 for (; i < fstab->num_entries; i++) {
Colin Cross5edee2a2014-01-23 14:22:48 -0800431 int len = strlen(fstab->recs[i].mount_point);
432 if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
433 (path[len] == '\0' || path[len] == '/')) {
434 return &fstab->recs[i];
435 }
436 }
JP Abgrallf786fe52014-06-18 07:28:14 +0000437 return NULL;
JP Abgralla794f862014-06-17 16:58:46 -0700438}
439
JP Abgrall5c01dac2014-06-18 14:54:37 -0700440/*
441 * Returns the 1st matching mount point.
442 * There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after()
443 * and give the fstab_rec from the previous search.
444 */
445struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
446{
447 return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
448}
449
Paul Lawrencec410b3b2015-03-26 15:49:42 +0000450int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
Colin Cross5edee2a2014-01-23 14:22:48 -0800451{
452 return fstab->fs_mgr_flags & MF_VOLDMANAGED;
453}
454
Paul Lawrencec410b3b2015-03-26 15:49:42 +0000455int fs_mgr_is_nonremovable(const struct fstab_rec *fstab)
Colin Cross5edee2a2014-01-23 14:22:48 -0800456{
457 return fstab->fs_mgr_flags & MF_NONREMOVABLE;
458}
459
Paul Lawrencec410b3b2015-03-26 15:49:42 +0000460int fs_mgr_is_verified(const struct fstab_rec *fstab)
Paul Lawrencebbb36312014-10-09 14:22:49 +0000461{
462 return fstab->fs_mgr_flags & MF_VERIFY;
463}
464
Paul Lawrencec410b3b2015-03-26 15:49:42 +0000465int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
Colin Cross5edee2a2014-01-23 14:22:48 -0800466{
Paul Lawrence2e5ae0a2014-04-04 09:34:19 -0700467 return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT);
Colin Cross5edee2a2014-01-23 14:22:48 -0800468}
469
Paul Lawrencec410b3b2015-03-26 15:49:42 +0000470int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
471{
472 return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
473}
474
475int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
Colin Cross5edee2a2014-01-23 14:22:48 -0800476{
477 return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
478}
JP Abgrallfe6fcc42015-03-05 17:30:53 -0800479
Paul Lawrence84b0bab2015-03-26 14:53:19 +0000480int fs_mgr_is_notrim(struct fstab_rec *fstab)
JP Abgrallfe6fcc42015-03-05 17:30:53 -0800481{
482 return fstab->fs_mgr_flags & MF_NOTRIM;
483}
Ed Tame498c7c2015-04-13 18:01:33 -0700484
Chris Fries79f33842013-09-05 13:19:21 -0500485int fs_mgr_is_formattable(struct fstab_rec *fstab)
486{
487 return fstab->fs_mgr_flags & (MF_FORMATTABLE);
488}
Daniel Rosenbergeb65ce02015-09-01 12:41:18 -0700489
Daniel Rosenberg8bb2f362015-08-27 13:42:20 -0700490int fs_mgr_is_slotselect(struct fstab_rec *fstab)
491{
492 return fstab->fs_mgr_flags & MF_SLOTSELECT;
493}