Home | History | Annotate | Download | only in updater
      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 #include <ctype.h>
     18 #include <errno.h>
     19 #include <fcntl.h>
     20 #include <inttypes.h>
     21 #include <pthread.h>
     22 #include <stdarg.h>
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <string.h>
     26 #include <sys/types.h>
     27 #include <sys/wait.h>
     28 #include <sys/ioctl.h>
     29 #include <time.h>
     30 #include <unistd.h>
     31 
     32 #include "applypatch/applypatch.h"
     33 #include "edify/expr.h"
     34 #include "mincrypt/sha.h"
     35 #include "minzip/DirUtil.h"
     36 #include "updater.h"
     37 
     38 #define BLOCKSIZE 4096
     39 
     40 // Set this to 0 to interpret 'erase' transfers to mean do a
     41 // BLKDISCARD ioctl (the normal behavior).  Set to 1 to interpret
     42 // erase to mean fill the region with zeroes.
     43 #define DEBUG_ERASE  0
     44 
     45 #ifndef BLKDISCARD
     46 #define BLKDISCARD _IO(0x12,119)
     47 #endif
     48 
     49 char* PrintSha1(const uint8_t* digest);
     50 
     51 typedef struct {
     52     int count;
     53     int size;
     54     int pos[0];
     55 } RangeSet;
     56 
     57 static RangeSet* parse_range(char* text) {
     58     char* save;
     59     int num;
     60     num = strtol(strtok_r(text, ",", &save), NULL, 0);
     61 
     62     RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int));
     63     if (out == NULL) {
     64         fprintf(stderr, "failed to allocate range of %zu bytes\n",
     65                 sizeof(RangeSet) + num * sizeof(int));
     66         exit(1);
     67     }
     68     out->count = num / 2;
     69     out->size = 0;
     70     int i;
     71     for (i = 0; i < num; ++i) {
     72         out->pos[i] = strtol(strtok_r(NULL, ",", &save), NULL, 0);
     73         if (i%2) {
     74             out->size += out->pos[i];
     75         } else {
     76             out->size -= out->pos[i];
     77         }
     78     }
     79 
     80     return out;
     81 }
     82 
     83 static void readblock(int fd, uint8_t* data, size_t size) {
     84     size_t so_far = 0;
     85     while (so_far < size) {
     86         ssize_t r = read(fd, data+so_far, size-so_far);
     87         if (r < 0 && errno != EINTR) {
     88             fprintf(stderr, "read failed: %s\n", strerror(errno));
     89             return;
     90         } else {
     91             so_far += r;
     92         }
     93     }
     94 }
     95 
     96 static void writeblock(int fd, const uint8_t* data, size_t size) {
     97     size_t written = 0;
     98     while (written < size) {
     99         ssize_t w = write(fd, data+written, size-written);
    100         if (w < 0 && errno != EINTR) {
    101             fprintf(stderr, "write failed: %s\n", strerror(errno));
    102             return;
    103         } else {
    104             written += w;
    105         }
    106     }
    107 }
    108 
    109 static void check_lseek(int fd, off64_t offset, int whence) {
    110     while (true) {
    111         off64_t ret = lseek64(fd, offset, whence);
    112         if (ret < 0) {
    113             if (errno != EINTR) {
    114                 fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
    115                 exit(1);
    116             }
    117         } else {
    118             break;
    119         }
    120     }
    121 }
    122 
    123 static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) {
    124     // if the buffer's big enough, reuse it.
    125     if (size <= *buffer_alloc) return;
    126 
    127     free(*buffer);
    128 
    129     *buffer = (uint8_t*) malloc(size);
    130     if (*buffer == NULL) {
    131         fprintf(stderr, "failed to allocate %zu bytes\n", size);
    132         exit(1);
    133     }
    134     *buffer_alloc = size;
    135 }
    136 
    137 typedef struct {
    138     int fd;
    139     RangeSet* tgt;
    140     int p_block;
    141     size_t p_remain;
    142 } RangeSinkState;
    143 
    144 static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) {
    145     RangeSinkState* rss = (RangeSinkState*) token;
    146 
    147     if (rss->p_remain <= 0) {
    148         fprintf(stderr, "range sink write overrun");
    149         exit(1);
    150     }
    151 
    152     ssize_t written = 0;
    153     while (size > 0) {
    154         size_t write_now = size;
    155         if (rss->p_remain < write_now) write_now = rss->p_remain;
    156         writeblock(rss->fd, data, write_now);
    157         data += write_now;
    158         size -= write_now;
    159 
    160         rss->p_remain -= write_now;
    161         written += write_now;
    162 
    163         if (rss->p_remain == 0) {
    164             // move to the next block
    165             ++rss->p_block;
    166             if (rss->p_block < rss->tgt->count) {
    167                 rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE;
    168                 check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET);
    169             } else {
    170                 // we can't write any more; return how many bytes have
    171                 // been written so far.
    172                 return written;
    173             }
    174         }
    175     }
    176 
    177     return written;
    178 }
    179 
    180 // All of the data for all the 'new' transfers is contained in one
    181 // file in the update package, concatenated together in the order in
    182 // which transfers.list will need it.  We want to stream it out of the
    183 // archive (it's compressed) without writing it to a temp file, but we
    184 // can't write each section until it's that transfer's turn to go.
    185 //
    186 // To achieve this, we expand the new data from the archive in a
    187 // background thread, and block that threads 'receive uncompressed
    188 // data' function until the main thread has reached a point where we
    189 // want some new data to be written.  We signal the background thread
    190 // with the destination for the data and block the main thread,
    191 // waiting for the background thread to complete writing that section.
    192 // Then it signals the main thread to wake up and goes back to
    193 // blocking waiting for a transfer.
    194 //
    195 // NewThreadInfo is the struct used to pass information back and forth
    196 // between the two threads.  When the main thread wants some data
    197 // written, it sets rss to the destination location and signals the
    198 // condition.  When the background thread is done writing, it clears
    199 // rss and signals the condition again.
    200 
    201 typedef struct {
    202     ZipArchive* za;
    203     const ZipEntry* entry;
    204 
    205     RangeSinkState* rss;
    206 
    207     pthread_mutex_t mu;
    208     pthread_cond_t cv;
    209 } NewThreadInfo;
    210 
    211 static bool receive_new_data(const unsigned char* data, int size, void* cookie) {
    212     NewThreadInfo* nti = (NewThreadInfo*) cookie;
    213 
    214     while (size > 0) {
    215         // Wait for nti->rss to be non-NULL, indicating some of this
    216         // data is wanted.
    217         pthread_mutex_lock(&nti->mu);
    218         while (nti->rss == NULL) {
    219             pthread_cond_wait(&nti->cv, &nti->mu);
    220         }
    221         pthread_mutex_unlock(&nti->mu);
    222 
    223         // At this point nti->rss is set, and we own it.  The main
    224         // thread is waiting for it to disappear from nti.
    225         ssize_t written = RangeSinkWrite(data, size, nti->rss);
    226         data += written;
    227         size -= written;
    228 
    229         if (nti->rss->p_block == nti->rss->tgt->count) {
    230             // we have written all the bytes desired by this rss.
    231 
    232             pthread_mutex_lock(&nti->mu);
    233             nti->rss = NULL;
    234             pthread_cond_broadcast(&nti->cv);
    235             pthread_mutex_unlock(&nti->mu);
    236         }
    237     }
    238 
    239     return true;
    240 }
    241 
    242 static void* unzip_new_data(void* cookie) {
    243     NewThreadInfo* nti = (NewThreadInfo*) cookie;
    244     mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti);
    245     return NULL;
    246 }
    247 
    248 // Do a source/target load for move/bsdiff/imgdiff in version 1.
    249 // 'wordsave' is the save_ptr of a strtok_r()-in-progress.  We expect
    250 // to parse the remainder of the string as:
    251 //
    252 //    <src_range> <tgt_range>
    253 //
    254 // The source range is loaded into the provided buffer, reallocating
    255 // it to make it larger if necessary.  The target ranges are returned
    256 // in *tgt, if tgt is non-NULL.
    257 
    258 static void LoadSrcTgtVersion1(char* wordsave, RangeSet** tgt, int* src_blocks,
    259                                uint8_t** buffer, size_t* buffer_alloc, int fd) {
    260     char* word;
    261 
    262     word = strtok_r(NULL, " ", &wordsave);
    263     RangeSet* src = parse_range(word);
    264 
    265     if (tgt != NULL) {
    266         word = strtok_r(NULL, " ", &wordsave);
    267         *tgt = parse_range(word);
    268     }
    269 
    270     allocate(src->size * BLOCKSIZE, buffer, buffer_alloc);
    271     size_t p = 0;
    272     int i;
    273     for (i = 0; i < src->count; ++i) {
    274         check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET);
    275         size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE;
    276         readblock(fd, *buffer+p, sz);
    277         p += sz;
    278     }
    279 
    280     *src_blocks = src->size;
    281     free(src);
    282 }
    283 
    284 static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) {
    285     // source contains packed data, which we want to move to the
    286     // locations given in *locs in the dest buffer.  source and dest
    287     // may be the same buffer.
    288 
    289     int start = locs->size;
    290     int i;
    291     for (i = locs->count-1; i >= 0; --i) {
    292         int blocks = locs->pos[i*2+1] - locs->pos[i*2];
    293         start -= blocks;
    294         memmove(dest + (locs->pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE),
    295                 blocks * BLOCKSIZE);
    296     }
    297 }
    298 
    299 // Do a source/target load for move/bsdiff/imgdiff in version 2.
    300 // 'wordsave' is the save_ptr of a strtok_r()-in-progress.  We expect
    301 // to parse the remainder of the string as one of:
    302 //
    303 //    <tgt_range> <src_block_count> <src_range>
    304 //        (loads data from source image only)
    305 //
    306 //    <tgt_range> <src_block_count> - <[stash_id:stash_range] ...>
    307 //        (loads data from stashes only)
    308 //
    309 //    <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...>
    310 //        (loads data from both source image and stashes)
    311 //
    312 // On return, buffer is filled with the loaded source data (rearranged
    313 // and combined with stashed data as necessary).  buffer may be
    314 // reallocated if needed to accommodate the source data.  *tgt is the
    315 // target RangeSet.  Any stashes required are taken from stash_table
    316 // and free()'d after being used.
    317 
    318 static void LoadSrcTgtVersion2(char* wordsave, RangeSet** tgt, int* src_blocks,
    319                                uint8_t** buffer, size_t* buffer_alloc, int fd,
    320                                uint8_t** stash_table) {
    321     char* word;
    322 
    323     if (tgt != NULL) {
    324         word = strtok_r(NULL, " ", &wordsave);
    325         *tgt = parse_range(word);
    326     }
    327 
    328     word = strtok_r(NULL, " ", &wordsave);
    329     *src_blocks = strtol(word, NULL, 0);
    330 
    331     allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc);
    332 
    333     word = strtok_r(NULL, " ", &wordsave);
    334     if (word[0] == '-' && word[1] == '\0') {
    335         // no source ranges, only stashes
    336     } else {
    337         RangeSet* src = parse_range(word);
    338 
    339         size_t p = 0;
    340         int i;
    341         for (i = 0; i < src->count; ++i) {
    342             check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET);
    343             size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE;
    344             readblock(fd, *buffer+p, sz);
    345             p += sz;
    346         }
    347         free(src);
    348 
    349         word = strtok_r(NULL, " ", &wordsave);
    350         if (word == NULL) {
    351             // no stashes, only source range
    352             return;
    353         }
    354 
    355         RangeSet* locs = parse_range(word);
    356         MoveRange(*buffer, locs, *buffer);
    357     }
    358 
    359     while ((word = strtok_r(NULL, " ", &wordsave)) != NULL) {
    360         // Each word is a an index into the stash table, a colon, and
    361         // then a rangeset describing where in the source block that
    362         // stashed data should go.
    363         char* colonsave = NULL;
    364         char* colon = strtok_r(word, ":", &colonsave);
    365         int stash_id = strtol(colon, NULL, 0);
    366         colon = strtok_r(NULL, ":", &colonsave);
    367         RangeSet* locs = parse_range(colon);
    368         MoveRange(*buffer, locs, stash_table[stash_id]);
    369         free(stash_table[stash_id]);
    370         stash_table[stash_id] = NULL;
    371         free(locs);
    372     }
    373 }
    374 
    375 // args:
    376 //    - block device (or file) to modify in-place
    377 //    - transfer list (blob)
    378 //    - new data stream (filename within package.zip)
    379 //    - patch stream (filename within package.zip, must be uncompressed)
    380 
    381 Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
    382     Value* blockdev_filename;
    383     Value* transfer_list_value;
    384     char* transfer_list = NULL;
    385     Value* new_data_fn;
    386     Value* patch_data_fn;
    387     bool success = false;
    388 
    389     if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
    390                       &new_data_fn, &patch_data_fn) < 0) {
    391         return NULL;
    392     }
    393 
    394     if (blockdev_filename->type != VAL_STRING) {
    395         ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
    396         goto done;
    397     }
    398     if (transfer_list_value->type != VAL_BLOB) {
    399         ErrorAbort(state, "transfer_list argument to %s must be blob", name);
    400         goto done;
    401     }
    402     if (new_data_fn->type != VAL_STRING) {
    403         ErrorAbort(state, "new_data_fn argument to %s must be string", name);
    404         goto done;
    405     }
    406     if (patch_data_fn->type != VAL_STRING) {
    407         ErrorAbort(state, "patch_data_fn argument to %s must be string", name);
    408         goto done;
    409     }
    410 
    411     UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
    412     FILE* cmd_pipe = ui->cmd_pipe;
    413 
    414     ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
    415 
    416     const ZipEntry* patch_entry = mzFindZipEntry(za, patch_data_fn->data);
    417     if (patch_entry == NULL) {
    418         ErrorAbort(state, "%s(): no file \"%s\" in package", name, patch_data_fn->data);
    419         goto done;
    420     }
    421 
    422     uint8_t* patch_start = ((UpdaterInfo*)(state->cookie))->package_zip_addr +
    423         mzGetZipEntryOffset(patch_entry);
    424 
    425     const ZipEntry* new_entry = mzFindZipEntry(za, new_data_fn->data);
    426     if (new_entry == NULL) {
    427         ErrorAbort(state, "%s(): no file \"%s\" in package", name, new_data_fn->data);
    428         goto done;
    429     }
    430 
    431     // The transfer list is a text file containing commands to
    432     // transfer data from one place to another on the target
    433     // partition.  We parse it and execute the commands in order:
    434     //
    435     //    zero [rangeset]
    436     //      - fill the indicated blocks with zeros
    437     //
    438     //    new [rangeset]
    439     //      - fill the blocks with data read from the new_data file
    440     //
    441     //    erase [rangeset]
    442     //      - mark the given blocks as empty
    443     //
    444     //    move <...>
    445     //    bsdiff <patchstart> <patchlen> <...>
    446     //    imgdiff <patchstart> <patchlen> <...>
    447     //      - read the source blocks, apply a patch (or not in the
    448     //        case of move), write result to target blocks.  bsdiff or
    449     //        imgdiff specifies the type of patch; move means no patch
    450     //        at all.
    451     //
    452     //        The format of <...> differs between versions 1 and 2;
    453     //        see the LoadSrcTgtVersion{1,2}() functions for a
    454     //        description of what's expected.
    455     //
    456     //    stash <stash_id> <src_range>
    457     //      - (version 2 only) load the given source range and stash
    458     //        the data in the given slot of the stash table.
    459     //
    460     // The creator of the transfer list will guarantee that no block
    461     // is read (ie, used as the source for a patch or move) after it
    462     // has been written.
    463     //
    464     // In version 2, the creator will guarantee that a given stash is
    465     // loaded (with a stash command) before it's used in a
    466     // move/bsdiff/imgdiff command.
    467     //
    468     // Within one command the source and target ranges may overlap so
    469     // in general we need to read the entire source into memory before
    470     // writing anything to the target blocks.
    471     //
    472     // All the patch data is concatenated into one patch_data file in
    473     // the update package.  It must be stored uncompressed because we
    474     // memory-map it in directly from the archive.  (Since patches are
    475     // already compressed, we lose very little by not compressing
    476     // their concatenation.)
    477 
    478     pthread_t new_data_thread;
    479     NewThreadInfo nti;
    480     nti.za = za;
    481     nti.entry = new_entry;
    482     nti.rss = NULL;
    483     pthread_mutex_init(&nti.mu, NULL);
    484     pthread_cond_init(&nti.cv, NULL);
    485 
    486     pthread_attr_t attr;
    487     pthread_attr_init(&attr);
    488     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    489     pthread_create(&new_data_thread, &attr, unzip_new_data, &nti);
    490 
    491     int i, j;
    492 
    493     char* linesave;
    494     char* wordsave;
    495 
    496     int fd = open(blockdev_filename->data, O_RDWR);
    497     if (fd < 0) {
    498         ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno));
    499         goto done;
    500     }
    501 
    502     char* line;
    503     char* word;
    504 
    505     // The data in transfer_list_value is not necessarily
    506     // null-terminated, so we need to copy it to a new buffer and add
    507     // the null that strtok_r will need.
    508     transfer_list = malloc(transfer_list_value->size+1);
    509     if (transfer_list == NULL) {
    510         fprintf(stderr, "failed to allocate %zd bytes for transfer list\n",
    511                 transfer_list_value->size+1);
    512         exit(1);
    513     }
    514     memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size);
    515     transfer_list[transfer_list_value->size] = '\0';
    516 
    517     line = strtok_r(transfer_list, "\n", &linesave);
    518 
    519     int version;
    520     // first line in transfer list is the version number; currently
    521     // there's only version 1.
    522     if (strcmp(line, "1") == 0) {
    523         version = 1;
    524     } else if (strcmp(line, "2") == 0) {
    525         version = 2;
    526     } else {
    527         ErrorAbort(state, "unexpected transfer list version [%s]\n", line);
    528         goto done;
    529     }
    530     printf("blockimg version is %d\n", version);
    531 
    532     // second line in transfer list is the total number of blocks we
    533     // expect to write.
    534     line = strtok_r(NULL, "\n", &linesave);
    535     int total_blocks = strtol(line, NULL, 0);
    536     // shouldn't happen, but avoid divide by zero.
    537     if (total_blocks == 0) ++total_blocks;
    538     int blocks_so_far = 0;
    539 
    540     uint8_t** stash_table = NULL;
    541     if (version >= 2) {
    542         // Next line is how many stash entries are needed simultaneously.
    543         line = strtok_r(NULL, "\n", &linesave);
    544         int stash_entries = strtol(line, NULL, 0);
    545 
    546         stash_table = (uint8_t**) calloc(stash_entries, sizeof(uint8_t*));
    547         if (stash_table == NULL) {
    548             fprintf(stderr, "failed to allocate %d-entry stash table\n", stash_entries);
    549             exit(1);
    550         }
    551 
    552         // Next line is the maximum number of blocks that will be
    553         // stashed simultaneously.  This could be used to verify that
    554         // enough memory or scratch disk space is available.
    555         line = strtok_r(NULL, "\n", &linesave);
    556         int stash_max_blocks = strtol(line, NULL, 0);
    557     }
    558 
    559     uint8_t* buffer = NULL;
    560     size_t buffer_alloc = 0;
    561 
    562     // third and subsequent lines are all individual transfer commands.
    563     for (line = strtok_r(NULL, "\n", &linesave); line;
    564          line = strtok_r(NULL, "\n", &linesave)) {
    565 
    566         char* style;
    567         style = strtok_r(line, " ", &wordsave);
    568 
    569         if (strcmp("move", style) == 0) {
    570             RangeSet* tgt;
    571             int src_blocks;
    572             if (version == 1) {
    573                 LoadSrcTgtVersion1(wordsave, &tgt, &src_blocks,
    574                                    &buffer, &buffer_alloc, fd);
    575             } else if (version == 2) {
    576                 LoadSrcTgtVersion2(wordsave, &tgt, &src_blocks,
    577                                    &buffer, &buffer_alloc, fd, stash_table);
    578             }
    579 
    580             printf("  moving %d blocks\n", src_blocks);
    581 
    582             size_t p = 0;
    583             for (i = 0; i < tgt->count; ++i) {
    584                 check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET);
    585                 size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE;
    586                 writeblock(fd, buffer+p, sz);
    587                 p += sz;
    588             }
    589 
    590             blocks_so_far += tgt->size;
    591             fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
    592             fflush(cmd_pipe);
    593 
    594             free(tgt);
    595 
    596         } else if (strcmp("stash", style) == 0) {
    597             word = strtok_r(NULL, " ", &wordsave);
    598             int stash_id = strtol(word, NULL, 0);
    599             int src_blocks;
    600             size_t stash_alloc = 0;
    601 
    602             // Even though the "stash" style only appears in version
    603             // 2, the version 1 source loader happens to do exactly
    604             // what we want to read data into the stash_table.
    605             LoadSrcTgtVersion1(wordsave, NULL, &src_blocks,
    606                                stash_table + stash_id, &stash_alloc, fd);
    607 
    608         } else if (strcmp("zero", style) == 0 ||
    609                    (DEBUG_ERASE && strcmp("erase", style) == 0)) {
    610             word = strtok_r(NULL, " ", &wordsave);
    611             RangeSet* tgt = parse_range(word);
    612 
    613             printf("  zeroing %d blocks\n", tgt->size);
    614 
    615             allocate(BLOCKSIZE, &buffer, &buffer_alloc);
    616             memset(buffer, 0, BLOCKSIZE);
    617             for (i = 0; i < tgt->count; ++i) {
    618                 check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET);
    619                 for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) {
    620                     writeblock(fd, buffer, BLOCKSIZE);
    621                 }
    622             }
    623 
    624             if (style[0] == 'z') {   // "zero" but not "erase"
    625                 blocks_so_far += tgt->size;
    626                 fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
    627                 fflush(cmd_pipe);
    628             }
    629 
    630             free(tgt);
    631         } else if (strcmp("new", style) == 0) {
    632 
    633             word = strtok_r(NULL, " ", &wordsave);
    634             RangeSet* tgt = parse_range(word);
    635 
    636             printf("  writing %d blocks of new data\n", tgt->size);
    637 
    638             RangeSinkState rss;
    639             rss.fd = fd;
    640             rss.tgt = tgt;
    641             rss.p_block = 0;
    642             rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
    643             check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET);
    644 
    645             pthread_mutex_lock(&nti.mu);
    646             nti.rss = &rss;
    647             pthread_cond_broadcast(&nti.cv);
    648             while (nti.rss) {
    649                 pthread_cond_wait(&nti.cv, &nti.mu);
    650             }
    651             pthread_mutex_unlock(&nti.mu);
    652 
    653             blocks_so_far += tgt->size;
    654             fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
    655             fflush(cmd_pipe);
    656 
    657             free(tgt);
    658 
    659         } else if (strcmp("bsdiff", style) == 0 ||
    660                    strcmp("imgdiff", style) == 0) {
    661             word = strtok_r(NULL, " ", &wordsave);
    662             size_t patch_offset = strtoul(word, NULL, 0);
    663             word = strtok_r(NULL, " ", &wordsave);
    664             size_t patch_len = strtoul(word, NULL, 0);
    665 
    666             RangeSet* tgt;
    667             int src_blocks;
    668             if (version == 1) {
    669                 LoadSrcTgtVersion1(wordsave, &tgt, &src_blocks,
    670                                    &buffer, &buffer_alloc, fd);
    671             } else if (version == 2) {
    672                 LoadSrcTgtVersion2(wordsave, &tgt, &src_blocks,
    673                                    &buffer, &buffer_alloc, fd, stash_table);
    674             }
    675 
    676             printf("  patching %d blocks to %d\n", src_blocks, tgt->size);
    677 
    678             Value patch_value;
    679             patch_value.type = VAL_BLOB;
    680             patch_value.size = patch_len;
    681             patch_value.data = (char*)(patch_start + patch_offset);
    682 
    683             RangeSinkState rss;
    684             rss.fd = fd;
    685             rss.tgt = tgt;
    686             rss.p_block = 0;
    687             rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
    688             check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET);
    689 
    690             if (style[0] == 'i') {      // imgdiff
    691                 ApplyImagePatch(buffer, src_blocks * BLOCKSIZE,
    692                                 &patch_value,
    693                                 &RangeSinkWrite, &rss, NULL, NULL);
    694             } else {
    695                 ApplyBSDiffPatch(buffer, src_blocks * BLOCKSIZE,
    696                                  &patch_value, 0,
    697                                  &RangeSinkWrite, &rss, NULL);
    698             }
    699 
    700             // We expect the output of the patcher to fill the tgt ranges exactly.
    701             if (rss.p_block != tgt->count || rss.p_remain != 0) {
    702                 fprintf(stderr, "range sink underrun?\n");
    703             }
    704 
    705             blocks_so_far += tgt->size;
    706             fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
    707             fflush(cmd_pipe);
    708 
    709             free(tgt);
    710         } else if (!DEBUG_ERASE && strcmp("erase", style) == 0) {
    711             struct stat st;
    712             if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) {
    713                 word = strtok_r(NULL, " ", &wordsave);
    714                 RangeSet* tgt = parse_range(word);
    715 
    716                 printf("  erasing %d blocks\n", tgt->size);
    717 
    718                 for (i = 0; i < tgt->count; ++i) {
    719                     uint64_t range[2];
    720                     // offset in bytes
    721                     range[0] = tgt->pos[i*2] * (uint64_t)BLOCKSIZE;
    722                     // len in bytes
    723                     range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * (uint64_t)BLOCKSIZE;
    724 
    725                     if (ioctl(fd, BLKDISCARD, &range) < 0) {
    726                         printf("    blkdiscard failed: %s\n", strerror(errno));
    727                     }
    728                 }
    729 
    730                 free(tgt);
    731             } else {
    732                 printf("  ignoring erase (not block device)\n");
    733             }
    734         } else {
    735             fprintf(stderr, "unknown transfer style \"%s\"\n", style);
    736             exit(1);
    737         }
    738     }
    739 
    740     pthread_join(new_data_thread, NULL);
    741     success = true;
    742 
    743     free(buffer);
    744     printf("wrote %d blocks; expected %d\n", blocks_so_far, total_blocks);
    745     printf("max alloc needed was %zu\n", buffer_alloc);
    746 
    747   done:
    748     free(transfer_list);
    749     FreeValue(blockdev_filename);
    750     FreeValue(transfer_list_value);
    751     FreeValue(new_data_fn);
    752     FreeValue(patch_data_fn);
    753     return StringValue(success ? strdup("t") : strdup(""));
    754 }
    755 
    756 Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
    757     Value* blockdev_filename;
    758     Value* ranges;
    759     const uint8_t* digest = NULL;
    760     if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
    761         return NULL;
    762     }
    763 
    764     if (blockdev_filename->type != VAL_STRING) {
    765         ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
    766         goto done;
    767     }
    768     if (ranges->type != VAL_STRING) {
    769         ErrorAbort(state, "ranges argument to %s must be string", name);
    770         goto done;
    771     }
    772 
    773     int fd = open(blockdev_filename->data, O_RDWR);
    774     if (fd < 0) {
    775         ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno));
    776         goto done;
    777     }
    778 
    779     RangeSet* rs = parse_range(ranges->data);
    780     uint8_t buffer[BLOCKSIZE];
    781 
    782     SHA_CTX ctx;
    783     SHA_init(&ctx);
    784 
    785     int i, j;
    786     for (i = 0; i < rs->count; ++i) {
    787         check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET);
    788         for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
    789             readblock(fd, buffer, BLOCKSIZE);
    790             SHA_update(&ctx, buffer, BLOCKSIZE);
    791         }
    792     }
    793     digest = SHA_final(&ctx);
    794     close(fd);
    795 
    796   done:
    797     FreeValue(blockdev_filename);
    798     FreeValue(ranges);
    799     if (digest == NULL) {
    800         return StringValue(strdup(""));
    801     } else {
    802         return StringValue(PrintSha1(digest));
    803     }
    804 }
    805 
    806 void RegisterBlockImageFunctions() {
    807     RegisterFunction("block_image_update", BlockImageUpdateFn);
    808     RegisterFunction("range_sha1", RangeSha1Fn);
    809 }
    810