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, "vfat") == 0) {
    115         result = mount(v->blk_device, v->mount_point, v->fs_type,
    116                        MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
    117         if (result == 0) return 0;
    118 
    119         LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno));
    120         return -1;
    121     }
    122 
    123     LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, v->mount_point);
    124     return -1;
    125 }
    126 
    127 int ensure_path_unmounted(const char* path) {
    128     Volume* v = volume_for_path(path);
    129     if (v == NULL) {
    130         LOGE("unknown volume for path [%s]\n", path);
    131         return -1;
    132     }
    133     if (strcmp(v->fs_type, "ramdisk") == 0) {
    134         // the ramdisk is always mounted; you can't unmount it.
    135         return -1;
    136     }
    137 
    138     int result;
    139     result = scan_mounted_volumes();
    140     if (result < 0) {
    141         LOGE("failed to scan mounted volumes\n");
    142         return -1;
    143     }
    144 
    145     const MountedVolume* mv =
    146         find_mounted_volume_by_mount_point(v->mount_point);
    147     if (mv == NULL) {
    148         // volume is already unmounted
    149         return 0;
    150     }
    151 
    152     return unmount_mounted_volume(mv);
    153 }
    154 
    155 static int exec_cmd(const char* path, char* const argv[]) {
    156     int status;
    157     pid_t child;
    158     if ((child = vfork()) == 0) {
    159         execv(path, argv);
    160         _exit(-1);
    161     }
    162     waitpid(child, &status, 0);
    163     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
    164         LOGE("%s failed with status %d\n", path, WEXITSTATUS(status));
    165     }
    166     return WEXITSTATUS(status);
    167 }
    168 
    169 int format_volume(const char* volume) {
    170     Volume* v = volume_for_path(volume);
    171     if (v == NULL) {
    172         LOGE("unknown volume \"%s\"\n", volume);
    173         return -1;
    174     }
    175     if (strcmp(v->fs_type, "ramdisk") == 0) {
    176         // you can't format the ramdisk.
    177         LOGE("can't format_volume \"%s\"", volume);
    178         return -1;
    179     }
    180     if (strcmp(v->mount_point, volume) != 0) {
    181         LOGE("can't give path \"%s\" to format_volume\n", volume);
    182         return -1;
    183     }
    184 
    185     if (ensure_path_unmounted(volume) != 0) {
    186         LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point);
    187         return -1;
    188     }
    189 
    190     if (strcmp(v->fs_type, "yaffs2") == 0 || strcmp(v->fs_type, "mtd") == 0) {
    191         mtd_scan_partitions();
    192         const MtdPartition* partition = mtd_find_partition_by_name(v->blk_device);
    193         if (partition == NULL) {
    194             LOGE("format_volume: no MTD partition \"%s\"\n", v->blk_device);
    195             return -1;
    196         }
    197 
    198         MtdWriteContext *write = mtd_write_partition(partition);
    199         if (write == NULL) {
    200             LOGW("format_volume: can't open MTD \"%s\"\n", v->blk_device);
    201             return -1;
    202         } else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
    203             LOGW("format_volume: can't erase MTD \"%s\"\n", v->blk_device);
    204             mtd_write_close(write);
    205             return -1;
    206         } else if (mtd_write_close(write)) {
    207             LOGW("format_volume: can't close MTD \"%s\"\n", v->blk_device);
    208             return -1;
    209         }
    210         return 0;
    211     }
    212 
    213     if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "f2fs") == 0) {
    214         // if there's a key_loc that looks like a path, it should be a
    215         // block device for storing encryption metadata.  wipe it too.
    216         if (v->key_loc != NULL && v->key_loc[0] == '/') {
    217             LOGI("wiping %s\n", v->key_loc);
    218             int fd = open(v->key_loc, O_WRONLY | O_CREAT, 0644);
    219             if (fd < 0) {
    220                 LOGE("format_volume: failed to open %s\n", v->key_loc);
    221                 return -1;
    222             }
    223             wipe_block_device(fd, get_file_size(fd));
    224             close(fd);
    225         }
    226 
    227         ssize_t length = 0;
    228         if (v->length != 0) {
    229             length = v->length;
    230         } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) {
    231             length = -CRYPT_FOOTER_OFFSET;
    232         }
    233         int result;
    234         if (strcmp(v->fs_type, "ext4") == 0) {
    235             result = make_ext4fs(v->blk_device, length, volume, sehandle);
    236         } else {   /* Has to be f2fs because we checked earlier. */
    237             if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) {
    238                 LOGE("format_volume: crypt footer + negative length (%zd) not supported on %s\n", length, v->fs_type);
    239                 return -1;
    240             }
    241             if (length < 0) {
    242                 LOGE("format_volume: negative length (%zd) not supported on %s\n", length, v->fs_type);
    243                 return -1;
    244             }
    245             char *num_sectors;
    246             if (asprintf(&num_sectors, "%zd", length / 512) <= 0) {
    247                 LOGE("format_volume: failed to create %s command for %s\n", v->fs_type, v->blk_device);
    248                 return -1;
    249             }
    250             const char *f2fs_path = "/sbin/mkfs.f2fs";
    251             const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", v->blk_device, num_sectors, NULL};
    252 
    253             result = exec_cmd(f2fs_path, (char* const*)f2fs_argv);
    254             free(num_sectors);
    255         }
    256         if (result != 0) {
    257             LOGE("format_volume: make %s failed on %s with %d(%s)\n", v->fs_type, v->blk_device, result, strerror(errno));
    258             return -1;
    259         }
    260         return 0;
    261     }
    262 
    263     LOGE("format_volume: fs_type \"%s\" unsupported\n", v->fs_type);
    264     return -1;
    265 }
    266 
    267 int setup_install_mounts() {
    268     if (fstab == NULL) {
    269         LOGE("can't set up install mounts: no fstab loaded\n");
    270         return -1;
    271     }
    272     for (int i = 0; i < fstab->num_entries; ++i) {
    273         Volume* v = fstab->recs + i;
    274 
    275         if (strcmp(v->mount_point, "/tmp") == 0 ||
    276             strcmp(v->mount_point, "/cache") == 0) {
    277             if (ensure_path_mounted(v->mount_point) != 0) {
    278                 LOGE("failed to mount %s\n", v->mount_point);
    279                 return -1;
    280             }
    281 
    282         } else {
    283             if (ensure_path_unmounted(v->mount_point) != 0) {
    284                 LOGE("failed to unmount %s\n", v->mount_point);
    285                 return -1;
    286             }
    287         }
    288     }
    289     return 0;
    290 }
    291