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 "roots.h"
     18 
     19 #include <ctype.h>
     20 #include <fcntl.h>
     21 #include <stdint.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <sys/mount.h>
     25 #include <sys/stat.h>
     26 #include <sys/types.h>
     27 #include <sys/wait.h>
     28 #include <unistd.h>
     29 
     30 #include <algorithm>
     31 #include <string>
     32 #include <vector>
     33 
     34 #include <android-base/logging.h>
     35 #include <android-base/properties.h>
     36 #include <android-base/stringprintf.h>
     37 #include <android-base/unique_fd.h>
     38 #include <cryptfs.h>
     39 #include <ext4_utils/wipe.h>
     40 #include <fs_mgr.h>
     41 
     42 #include "mounts.h"
     43 
     44 static struct fstab* fstab = nullptr;
     45 
     46 extern struct selabel_handle* sehandle;
     47 
     48 void load_volume_table() {
     49   fstab = fs_mgr_read_fstab_default();
     50   if (!fstab) {
     51     LOG(ERROR) << "Failed to read default fstab";
     52     return;
     53   }
     54 
     55   int ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk");
     56   if (ret == -1) {
     57     LOG(ERROR) << "Failed to add /tmp entry to fstab";
     58     fs_mgr_free_fstab(fstab);
     59     fstab = nullptr;
     60     return;
     61   }
     62 
     63   printf("recovery filesystem table\n");
     64   printf("=========================\n");
     65   for (int i = 0; i < fstab->num_entries; ++i) {
     66     const Volume* v = &fstab->recs[i];
     67     printf("  %d %s %s %s %lld\n", i, v->mount_point, v->fs_type, v->blk_device, v->length);
     68   }
     69   printf("\n");
     70 }
     71 
     72 Volume* volume_for_mount_point(const std::string& mount_point) {
     73   return fs_mgr_get_entry_for_mount_point(fstab, mount_point);
     74 }
     75 
     76 // Finds the volume specified by the given path. fs_mgr_get_entry_for_mount_point() does exact match
     77 // only, so it attempts the prefixes recursively (e.g. "/cache/recovery/last_log",
     78 // "/cache/recovery", "/cache", "/" for a given path of "/cache/recovery/last_log") and returns the
     79 // first match or nullptr.
     80 static Volume* volume_for_path(const char* path) {
     81   if (path == nullptr || path[0] == '\0') return nullptr;
     82   std::string str(path);
     83   while (true) {
     84     Volume* result = fs_mgr_get_entry_for_mount_point(fstab, str);
     85     if (result != nullptr || str == "/") {
     86       return result;
     87     }
     88     size_t slash = str.find_last_of('/');
     89     if (slash == std::string::npos) return nullptr;
     90     if (slash == 0) {
     91       str = "/";
     92     } else {
     93       str = str.substr(0, slash);
     94     }
     95   }
     96   return nullptr;
     97 }
     98 
     99 // Mount the volume specified by path at the given mount_point.
    100 int ensure_path_mounted_at(const char* path, const char* mount_point) {
    101   Volume* v = volume_for_path(path);
    102   if (v == nullptr) {
    103     LOG(ERROR) << "unknown volume for path [" << path << "]";
    104     return -1;
    105   }
    106   if (strcmp(v->fs_type, "ramdisk") == 0) {
    107     // The ramdisk is always mounted.
    108     return 0;
    109   }
    110 
    111   if (!scan_mounted_volumes()) {
    112     LOG(ERROR) << "Failed to scan mounted volumes";
    113     return -1;
    114   }
    115 
    116   if (!mount_point) {
    117     mount_point = v->mount_point;
    118   }
    119 
    120   const MountedVolume* mv = find_mounted_volume_by_mount_point(mount_point);
    121   if (mv != nullptr) {
    122     // Volume is already mounted.
    123     return 0;
    124   }
    125 
    126   mkdir(mount_point, 0755);  // in case it doesn't already exist
    127 
    128   if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "squashfs") == 0 ||
    129       strcmp(v->fs_type, "vfat") == 0) {
    130     int result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options);
    131     if (result == -1 && fs_mgr_is_formattable(v)) {
    132       PLOG(ERROR) << "Failed to mount " << mount_point << "; formatting";
    133       bool crypt_footer = fs_mgr_is_encryptable(v) && !strcmp(v->key_loc, "footer");
    134       if (fs_mgr_do_format(v, crypt_footer) == 0) {
    135         result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options);
    136       } else {
    137         PLOG(ERROR) << "Failed to format " << mount_point;
    138         return -1;
    139       }
    140     }
    141 
    142     if (result == -1) {
    143       PLOG(ERROR) << "Failed to mount " << mount_point;
    144       return -1;
    145     }
    146     return 0;
    147   }
    148 
    149   LOG(ERROR) << "unknown fs_type \"" << v->fs_type << "\" for " << mount_point;
    150   return -1;
    151 }
    152 
    153 int ensure_path_mounted(const char* path) {
    154   // Mount at the default mount point.
    155   return ensure_path_mounted_at(path, nullptr);
    156 }
    157 
    158 int ensure_path_unmounted(const char* path) {
    159   const Volume* v = volume_for_path(path);
    160   if (v == nullptr) {
    161     LOG(ERROR) << "unknown volume for path [" << path << "]";
    162     return -1;
    163   }
    164   if (strcmp(v->fs_type, "ramdisk") == 0) {
    165     // The ramdisk is always mounted; you can't unmount it.
    166     return -1;
    167   }
    168 
    169   if (!scan_mounted_volumes()) {
    170     LOG(ERROR) << "Failed to scan mounted volumes";
    171     return -1;
    172   }
    173 
    174   MountedVolume* mv = find_mounted_volume_by_mount_point(v->mount_point);
    175   if (mv == nullptr) {
    176     // Volume is already unmounted.
    177     return 0;
    178   }
    179 
    180   return unmount_mounted_volume(mv);
    181 }
    182 
    183 static int exec_cmd(const std::vector<std::string>& args) {
    184   CHECK_NE(static_cast<size_t>(0), args.size());
    185 
    186   std::vector<char*> argv(args.size());
    187   std::transform(args.cbegin(), args.cend(), argv.begin(),
    188                  [](const std::string& arg) { return const_cast<char*>(arg.c_str()); });
    189   argv.push_back(nullptr);
    190 
    191   pid_t child;
    192   if ((child = fork()) == 0) {
    193     execv(argv[0], argv.data());
    194     _exit(EXIT_FAILURE);
    195   }
    196 
    197   int status;
    198   waitpid(child, &status, 0);
    199   if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
    200     LOG(ERROR) << args[0] << " failed with status " << WEXITSTATUS(status);
    201   }
    202   return WEXITSTATUS(status);
    203 }
    204 
    205 static int64_t get_file_size(int fd, uint64_t reserve_len) {
    206   struct stat buf;
    207   int ret = fstat(fd, &buf);
    208   if (ret) return 0;
    209 
    210   int64_t computed_size;
    211   if (S_ISREG(buf.st_mode)) {
    212     computed_size = buf.st_size - reserve_len;
    213   } else if (S_ISBLK(buf.st_mode)) {
    214     uint64_t block_device_size = get_block_device_size(fd);
    215     if (block_device_size < reserve_len ||
    216         block_device_size > std::numeric_limits<int64_t>::max()) {
    217       computed_size = 0;
    218     } else {
    219       computed_size = block_device_size - reserve_len;
    220     }
    221   } else {
    222     computed_size = 0;
    223   }
    224 
    225   return computed_size;
    226 }
    227 
    228 int format_volume(const char* volume, const char* directory) {
    229   const Volume* v = volume_for_path(volume);
    230   if (v == nullptr) {
    231     LOG(ERROR) << "unknown volume \"" << volume << "\"";
    232     return -1;
    233   }
    234   if (strcmp(v->fs_type, "ramdisk") == 0) {
    235     LOG(ERROR) << "can't format_volume \"" << volume << "\"";
    236     return -1;
    237   }
    238   if (strcmp(v->mount_point, volume) != 0) {
    239     LOG(ERROR) << "can't give path \"" << volume << "\" to format_volume";
    240     return -1;
    241   }
    242   if (ensure_path_unmounted(volume) != 0) {
    243     LOG(ERROR) << "format_volume: Failed to unmount \"" << v->mount_point << "\"";
    244     return -1;
    245   }
    246   if (strcmp(v->fs_type, "ext4") != 0 && strcmp(v->fs_type, "f2fs") != 0) {
    247     LOG(ERROR) << "format_volume: fs_type \"" << v->fs_type << "\" unsupported";
    248     return -1;
    249   }
    250 
    251   // If there's a key_loc that looks like a path, it should be a block device for storing encryption
    252   // metadata. Wipe it too.
    253   if (v->key_loc != nullptr && v->key_loc[0] == '/') {
    254     LOG(INFO) << "Wiping " << v->key_loc;
    255     int fd = open(v->key_loc, O_WRONLY | O_CREAT, 0644);
    256     if (fd == -1) {
    257       PLOG(ERROR) << "format_volume: Failed to open " << v->key_loc;
    258       return -1;
    259     }
    260     wipe_block_device(fd, get_file_size(fd));
    261     close(fd);
    262   }
    263 
    264   int64_t length = 0;
    265   if (v->length > 0) {
    266     length = v->length;
    267   } else if (v->length < 0 ||
    268              (v->key_loc != nullptr && strcmp(v->key_loc, "footer") == 0)) {
    269     android::base::unique_fd fd(open(v->blk_device, O_RDONLY));
    270     if (fd == -1) {
    271       PLOG(ERROR) << "format_volume: failed to open " << v->blk_device;
    272       return -1;
    273     }
    274     length =
    275         get_file_size(fd.get(), v->length ? -v->length : CRYPT_FOOTER_OFFSET);
    276     if (length <= 0) {
    277       LOG(ERROR) << "get_file_size: invalid size " << length << " for "
    278                  << v->blk_device;
    279       return -1;
    280     }
    281   }
    282 
    283   if (strcmp(v->fs_type, "ext4") == 0) {
    284     static constexpr int kBlockSize = 4096;
    285     std::vector<std::string> mke2fs_args = {
    286       "/sbin/mke2fs_static", "-F", "-t", "ext4", "-b", std::to_string(kBlockSize),
    287     };
    288 
    289     int raid_stride = v->logical_blk_size / kBlockSize;
    290     int raid_stripe_width = v->erase_blk_size / kBlockSize;
    291     // stride should be the max of 8KB and logical block size
    292     if (v->logical_blk_size != 0 && v->logical_blk_size < 8192) {
    293       raid_stride = 8192 / kBlockSize;
    294     }
    295     if (v->erase_blk_size != 0 && v->logical_blk_size != 0) {
    296       mke2fs_args.push_back("-E");
    297       mke2fs_args.push_back(
    298           android::base::StringPrintf("stride=%d,stripe-width=%d", raid_stride, raid_stripe_width));
    299     }
    300     mke2fs_args.push_back(v->blk_device);
    301     if (length != 0) {
    302       mke2fs_args.push_back(std::to_string(length / kBlockSize));
    303     }
    304 
    305     int result = exec_cmd(mke2fs_args);
    306     if (result == 0 && directory != nullptr) {
    307       std::vector<std::string> e2fsdroid_args = {
    308         "/sbin/e2fsdroid_static",
    309         "-e",
    310         "-f",
    311         directory,
    312         "-a",
    313         volume,
    314         v->blk_device,
    315       };
    316       result = exec_cmd(e2fsdroid_args);
    317     }
    318 
    319     if (result != 0) {
    320       PLOG(ERROR) << "format_volume: Failed to make ext4 on " << v->blk_device;
    321       return -1;
    322     }
    323     return 0;
    324   }
    325 
    326   // Has to be f2fs because we checked earlier.
    327   static constexpr int kSectorSize = 4096;
    328   std::string cmd("/sbin/mkfs.f2fs");
    329   // clang-format off
    330   std::vector<std::string> make_f2fs_cmd = {
    331     cmd,
    332     "-d1",
    333     "-f",
    334     "-O", "encrypt",
    335     "-O", "quota",
    336     "-O", "verity",
    337     "-w", std::to_string(kSectorSize),
    338     v->blk_device,
    339   };
    340   // clang-format on
    341   if (length >= kSectorSize) {
    342     make_f2fs_cmd.push_back(std::to_string(length / kSectorSize));
    343   }
    344 
    345   int result = exec_cmd(make_f2fs_cmd);
    346   if (result == 0 && directory != nullptr) {
    347     cmd = "/sbin/sload.f2fs";
    348     // clang-format off
    349     std::vector<std::string> sload_f2fs_cmd = {
    350       cmd,
    351       "-f", directory,
    352       "-t", volume,
    353       v->blk_device,
    354     };
    355     // clang-format on
    356     result = exec_cmd(sload_f2fs_cmd);
    357   }
    358   if (result != 0) {
    359     PLOG(ERROR) << "format_volume: Failed " << cmd << " on " << v->blk_device;
    360     return -1;
    361   }
    362   return 0;
    363 }
    364 
    365 int format_volume(const char* volume) {
    366   return format_volume(volume, nullptr);
    367 }
    368 
    369 int setup_install_mounts() {
    370   if (fstab == nullptr) {
    371     LOG(ERROR) << "can't set up install mounts: no fstab loaded";
    372     return -1;
    373   }
    374   for (int i = 0; i < fstab->num_entries; ++i) {
    375     const Volume* v = fstab->recs + i;
    376 
    377     // We don't want to do anything with "/".
    378     if (strcmp(v->mount_point, "/") == 0) {
    379       continue;
    380     }
    381 
    382     if (strcmp(v->mount_point, "/tmp") == 0 || strcmp(v->mount_point, "/cache") == 0) {
    383       if (ensure_path_mounted(v->mount_point) != 0) {
    384         LOG(ERROR) << "Failed to mount " << v->mount_point;
    385         return -1;
    386       }
    387     } else {
    388       if (ensure_path_unmounted(v->mount_point) != 0) {
    389         LOG(ERROR) << "Failed to unmount " << v->mount_point;
    390         return -1;
    391       }
    392     }
    393   }
    394   return 0;
    395 }
    396