summaryrefslogtreecommitdiffstats
path: root/updater
diff options
context:
space:
mode:
Diffstat (limited to 'updater')
-rw-r--r--updater/Android.mk3
-rw-r--r--updater/MODULE_LICENSE_GPL0
-rw-r--r--updater/NOTICE339
-rw-r--r--updater/blockimg.c645
-rw-r--r--updater/blockimg.h22
-rw-r--r--updater/install.c277
-rw-r--r--updater/install.h2
-rw-r--r--updater/updater.c22
-rw-r--r--updater/updater.h3
9 files changed, 1221 insertions, 92 deletions
diff --git a/updater/Android.mk b/updater/Android.mk
index 67e98ecd4..a3a900a80 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -4,6 +4,7 @@ LOCAL_PATH := $(call my-dir)
updater_src_files := \
install.c \
+ blockimg.c \
updater.c
#
@@ -20,6 +21,7 @@ LOCAL_SRC_FILES := $(updater_src_files)
ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
LOCAL_CFLAGS += -DUSE_EXT4
+LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_C_INCLUDES += system/extras/ext4_utils
LOCAL_STATIC_LIBRARIES += \
libext4_utils_static \
@@ -30,7 +32,6 @@ endif
LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
LOCAL_STATIC_LIBRARIES += libmincrypt libbz
-LOCAL_STATIC_LIBRARIES += libminelf
LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc
LOCAL_STATIC_LIBRARIES += libselinux
LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
diff --git a/updater/MODULE_LICENSE_GPL b/updater/MODULE_LICENSE_GPL
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/updater/MODULE_LICENSE_GPL
diff --git a/updater/NOTICE b/updater/NOTICE
new file mode 100644
index 000000000..e77696ae8
--- /dev/null
+++ b/updater/NOTICE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/updater/blockimg.c b/updater/blockimg.c
new file mode 100644
index 000000000..c3319c973
--- /dev/null
+++ b/updater/blockimg.c
@@ -0,0 +1,645 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "applypatch/applypatch.h"
+#include "edify/expr.h"
+#include "mincrypt/sha.h"
+#include "minzip/DirUtil.h"
+#include "updater.h"
+
+#define BLOCKSIZE 4096
+
+// Set this to 0 to interpret 'erase' transfers to mean do a
+// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret
+// erase to mean fill the region with zeroes.
+#define DEBUG_ERASE 0
+
+#ifndef BLKDISCARD
+#define BLKDISCARD _IO(0x12,119)
+#endif
+
+char* PrintSha1(const uint8_t* digest);
+
+typedef struct {
+ int count;
+ int size;
+ int pos[0];
+} RangeSet;
+
+static RangeSet* parse_range(char* text) {
+ char* save;
+ int num;
+ num = strtol(strtok_r(text, ",", &save), NULL, 0);
+
+ RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int));
+ if (out == NULL) {
+ fprintf(stderr, "failed to allocate range of %lu bytes\n",
+ sizeof(RangeSet) + num * sizeof(int));
+ exit(1);
+ }
+ out->count = num / 2;
+ out->size = 0;
+ int i;
+ for (i = 0; i < num; ++i) {
+ out->pos[i] = strtol(strtok_r(NULL, ",", &save), NULL, 0);
+ if (i%2) {
+ out->size += out->pos[i];
+ } else {
+ out->size -= out->pos[i];
+ }
+ }
+
+ return out;
+}
+
+static void readblock(int fd, uint8_t* data, size_t size) {
+ size_t so_far = 0;
+ while (so_far < size) {
+ ssize_t r = read(fd, data+so_far, size-so_far);
+ if (r < 0 && errno != EINTR) {
+ fprintf(stderr, "read failed: %s\n", strerror(errno));
+ return;
+ } else {
+ so_far += r;
+ }
+ }
+}
+
+static void writeblock(int fd, const uint8_t* data, size_t size) {
+ size_t written = 0;
+ while (written < size) {
+ ssize_t w = write(fd, data+written, size-written);
+ if (w < 0 && errno != EINTR) {
+ fprintf(stderr, "write failed: %s\n", strerror(errno));
+ return;
+ } else {
+ written += w;
+ }
+ }
+}
+
+static void check_lseek(int fd, off64_t offset, int whence) {
+ while (true) {
+ off64_t ret = lseek64(fd, offset, whence);
+ if (ret < 0) {
+ if (errno != EINTR) {
+ fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ } else {
+ break;
+ }
+ }
+}
+
+static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) {
+ // if the buffer's big enough, reuse it.
+ if (size <= *buffer_alloc) return;
+
+ free(*buffer);
+
+ *buffer = (uint8_t*) malloc(size);
+ if (*buffer == NULL) {
+ fprintf(stderr, "failed to allocate %zu bytes\n", size);
+ exit(1);
+ }
+ *buffer_alloc = size;
+}
+
+typedef struct {
+ int fd;
+ RangeSet* tgt;
+ int p_block;
+ size_t p_remain;
+} RangeSinkState;
+
+static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) {
+ RangeSinkState* rss = (RangeSinkState*) token;
+
+ if (rss->p_remain <= 0) {
+ fprintf(stderr, "range sink write overrun");
+ exit(1);
+ }
+
+ ssize_t written = 0;
+ while (size > 0) {
+ size_t write_now = size;
+ if (rss->p_remain < write_now) write_now = rss->p_remain;
+ writeblock(rss->fd, data, write_now);
+ data += write_now;
+ size -= write_now;
+
+ rss->p_remain -= write_now;
+ written += write_now;
+
+ if (rss->p_remain == 0) {
+ // move to the next block
+ ++rss->p_block;
+ if (rss->p_block < rss->tgt->count) {
+ rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE;
+ check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET);
+ } else {
+ // we can't write any more; return how many bytes have
+ // been written so far.
+ return written;
+ }
+ }
+ }
+
+ return written;
+}
+
+// All of the data for all the 'new' transfers is contained in one
+// file in the update package, concatenated together in the order in
+// which transfers.list will need it. We want to stream it out of the
+// archive (it's compressed) without writing it to a temp file, but we
+// can't write each section until it's that transfer's turn to go.
+//
+// To achieve this, 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;
+
+static bool receive_new_data(const unsigned char* data, int size, void* cookie) {
+ NewThreadInfo* nti = (NewThreadInfo*) cookie;
+
+ while (size > 0) {
+ // Wait for nti->rss to be non-NULL, indicating some of this
+ // data is wanted.
+ pthread_mutex_lock(&nti->mu);
+ while (nti->rss == NULL) {
+ pthread_cond_wait(&nti->cv, &nti->mu);
+ }
+ pthread_mutex_unlock(&nti->mu);
+
+ // At this point nti->rss is set, and we own it. The main
+ // thread is waiting for it to disappear from nti.
+ ssize_t written = RangeSinkWrite(data, size, nti->rss);
+ data += written;
+ size -= written;
+
+ if (nti->rss->p_block == nti->rss->tgt->count) {
+ // we have written all the bytes desired by this rss.
+
+ pthread_mutex_lock(&nti->mu);
+ nti->rss = NULL;
+ pthread_cond_broadcast(&nti->cv);
+ pthread_mutex_unlock(&nti->mu);
+ }
+ }
+
+ return true;
+}
+
+static void* unzip_new_data(void* cookie) {
+ NewThreadInfo* nti = (NewThreadInfo*) cookie;
+ mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti);
+ return NULL;
+}
+
+// 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;
+
+ 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(""));
+}
+
+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));
+ }
+}
+
+void RegisterBlockImageFunctions() {
+ RegisterFunction("block_image_update", BlockImageUpdateFn);
+ RegisterFunction("range_sha1", RangeSha1Fn);
+}
diff --git a/updater/blockimg.h b/updater/blockimg.h
new file mode 100644
index 000000000..2f4ad3c04
--- /dev/null
+++ b/updater/blockimg.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UPDATER_BLOCKIMG_H_
+#define _UPDATER_BLOCKIMG_H_
+
+void RegisterBlockImageFunctions();
+
+#endif
diff --git a/updater/install.c b/updater/install.c
index aebd4f34b..ff7de4793 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -45,27 +45,73 @@
#include "mtdutils/mounts.h"
#include "mtdutils/mtdutils.h"
#include "updater.h"
+#include "install.h"
#ifdef USE_EXT4
#include "make_ext4fs.h"
+#include "wipe.h"
#endif
+void uiPrint(State* state, char* buffer) {
+ char* line = strtok(buffer, "\n");
+ UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
+ while (line) {
+ fprintf(ui->cmd_pipe, "ui_print %s\n", line);
+ line = strtok(NULL, "\n");
+ }
+ fprintf(ui->cmd_pipe, "ui_print\n");
+}
+
+__attribute__((__format__(printf, 2, 3))) __nonnull((2))
+void uiPrintf(State* state, const char* format, ...) {
+ char error_msg[1024];
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(error_msg, sizeof(error_msg), format, ap);
+ va_end(ap);
+ uiPrint(state, error_msg);
+}
+
+// Take a sha-1 digest and return it as a newly-allocated hex string.
+char* PrintSha1(const uint8_t* digest) {
+ char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
+ int i;
+ const char* alphabet = "0123456789abcdef";
+ for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
+ buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
+ buffer[i*2+1] = alphabet[digest[i] & 0xf];
+ }
+ buffer[i*2] = '\0';
+ return buffer;
+}
+
// mount(fs_type, partition_type, location, mount_point)
//
// fs_type="yaffs2" partition_type="MTD" location=partition
// fs_type="ext4" partition_type="EMMC" location=device
Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
- if (argc != 4) {
- return ErrorAbort(state, "%s() expects 4 args, got %d", name, argc);
+ if (argc != 4 && argc != 5) {
+ return ErrorAbort(state, "%s() expects 4-5 args, got %d", name, argc);
}
char* fs_type;
char* partition_type;
char* location;
char* mount_point;
- if (ReadArgs(state, argv, 4, &fs_type, &partition_type,
+ char* mount_options;
+ bool has_mount_options;
+ if (argc == 5) {
+ has_mount_options = true;
+ if (ReadArgs(state, argv, 5, &fs_type, &partition_type,
+ &location, &mount_point, &mount_options) < 0) {
+ return NULL;
+ }
+ } else {
+ has_mount_options = false;
+ if (ReadArgs(state, argv, 4, &fs_type, &partition_type,
&location, &mount_point) < 0) {
- return NULL;
+ return NULL;
+ }
}
if (strlen(fs_type) == 0) {
@@ -105,13 +151,13 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
const MtdPartition* mtd;
mtd = mtd_find_partition_by_name(location);
if (mtd == NULL) {
- printf("%s: no mtd partition named \"%s\"",
+ uiPrintf(state, "%s: no mtd partition named \"%s\"",
name, location);
result = strdup("");
goto done;
}
if (mtd_mount_partition(mtd, mount_point, fs_type, 0 /* rw */) != 0) {
- printf("mtd mount of %s failed: %s\n",
+ uiPrintf(state, "mtd mount of %s failed: %s\n",
location, strerror(errno));
result = strdup("");
goto done;
@@ -119,8 +165,9 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
result = mount_point;
} else {
if (mount(location, mount_point, fs_type,
- MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) {
- printf("%s: failed to mount %s at %s: %s\n",
+ MS_NOATIME | MS_NODEV | MS_NODIRATIME,
+ has_mount_options ? mount_options : "") < 0) {
+ uiPrintf(state, "%s: failed to mount %s at %s: %s\n",
name, location, mount_point, strerror(errno));
result = strdup("");
} else {
@@ -133,6 +180,7 @@ done:
free(partition_type);
free(location);
if (result != mount_point) free(mount_point);
+ if (has_mount_options) free(mount_options);
return StringValue(result);
}
@@ -183,10 +231,14 @@ Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
scan_mounted_volumes();
const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
if (vol == NULL) {
- printf("unmount of %s failed; no such volume\n", mount_point);
+ uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point);
result = strdup("");
} else {
- unmount_mounted_volume(vol);
+ int ret = unmount_mounted_volume(vol);
+ if (ret != 0) {
+ uiPrintf(state, "unmount of %s failed (%d): %s\n",
+ mount_point, ret, strerror(errno));
+ }
result = mount_point;
}
@@ -195,14 +247,29 @@ done:
return StringValue(result);
}
+static int exec_cmd(const char* path, char* const argv[]) {
+ int status;
+ pid_t child;
+ if ((child = vfork()) == 0) {
+ execv(path, argv);
+ _exit(-1);
+ }
+ waitpid(child, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ printf("%s failed with status %d\n", path, WEXITSTATUS(status));
+ }
+ return WEXITSTATUS(status);
+}
+
// format(fs_type, partition_type, location, fs_size, mount_point)
//
// fs_type="yaffs2" partition_type="MTD" location=partition fs_size=<bytes> mount_point=<location>
// fs_type="ext4" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location>
-// if fs_size == 0, then make_ext4fs uses the entire partition.
+// fs_type="f2fs" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location>
+// if fs_size == 0, then make fs uses the entire partition.
// if fs_size > 0, that is the size to use
-// if fs_size < 0, then reserve that many bytes at the end of the partition
+// if fs_size < 0, then reserve that many bytes at the end of the partition (not for "f2fs")
Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
if (argc != 5) {
@@ -274,6 +341,24 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
goto done;
}
result = location;
+ } else if (strcmp(fs_type, "f2fs") == 0) {
+ char *num_sectors;
+ if (asprintf(&num_sectors, "%lld", atoll(fs_size) / 512) <= 0) {
+ printf("format_volume: failed to create %s command for %s\n", fs_type, location);
+ result = strdup("");
+ goto done;
+ }
+ const char *f2fs_path = "/sbin/mkfs.f2fs";
+ const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", location, num_sectors, NULL};
+ int status = exec_cmd(f2fs_path, (char* const*)f2fs_argv);
+ free(num_sectors);
+ if (status != 0) {
+ printf("%s: mkfs.f2fs failed (%d) on %s",
+ name, status, location);
+ result = strdup("");
+ goto done;
+ }
+ result = location;
#endif
} else {
printf("%s: unsupported fs_type \"%s\" partition_type \"%s\"",
@@ -304,13 +389,17 @@ Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) {
goto done;
}
if (strlen(dst_name) == 0) {
- ErrorAbort(state, "dst_name argument to %s() can't be empty",
- name);
+ ErrorAbort(state, "dst_name argument to %s() can't be empty", name);
goto done;
}
-
- if (rename(src_name, dst_name) != 0) {
- ErrorAbort(state, "Rename of %s() to %s() failed, error %s()",
+ if (make_parents(dst_name) != 0) {
+ ErrorAbort(state, "Creating parent of %s failed, error %s",
+ dst_name, strerror(errno));
+ } else if (access(dst_name, F_OK) == 0 && access(src_name, F_OK) != 0) {
+ // File was already moved
+ result = dst_name;
+ } else if (rename(src_name, dst_name) != 0) {
+ ErrorAbort(state, "Rename of %s to %s failed, error %s",
src_name, dst_name, strerror(errno));
} else {
result = dst_name;
@@ -421,19 +510,23 @@ Value* PackageExtractDirFn(const char* name, State* state,
// function (the char* returned is actually a FileContents*).
Value* PackageExtractFileFn(const char* name, State* state,
int argc, Expr* argv[]) {
- if (argc != 1 && argc != 2) {
+ if (argc < 1 || argc > 2) {
return ErrorAbort(state, "%s() expects 1 or 2 args, got %d",
name, argc);
}
bool success = false;
+
+ UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
+
if (argc == 2) {
// The two-argument version extracts to a file.
+ ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
+
char* zip_path;
char* dest_path;
if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
- ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
const ZipEntry* entry = mzFindZipEntry(za, zip_path);
if (entry == NULL) {
printf("%s: no %s in package\n", name, zip_path);
@@ -502,7 +595,7 @@ static int make_parents(char* name) {
*p = '\0';
if (make_parents(name) < 0) return -1;
int result = mkdir(name, 0700);
- if (result == 0) printf("symlink(): created [%s]\n", name);
+ if (result == 0) printf("created [%s]\n", name);
*p = '/';
if (result == 0 || errno == EEXIST) {
// successfully created or already existed; we're done
@@ -577,7 +670,7 @@ struct perm_parsed_args {
uint64_t capabilities;
};
-static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
+static struct perm_parsed_args ParsePermArgs(State * state, int argc, char** args) {
int i;
struct perm_parsed_args parsed;
int bad = 0;
@@ -592,7 +685,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.uid = uid;
parsed.has_uid = true;
} else {
- printf("ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -603,7 +696,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.gid = gid;
parsed.has_gid = true;
} else {
- printf("ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -614,7 +707,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.mode = mode;
parsed.has_mode = true;
} else {
- printf("ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -625,7 +718,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.dmode = mode;
parsed.has_dmode = true;
} else {
- printf("ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -636,7 +729,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.fmode = mode;
parsed.has_fmode = true;
} else {
- printf("ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -647,7 +740,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.capabilities = capabilities;
parsed.has_capabilities = true;
} else {
- printf("ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -657,7 +750,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
parsed.selabel = args[i+1];
parsed.has_selabel = true;
} else {
- printf("ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]);
bad++;
}
continue;
@@ -674,71 +767,71 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
}
static int ApplyParsedPerms(
+ State * state,
const char* filename,
const struct stat *statptr,
struct perm_parsed_args parsed)
{
int bad = 0;
+ if (parsed.has_selabel) {
+ if (lsetfilecon(filename, parsed.selabel) != 0) {
+ uiPrintf(state, "ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n",
+ filename, parsed.selabel, strerror(errno));
+ bad++;
+ }
+ }
+
/* ignore symlinks */
if (S_ISLNK(statptr->st_mode)) {
- return 0;
+ return bad;
}
if (parsed.has_uid) {
if (chown(filename, parsed.uid, -1) < 0) {
- printf("ApplyParsedPerms: chown of %s to %d failed: %s\n",
- filename, parsed.uid, strerror(errno));
+ uiPrintf(state, "ApplyParsedPerms: chown of %s to %d failed: %s\n",
+ filename, parsed.uid, strerror(errno));
bad++;
}
}
if (parsed.has_gid) {
if (chown(filename, -1, parsed.gid) < 0) {
- printf("ApplyParsedPerms: chgrp of %s to %d failed: %s\n",
- filename, parsed.gid, strerror(errno));
+ uiPrintf(state, "ApplyParsedPerms: chgrp of %s to %d failed: %s\n",
+ filename, parsed.gid, strerror(errno));
bad++;
}
}
if (parsed.has_mode) {
if (chmod(filename, parsed.mode) < 0) {
- printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
- filename, parsed.mode, strerror(errno));
+ uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n",
+ filename, parsed.mode, strerror(errno));
bad++;
}
}
if (parsed.has_dmode && S_ISDIR(statptr->st_mode)) {
if (chmod(filename, parsed.dmode) < 0) {
- printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
- filename, parsed.dmode, strerror(errno));
+ uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n",
+ filename, parsed.dmode, strerror(errno));
bad++;
}
}
if (parsed.has_fmode && S_ISREG(statptr->st_mode)) {
if (chmod(filename, parsed.fmode) < 0) {
- printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
+ uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n",
filename, parsed.fmode, strerror(errno));
bad++;
}
}
- if (parsed.has_selabel) {
- // TODO: Don't silently ignore ENOTSUP
- if (lsetfilecon(filename, parsed.selabel) && (errno != ENOTSUP)) {
- printf("ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n",
- filename, parsed.selabel, strerror(errno));
- bad++;
- }
- }
-
if (parsed.has_capabilities && S_ISREG(statptr->st_mode)) {
if (parsed.capabilities == 0) {
if ((removexattr(filename, XATTR_NAME_CAPS) == -1) && (errno != ENODATA)) {
// Report failure unless it's ENODATA (attribute not set)
- printf("ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n",
+ uiPrintf(state, "ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n",
filename, parsed.capabilities, strerror(errno));
bad++;
}
@@ -751,8 +844,8 @@ static int ApplyParsedPerms(
cap_data.data[1].permitted = (uint32_t) (parsed.capabilities >> 32);
cap_data.data[1].inheritable = 0;
if (setxattr(filename, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) < 0) {
- printf("ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n",
- filename, parsed.capabilities, strerror(errno));
+ uiPrintf(state, "ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n",
+ filename, parsed.capabilities, strerror(errno));
bad++;
}
}
@@ -764,10 +857,11 @@ static int ApplyParsedPerms(
// nftw doesn't allow us to pass along context, so we need to use
// global variables. *sigh*
static struct perm_parsed_args recursive_parsed_args;
+static State* recursive_state;
static int do_SetMetadataRecursive(const char* filename, const struct stat *statptr,
int fileflags, struct FTW *pfwt) {
- return ApplyParsedPerms(filename, statptr, recursive_parsed_args);
+ return ApplyParsedPerms(recursive_state, filename, statptr, recursive_parsed_args);
}
static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv[]) {
@@ -792,14 +886,16 @@ static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv
goto done;
}
- struct perm_parsed_args parsed = ParsePermArgs(argc, args);
+ struct perm_parsed_args parsed = ParsePermArgs(state, argc, args);
if (recursive) {
recursive_parsed_args = parsed;
+ recursive_state = state;
bad += nftw(args[0], do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS);
memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args));
+ recursive_state = NULL;
} else {
- bad += ApplyParsedPerms(args[0], &sb, parsed);
+ bad += ApplyParsedPerms(state, args[0], &sb, parsed);
}
done:
@@ -838,8 +934,8 @@ Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
// file_getprop(file, key)
//
// interprets 'file' as a getprop-style file (key=value pairs, one
-// per line, # comment lines and blank lines okay), and returns the value
-// for 'key' (or "" if it isn't defined).
+// per line. # comment lines,blank lines, lines without '=' ignored),
+// and returns the value for 'key' (or "" if it isn't defined).
Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
char* result = NULL;
char* buffer = NULL;
@@ -866,7 +962,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
buffer = malloc(st.st_size+1);
if (buffer == NULL) {
- ErrorAbort(state, "%s: failed to alloc %lld bytes", name, st.st_size+1);
+ ErrorAbort(state, "%s: failed to alloc %lld bytes", name, (long long)st.st_size+1);
goto done;
}
@@ -879,7 +975,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
if (fread(buffer, 1, st.st_size, f) != st.st_size) {
ErrorAbort(state, "%s: failed to read %lld bytes from %s",
- name, st.st_size+1, filename);
+ name, (long long)st.st_size+1, filename);
fclose(f);
goto done;
}
@@ -897,9 +993,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
char* equal = strchr(line, '=');
if (equal == NULL) {
- ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?",
- name, line, filename);
- goto done;
+ continue;
}
// trim whitespace between key and '='
@@ -1053,8 +1147,8 @@ Value* ApplyPatchSpaceFn(const char* name, State* state,
return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t"));
}
+// apply_patch(file, size, init_sha1, tgt_sha1, patch)
-// 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 "
@@ -1173,15 +1267,7 @@ Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
}
free(args);
buffer[size] = '\0';
-
- char* line = strtok(buffer, "\n");
- while (line) {
- fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe,
- "ui_print %s\n", line);
- line = strtok(NULL, "\n");
- }
- fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n");
-
+ uiPrint(state, buffer);
return StringValue(buffer);
}
@@ -1239,19 +1325,6 @@ Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
return StringValue(strdup(buffer));
}
-// Take a sha-1 digest and return it as a newly-allocated hex string.
-static char* PrintSha1(uint8_t* digest) {
- char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
- int i;
- const char* alphabet = "0123456789abcdef";
- for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
- buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
- buffer[i*2+1] = alphabet[digest[i] & 0xf];
- }
- buffer[i*2] = '\0';
- return buffer;
-}
-
// sha1_check(data)
// to return the sha1 of the data (given in the format returned by
// read_file).
@@ -1322,7 +1395,7 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
v->type = VAL_BLOB;
FileContents fc;
- if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) {
+ if (LoadFileContents(filename, &fc) != 0) {
free(filename);
v->size = -1;
v->data = NULL;
@@ -1419,7 +1492,7 @@ Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
// Return the value most recently saved with SetStageFn. The argument
// is the block device for the misc partition.
Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
- if (argc != 2) {
+ if (argc != 1) {
return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
}
@@ -1436,6 +1509,36 @@ Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
return StringValue(strdup(buffer));
}
+Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) {
+ if (argc != 2) {
+ return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
+ }
+
+ char* filename;
+ char* len_str;
+ if (ReadArgs(state, argv, 2, &filename, &len_str) < 0) return NULL;
+
+ size_t len = strtoull(len_str, NULL, 0);
+ int fd = open(filename, O_WRONLY, 0644);
+ int success = wipe_block_device(fd, len);
+
+ free(filename);
+ free(len_str);
+
+ close(fd);
+
+ return StringValue(strdup(success ? "t" : ""));
+}
+
+Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) {
+ if (argc != 0) {
+ return ErrorAbort(state, "%s() expects no args, got %d", name, argc);
+ }
+ UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
+ fprintf(ui->cmd_pipe, "enable_reboot\n");
+ return StringValue(strdup("t"));
+}
+
void RegisterInstallFunctions() {
RegisterFunction("mount", MountFn);
RegisterFunction("is_mounted", IsMountedFn);
@@ -1469,6 +1572,8 @@ void RegisterInstallFunctions() {
RegisterFunction("apply_patch_check", ApplyPatchCheckFn);
RegisterFunction("apply_patch_space", ApplyPatchSpaceFn);
+ RegisterFunction("wipe_block_device", WipeBlockDeviceFn);
+
RegisterFunction("read_file", ReadFileFn);
RegisterFunction("sha1_check", Sha1CheckFn);
RegisterFunction("rename", RenameFn);
@@ -1482,4 +1587,6 @@ void RegisterInstallFunctions() {
RegisterFunction("reboot_now", RebootNowFn);
RegisterFunction("get_stage", GetStageFn);
RegisterFunction("set_stage", SetStageFn);
+
+ RegisterFunction("enable_reboot", EnableRebootFn);
}
diff --git a/updater/install.h b/updater/install.h
index 94f344f8e..659c8b41c 100644
--- a/updater/install.h
+++ b/updater/install.h
@@ -19,4 +19,6 @@
void RegisterInstallFunctions();
+static int make_parents(char* name);
+
#endif
diff --git a/updater/updater.c b/updater/updater.c
index c7009feac..465e1238e 100644
--- a/updater/updater.c
+++ b/updater/updater.c
@@ -21,7 +21,9 @@
#include "edify/expr.h"
#include "updater.h"
#include "install.h"
+#include "blockimg.h"
#include "minzip/Zip.h"
+#include "minzip/SysUtil.h"
// Generated by the makefile, this function defines the
// RegisterDeviceExtensions() function, which calls all the
@@ -65,19 +67,24 @@ int main(int argc, char** argv) {
// Extract the script from the package.
- char* package_data = argv[3];
+ const char* package_filename = argv[3];
+ MemMapping map;
+ if (sysMapFile(package_filename, &map) != 0) {
+ printf("failed to map package %s\n", argv[3]);
+ return 3;
+ }
ZipArchive za;
int err;
- err = mzOpenZipArchive(package_data, &za);
+ err = mzOpenZipArchive(map.addr, map.length, &za);
if (err != 0) {
printf("failed to open package %s: %s\n",
- package_data, strerror(err));
+ argv[3], strerror(err));
return 3;
}
const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME);
if (script_entry == NULL) {
- printf("failed to find %s in %s\n", SCRIPT_NAME, package_data);
+ printf("failed to find %s in %s\n", SCRIPT_NAME, package_filename);
return 4;
}
@@ -92,6 +99,7 @@ int main(int argc, char** argv) {
RegisterBuiltins();
RegisterInstallFunctions();
+ RegisterBlockImageFunctions();
RegisterDeviceExtensions();
FinishRegistration();
@@ -99,8 +107,7 @@ int main(int argc, char** argv) {
Expr* root;
int error_count = 0;
- yy_scan_string(script);
- int error = yyparse(&root, &error_count);
+ int error = parse_string(script, &root, &error_count);
if (error != 0 || error_count > 0) {
printf("%d parse errors\n", error_count);
return 6;
@@ -122,6 +129,8 @@ int main(int argc, char** argv) {
updater_info.cmd_pipe = cmd_pipe;
updater_info.package_zip = &za;
updater_info.version = atoi(version);
+ updater_info.package_zip_addr = map.addr;
+ updater_info.package_zip_len = map.length;
State state;
state.cookie = &updater_info;
@@ -152,6 +161,7 @@ int main(int argc, char** argv) {
if (updater_info.package_zip) {
mzCloseZipArchive(updater_info.package_zip);
}
+ sysReleaseMap(&map);
free(script);
return 0;
diff --git a/updater/updater.h b/updater/updater.h
index d2e901141..d1dfdd05e 100644
--- a/updater/updater.h
+++ b/updater/updater.h
@@ -27,6 +27,9 @@ typedef struct {
FILE* cmd_pipe;
ZipArchive* package_zip;
int version;
+
+ uint8_t* package_zip_addr;
+ size_t package_zip_len;
} UpdaterInfo;
extern struct selabel_handle *sehandle;