Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { Value* blockdev_filename; Value* ranges; const uint8_t* digest = NULL; if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) { return NULL; } if (blockdev_filename->type != VAL_STRING) { ErrorAbort(state, "blockdev_filename argument to %s must be string", name); goto done; } if (ranges->type != VAL_STRING) { ErrorAbort(state, "ranges argument to %s must be string", name); goto done; } int fd = open(blockdev_filename->data, O_RDWR); if (fd < 0) { ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno)); goto done; } RangeSet* rs = parse_range(ranges->data); uint8_t buffer[BLOCKSIZE]; SHA_CTX ctx; SHA_init(&ctx); int i, j; for (i = 0; i < rs->count; ++i) { check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET); for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) { readblock(fd, buffer, BLOCKSIZE); SHA_update(&ctx, buffer, BLOCKSIZE); } } digest = SHA_final(&ctx); close(fd); done: FreeValue(blockdev_filename); FreeValue(ranges); if (digest == NULL) { return StringValue(strdup("")); } else { return StringValue(PrintSha1(digest)); } }
// sha1_check(data) // to return the sha1 of the data (given in the format returned by // read_file). // // sha1_check(data, sha1_hex, [sha1_hex, ...]) // returns the sha1 of the file if it matches any of the hex // strings passed, or "" if it does not equal any of them. // Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 1) { return ErrorAbort(state, "%s() expects at least 1 arg", name); } Value** args = ReadValueVarArgs(state, argc, argv); if (args == NULL) { return NULL; } if (args[0]->size < 0) { fprintf(stderr, "%s(): no file contents received", name); return StringValue(strdup("")); } uint8_t digest[SHA_DIGEST_SIZE]; SHA(args[0]->data, args[0]->size, digest); FreeValue(args[0]); if (argc == 1) { return StringValue(PrintSha1(digest)); } int i; uint8_t* arg_digest = malloc(SHA_DIGEST_SIZE); for (i = 1; i < argc; ++i) { if (args[i]->type != VAL_STRING) { fprintf(stderr, "%s(): arg %d is not a string; skipping", name, i); } else if (ParseSha1(args[i]->data, arg_digest) != 0) { // Warn about bad args and skip them. fprintf(stderr, "%s(): error parsing \"%s\" as sha-1; skipping", name, args[i]->data); } else if (memcmp(digest, arg_digest, SHA_DIGEST_SIZE) == 0) { break; } FreeValue(args[i]); } if (i >= argc) { // Didn't match any of the hex strings; return false. return StringValue(strdup("")); } // Found a match; free all the remaining arguments and return the // matched one. int j; for (j = i+1; j < argc; ++j) { FreeValue(args[j]); } return args[i]; }
Value* UpdateFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 1) { return ErrorAbort(state, "%s() expects 6 args, got %d", name, argc); } char* type = strrchr(name, '_'); if (type == NULL || *(type+1) == '\0') { return ErrorAbort(state, "%s() couldn't get type from function name", name); } ++type; Value* image; if (ReadValueArgs(state, argv, 1, &image) <0) { return NULL; } if (image->type != VAL_BLOB) { printf("image argument is not blob (is type %d)\n", image->type); goto done; } install_firmware_update(type, image->data, image->size, "/tmp/recovery.log"); printf("%s: install_firmware_update returned!\n", name); done: FreeValue(image); // install_firmware_update should reboot. If it returns, it failed. return StringValue(strdup("")); }
/* Function: LListDestroy * ---------------------- * Destroys and frees memory of a linked list. * * root: LList_Node pointer to start of linked list. * FreeValue: Function which frees the memory pointed to by a node. */ void LListDestroy(LList_Node *root, void (*FreeValue)(void *node_value)) { LList_Node *next; if (LListIsEmpty(root)) { free(root); return; } next = root->next; while (next != NULL) { FreeValue(root->node_value); free(root); root = next; next = next->next; } FreeValue(root->node_value); free(root); }
// Evaluate the expressions in argv, giving 'count' Value* (the ... is // zero or more Value** to put them in). If any expression evaluates // to NULL, free the rest and return -1. Return 0 on success. int ReadValueArgs (State * state, Expr * argv[], int count, ...) { Value **args = malloc (count * sizeof (Value *)); va_list v; va_start (v, count); int i; for (i = 0; i < count; ++i) { args[i] = EvaluateValue (state, argv[i]); if (args[i] == NULL) { va_end (v); int j; for (j = 0; j < i; ++j) { FreeValue (args[j]); } free (args); return -1; } *(va_arg (v, Value **)) = args[i]; } va_end (v); free (args); return 0; }
void FreeExpression( AIExpType_t *exp ) { if ( !exp ) { return; } if ( *exp == EX_FUNC ) { AIValueFunc_t *v = ( AIValueFunc_t * ) exp; FreeValueFunc( v ); } else if ( *exp == EX_VALUE ) { AIValue_t *v = ( AIValue_t * ) exp; FreeValue( v ); } else if ( *exp == EX_OP ) { AIOp_t *op = ( AIOp_t * ) exp; FreeOp( op ); } }
CIdValue::~CIdValue() { if (isLink || type == &CTokenTypes::Pointer)return; for (size_t k = 0; k < values.size(); k++) FreeValue(k); values.clear(); }
void CIdValue::TruncTo(size_t k) { if (k >= Size() || isLink)return; while (Size()>k) { FreeValue(Size()-1); values.pop_back(); } }
Value * SequenceFn (const char *name, State * state, int argc, Expr * argv[]) { Value *left = EvaluateValue (state, argv[0]); if (left == NULL) return NULL; FreeValue (left); return EvaluateValue (state, argv[1]); }
char* Evaluate(State* state, Expr* expr) { Value* v = expr->fn(expr->name, state, expr->argc, expr->argv); if (v == NULL) return NULL; if (v->type != VAL_STRING) { ErrorAbort(state, "expecting string, got value type %d", v->type); FreeValue(v); return NULL; } char* result = v->data; free(v); return result; }
Value* WriteBootloaderFn(const char* name, State* state, int argc, Expr* argv[]) { int result = -1; Value* img; Value* xloader_loc; Value* sbl_loc; if (argc != 3) { return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc); } if (ReadValueArgs(state, argv, 3, &img, &xloader_loc, &sbl_loc) < 0) { return NULL; } if(img->type != VAL_BLOB || xloader_loc->type != VAL_STRING || sbl_loc->type != VAL_STRING) { FreeValue(img); FreeValue(xloader_loc); FreeValue(sbl_loc); return ErrorAbort(state, "%s(): argument types are incorrect", name); } result = update_bootloader(img->data, img->size, xloader_loc->data, sbl_loc->data); FreeValue(img); FreeValue(xloader_loc); FreeValue(sbl_loc); return StringValue(strdup(result == 0 ? "t" : "")); }
// write_raw_image(filename_or_blob, partition) Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; Value* partition_value; Value* contents; if (ReadValueArgs(state, argv, 2, &contents, &partition_value) < 0) { return NULL; } char* partition = NULL; if (partition_value->type != VAL_STRING) { ErrorAbort(state, "partition argument to %s must be string", name); goto done; } partition = partition_value->data; if (strlen(partition) == 0) { ErrorAbort(state, "partition argument to %s can't be empty", name); goto done; } if (contents->type == VAL_STRING && strlen((char*) contents->data) == 0) { ErrorAbort(state, "file argument to %s can't be empty", name); goto done; } char* filename = contents->data; if (0 == restore_raw_partition(NULL, partition, filename)) result = strdup(partition); else { result = strdup(""); goto done; } done: if (result != partition) FreeValue(partition_value); FreeValue(contents); return StringValue(result); }
// ELEMENT の削除 void FreeElement(ELEMENT *e) { UINT i; // 引数チェック if (e == NULL) { return; } for (i = 0;i < e->num_value;i++) { FreeValue(e->values[i], e->type); } Free(e->values); Free(e); }
// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1_1, patch_1, ...) Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 6 || (argc % 2) == 1) { return ErrorAbort(state, "%s(): expected at least 6 args and an " "even number, got %d", name, argc); } char* source_filename; char* target_filename; char* target_sha1; char* target_size_str; if (ReadArgs(state, argv, 4, &source_filename, &target_filename, &target_sha1, &target_size_str) < 0) { return NULL; } char* endptr; size_t target_size = strtol(target_size_str, &endptr, 10); if (target_size == 0 && endptr == target_size_str) { ErrorAbort(state, "%s(): can't parse \"%s\" as byte count", name, target_size_str); free(source_filename); free(target_filename); free(target_sha1); free(target_size_str); return NULL; } int patchcount = (argc-4) / 2; Value** patches = ReadValueVarArgs(state, argc-4, argv+4); int i; for (i = 0; i < patchcount; ++i) { if (patches[i*2]->type != VAL_STRING) { ErrorAbort(state, "%s(): sha-1 #%d is not string", name, i); break; } if (patches[i*2+1]->type != VAL_BLOB) { ErrorAbort(state, "%s(): patch #%d is not blob", name, i); break; } } if (i != patchcount) { for (i = 0; i < patchcount*2; ++i) { FreeValue(patches[i]); } free(patches); return NULL; } char** patch_sha_str = malloc(patchcount * sizeof(char*)); for (i = 0; i < patchcount; ++i) { patch_sha_str[i] = patches[i*2]->data; patches[i*2]->data = NULL; FreeValue(patches[i*2]); patches[i] = patches[i*2+1]; } int result = applypatch(source_filename, target_filename, target_sha1, target_size, patchcount, patch_sha_str, patches); for (i = 0; i < patchcount; ++i) { FreeValue(patches[i]); } free(patch_sha_str); free(patches); return StringValue(strdup(result == 0 ? "t" : "")); }
// write_raw_image(filename_or_blob, partition) Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; Value* partition_value; Value* contents; if (ReadValueArgs(state, argv, 2, &contents, &partition_value) < 0) { return NULL; } char* partition = NULL; if (partition_value->type != VAL_STRING) { ErrorAbort(state, "partition argument to %s must be string", name); goto done; } partition = partition_value->data; if (strlen(partition) == 0) { ErrorAbort(state, "partition argument to %s can't be empty", name); goto done; } if (contents->type == VAL_STRING && strlen((char*) contents->data) == 0) { ErrorAbort(state, "file argument to %s can't be empty", name); goto done; } mtd_scan_partitions(); const MtdPartition* mtd = mtd_find_partition_by_name(partition); if (mtd == NULL) { fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition); result = strdup(""); goto done; } MtdWriteContext* ctx = mtd_write_partition(mtd); if (ctx == NULL) { fprintf(stderr, "%s: can't write mtd partition \"%s\"\n", name, partition); result = strdup(""); goto done; } bool success; if (contents->type == VAL_STRING) { // we're given a filename as the contents char* filename = contents->data; FILE* f = fopen(filename, "rb"); if (f == NULL) { fprintf(stderr, "%s: can't open %s: %s\n", name, filename, strerror(errno)); result = strdup(""); goto done; } success = true; char* buffer = malloc(BUFSIZ); int read; while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) { int wrote = mtd_write_data(ctx, buffer, read); success = success && (wrote == read); } free(buffer); fclose(f); } else { // we're given a blob as the contents ssize_t wrote = mtd_write_data(ctx, contents->data, contents->size); success = (wrote == contents->size); } if (!success) { fprintf(stderr, "mtd_write_data to %s failed: %s\n", partition, strerror(errno)); } if (mtd_erase_blocks(ctx, -1) == -1) { fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition); } if (mtd_write_close(ctx) != 0) { fprintf(stderr, "%s: error closing write of %s\n", name, partition); } printf("%s %s partition\n", success ? "wrote" : "failed to write", partition); result = success ? partition : strdup(""); done: if (result != partition) FreeValue(partition_value); FreeValue(contents); return StringValue(result); }
Value* UpdateFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 7) { return ErrorAbort(state, "%s() expects 7 args, got %d", name, argc); } char* type = strrchr(name, '_'); if (type == NULL || *(type+1) == '\0') { return ErrorAbort(state, "%s() couldn't get type from function name", name); } ++type; Value* image; Value* width_string; Value* height_string; Value* bpp_string; Value* busy; Value* fail; Value* expected_sha1_string; if (ReadValueArgs(state, argv, 7, &image, &width_string, &height_string, &bpp_string, &busy, &fail, &expected_sha1_string) < 0) { return NULL; } // close the package ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; mzCloseZipArchive(za); ((UpdaterInfo*)(state->cookie))->package_zip = NULL; // Try to unmount /cache. If we fail (because we're running in an // older recovery that still has the package file open), try to // remount it read-only. If that fails, abort. sync(); scan_mounted_volumes(); MountedVolume* vol = find_mounted_volume_by_mount_point("/cache"); int result = unmount_mounted_volume(vol); if (result != 0) { printf("%s(): failed to unmount cache (%d: %s)\n", name, result, strerror(errno)); result = remount_read_only(vol); if (result != 0) { printf("%s(): failed to remount cache (%d: %s)\n", name, result, strerror(errno)); return StringValue(strdup("")); } else { printf("%s(): remounted cache\n", name); } sync(); } else { printf("%s(): unmounted cache\n", name); } int width = 0, height = 0, bpp = 0; if (width_string->type != VAL_STRING || (width = strtol(width_string->data, NULL, 10)) == 0) { printf("%s(): bad width argument", name); } if (height_string->type != VAL_STRING || (height = strtol(height_string->data, NULL, 10)) == 0) { printf("%s(): bad height argument", name); } if (bpp_string->type != VAL_STRING || (bpp = strtol(bpp_string->data, NULL, 10)) == 0) { printf("%s(): bad bpp argument", name); } if (image->type != VAL_BLOB) { printf("image argument is not blob (is type %d)\n", image->type); goto done; } uint8_t expected_sha1[SHA_DIGEST_SIZE]; char* data = expected_sha1_string->data; if (expected_sha1_string->type != VAL_STRING || strlen(data) != SHA_DIGEST_SIZE*2) { printf("%s(): bad expected_sha1 argument", name); goto done; } printf("expected sha1 is: "); int i; for (i = 0; i < SHA_DIGEST_SIZE; ++i) { char temp = data[i*2+2]; data[i*2+2] = '\0'; expected_sha1[i] = strtol(data+i*2, NULL, 16); data[i*2+2] = temp; printf("%02x", expected_sha1[i]); } printf("\n"); install_firmware_update( type, image->data, image->size, width, height, bpp, busy->size > 0 ? busy->data : NULL, fail->size > 0 ? fail->data : NULL, "/tmp/recovery.log", expected_sha1); printf("%s: install_firmware_update returned!\n", name); done: FreeValue(image); FreeValue(width_string); FreeValue(height_string); FreeValue(bpp_string); FreeValue(busy); FreeValue(fail); // install_firmware_update should reboot. If it returns, it failed. return StringValue(strdup("")); }
Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) { Value* blockdev_filename; Value* transfer_list_value; char* transfer_list = NULL; Value* new_data_fn; Value* patch_data_fn; bool success = false; if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value, &new_data_fn, &patch_data_fn) < 0) { return NULL; } if (blockdev_filename->type != VAL_STRING) { ErrorAbort(state, "blockdev_filename argument to %s must be string", name); goto done; } if (transfer_list_value->type != VAL_BLOB) { ErrorAbort(state, "transfer_list argument to %s must be blob", name); goto done; } if (new_data_fn->type != VAL_STRING) { ErrorAbort(state, "new_data_fn argument to %s must be string", name); goto done; } if (patch_data_fn->type != VAL_STRING) { ErrorAbort(state, "patch_data_fn argument to %s must be string", name); goto done; } UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); FILE* cmd_pipe = ui->cmd_pipe; ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; const ZipEntry* patch_entry = mzFindZipEntry(za, patch_data_fn->data); if (patch_entry == NULL) { ErrorAbort(state, "%s(): no file \"%s\" in package", name, patch_data_fn->data); goto done; } uint8_t* patch_start = ((UpdaterInfo*)(state->cookie))->package_zip_addr + mzGetZipEntryOffset(patch_entry); const ZipEntry* new_entry = mzFindZipEntry(za, new_data_fn->data); if (new_entry == NULL) { ErrorAbort(state, "%s(): no file \"%s\" in package", name, new_data_fn->data); goto done; } // The transfer list is a text file containing commands to // transfer data from one place to another on the target // partition. We parse it and execute the commands in order: // // zero [rangeset] // - fill the indicated blocks with zeros // // new [rangeset] // - fill the blocks with data read from the new_data file // // bsdiff patchstart patchlen [src rangeset] [tgt rangeset] // imgdiff patchstart patchlen [src rangeset] [tgt rangeset] // - read the source blocks, apply a patch, write result to // target blocks. bsdiff or imgdiff specifies the type of // patch. // // move [src rangeset] [tgt rangeset] // - copy data from source blocks to target blocks (no patch // needed; rangesets are the same size) // // erase [rangeset] // - mark the given blocks as empty // // The creator of the transfer list will guarantee that no block // is read (ie, used as the source for a patch or move) after it // has been written. // // Within one command the source and target ranges may overlap so // in general we need to read the entire source into memory before // writing anything to the target blocks. // // All the patch data is concatenated into one patch_data file in // the update package. It must be stored uncompressed because we // memory-map it in directly from the archive. (Since patches are // already compressed, we lose very little by not compressing // their concatenation.) pthread_t new_data_thread; NewThreadInfo nti; nti.za = za; nti.entry = new_entry; nti.rss = NULL; pthread_mutex_init(&nti.mu, NULL); pthread_cond_init(&nti.cv, NULL); pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&new_data_thread, &attr, unzip_new_data, &nti); int i, j; char* linesave; char* wordsave; int fd = open(blockdev_filename->data, O_RDWR); if (fd < 0) { ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno)); goto done; } char* line; char* word; // The data in transfer_list_value is not necessarily // null-terminated, so we need to copy it to a new buffer and add // the null that strtok_r will need. transfer_list = malloc(transfer_list_value->size+1); if (transfer_list == NULL) { fprintf(stderr, "failed to allocate %zd bytes for transfer list\n", transfer_list_value->size+1); exit(1); } memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size); transfer_list[transfer_list_value->size] = '\0'; line = strtok_r(transfer_list, "\n", &linesave); // first line in transfer list is the version number; currently // there's only version 1. if (strcmp(line, "1") != 0) { ErrorAbort(state, "unexpected transfer list version [%s]\n", line); goto done; } // second line in transfer list is the total number of blocks we // expect to write. line = strtok_r(NULL, "\n", &linesave); int total_blocks = strtol(line, NULL, 0); // shouldn't happen, but avoid divide by zero. if (total_blocks == 0) ++total_blocks; int blocks_so_far = 0; uint8_t* buffer = NULL; size_t buffer_alloc = 0; // third and subsequent lines are all individual transfer commands. for (line = strtok_r(NULL, "\n", &linesave); line; line = strtok_r(NULL, "\n", &linesave)) { char* style; style = strtok_r(line, " ", &wordsave); if (strcmp("move", style) == 0) { word = strtok_r(NULL, " ", &wordsave); RangeSet* src = parse_range(word); word = strtok_r(NULL, " ", &wordsave); RangeSet* tgt = parse_range(word); printf(" moving %d blocks\n", src->size); allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; } p = 0; for (i = 0; i < tgt->count; ++i) { check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; writeblock(fd, buffer+p, sz); p += sz; } blocks_so_far += tgt->size; fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); fflush(cmd_pipe); free(src); free(tgt); } else if (strcmp("zero", style) == 0 || (DEBUG_ERASE && strcmp("erase", style) == 0)) { word = strtok_r(NULL, " ", &wordsave); RangeSet* tgt = parse_range(word); printf(" zeroing %d blocks\n", tgt->size); allocate(BLOCKSIZE, &buffer, &buffer_alloc); memset(buffer, 0, BLOCKSIZE); for (i = 0; i < tgt->count; ++i) { check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) { writeblock(fd, buffer, BLOCKSIZE); } } if (style[0] == 'z') { // "zero" but not "erase" blocks_so_far += tgt->size; fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); fflush(cmd_pipe); } free(tgt); } else if (strcmp("new", style) == 0) { word = strtok_r(NULL, " ", &wordsave); RangeSet* tgt = parse_range(word); printf(" writing %d blocks of new data\n", tgt->size); RangeSinkState rss; rss.fd = fd; rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET); pthread_mutex_lock(&nti.mu); nti.rss = &rss; pthread_cond_broadcast(&nti.cv); while (nti.rss) { pthread_cond_wait(&nti.cv, &nti.mu); } pthread_mutex_unlock(&nti.mu); blocks_so_far += tgt->size; fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); fflush(cmd_pipe); free(tgt); } else if (strcmp("bsdiff", style) == 0 || strcmp("imgdiff", style) == 0) { word = strtok_r(NULL, " ", &wordsave); size_t patch_offset = strtoul(word, NULL, 0); word = strtok_r(NULL, " ", &wordsave); size_t patch_len = strtoul(word, NULL, 0); word = strtok_r(NULL, " ", &wordsave); RangeSet* src = parse_range(word); word = strtok_r(NULL, " ", &wordsave); RangeSet* tgt = parse_range(word); printf(" patching %d blocks to %d\n", src->size, tgt->size); // Read the source into memory. allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; } Value patch_value; patch_value.type = VAL_BLOB; patch_value.size = patch_len; patch_value.data = (char*)(patch_start + patch_offset); RangeSinkState rss; rss.fd = fd; rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET); if (style[0] == 'i') { // imgdiff ApplyImagePatch(buffer, src->size * BLOCKSIZE, &patch_value, &RangeSinkWrite, &rss, NULL, NULL); } else { ApplyBSDiffPatch(buffer, src->size * BLOCKSIZE, &patch_value, 0, &RangeSinkWrite, &rss, NULL); } // We expect the output of the patcher to fill the tgt ranges exactly. if (rss.p_block != tgt->count || rss.p_remain != 0) { fprintf(stderr, "range sink underrun?\n"); } blocks_so_far += tgt->size; fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); fflush(cmd_pipe); free(src); free(tgt); } else if (!DEBUG_ERASE && strcmp("erase", style) == 0) { struct stat st; if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) { word = strtok_r(NULL, " ", &wordsave); RangeSet* tgt = parse_range(word); printf(" erasing %d blocks\n", tgt->size); for (i = 0; i < tgt->count; ++i) { uint64_t range[2]; // offset in bytes range[0] = tgt->pos[i*2] * (uint64_t)BLOCKSIZE; // len in bytes range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * (uint64_t)BLOCKSIZE; if (ioctl(fd, BLKDISCARD, &range) < 0) { printf(" blkdiscard failed: %s\n", strerror(errno)); } } free(tgt); } else { printf(" ignoring erase (not block device)\n"); } } else { fprintf(stderr, "unknown transfer style \"%s\"\n", style); exit(1); } } pthread_join(new_data_thread, NULL); success = true; free(buffer); printf("wrote %d blocks; expected %d\n", blocks_so_far, total_blocks); printf("max alloc needed was %zu\n", buffer_alloc); done: free(transfer_list); FreeValue(blockdev_filename); FreeValue(transfer_list_value); FreeValue(new_data_fn); FreeValue(patch_data_fn); return StringValue(success ? strdup("t") : strdup("")); }