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