fs_mgr.c revision e50ac5f7771872331df70251d23d6bd8155da4a7
1/*
2 * Copyright (C) 2012 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/* TO DO:
18 *   1. Re-direct fsck output to the kernel log?
19 *
20 */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26#include <fcntl.h>
27#include <ctype.h>
28#include <sys/mount.h>
29#include <sys/stat.h>
30#include <errno.h>
31#include <sys/types.h>
32#include <sys/wait.h>
33#include <libgen.h>
34#include <time.h>
35
36#include <private/android_filesystem_config.h>
37#include <cutils/partition_utils.h>
38#include <cutils/properties.h>
39
40#include "fs_mgr_priv.h"
41
42#define KEY_LOC_PROP   "ro.crypto.keyfile.userdata"
43#define KEY_IN_FOOTER  "footer"
44
45#define E2FSCK_BIN      "/system/bin/e2fsck"
46
47struct flag_list {
48    const char *name;
49    unsigned flag;
50};
51
52static struct flag_list mount_flags[] = {
53    { "noatime",    MS_NOATIME },
54    { "noexec",     MS_NOEXEC },
55    { "nosuid",     MS_NOSUID },
56    { "nodev",      MS_NODEV },
57    { "nodiratime", MS_NODIRATIME },
58    { "ro",         MS_RDONLY },
59    { "rw",         0 },
60    { "remount",    MS_REMOUNT },
61    { "bind",       MS_BIND },
62    { "rec",        MS_REC },
63    { "unbindable", MS_UNBINDABLE },
64    { "private",    MS_PRIVATE },
65    { "slave",      MS_SLAVE },
66    { "shared",     MS_SHARED },
67    { "defaults",   0 },
68    { 0,            0 },
69};
70
71static struct flag_list fs_mgr_flags[] = {
72    { "wait",        MF_WAIT },
73    { "check",       MF_CHECK },
74    { "encryptable=",MF_CRYPT },
75    { "defaults",    0 },
76    { 0,             0 },
77};
78
79/*
80 * gettime() - returns the time in seconds of the system's monotonic clock or
81 * zero on error.
82 */
83static time_t gettime(void)
84{
85    struct timespec ts;
86    int ret;
87
88    ret = clock_gettime(CLOCK_MONOTONIC, &ts);
89    if (ret < 0) {
90        ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno));
91        return 0;
92    }
93
94    return ts.tv_sec;
95}
96
97static int wait_for_file(const char *filename, int timeout)
98{
99    struct stat info;
100    time_t timeout_time = gettime() + timeout;
101    int ret = -1;
102
103    while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
104        usleep(10000);
105
106    return ret;
107}
108
109static int parse_flags(char *flags, struct flag_list *fl, char **key_loc,
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 key_loc to null, if we find an MF_CRYPT flag,
118     * then we'll set key_loc to the proper value */
119    if (key_loc) {
120        *key_loc = NULL;
121    }
122    /* initialize fs_options to the null string */
123    if (fs_options && (fs_options_len > 0)) {
124        fs_options[0] = '\0';
125    }
126
127    p = strtok_r(flags, ",", &savep);
128    while (p) {
129        /* Look for the flag "p" in the flag list "fl"
130         * If not found, the loop exits with fl[i].name being null.
131         */
132        for (i = 0; fl[i].name; i++) {
133            if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
134                f |= fl[i].flag;
135                if ((fl[i].flag == MF_CRYPT) && key_loc) {
136                    /* The encryptable flag is followed by an = and the
137                     * location of the keys.  Get it and return it.
138                     */
139                    *key_loc = strdup(strchr(p, '=') + 1);
140                }
141                break;
142            }
143        }
144
145        if (!fl[i].name) {
146            if (fs_options) {
147                /* It's not a known flag, so it must be a filesystem specific
148                 * option.  Add it to fs_options if it was passed in.
149                 */
150                strlcat(fs_options, p, fs_options_len);
151                strlcat(fs_options, ",", fs_options_len);
152            } else {
153                /* fs_options was not passed in, so if the flag is unknown
154                 * it's an error.
155                 */
156                ERROR("Warning: unknown flag %s\n", p);
157            }
158        }
159        p = strtok_r(NULL, ",", &savep);
160    }
161
162out:
163    if (fs_options && fs_options[0]) {
164        /* remove the last trailing comma from the list of options */
165        fs_options[strlen(fs_options) - 1] = '\0';
166    }
167
168    return f;
169}
170
171/* Read a line of text till the next newline character.
172 * If no newline is found before the buffer is full, continue reading till a new line is seen,
173 * then return an empty buffer.  This effectively ignores lines that are too long.
174 * On EOF, return null.
175 */
176static char *getline(char *buf, int size, FILE *file)
177{
178    int cnt = 0;
179    int eof = 0;
180    int eol = 0;
181    int c;
182
183    if (size < 1) {
184        return NULL;
185    }
186
187    while (cnt < (size - 1)) {
188        c = getc(file);
189        if (c == EOF) {
190            eof = 1;
191            break;
192        }
193
194        *(buf + cnt) = c;
195        cnt++;
196
197        if (c == '\n') {
198            eol = 1;
199            break;
200        }
201    }
202
203    /* Null terminate what we've read */
204    *(buf + cnt) = '\0';
205
206    if (eof) {
207        if (cnt) {
208            return buf;
209        } else {
210            return NULL;
211        }
212    } else if (eol) {
213        return buf;
214    } else {
215        /* The line is too long.  Read till a newline or EOF.
216         * If EOF, return null, if newline, return an empty buffer.
217         */
218        while(1) {
219            c = getc(file);
220            if (c == EOF) {
221                return NULL;
222            } else if (c == '\n') {
223                *buf = '\0';
224                return buf;
225            }
226        }
227    }
228}
229
230static struct fstab_rec *read_fstab(char *fstab_path)
231{
232    FILE *fstab_file;
233    int cnt, entries;
234    int len;
235    char line[256];
236    const char *delim = " \t";
237    char *save_ptr, *p;
238    struct fstab_rec *fstab;
239    char *key_loc;
240#define FS_OPTIONS_LEN 1024
241    char tmp_fs_options[FS_OPTIONS_LEN];
242
243    fstab_file = fopen(fstab_path, "r");
244    if (!fstab_file) {
245        ERROR("Cannot open file %s\n", fstab_path);
246        return 0;
247    }
248
249    entries = 0;
250    while (getline(line, sizeof(line), fstab_file)) {
251        /* if the last character is a newline, shorten the string by 1 byte */
252        len = strlen(line);
253        if (line[len - 1] == '\n') {
254            line[len - 1] = '\0';
255        }
256        /* Skip any leading whitespace */
257        p = line;
258        while (isspace(*p)) {
259            p++;
260        }
261        /* ignore comments or empty lines */
262        if (*p == '#' || *p == '\0')
263            continue;
264        entries++;
265    }
266
267    if (!entries) {
268        ERROR("No entries found in fstab\n");
269        return 0;
270    }
271
272    fstab = calloc(entries + 1, sizeof(struct fstab_rec));
273
274    fseek(fstab_file, 0, SEEK_SET);
275
276    cnt = 0;
277    while (getline(line, sizeof(line), fstab_file)) {
278        /* if the last character is a newline, shorten the string by 1 byte */
279        len = strlen(line);
280        if (line[len - 1] == '\n') {
281            line[len - 1] = '\0';
282        }
283
284        /* Skip any leading whitespace */
285        p = line;
286        while (isspace(*p)) {
287            p++;
288        }
289        /* ignore comments or empty lines */
290        if (*p == '#' || *p == '\0')
291            continue;
292
293        /* If a non-comment entry is greater than the size we allocated, give an
294         * error and quit.  This can happen in the unlikely case the file changes
295         * between the two reads.
296         */
297        if (cnt >= entries) {
298            ERROR("Tried to process more entries than counted\n");
299            break;
300        }
301
302        if (!(p = strtok_r(line, delim, &save_ptr))) {
303            ERROR("Error parsing mount source\n");
304            return 0;
305        }
306        fstab[cnt].blk_dev = strdup(p);
307
308        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
309            ERROR("Error parsing mnt_point\n");
310            return 0;
311        }
312        fstab[cnt].mnt_point = strdup(p);
313
314        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
315            ERROR("Error parsing fs_type\n");
316            return 0;
317        }
318        fstab[cnt].type = strdup(p);
319
320        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
321            ERROR("Error parsing mount_flags\n");
322            return 0;
323        }
324        tmp_fs_options[0] = '\0';
325        fstab[cnt].flags = parse_flags(p, mount_flags, 0, tmp_fs_options, FS_OPTIONS_LEN);
326
327        /* fs_options are optional */
328        if (tmp_fs_options[0]) {
329            fstab[cnt].fs_options = strdup(tmp_fs_options);
330        } else {
331            fstab[cnt].fs_options = NULL;
332        }
333
334        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
335            ERROR("Error parsing fs_mgr_options\n");
336            return 0;
337        }
338        fstab[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags, &key_loc, 0, 0);
339        fstab[cnt].key_loc = key_loc;
340
341        cnt++;
342    }
343    fclose(fstab_file);
344
345    return fstab;
346}
347
348static void free_fstab(struct fstab_rec *fstab)
349{
350    int i = 0;
351
352    while (fstab[i].blk_dev) {
353        /* Free the pointers return by strdup(3) */
354        free(fstab[i].blk_dev);
355        free(fstab[i].mnt_point);
356        free(fstab[i].type);
357        free(fstab[i].fs_options);
358        free(fstab[i].key_loc);
359
360        i++;
361    }
362
363    /* Free the actual fstab array created by calloc(3) */
364    free(fstab);
365}
366
367static void check_fs(char *blk_dev, char *type, char *target)
368{
369    pid_t pid;
370    int status;
371    int ret;
372    long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
373    char *tmpmnt_opts = "nomblk_io_submit,errors=remount-ro";
374
375    /* Check for the types of filesystems we know how to check */
376    if (!strcmp(type, "ext2") || !strcmp(type, "ext3") || !strcmp(type, "ext4")) {
377        /*
378         * First try to mount and unmount the filesystem.  We do this because
379         * the kernel is more efficient than e2fsck in running the journal and
380         * processing orphaned inodes, and on at least one device with a
381         * performance issue in the emmc firmware, it can take e2fsck 2.5 minutes
382         * to do what the kernel does in about a second.
383         *
384         * After mounting and unmounting the filesystem, run e2fsck, and if an
385         * error is recorded in the filesystem superblock, e2fsck will do a full
386         * check.  Otherwise, it does nothing.  If the kernel cannot mount the
387         * filesytsem due to an error, e2fsck is still run to do a full check
388         * fix the filesystem.
389         */
390        ret = mount(blk_dev, target, type, tmpmnt_flags, tmpmnt_opts);
391        if (! ret) {
392            umount(target);
393        }
394
395        INFO("Running %s on %s\n", E2FSCK_BIN, blk_dev);
396        pid = fork();
397        if (pid > 0) {
398            /* Parent, wait for the child to return */
399            waitpid(pid, &status, 0);
400        } else if (pid == 0) {
401            /* child, run checker */
402            execlp(E2FSCK_BIN, E2FSCK_BIN, "-y", blk_dev, (char *)NULL);
403
404            /* Only gets here on error */
405            ERROR("Cannot run fs_mgr binary %s\n", E2FSCK_BIN);
406        } else {
407            /* No need to check for error in fork, we can't really handle it now */
408            ERROR("Fork failed trying to run %s\n", E2FSCK_BIN);
409        }
410    }
411
412    return;
413}
414
415static void remove_trailing_slashes(char *n)
416{
417    int len;
418
419    len = strlen(n) - 1;
420    while ((*(n + len) == '/') && len) {
421      *(n + len) = '\0';
422      len--;
423    }
424}
425
426static int fs_match(char *in1, char *in2)
427{
428    char *n1;
429    char *n2;
430    int ret;
431
432    n1 = strdup(in1);
433    n2 = strdup(in2);
434
435    remove_trailing_slashes(n1);
436    remove_trailing_slashes(n2);
437
438    ret = !strcmp(n1, n2);
439
440    free(n1);
441    free(n2);
442
443    return ret;
444}
445
446int fs_mgr_mount_all(char *fstab_file)
447{
448    int i = 0;
449    int encrypted = 0;
450    int ret = -1;
451    int mret;
452    struct fstab_rec *fstab = 0;
453
454    if (!(fstab = read_fstab(fstab_file))) {
455        return ret;
456    }
457
458    for (i = 0; fstab[i].blk_dev; i++) {
459        if (fstab[i].fs_mgr_flags & MF_WAIT) {
460            wait_for_file(fstab[i].blk_dev, WAIT_TIMEOUT);
461        }
462
463        if (fstab[i].fs_mgr_flags & MF_CHECK) {
464            check_fs(fstab[i].blk_dev, fstab[i].type, fstab[i].mnt_point);
465        }
466
467        mret = mount(fstab[i].blk_dev, fstab[i].mnt_point, fstab[i].type,
468                     fstab[i].flags, fstab[i].fs_options);
469        if (!mret) {
470            /* Success!  Go get the next one */
471            continue;
472        }
473
474        /* mount(2) returned an error, check if it's encrypted and deal with it */
475        if ((fstab[i].fs_mgr_flags & MF_CRYPT) && !partition_wiped(fstab[i].blk_dev)) {
476            /* Need to mount a tmpfs at this mountpoint for now, and set
477             * properties that vold will query later for decrypting
478             */
479            if (mount("tmpfs", fstab[i].mnt_point, "tmpfs",
480                  MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS) < 0) {
481                ERROR("Cannot mount tmpfs filesystem for encrypted fs at %s\n",
482                        fstab[i].mnt_point);
483                goto out;
484            }
485            encrypted = 1;
486        } else {
487            ERROR("Cannot mount filesystem on %s at %s\n",
488                    fstab[i].blk_dev, fstab[i].mnt_point);
489            goto out;
490        }
491    }
492
493    if (encrypted) {
494        ret = 1;
495    } else {
496        ret = 0;
497    }
498
499out:
500    free_fstab(fstab);
501    return ret;
502}
503
504/* If tmp_mnt_point is non-null, mount the filesystem there.  This is for the
505 * tmp mount we do to check the user password
506 */
507int fs_mgr_do_mount(char *fstab_file, char *n_name, char *n_blk_dev, char *tmp_mnt_point)
508{
509    int i = 0;
510    int ret = -1;
511    struct fstab_rec *fstab = 0;
512    char *m;
513
514    if (!(fstab = read_fstab(fstab_file))) {
515        return ret;
516    }
517
518    for (i = 0; fstab[i].blk_dev; i++) {
519        if (!fs_match(fstab[i].mnt_point, n_name)) {
520            continue;
521        }
522
523        /* We found our match */
524        /* First check the filesystem if requested */
525        if (fstab[i].fs_mgr_flags & MF_WAIT) {
526            wait_for_file(fstab[i].blk_dev, WAIT_TIMEOUT);
527        }
528
529        if (fstab[i].fs_mgr_flags & MF_CHECK) {
530            check_fs(fstab[i].blk_dev, fstab[i].type, fstab[i].mnt_point);
531        }
532
533        /* Now mount it where requested */
534        if (tmp_mnt_point) {
535            m = tmp_mnt_point;
536        } else {
537            m = fstab[i].mnt_point;
538        }
539        if (mount(n_blk_dev, m, fstab[i].type,
540                  fstab[i].flags, fstab[i].fs_options)) {
541            ERROR("Cannot mount filesystem on %s at %s\n",
542                    n_blk_dev, m);
543            goto out;
544        } else {
545            ret = 0;
546            goto out;
547        }
548    }
549
550    /* We didn't find a match, say so and return an error */
551    ERROR("Cannot find mount point %s in fstab\n", fstab[i].mnt_point);
552
553out:
554    free_fstab(fstab);
555    return ret;
556}
557
558/*
559 * mount a tmpfs filesystem at the given point.
560 * return 0 on success, non-zero on failure.
561 */
562int fs_mgr_do_tmpfs_mount(char *n_name)
563{
564    int ret;
565
566    ret = mount("tmpfs", n_name, "tmpfs",
567                MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS);
568    if (ret < 0) {
569        ERROR("Cannot mount tmpfs filesystem at %s\n", n_name);
570        return -1;
571    }
572
573    /* Success */
574    return 0;
575}
576
577int fs_mgr_unmount_all(char *fstab_file)
578{
579    int i = 0;
580    int ret = 0;
581    struct fstab_rec *fstab = 0;
582
583    if (!(fstab = read_fstab(fstab_file))) {
584        return -1;
585    }
586
587    while (fstab[i].blk_dev) {
588        if (umount(fstab[i].mnt_point)) {
589            ERROR("Cannot unmount filesystem at %s\n", fstab[i].mnt_point);
590            ret = -1;
591        }
592        i++;
593    }
594
595    free_fstab(fstab);
596    return ret;
597}
598/*
599 * key_loc must be at least PROPERTY_VALUE_MAX bytes long
600 *
601 * real_blk_dev must be at least PROPERTY_VALUE_MAX bytes long
602 */
603int fs_mgr_get_crypt_info(char *fstab_file, char *key_loc, char *real_blk_dev, int size)
604{
605    int i = 0;
606    struct fstab_rec *fstab = 0;
607
608    if (!(fstab = read_fstab(fstab_file))) {
609        return -1;
610    }
611    /* Initialize return values to null strings */
612    if (key_loc) {
613        *key_loc = '\0';
614    }
615    if (real_blk_dev) {
616        *real_blk_dev = '\0';
617    }
618
619    /* Look for the encryptable partition to find the data */
620    for (i = 0; fstab[i].blk_dev; i++) {
621        if (!(fstab[i].fs_mgr_flags & MF_CRYPT)) {
622            continue;
623        }
624
625        /* We found a match */
626        if (key_loc) {
627            strlcpy(key_loc, fstab[i].key_loc, size);
628        }
629        if (real_blk_dev) {
630            strlcpy(real_blk_dev, fstab[i].blk_dev, size);
631        }
632        break;
633    }
634
635    free_fstab(fstab);
636    return 0;
637}
638
639