• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
parse_range(char * text)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 
readblock(int fd,uint8_t * data,size_t size)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 
writeblock(int fd,const uint8_t * data,size_t size)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 
check_lseek(int fd,off64_t offset,int whence)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 
allocate(size_t size,uint8_t ** buffer,size_t * buffer_alloc)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 
RangeSinkWrite(const uint8_t * data,ssize_t size,void * token)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 
receive_new_data(const unsigned char * data,int size,void * cookie)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 
unzip_new_data(void * cookie)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 
LoadSrcTgtVersion1(char * wordsave,RangeSet ** tgt,int * src_blocks,uint8_t ** buffer,size_t * buffer_alloc,int fd)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 
MoveRange(uint8_t * dest,RangeSet * locs,const uint8_t * source)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 
LoadSrcTgtVersion2(char * wordsave,RangeSet ** tgt,int * src_blocks,uint8_t ** buffer,size_t * buffer_alloc,int fd,uint8_t ** stash_table)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 
BlockImageUpdateFn(const char * name,State * state,int argc,Expr * argv[])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             int ret;
691             if (style[0] == 'i') {      // imgdiff
692                 ret = ApplyImagePatch(buffer, src_blocks * BLOCKSIZE,
693                                       &patch_value,
694                                       &RangeSinkWrite, &rss, NULL, NULL);
695             } else {
696                 ret = ApplyBSDiffPatch(buffer, src_blocks * BLOCKSIZE,
697                                        &patch_value, 0,
698                                        &RangeSinkWrite, &rss, NULL);
699             }
700 
701             if (ret != 0) {
702                 ErrorAbort(state, "patch failed\n");
703                 goto done;
704             }
705 
706             // We expect the output of the patcher to fill the tgt ranges exactly.
707             if (rss.p_block != tgt->count || rss.p_remain != 0) {
708                 ErrorAbort(state, "range sink underrun?\n");
709                 goto done;
710             }
711 
712             blocks_so_far += tgt->size;
713             fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks);
714             fflush(cmd_pipe);
715 
716             free(tgt);
717         } else if (!DEBUG_ERASE && strcmp("erase", style) == 0) {
718             struct stat st;
719             if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) {
720                 word = strtok_r(NULL, " ", &wordsave);
721                 RangeSet* tgt = parse_range(word);
722 
723                 printf("  erasing %d blocks\n", tgt->size);
724 
725                 for (i = 0; i < tgt->count; ++i) {
726                     uint64_t range[2];
727                     // offset in bytes
728                     range[0] = tgt->pos[i*2] * (uint64_t)BLOCKSIZE;
729                     // len in bytes
730                     range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * (uint64_t)BLOCKSIZE;
731 
732                     if (ioctl(fd, BLKDISCARD, &range) < 0) {
733                         ErrorAbort(state, "    blkdiscard failed: %s\n", strerror(errno));
734                         goto done;
735                     }
736                 }
737 
738                 free(tgt);
739             } else {
740                 printf("  ignoring erase (not block device)\n");
741             }
742         } else {
743             ErrorAbort(state, "unknown transfer style \"%s\"\n", style);
744             goto done;
745         }
746     }
747 
748     pthread_join(new_data_thread, NULL);
749     success = true;
750 
751     free(buffer);
752     printf("wrote %d blocks; expected %d\n", blocks_so_far, total_blocks);
753     printf("max alloc needed was %zu\n", buffer_alloc);
754 
755 done:
756     free(transfer_list);
757     FreeValue(blockdev_filename);
758     FreeValue(transfer_list_value);
759     FreeValue(new_data_fn);
760     FreeValue(patch_data_fn);
761     if (success) {
762         return StringValue(strdup("t"));
763     } else {
764         // NULL will be passed to its caller at Evaluate() and abort the OTA
765         // process.
766         return NULL;
767     }
768 }
769 
RangeSha1Fn(const char * name,State * state,int argc,Expr * argv[])770 Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) {
771     Value* blockdev_filename;
772     Value* ranges;
773     const uint8_t* digest = NULL;
774     if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
775         return NULL;
776     }
777 
778     if (blockdev_filename->type != VAL_STRING) {
779         ErrorAbort(state, "blockdev_filename argument to %s must be string", name);
780         goto done;
781     }
782     if (ranges->type != VAL_STRING) {
783         ErrorAbort(state, "ranges argument to %s must be string", name);
784         goto done;
785     }
786 
787     int fd = open(blockdev_filename->data, O_RDWR);
788     if (fd < 0) {
789         ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno));
790         goto done;
791     }
792 
793     RangeSet* rs = parse_range(ranges->data);
794     uint8_t buffer[BLOCKSIZE];
795 
796     SHA_CTX ctx;
797     SHA_init(&ctx);
798 
799     int i, j;
800     for (i = 0; i < rs->count; ++i) {
801         check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET);
802         for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
803             readblock(fd, buffer, BLOCKSIZE);
804             SHA_update(&ctx, buffer, BLOCKSIZE);
805         }
806     }
807     digest = SHA_final(&ctx);
808     close(fd);
809 
810 done:
811     FreeValue(blockdev_filename);
812     FreeValue(ranges);
813     if (digest == NULL) {
814         return NULL;
815     } else {
816         return StringValue(PrintSha1(digest));
817     }
818 }
819 
RegisterBlockImageFunctions()820 void RegisterBlockImageFunctions() {
821     RegisterFunction("block_image_update", BlockImageUpdateFn);
822     RegisterFunction("range_sha1", RangeSha1Fn);
823 }
824