block_image_update("/dev/block/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat");
// args:
// - block device (or file) to modify in-place
// - transfer list (blob)
// - new data stream (filename within package.zip)
// - patch stream (filename within package.zip, must be uncompressed)
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
;
// 因为
block_image_update
中有类似p
ackage_extract_file("system.transfer.list")这种还需要执行
// 才能得到返回值的函数
// 在
ReadValueArgs
中利用va_list等C语言的可变参数宏,将block_image_update的四个输入参数
"/dev/block/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat",分别赋值给
blockdev_filename
,
transfer_list_value
,
new_data_fn
,
patch_data_fn
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;
}
//在
package_extract_file("system.transfer.list"),
中将type设为了VAL_BLOB
//
BLOB
(binary large object),二进制大对象,是一个可以存储二进制文件的容器
BLOB
是一个大文件,典型的
BLOB
是一张图片或一个声音文件,由于它们的尺寸,必须使用特殊的方式来处理(例如:上传、下载或者存放到一个数据库)
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;
}
( 这里的ui是 updater info的含义 ,而不是user interface )
UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
FILE
* cmd_pipe = ui->cmd_pipe;
ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
//patch_data_fn->data指向的是“
system.patch.dat
”这段字符串,而不是
ystem.patch.dat
这个文件的内容,
//因此patch_entry就代表内存中的zip安装包中的
system.patch.dat
这一项
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;
}
//计算出
patch_entry
的起始地址,因为注释中说patch stream must be uncompressed
uint8_t* patch_start = ((UpdaterInfo*)(state->cookie))->package_zip_addr +
mzGetZipEntryOffset(patch_entry);
//
new_data_fn->data指向的数据是“
system.new.dat
”,而不是
ystem.new.dat
这个文件的内容,
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;
// we expand the new data from the archive in a
// background thread, and block that threads 'receive uncompressed
// data' function until the main thread has reached a point where we
// want some new data to be written. We signal the background thread
// with the destination for the data and block the main thread,
// waiting for the background thread to complete writing that section.
// Then it signals the main thread to wake up and goes back to
// blocking waiting for a transfer.
//
// NewThreadInfo is the struct used to pass information back and forth
// between the two threads. When the main thread wants some data
// written, it sets rss to the destination location and signals the
// condition. When the background thread is done writing, it clears
// rss and signals the condition again.
typedef struct {
ZipArchive* za;
const ZipEntry* entry;
RangeSinkState* rss;
pthread_mutex_t mu;
pthread_cond_t cv;
} NewThreadInfo;
NewThreadInfo nti;
nti.za = za;
nti.entry = new_entry;
nti.rss = NULL; //先将rss标记置空
pthread_mutex_init(&nti.mu, NULL); //
互斥锁的初始化
pthread_cond_init(&nti.cv, NULL); //
创建一个条件变量,cv就是condition value的意思
extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));
其中cond是一个指向结构pthread_cond_t的指针,cond_attr是一个指向结构pthread_condattr_t的指 针。结构 pthread_condattr_t是条件变量的属性结构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用, 默认值是 PTHREAD_ PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用
pthread_attr_t attr; //
线程具有属性,用
pthread_attr_t
表示,在对该结构进行处理之前必须进行初始化,我们用pthread_attr_init函数对其初始化,
用pthread_attr_destroy对其去除初始化
pthread_attr_init(&attr);
//pthread_attr_setdetachstate
修改线程的分离状态属性,可以使用pthread_attr_setdetachstate函数把线程属性detachstate设置为下面的两个合法值之一:设置为PTHREAD_CREATE_DETACHED,以分离状态启动线程;或者设置为PTHREAD_CREATE_JOINABLE,正常启动线程。线程的分离状态决定一个线程以什么样的方式来终止自己。在默认情况下线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
//pthread_create的四个参数:1指向线程
标识符的指针 2 设置线程属性 3 线程运行函数的起始地址 4 运行函数的参数。
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.
// char* transfer_list = NULL;
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);
}
//将system.transfer.list
文件的所有内容读取到了transfer_list中
memcpy
(transfer_list, transfer_list_value->data, transfer_list_value->size);
transfer_list[transfer_list_value->size] =
'\0'
;
//
按行分割读取
system.transfer.list
中的命令
line = strtok_r(transfer_list,
"\n"
, &linesave);
// first line in transfer list is the version number; currently
// there's only version 1.
// recovery 5.0对应的api是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.避免除以0
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循环中依次读取每行命令
for
(line = strtok_r(NULL,
"\n"
, &linesave); line;
line = strtok_r(NULL,
"\n"
, &linesave)) {
//
style代表每行前的命令名称,如
move
,bsdiff等
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(
""
));
}
标签:5.0,blocks,tgt,transfer,list,patch,aosp,源码,data
From: https://blog.51cto.com/u_16248677/7384753