Home | History | Annotate | Download | only in recovery
      1 /*
      2  * Copyright (C) 2007 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 <errno.h>
     18 #include <stdlib.h>
     19 #include <sys/mount.h>
     20 #include <sys/stat.h>
     21 #include <sys/types.h>
     22 #include <sys/wait.h>
     23 #include <unistd.h>
     24 #include <ctype.h>
     25 #include <fcntl.h>
     26 
     27 #include <fs_mgr.h>
     28 #include "mtdutils/mtdutils.h"
     29 #include "mtdutils/mounts.h"
     30 #include "roots.h"
     31 #include "common.h"
     32 #include "make_ext4fs.h"
     33 extern "C" {
     34 #include "wipe.h"
     35 #include "cryptfs.h"
     36 }
     37 
     38 static struct fstab *fstab = NULL;
     39 
     40 extern struct selabel_handle *sehandle;
     41 
     42 void load_volume_table()
     43 {
     44     int i;
     45     int ret;
     46 
     47     fstab = fs_mgr_read_fstab("/etc/recovery.fstab");
     48     if (!fstab) {
     49         LOGE("failed to read /etc/recovery.fstab\n");
     50         return;
     51     }
     52 
     53     ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk");
     54     if (ret < 0 ) {
     55         LOGE("failed to add /tmp entry to fstab\n");
     56         fs_mgr_free_fstab(fstab);
     57         fstab = NULL;
     58         return;
     59     }
     60 
     61     printf("recovery filesystem table\n");
     62     printf("=========================\n");
     63     for (i = 0; i < fstab->num_entries; ++i) {
     64         Volume* v = &fstab->recs[i];
     65         printf("  %d %s %s %s %lld\n", i, v->mount_point, v->fs_type,
     66                v->blk_device, v->length);
     67     }
     68     printf("\n");
     69 }
     70 
     71 Volume* volume_for_path(const char* path) {
     72     return fs_mgr_get_entry_for_mount_point(fstab, path);
     73 }
     74 
     75 int ensure_path_mounted(const char* path) {
     76     Volume* v = volume_for_path(path);
     77     if (v == NULL) {
     78         LOGE("unknown volume for path [%s]\n", path);
     79         return -1;
     80     }
     81     if (strcmp(v->fs_type, "ramdisk") == 0) {
     82         // the ramdisk is always mounted.
     83         return 0;
     84     }
     85 
     86     int result;
     87     result = scan_mounted_volumes();
     88     if (result < 0) {
     89         LOGE("failed to scan mounted volumes\n");
     90         return -1;
     91     }
     92 
     93     const MountedVolume* mv =
     94         find_mounted_volume_by_mount_point(v->mount_point);
     95     if (mv) {
     96         // volume is already mounted
     97         return 0;
     98     }
     99 
    100     mkdir(v->mount_point, 0755);  // in case it doesn't already exist
    101 
    102     if (strcmp(v->fs_type, "yaffs2") == 0) {
    103         // mount an MTD partition as a YAFFS2 filesystem.
    104         mtd_scan_partitions();
    105         const MtdPartition* partition;
    106         partition = mtd_find_partition_by_name(v->blk_device);
    107         if (partition == NULL) {
    108             LOGE("failed to find \"%s\" partition to mount at \"%s\"\n",
    109                  v->blk_device, v->mount_point);
    110             return -1;
    111         }
    112         return mtd_mount_partition(partition, v->mount_point, v->fs_type, 0);
    113     } else if (strcmp(v->fs_type, "ext4") == 0 ||
    114                strcmp(v->fs_type, "squashfs") == 0 ||
    115                strcmp(v->fs_type, "vfat") == 0) {
    116         result = mount(v->blk_device, v->mount_point, v->fs_type,
    117                        v->flags, v->fs_options);
    118         if (result == 0) return 0;
    119 
    120         LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno));
    121         return -1;
    122     }
    123 
    124     LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, v->mount_point);
    125     return -1;
    126 }
    127 
    128 int ensure_path_unmounted(const char* path) {
    129     Volume* v = volume_for_path(path);
    130     if (v == NULL) {
    131         LOGE("unknown volume for path [%s]\n", path);
    132         return -1;
    133     }
    134     if (strcmp(v->fs_type, "ramdisk") == 0) {
    135         // the ramdisk is always mounted; you can't unmount it.
    136         return -1;
    137     }
    138 
    139     int result;
    140     result = scan_mounted_volumes();
    141     if (result < 0) {
    142         LOGE("failed to scan mounted volumes\n");
    143         return -1;
    144     }
    145 
    146     const MountedVolume* mv =
    147         find_mounted_volume_by_mount_point(v->mount_point);
    148     if (mv == NULL) {
    149         // volume is already unmounted
    150         return 0;
    151     }
    152 
    153     return unmount_mounted_volume(mv);
    154 }
    155 
    156 static int exec_cmd(const char* path, char* const argv[]) {
    157     int status;
    158     pid_t child;
    159     if ((child = vfork()) == 0) {
    160         execv(path, argv);
    161         _exit(-1);
    162     }
    163     waitpid(child, &status, 0);
    164     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
    165         LOGE("%s failed with status %d\n", path, WEXITSTATUS(status));
    166     }
    167     return WEXITSTATUS(status);
    168 }
    169 
    170 int format_volume(const char* volume) {
    171     Volume* v = volume_for_path(volume);
    172     if (v == NULL) {
    173         LOGE("unknown volume \"%s\"\n", volume);
    174         return -1;
    175     }
    176     if (strcmp(v->fs_type, "ramdisk") == 0) {
    177         // you can't format the ramdisk.
    178         LOGE("can't format_volume \"%s\"", volume);
    179         return -1;
    180     }
    181     if (strcmp(v->mount_point, volume) != 0) {
    182         LOGE("can't give path \"%s\" to format_volume\n", volume);
    183         return -1;
    184     }
    185 
    186     if (ensure_path_unmounted(volume) != 0) {
    187         LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point);
    188         return -1;
    189     }
    190 
    191     if (strcmp(v->fs_type, "yaffs2") == 0 || strcmp(v->fs_type, "mtd") == 0) {
    192         mtd_scan_partitions();
    193         const MtdPartition* partition = mtd_find_partition_by_name(v->blk_device);
    194         if (partition == NULL) {
    195             LOGE("format_volume: no MTD partition \"%s\"\n", v->blk_device);
    196             return -1;
    197         }
    198 
    199         MtdWriteContext *write = mtd_write_partition(partition);
    200         if (write == NULL) {
    201             LOGW("format_volume: can't open MTD \"%s\"\n", v->blk_device);
    202             return -1;
    203         } else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
    204             LOGW("format_volume: can't erase MTD \"%s\"\n", v->blk_device);
    205             mtd_write_close(write);
    206             return -1;
    207         } else if (mtd_write_close(write)) {
    208             LOGW("format_volume: can't close MTD \"%s\"\n", v->blk_device);
    209             return -1;
    210         }
    211         return 0;
    212     }
    213 
    214     if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "f2fs") == 0) {
    215         // if there's a key_loc that looks like a path, it should be a
    216         // block device for storing encryption metadata.  wipe it too.
    217         if (v->key_loc != NULL && v->key_loc[0] == '/') {
    218             LOGI("wiping %s\n", v->key_loc);
    219             int fd = open(v->key_loc, O_WRONLY | O_CREAT, 0644);
    220             if (fd < 0) {
    221                 LOGE("format_volume: failed to open %s\n", v->key_loc);
    222                 return -1;
    223             }
    224             wipe_block_device(fd, get_file_size(fd));
    225             close(fd);
    226         }
    227 
    228         ssize_t length = 0;
    229         if (v->length != 0) {
    230             length = v->length;
    231         } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) {
    232             length = -CRYPT_FOOTER_OFFSET;
    233         }
    234         int result;
    235         if (strcmp(v->fs_type, "ext4") == 0) {
    236             result = make_ext4fs(v->blk_device, length, volume, sehandle);
    237         } else {   /* Has to be f2fs because we checked earlier. */
    238             if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) {
    239                 LOGE("format_volume: crypt footer + negative length (%zd) not supported on %s\n", length, v->fs_type);
    240                 return -1;
    241             }
    242             if (length < 0) {
    243                 LOGE("format_volume: negative length (%zd) not supported on %s\n", length, v->fs_type);
    244                 return -1;
    245             }
    246             char *num_sectors;
    247             if (asprintf(&num_sectors, "%zd", length / 512) <= 0) {
    248                 LOGE("format_volume: failed to create %s command for %s\n", v->fs_type, v->blk_device);
    249                 return -1;
    250             }
    251             const char *f2fs_path = "/sbin/mkfs.f2fs";
    252             const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", v->blk_device, num_sectors, NULL};
    253 
    254             result = exec_cmd(f2fs_path, (char* const*)f2fs_argv);
    255             free(num_sectors);
    256         }
    257         if (result != 0) {
    258             LOGE("format_volume: make %s failed on %s with %d(%s)\n", v->fs_type, v->blk_device, result, strerror(errno));
    259             return -1;
    260         }
    261         return 0;
    262     }
    263 
    264     LOGE("format_volume: fs_type \"%s\" unsupported\n", v->fs_type);
    265     return -1;
    266 }
    267 
    268 int setup_install_mounts() {
    269     if (fstab == NULL) {
    270         LOGE("can't set up install mounts: no fstab loaded\n");
    271         return -1;
    272     }
    273     for (int i = 0; i < fstab->num_entries; ++i) {
    274         Volume* v = fstab->recs + i;
    275 
    276         if (strcmp(v->mount_point, "/tmp") == 0 ||
    277             strcmp(v->mount_point, "/cache") == 0) {
    278             if (ensure_path_mounted(v->mount_point) != 0) {
    279                 LOGE("failed to mount %s\n", v->mount_point);
    280                 return -1;
    281             }
    282 
    283         } else {
    284             if (ensure_path_unmounted(v->mount_point) != 0) {
    285                 LOGE("failed to unmount %s\n", v->mount_point);
    286                 return -1;
    287             }
    288         }
    289     }
    290     return 0;
    291 }
    292