blob: 21b4c740d6423f9f330021f3971322fd67149b3f [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;
Paul Lawrence4e898a02016-05-24 04:56:37 -070035 unsigned int file_encryption_mode;
Colin Cross5edee2a2014-01-23 14:22:48 -080036};
37
38struct flag_list {
39 const char *name;
Paul Lawrence4e898a02016-05-24 04:56:37 -070040 unsigned int flag;
Colin Cross5edee2a2014-01-23 14:22:48 -080041};
42
43static struct flag_list mount_flags[] = {
44 { "noatime", MS_NOATIME },
45 { "noexec", MS_NOEXEC },
46 { "nosuid", MS_NOSUID },
47 { "nodev", MS_NODEV },
48 { "nodiratime", MS_NODIRATIME },
49 { "ro", MS_RDONLY },
50 { "rw", 0 },
51 { "remount", MS_REMOUNT },
52 { "bind", MS_BIND },
53 { "rec", MS_REC },
54 { "unbindable", MS_UNBINDABLE },
55 { "private", MS_PRIVATE },
56 { "slave", MS_SLAVE },
57 { "shared", MS_SHARED },
58 { "defaults", 0 },
59 { 0, 0 },
60};
61
62static struct flag_list fs_mgr_flags[] = {
63 { "wait", MF_WAIT },
64 { "check", MF_CHECK },
65 { "encryptable=",MF_CRYPT },
Paul Lawrence2e5ae0a2014-04-04 09:34:19 -070066 { "forceencrypt=",MF_FORCECRYPT },
Paul Lawrence5b8a1082016-05-05 11:00:44 -070067 { "fileencryption=",MF_FILEENCRYPTION },
Paul Lawrenceb262d682015-10-29 10:31:02 -070068 { "forcefdeorfbe=",MF_FORCEFDEORFBE },
Colin Cross5edee2a2014-01-23 14:22:48 -080069 { "nonremovable",MF_NONREMOVABLE },
70 { "voldmanaged=",MF_VOLDMANAGED},
71 { "length=", MF_LENGTH },
72 { "recoveryonly",MF_RECOVERYONLY },
73 { "swapprio=", MF_SWAPPRIO },
74 { "zramsize=", MF_ZRAMSIZE },
75 { "verify", MF_VERIFY },
76 { "noemulatedsd", MF_NOEMULATEDSD },
JP Abgrallfe6fcc42015-03-05 17:30:53 -080077 { "notrim", MF_NOTRIM },
Chris Fries79f33842013-09-05 13:19:21 -050078 { "formattable", MF_FORMATTABLE },
Daniel Rosenberg8bb2f362015-08-27 13:42:20 -070079 { "slotselect", MF_SLOTSELECT },
Daniel Rosenbergd38e3c52016-04-07 20:10:25 -070080 { "nofail", MF_NOFAIL },
Wei Wang8e5c9b82016-08-23 11:58:09 -070081 { "latemount", MF_LATEMOUNT },
Colin Cross5edee2a2014-01-23 14:22:48 -080082 { "defaults", 0 },
83 { 0, 0 },
84};
85
Paul Lawrence4e898a02016-05-24 04:56:37 -070086#define EM_SOFTWARE 1
87#define EM_ICE 2
88
89static struct flag_list encryption_modes[] = {
90 {"software", EM_SOFTWARE},
91 {"ice", EM_ICE},
Paul Lawrence5b8a1082016-05-05 11:00:44 -070092 {0, 0}
93};
94
Iliyan Malchev3ea902f2015-05-01 14:05:04 -070095static uint64_t calculate_zram_size(unsigned int percentage)
96{
97 uint64_t total;
98
99 total = sysconf(_SC_PHYS_PAGES);
100 total *= percentage;
101 total /= 100;
102
103 total *= sysconf(_SC_PAGESIZE);
104
105 return total;
106}
107
Colin Cross5edee2a2014-01-23 14:22:48 -0800108static int parse_flags(char *flags, struct flag_list *fl,
109 struct fs_mgr_flag_values *flag_vals,
110 char *fs_options, int fs_options_len)
111{
112 int f = 0;
113 int i;
114 char *p;
115 char *savep;
116
117 /* initialize flag values. If we find a relevant flag, we'll
118 * update the value */
119 if (flag_vals) {
120 memset(flag_vals, 0, sizeof(*flag_vals));
121 flag_vals->partnum = -1;
122 flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
123 }
124
125 /* initialize fs_options to the null string */
126 if (fs_options && (fs_options_len > 0)) {
127 fs_options[0] = '\0';
128 }
129
130 p = strtok_r(flags, ",", &savep);
131 while (p) {
132 /* Look for the flag "p" in the flag list "fl"
133 * If not found, the loop exits with fl[i].name being null.
134 */
135 for (i = 0; fl[i].name; i++) {
136 if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
137 f |= fl[i].flag;
138 if ((fl[i].flag == MF_CRYPT) && flag_vals) {
139 /* The encryptable 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);
Sami Tolvanen946a0f32015-03-22 12:40:05 +0000143 } else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
144 /* If the verify flag is followed by an = and the
145 * location for the verity state, get it and return it.
146 */
147 char *start = strchr(p, '=');
148 if (start) {
149 flag_vals->verity_loc = strdup(start + 1);
150 }
Paul Lawrence2e5ae0a2014-04-04 09:34:19 -0700151 } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
152 /* The forceencrypt flag is followed by an = and the
153 * location of the keys. Get it and return it.
154 */
155 flag_vals->key_loc = strdup(strchr(p, '=') + 1);
Paul Lawrenceb262d682015-10-29 10:31:02 -0700156 } else if ((fl[i].flag == MF_FORCEFDEORFBE) && flag_vals) {
157 /* The forcefdeorfbe flag is followed by an = and the
158 * location of the keys. Get it and return it.
159 */
160 flag_vals->key_loc = strdup(strchr(p, '=') + 1);
Paul Lawrence4e898a02016-05-24 04:56:37 -0700161 flag_vals->file_encryption_mode = EM_SOFTWARE;
Paul Lawrence5b8a1082016-05-05 11:00:44 -0700162 } else if ((fl[i].flag == MF_FILEENCRYPTION) && flag_vals) {
163 /* The fileencryption flag is followed by an = and the
164 * type of the encryption. Get it and return it.
165 */
166 const struct flag_list *j;
Paul Lawrence4e898a02016-05-24 04:56:37 -0700167 const char *mode = strchr(p, '=') + 1;
168 for (j = encryption_modes; j->name; ++j) {
169 if (!strcmp(mode, j->name)) {
170 flag_vals->file_encryption_mode = j->flag;
Paul Lawrence5b8a1082016-05-05 11:00:44 -0700171 }
172 }
Paul Lawrence4e898a02016-05-24 04:56:37 -0700173 if (flag_vals->file_encryption_mode == 0) {
174 ERROR("Unknown file encryption mode: %s\n", mode);
Paul Lawrence5b8a1082016-05-05 11:00:44 -0700175 }
Colin Cross5edee2a2014-01-23 14:22:48 -0800176 } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
177 /* The length flag is followed by an = and the
178 * size of the partition. Get it and return it.
179 */
180 flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
181 } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
182 /* The voldmanaged flag is followed by an = and the
183 * label, a colon and the partition number or the
184 * word "auto", e.g.
185 * voldmanaged=sdcard:3
186 * Get and return them.
187 */
188 char *label_start;
189 char *label_end;
190 char *part_start;
191
192 label_start = strchr(p, '=') + 1;
193 label_end = strchr(p, ':');
194 if (label_end) {
195 flag_vals->label = strndup(label_start,
196 (int) (label_end - label_start));
197 part_start = strchr(p, ':') + 1;
198 if (!strcmp(part_start, "auto")) {
199 flag_vals->partnum = -1;
200 } else {
201 flag_vals->partnum = strtol(part_start, NULL, 0);
202 }
203 } else {
204 ERROR("Warning: voldmanaged= flag malformed\n");
205 }
206 } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
207 flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
208 } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
Iliyan Malchev3ea902f2015-05-01 14:05:04 -0700209 int is_percent = !!strrchr(p, '%');
210 unsigned int val = strtoll(strchr(p, '=') + 1, NULL, 0);
211 if (is_percent)
212 flag_vals->zram_size = calculate_zram_size(val);
213 else
214 flag_vals->zram_size = val;
Colin Cross5edee2a2014-01-23 14:22:48 -0800215 }
216 break;
217 }
218 }
219
220 if (!fl[i].name) {
221 if (fs_options) {
222 /* It's not a known flag, so it must be a filesystem specific
223 * option. Add it to fs_options if it was passed in.
224 */
225 strlcat(fs_options, p, fs_options_len);
226 strlcat(fs_options, ",", fs_options_len);
227 } else {
228 /* fs_options was not passed in, so if the flag is unknown
229 * it's an error.
230 */
231 ERROR("Warning: unknown flag %s\n", p);
232 }
233 }
234 p = strtok_r(NULL, ",", &savep);
235 }
236
Colin Cross5edee2a2014-01-23 14:22:48 -0800237 if (fs_options && fs_options[0]) {
238 /* remove the last trailing comma from the list of options */
239 fs_options[strlen(fs_options) - 1] = '\0';
240 }
241
242 return f;
243}
244
245struct fstab *fs_mgr_read_fstab(const char *fstab_path)
246{
247 FILE *fstab_file;
248 int cnt, entries;
249 ssize_t len;
250 size_t alloc_len = 0;
251 char *line = NULL;
252 const char *delim = " \t";
253 char *save_ptr, *p;
254 struct fstab *fstab = NULL;
Colin Cross5edee2a2014-01-23 14:22:48 -0800255 struct fs_mgr_flag_values flag_vals;
256#define FS_OPTIONS_LEN 1024
257 char tmp_fs_options[FS_OPTIONS_LEN];
258
259 fstab_file = fopen(fstab_path, "r");
260 if (!fstab_file) {
261 ERROR("Cannot open file %s\n", fstab_path);
262 return 0;
263 }
264
265 entries = 0;
266 while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
267 /* if the last character is a newline, shorten the string by 1 byte */
268 if (line[len - 1] == '\n') {
269 line[len - 1] = '\0';
270 }
271 /* Skip any leading whitespace */
272 p = line;
273 while (isspace(*p)) {
274 p++;
275 }
276 /* ignore comments or empty lines */
277 if (*p == '#' || *p == '\0')
278 continue;
279 entries++;
280 }
281
282 if (!entries) {
283 ERROR("No entries found in fstab\n");
284 goto err;
285 }
286
287 /* Allocate and init the fstab structure */
288 fstab = calloc(1, sizeof(struct fstab));
289 fstab->num_entries = entries;
290 fstab->fstab_filename = strdup(fstab_path);
291 fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));
292
293 fseek(fstab_file, 0, SEEK_SET);
294
295 cnt = 0;
296 while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
297 /* if the last character is a newline, shorten the string by 1 byte */
298 if (line[len - 1] == '\n') {
299 line[len - 1] = '\0';
300 }
301
302 /* Skip any leading whitespace */
303 p = line;
304 while (isspace(*p)) {
305 p++;
306 }
307 /* ignore comments or empty lines */
308 if (*p == '#' || *p == '\0')
309 continue;
310
311 /* If a non-comment entry is greater than the size we allocated, give an
312 * error and quit. This can happen in the unlikely case the file changes
313 * between the two reads.
314 */
315 if (cnt >= entries) {
316 ERROR("Tried to process more entries than counted\n");
317 break;
318 }
319
320 if (!(p = strtok_r(line, delim, &save_ptr))) {
321 ERROR("Error parsing mount source\n");
322 goto err;
323 }
324 fstab->recs[cnt].blk_device = strdup(p);
325
326 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
327 ERROR("Error parsing mount_point\n");
328 goto err;
329 }
330 fstab->recs[cnt].mount_point = strdup(p);
331
332 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
333 ERROR("Error parsing fs_type\n");
334 goto err;
335 }
336 fstab->recs[cnt].fs_type = strdup(p);
337
338 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
339 ERROR("Error parsing mount_flags\n");
340 goto err;
341 }
342 tmp_fs_options[0] = '\0';
343 fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
344 tmp_fs_options, FS_OPTIONS_LEN);
345
346 /* fs_options are optional */
347 if (tmp_fs_options[0]) {
348 fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
349 } else {
350 fstab->recs[cnt].fs_options = NULL;
351 }
352
353 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
354 ERROR("Error parsing fs_mgr_options\n");
355 goto err;
356 }
357 fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
358 &flag_vals, NULL, 0);
359 fstab->recs[cnt].key_loc = flag_vals.key_loc;
Sami Tolvanen946a0f32015-03-22 12:40:05 +0000360 fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
Colin Cross5edee2a2014-01-23 14:22:48 -0800361 fstab->recs[cnt].length = flag_vals.part_length;
362 fstab->recs[cnt].label = flag_vals.label;
363 fstab->recs[cnt].partnum = flag_vals.partnum;
364 fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
365 fstab->recs[cnt].zram_size = flag_vals.zram_size;
Paul Lawrence4e898a02016-05-24 04:56:37 -0700366 fstab->recs[cnt].file_encryption_mode = flag_vals.file_encryption_mode;
Colin Cross5edee2a2014-01-23 14:22:48 -0800367 cnt++;
368 }
David Zeuthen227ef3c2015-09-03 12:23:12 -0400369 /* If an A/B partition, modify block device to be the real block device */
370 if (fs_mgr_update_for_slotselect(fstab) != 0) {
371 ERROR("Error updating for slotselect\n");
David Zeuthen744a8f82015-09-09 18:03:13 -0400372 goto err;
David Zeuthen227ef3c2015-09-03 12:23:12 -0400373 }
Colin Cross5edee2a2014-01-23 14:22:48 -0800374 fclose(fstab_file);
375 free(line);
376 return fstab;
377
378err:
379 fclose(fstab_file);
380 free(line);
381 if (fstab)
382 fs_mgr_free_fstab(fstab);
383 return NULL;
384}
385
386void fs_mgr_free_fstab(struct fstab *fstab)
387{
388 int i;
389
390 if (!fstab) {
391 return;
392 }
393
394 for (i = 0; i < fstab->num_entries; i++) {
395 /* Free the pointers return by strdup(3) */
396 free(fstab->recs[i].blk_device);
397 free(fstab->recs[i].mount_point);
398 free(fstab->recs[i].fs_type);
399 free(fstab->recs[i].fs_options);
400 free(fstab->recs[i].key_loc);
401 free(fstab->recs[i].label);
402 }
403
404 /* Free the fstab_recs array created by calloc(3) */
405 free(fstab->recs);
406
407 /* Free the fstab filename */
408 free(fstab->fstab_filename);
409
410 /* Free fstab */
411 free(fstab);
412}
413
414/* Add an entry to the fstab, and return 0 on success or -1 on error */
415int fs_mgr_add_entry(struct fstab *fstab,
416 const char *mount_point, const char *fs_type,
Sasha Levitskiycdc1cfb2014-04-10 17:10:21 -0700417 const char *blk_device)
Colin Cross5edee2a2014-01-23 14:22:48 -0800418{
419 struct fstab_rec *new_fstab_recs;
420 int n = fstab->num_entries;
421
422 new_fstab_recs = (struct fstab_rec *)
423 realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
424
425 if (!new_fstab_recs) {
426 return -1;
427 }
428
429 /* A new entry was added, so initialize it */
430 memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
431 new_fstab_recs[n].mount_point = strdup(mount_point);
432 new_fstab_recs[n].fs_type = strdup(fs_type);
433 new_fstab_recs[n].blk_device = strdup(blk_device);
434 new_fstab_recs[n].length = 0;
435
436 /* Update the fstab struct */
437 fstab->recs = new_fstab_recs;
438 fstab->num_entries++;
439
440 return 0;
441}
442
JP Abgrall5c01dac2014-06-18 14:54:37 -0700443/*
444 * Returns the 1st matching fstab_rec that follows the start_rec.
445 * start_rec is the result of a previous search or NULL.
446 */
447struct 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 -0800448{
449 int i;
Colin Cross5edee2a2014-01-23 14:22:48 -0800450 if (!fstab) {
451 return NULL;
452 }
453
JP Abgrall5c01dac2014-06-18 14:54:37 -0700454 if (start_rec) {
455 for (i = 0; i < fstab->num_entries; i++) {
456 if (&fstab->recs[i] == start_rec) {
457 i++;
458 break;
459 }
460 }
461 } else {
462 i = 0;
463 }
464 for (; i < fstab->num_entries; i++) {
Colin Cross5edee2a2014-01-23 14:22:48 -0800465 int len = strlen(fstab->recs[i].mount_point);
466 if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
467 (path[len] == '\0' || path[len] == '/')) {
468 return &fstab->recs[i];
469 }
470 }
JP Abgrallf786fe52014-06-18 07:28:14 +0000471 return NULL;
JP Abgralla794f862014-06-17 16:58:46 -0700472}
473
JP Abgrall5c01dac2014-06-18 14:54:37 -0700474/*
475 * Returns the 1st matching mount point.
476 * There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after()
477 * and give the fstab_rec from the previous search.
478 */
479struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
480{
481 return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
482}
483
Paul Lawrencec410b3b2015-03-26 15:49:42 +0000484int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
Colin Cross5edee2a2014-01-23 14:22:48 -0800485{
486 return fstab->fs_mgr_flags & MF_VOLDMANAGED;
487}
488
Paul Lawrencec410b3b2015-03-26 15:49:42 +0000489int fs_mgr_is_nonremovable(const struct fstab_rec *fstab)
Colin Cross5edee2a2014-01-23 14:22:48 -0800490{
491 return fstab->fs_mgr_flags & MF_NONREMOVABLE;
492}
493
Paul Lawrencec410b3b2015-03-26 15:49:42 +0000494int fs_mgr_is_verified(const struct fstab_rec *fstab)
Paul Lawrencebbb36312014-10-09 14:22:49 +0000495{
496 return fstab->fs_mgr_flags & MF_VERIFY;
497}
498
Paul Lawrencec410b3b2015-03-26 15:49:42 +0000499int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
Colin Cross5edee2a2014-01-23 14:22:48 -0800500{
Paul Lawrenceb262d682015-10-29 10:31:02 -0700501 return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE);
Colin Cross5edee2a2014-01-23 14:22:48 -0800502}
503
Paul Lawrencec410b3b2015-03-26 15:49:42 +0000504int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
505{
506 return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
507}
508
Paul Lawrence4e898a02016-05-24 04:56:37 -0700509const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec *fstab)
510{
511 const struct flag_list *j;
512 for (j = encryption_modes; j->name; ++j) {
513 if (fstab->file_encryption_mode == j->flag) {
514 return j->name;
515 }
516 }
517 return NULL;
518}
519
Paul Lawrenceb262d682015-10-29 10:31:02 -0700520int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab)
521{
522 return fstab->fs_mgr_flags & MF_FORCEFDEORFBE;
523}
524
Paul Lawrencec410b3b2015-03-26 15:49:42 +0000525int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
Colin Cross5edee2a2014-01-23 14:22:48 -0800526{
527 return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
528}
JP Abgrallfe6fcc42015-03-05 17:30:53 -0800529
Paul Lawrence84b0bab2015-03-26 14:53:19 +0000530int fs_mgr_is_notrim(struct fstab_rec *fstab)
JP Abgrallfe6fcc42015-03-05 17:30:53 -0800531{
532 return fstab->fs_mgr_flags & MF_NOTRIM;
533}
Ed Tame498c7c2015-04-13 18:01:33 -0700534
Chris Fries79f33842013-09-05 13:19:21 -0500535int fs_mgr_is_formattable(struct fstab_rec *fstab)
536{
537 return fstab->fs_mgr_flags & (MF_FORMATTABLE);
538}
Daniel Rosenbergeb65ce02015-09-01 12:41:18 -0700539
Daniel Rosenberg8bb2f362015-08-27 13:42:20 -0700540int fs_mgr_is_slotselect(struct fstab_rec *fstab)
541{
542 return fstab->fs_mgr_flags & MF_SLOTSELECT;
543}
Daniel Rosenberge3a32882016-04-12 14:28:09 -0700544
Daniel Rosenbergd38e3c52016-04-07 20:10:25 -0700545int fs_mgr_is_nofail(struct fstab_rec *fstab)
546{
547 return fstab->fs_mgr_flags & MF_NOFAIL;
548}
Wei Wang8e5c9b82016-08-23 11:58:09 -0700549
550int fs_mgr_is_latemount(struct fstab_rec *fstab)
551{
552 return fstab->fs_mgr_flags & MF_LATEMOUNT;
553}