Home | History | Annotate | Download | only in uncrypt
      1 /*
      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 // This program takes a file on an ext4 filesystem and produces a list
     18 // of the blocks that file occupies, which enables the file contents
     19 // to be read directly from the block device without mounting the
     20 // filesystem.
     21 //
     22 // If the filesystem is using an encrypted block device, it will also
     23 // read the file and rewrite it to the same blocks of the underlying
     24 // (unencrypted) block device, so the file contents can be read
     25 // without the need for the decryption key.
     26 //
     27 // The output of this program is a "block map" which looks like this:
     28 //
     29 //     /dev/block/platform/msm_sdcc.1/by-name/userdata     # block device
     30 //     49652 4096                        # file size in bytes, block size
     31 //     3                                 # count of block ranges
     32 //     1000 1008                         # block range 0
     33 //     2100 2102                         # ... block range 1
     34 //     30 33                             # ... block range 2
     35 //
     36 // Each block range represents a half-open interval; the line "30 33"
     37 // reprents the blocks [30, 31, 32].
     38 //
     39 // Recovery can take this block map file and retrieve the underlying
     40 // file data to use as an update package.
     41 
     42 #include <errno.h>
     43 #include <fcntl.h>
     44 #include <linux/fs.h>
     45 #include <stdarg.h>
     46 #include <stdio.h>
     47 #include <stdlib.h>
     48 #include <string.h>
     49 #include <sys/mman.h>
     50 #include <sys/stat.h>
     51 #include <sys/types.h>
     52 #include <unistd.h>
     53 
     54 #include <base/file.h>
     55 #include <base/strings.h>
     56 #include <cutils/properties.h>
     57 #include <fs_mgr.h>
     58 #define LOG_TAG "uncrypt"
     59 #include <log/log.h>
     60 
     61 #define WINDOW_SIZE 5
     62 
     63 static const std::string cache_block_map = "/cache/recovery/block.map";
     64 static const std::string status_file = "/cache/recovery/uncrypt_status";
     65 static const std::string uncrypt_file = "/cache/recovery/uncrypt_file";
     66 
     67 static struct fstab* fstab = NULL;
     68 
     69 static int write_at_offset(unsigned char* buffer, size_t size, int wfd, off64_t offset) {
     70     if (TEMP_FAILURE_RETRY(lseek64(wfd, offset, SEEK_SET)) == -1) {
     71         ALOGE("error seeking to offset %lld: %s\n", offset, strerror(errno));
     72         return -1;
     73     }
     74     size_t written = 0;
     75     while (written < size) {
     76         ssize_t wrote = TEMP_FAILURE_RETRY(write(wfd, buffer + written, size - written));
     77         if (wrote == -1) {
     78             ALOGE("error writing offset %lld: %s\n", (offset + written), strerror(errno));
     79             return -1;
     80         }
     81         written += wrote;
     82     }
     83     return 0;
     84 }
     85 
     86 static void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int new_block) {
     87     // If the current block start is < 0, set the start to the new
     88     // block.  (This only happens for the very first block of the very
     89     // first range.)
     90     if ((*ranges)[*range_used*2-2] < 0) {
     91         (*ranges)[*range_used*2-2] = new_block;
     92         (*ranges)[*range_used*2-1] = new_block;
     93     }
     94 
     95     if (new_block == (*ranges)[*range_used*2-1]) {
     96         // If the new block comes immediately after the current range,
     97         // all we have to do is extend the current range.
     98         ++(*ranges)[*range_used*2-1];
     99     } else {
    100         // We need to start a new range.
    101 
    102         // If there isn't enough room in the array, we need to expand it.
    103         if (*range_used >= *range_alloc) {
    104             *range_alloc *= 2;
    105             *ranges = reinterpret_cast<int*>(realloc(*ranges, *range_alloc * 2 * sizeof(int)));
    106         }
    107 
    108         ++*range_used;
    109         (*ranges)[*range_used*2-2] = new_block;
    110         (*ranges)[*range_used*2-1] = new_block+1;
    111     }
    112 }
    113 
    114 static struct fstab* read_fstab() {
    115     fstab = NULL;
    116 
    117     // The fstab path is always "/fstab.${ro.hardware}".
    118     char fstab_path[PATH_MAX+1] = "/fstab.";
    119     if (!property_get("ro.hardware", fstab_path+strlen(fstab_path), "")) {
    120         ALOGE("failed to get ro.hardware\n");
    121         return NULL;
    122     }
    123 
    124     fstab = fs_mgr_read_fstab(fstab_path);
    125     if (!fstab) {
    126         ALOGE("failed to read %s\n", fstab_path);
    127         return NULL;
    128     }
    129 
    130     return fstab;
    131 }
    132 
    133 static const char* find_block_device(const char* path, bool* encryptable, bool* encrypted) {
    134     // Look for a volume whose mount point is the prefix of path and
    135     // return its block device.  Set encrypted if it's currently
    136     // encrypted.
    137     for (int i = 0; i < fstab->num_entries; ++i) {
    138         struct fstab_rec* v = &fstab->recs[i];
    139         if (!v->mount_point) {
    140             continue;
    141         }
    142         int len = strlen(v->mount_point);
    143         if (strncmp(path, v->mount_point, len) == 0 &&
    144             (path[len] == '/' || path[len] == 0)) {
    145             *encrypted = false;
    146             *encryptable = false;
    147             if (fs_mgr_is_encryptable(v)) {
    148                 *encryptable = true;
    149                 char buffer[PROPERTY_VALUE_MAX+1];
    150                 if (property_get("ro.crypto.state", buffer, "") &&
    151                     strcmp(buffer, "encrypted") == 0) {
    152                     *encrypted = true;
    153                 }
    154             }
    155             return v->blk_device;
    156         }
    157     }
    158 
    159     return NULL;
    160 }
    161 
    162 // Parse uncrypt_file to find the update package name.
    163 static bool find_uncrypt_package(std::string& package_name)
    164 {
    165     if (!android::base::ReadFileToString(uncrypt_file, &package_name)) {
    166         ALOGE("failed to open \"%s\": %s\n", uncrypt_file.c_str(), strerror(errno));
    167         return false;
    168     }
    169 
    170     // Remove the trailing '\n' if present.
    171     package_name = android::base::Trim(package_name);
    172 
    173     return true;
    174 }
    175 
    176 static int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
    177                              bool encrypted, int status_fd) {
    178     int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
    179     if (mapfd == -1) {
    180         ALOGE("failed to open %s\n", map_file);
    181         return -1;
    182     }
    183     FILE* mapf = fdopen(mapfd, "w");
    184 
    185     // Make sure we can write to the status_file.
    186     if (!android::base::WriteStringToFd("0\n", status_fd)) {
    187         ALOGE("failed to update \"%s\"\n", status_file.c_str());
    188         return -1;
    189     }
    190 
    191     struct stat sb;
    192     int ret = stat(path, &sb);
    193     if (ret != 0) {
    194         ALOGE("failed to stat %s\n", path);
    195         return -1;
    196     }
    197 
    198     ALOGI(" block size: %ld bytes\n", (long)sb.st_blksize);
    199 
    200     int blocks = ((sb.st_size-1) / sb.st_blksize) + 1;
    201     ALOGI("  file size: %lld bytes, %d blocks\n", (long long)sb.st_size, blocks);
    202 
    203     int range_alloc = 1;
    204     int range_used = 1;
    205     int* ranges = reinterpret_cast<int*>(malloc(range_alloc * 2 * sizeof(int)));
    206     ranges[0] = -1;
    207     ranges[1] = -1;
    208 
    209     fprintf(mapf, "%s\n%lld %lu\n", blk_dev, (long long)sb.st_size, (unsigned long)sb.st_blksize);
    210 
    211     unsigned char* buffers[WINDOW_SIZE];
    212     if (encrypted) {
    213         for (size_t i = 0; i < WINDOW_SIZE; ++i) {
    214             buffers[i] = reinterpret_cast<unsigned char*>(malloc(sb.st_blksize));
    215         }
    216     }
    217     int head_block = 0;
    218     int head = 0, tail = 0;
    219     size_t pos = 0;
    220 
    221     int fd = open(path, O_RDONLY);
    222     if (fd < 0) {
    223         ALOGE("failed to open fd for reading: %s\n", strerror(errno));
    224         return -1;
    225     }
    226 
    227     int wfd = -1;
    228     if (encrypted) {
    229         wfd = open(blk_dev, O_WRONLY | O_SYNC);
    230         if (wfd < 0) {
    231             ALOGE("failed to open fd for writing: %s\n", strerror(errno));
    232             return -1;
    233         }
    234     }
    235 
    236     int last_progress = 0;
    237     while (pos < sb.st_size) {
    238         // Update the status file, progress must be between [0, 99].
    239         int progress = static_cast<int>(100 * (double(pos) / double(sb.st_size)));
    240         if (progress > last_progress) {
    241           last_progress = progress;
    242           android::base::WriteStringToFd(std::to_string(progress) + "\n", status_fd);
    243         }
    244 
    245         if ((tail+1) % WINDOW_SIZE == head) {
    246             // write out head buffer
    247             int block = head_block;
    248             ret = ioctl(fd, FIBMAP, &block);
    249             if (ret != 0) {
    250                 ALOGE("failed to find block %d\n", head_block);
    251                 return -1;
    252             }
    253             add_block_to_ranges(&ranges, &range_alloc, &range_used, block);
    254             if (encrypted) {
    255                 if (write_at_offset(buffers[head], sb.st_blksize, wfd,
    256                         (off64_t)sb.st_blksize * block) != 0) {
    257                     return -1;
    258                 }
    259             }
    260             head = (head + 1) % WINDOW_SIZE;
    261             ++head_block;
    262         }
    263 
    264         // read next block to tail
    265         if (encrypted) {
    266             size_t so_far = 0;
    267             while (so_far < sb.st_blksize && pos < sb.st_size) {
    268                 ssize_t this_read =
    269                         TEMP_FAILURE_RETRY(read(fd, buffers[tail] + so_far, sb.st_blksize - so_far));
    270                 if (this_read == -1) {
    271                     ALOGE("failed to read: %s\n", strerror(errno));
    272                     return -1;
    273                 }
    274                 so_far += this_read;
    275                 pos += this_read;
    276             }
    277         } else {
    278             // If we're not encrypting; we don't need to actually read
    279             // anything, just skip pos forward as if we'd read a
    280             // block.
    281             pos += sb.st_blksize;
    282         }
    283         tail = (tail+1) % WINDOW_SIZE;
    284     }
    285 
    286     while (head != tail) {
    287         // write out head buffer
    288         int block = head_block;
    289         ret = ioctl(fd, FIBMAP, &block);
    290         if (ret != 0) {
    291             ALOGE("failed to find block %d\n", head_block);
    292             return -1;
    293         }
    294         add_block_to_ranges(&ranges, &range_alloc, &range_used, block);
    295         if (encrypted) {
    296             if (write_at_offset(buffers[head], sb.st_blksize, wfd,
    297                     (off64_t)sb.st_blksize * block) != 0) {
    298                 return -1;
    299             }
    300         }
    301         head = (head + 1) % WINDOW_SIZE;
    302         ++head_block;
    303     }
    304 
    305     fprintf(mapf, "%d\n", range_used);
    306     for (int i = 0; i < range_used; ++i) {
    307         fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]);
    308     }
    309 
    310     if (fsync(mapfd) == -1) {
    311         ALOGE("failed to fsync \"%s\": %s\n", map_file, strerror(errno));
    312         return -1;
    313     }
    314     fclose(mapf);
    315     close(fd);
    316     if (encrypted) {
    317         if (fsync(wfd) == -1) {
    318             ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno));
    319             return -1;
    320         }
    321         close(wfd);
    322     }
    323 
    324     return 0;
    325 }
    326 
    327 static void wipe_misc() {
    328     ALOGI("removing old commands from misc");
    329     for (int i = 0; i < fstab->num_entries; ++i) {
    330         struct fstab_rec* v = &fstab->recs[i];
    331         if (!v->mount_point) continue;
    332         if (strcmp(v->mount_point, "/misc") == 0) {
    333             int fd = open(v->blk_device, O_WRONLY | O_SYNC);
    334             uint8_t zeroes[1088];   // sizeof(bootloader_message) from recovery
    335             memset(zeroes, 0, sizeof(zeroes));
    336 
    337             size_t written = 0;
    338             size_t size = sizeof(zeroes);
    339             while (written < size) {
    340                 ssize_t w = TEMP_FAILURE_RETRY(write(fd, zeroes, size-written));
    341                 if (w == -1) {
    342                     ALOGE("zero write failed: %s\n", strerror(errno));
    343                     return;
    344                 } else {
    345                     written += w;
    346                 }
    347             }
    348             if (fsync(fd) == -1) {
    349                 ALOGE("failed to fsync \"%s\": %s\n", v->blk_device, strerror(errno));
    350                 close(fd);
    351                 return;
    352             }
    353             close(fd);
    354         }
    355     }
    356 }
    357 
    358 static void reboot_to_recovery() {
    359     ALOGI("rebooting to recovery");
    360     property_set("sys.powerctl", "reboot,recovery");
    361     sleep(10);
    362     ALOGE("reboot didn't succeed?");
    363 }
    364 
    365 int uncrypt(const char* input_path, const char* map_file, int status_fd) {
    366 
    367     ALOGI("update package is \"%s\"", input_path);
    368 
    369     // Turn the name of the file we're supposed to convert into an
    370     // absolute path, so we can find what filesystem it's on.
    371     char path[PATH_MAX+1];
    372     if (realpath(input_path, path) == NULL) {
    373         ALOGE("failed to convert \"%s\" to absolute path: %s", input_path, strerror(errno));
    374         return 1;
    375     }
    376 
    377     if (read_fstab() == NULL) {
    378         return 1;
    379     }
    380 
    381     bool encryptable;
    382     bool encrypted;
    383     const char* blk_dev = find_block_device(path, &encryptable, &encrypted);
    384     if (blk_dev == NULL) {
    385         ALOGE("failed to find block device for %s", path);
    386         return 1;
    387     }
    388 
    389     // If the filesystem it's on isn't encrypted, we only produce the
    390     // block map, we don't rewrite the file contents (it would be
    391     // pointless to do so).
    392     ALOGI("encryptable: %s\n", encryptable ? "yes" : "no");
    393     ALOGI("  encrypted: %s\n", encrypted ? "yes" : "no");
    394 
    395     // Recovery supports installing packages from 3 paths: /cache,
    396     // /data, and /sdcard.  (On a particular device, other locations
    397     // may work, but those are three we actually expect.)
    398     //
    399     // On /data we want to convert the file to a block map so that we
    400     // can read the package without mounting the partition.  On /cache
    401     // and /sdcard we leave the file alone.
    402     if (strncmp(path, "/data/", 6) == 0) {
    403         ALOGI("writing block map %s", map_file);
    404         if (produce_block_map(path, map_file, blk_dev, encrypted, status_fd) != 0) {
    405             return 1;
    406         }
    407     }
    408 
    409     return 0;
    410 }
    411 
    412 int main(int argc, char** argv) {
    413     const char* input_path;
    414     const char* map_file;
    415 
    416     if (argc != 3 && argc != 1 && (argc == 2 && strcmp(argv[1], "--reboot") != 0)) {
    417         fprintf(stderr, "usage: %s [--reboot] [<transform_path> <map_file>]\n", argv[0]);
    418         return 2;
    419     }
    420 
    421     // When uncrypt is started with "--reboot", it wipes misc and reboots.
    422     // Otherwise it uncrypts the package and writes the block map.
    423     if (argc == 2) {
    424         if (read_fstab() == NULL) {
    425             return 1;
    426         }
    427         wipe_misc();
    428         reboot_to_recovery();
    429     } else {
    430         // The pipe has been created by the system server.
    431         int status_fd = open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
    432         if (status_fd == -1) {
    433             ALOGE("failed to open pipe \"%s\": %s\n", status_file.c_str(), strerror(errno));
    434             return 1;
    435         }
    436 
    437         if (argc == 3) {
    438             // when command-line args are given this binary is being used
    439             // for debugging.
    440             input_path = argv[1];
    441             map_file = argv[2];
    442         } else {
    443             std::string package;
    444             if (!find_uncrypt_package(package)) {
    445                 android::base::WriteStringToFd("-1\n", status_fd);
    446                 close(status_fd);
    447                 return 1;
    448             }
    449             input_path = package.c_str();
    450             map_file = cache_block_map.c_str();
    451         }
    452 
    453         int status = uncrypt(input_path, map_file, status_fd);
    454         if (status != 0) {
    455             android::base::WriteStringToFd("-1\n", status_fd);
    456             close(status_fd);
    457             return 1;
    458         }
    459 
    460         android::base::WriteStringToFd("100\n", status_fd);
    461         close(status_fd);
    462     }
    463 
    464     return 0;
    465 }
    466