From cf5b17055b1276c187537cf5a054cf6ebe753113 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn <> Date: Tue, 24 Mar 2009 18:36:43 -0700 Subject: Automated import from //branches/donutburger/...@140818,140818 --- NOTICE | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 NOTICE diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + -- cgit v1.2.3 From bc012de46e99fc936e6c06d19416b8414835c7b6 Mon Sep 17 00:00:00 2001 From: Doug Zongker <> Date: Tue, 24 Mar 2009 21:30:32 -0700 Subject: Automated import from //branches/donutburger/...@142141,142141 --- tools/ota/make-update-script.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/ota/make-update-script.c b/tools/ota/make-update-script.c index 225dc526a..1e1148bba 100644 --- a/tools/ota/make-update-script.c +++ b/tools/ota/make-update-script.c @@ -185,16 +185,21 @@ int main(int argc, char *argv[]) { } // The lines we're looking for look like: - // version-bootloader=x.yy.zzzz + // version-bootloader=x.yy.zzzz|x.yy.zzzz|... // or: - // require version-bootloader=x.yy.zzzz + // require version-bootloader=x.yy.zzzz|x.yy.zzzz|... char line[256]; while (fgets(line, sizeof(line), fp)) { - const char *name = strtok(line, "="), *value = strtok(NULL, "\n"); + const char *name = strtok(line, "="), *value = strtok(NULL, "|\n"); if (value != NULL && (!strcmp(name, "version-bootloader") || !strcmp(name, "require version-bootloader"))) { - printf("assert getprop(\"ro.bootloader\") == \"%s\"\n", value); + printf("assert getprop(\"ro.bootloader\") == \"%s\"", value); + + while ((value = strtok(NULL, "|\n")) != NULL) { + printf(" || getprop(\"ro.bootloader\") == \"%s\"", value); + } + printf("\n"); } // We also used to check version-baseband, but we update radio.img // ourselves, so there's no need. -- cgit v1.2.3 From 58bde316e22e392885de71d2391f2bc7f438ff1f Mon Sep 17 00:00:00 2001 From: Doug Zongker <> Date: Fri, 27 Mar 2009 13:25:30 -0700 Subject: AI 143128: Use PNG instead of BMP for recovery image icons. This saves about 60k from the recovery and system images. Automated import of CL 143128 --- Android.mk | 2 +- minui/resources.c | 167 +++++++++++--------------- res/images/icon_error.bmp | Bin 91076 -> 0 bytes res/images/icon_error.png | Bin 0 -> 9616 bytes res/images/icon_firmware_error.bmp | Bin 91076 -> 0 bytes res/images/icon_firmware_error.png | Bin 0 -> 8088 bytes res/images/icon_firmware_install.bmp | Bin 91076 -> 0 bytes res/images/icon_firmware_install.png | Bin 0 -> 11986 bytes res/images/icon_installing.bmp | Bin 91076 -> 0 bytes res/images/icon_installing.png | Bin 0 -> 10138 bytes res/images/icon_unpacking.bmp | Bin 91076 -> 0 bytes res/images/icon_unpacking.png | Bin 0 -> 7180 bytes res/images/indeterminate1.bmp | Bin 20214 -> 0 bytes res/images/indeterminate1.png | Bin 0 -> 2249 bytes res/images/indeterminate2.bmp | Bin 20214 -> 0 bytes res/images/indeterminate2.png | Bin 0 -> 2251 bytes res/images/indeterminate3.bmp | Bin 20214 -> 0 bytes res/images/indeterminate3.png | Bin 0 -> 2254 bytes res/images/indeterminate4.bmp | Bin 20214 -> 0 bytes res/images/indeterminate4.png | Bin 0 -> 2249 bytes res/images/indeterminate5.bmp | Bin 20214 -> 0 bytes res/images/indeterminate5.png | Bin 0 -> 2246 bytes res/images/indeterminate6.bmp | Bin 20214 -> 0 bytes res/images/indeterminate6.png | Bin 0 -> 2262 bytes res/images/progress_bar_empty.bmp | Bin 136 -> 0 bytes res/images/progress_bar_empty.png | Bin 0 -> 148 bytes res/images/progress_bar_empty_left_round.bmp | Bin 294 -> 0 bytes res/images/progress_bar_empty_left_round.png | Bin 0 -> 220 bytes res/images/progress_bar_empty_right_round.bmp | Bin 294 -> 0 bytes res/images/progress_bar_empty_right_round.png | Bin 0 -> 211 bytes res/images/progress_bar_fill.bmp | Bin 136 -> 0 bytes res/images/progress_bar_fill.png | Bin 0 -> 117 bytes res/images/progress_bar_left_round.bmp | Bin 294 -> 0 bytes res/images/progress_bar_left_round.png | Bin 0 -> 195 bytes res/images/progress_bar_right_round.bmp | Bin 294 -> 0 bytes res/images/progress_bar_right_round.png | Bin 0 -> 192 bytes 36 files changed, 68 insertions(+), 101 deletions(-) delete mode 100644 res/images/icon_error.bmp create mode 100644 res/images/icon_error.png delete mode 100644 res/images/icon_firmware_error.bmp create mode 100644 res/images/icon_firmware_error.png delete mode 100644 res/images/icon_firmware_install.bmp create mode 100644 res/images/icon_firmware_install.png delete mode 100644 res/images/icon_installing.bmp create mode 100644 res/images/icon_installing.png delete mode 100644 res/images/icon_unpacking.bmp create mode 100644 res/images/icon_unpacking.png delete mode 100644 res/images/indeterminate1.bmp create mode 100644 res/images/indeterminate1.png delete mode 100644 res/images/indeterminate2.bmp create mode 100644 res/images/indeterminate2.png delete mode 100644 res/images/indeterminate3.bmp create mode 100644 res/images/indeterminate3.png delete mode 100644 res/images/indeterminate4.bmp create mode 100644 res/images/indeterminate4.png delete mode 100644 res/images/indeterminate5.bmp create mode 100644 res/images/indeterminate5.png delete mode 100644 res/images/indeterminate6.bmp create mode 100644 res/images/indeterminate6.png delete mode 100644 res/images/progress_bar_empty.bmp create mode 100644 res/images/progress_bar_empty.png delete mode 100644 res/images/progress_bar_empty_left_round.bmp create mode 100644 res/images/progress_bar_empty_left_round.png delete mode 100644 res/images/progress_bar_empty_right_round.bmp create mode 100644 res/images/progress_bar_empty_right_round.png delete mode 100644 res/images/progress_bar_fill.bmp create mode 100644 res/images/progress_bar_fill.png delete mode 100644 res/images/progress_bar_left_round.bmp create mode 100644 res/images/progress_bar_left_round.png delete mode 100644 res/images/progress_bar_right_round.bmp create mode 100644 res/images/progress_bar_right_round.png diff --git a/Android.mk b/Android.mk index 816d143cc..6198ab338 100644 --- a/Android.mk +++ b/Android.mk @@ -30,7 +30,7 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_TAGS := eng LOCAL_STATIC_LIBRARIES := libminzip libunz libamend libmtdutils libmincrypt -LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libcutils +LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils LOCAL_STATIC_LIBRARIES += libstdc++ libc # Specify a C-includable file containing the OTA public keys. diff --git a/minui/resources.c b/minui/resources.c index 5beb6a6d9..7ecfeefce 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -29,97 +29,84 @@ #include +#include + #include "minui.h" -// File signature for BMP files. -// The letters 'BM' as a little-endian unsigned short. - -#define BMP_SIGNATURE 0x4d42 - -typedef struct { - // constant, value should equal BMP_SIGNATURE - unsigned short bfType; - // size of the file in bytes. - unsigned long bfSize; - // must always be set to zero. - unsigned short bfReserved1; - // must always be set to zero. - unsigned short bfReserved2; - // offset from the beginning of the file to the bitmap data. - unsigned long bfOffBits; - - // The BITMAPINFOHEADER: - // size of the BITMAPINFOHEADER structure, in bytes. - unsigned long biSize; - // width of the image, in pixels. - unsigned long biWidth; - // height of the image, in pixels. - unsigned long biHeight; - // number of planes of the target device, must be set to 1. - unsigned short biPlanes; - // number of bits per pixel. - unsigned short biBitCount; - // type of compression, zero means no compression. - unsigned long biCompression; - // size of the image data, in bytes. If there is no compression, - // it is valid to set this member to zero. - unsigned long biSizeImage; - // horizontal pixels per meter on the designated targer device, - // usually set to zero. - unsigned long biXPelsPerMeter; - // vertical pixels per meter on the designated targer device, - // usually set to zero. - unsigned long biYPelsPerMeter; - // number of colors used in the bitmap, if set to zero the - // number of colors is calculated using the biBitCount member. - unsigned long biClrUsed; - // number of color that are 'important' for the bitmap, - // if set to zero, all colors are important. - unsigned long biClrImportant; -} __attribute__((packed)) BitMapFileHeader; +// libpng gives "undefined reference to 'pow'" errors, and I have no +// idea how to convince the build system to link with -lm. We don't +// need this functionality (it's used for gamma adjustment) so provide +// a dummy implementation to satisfy the linker. +double pow(double x, double y) { + return x; +} int res_create_surface(const char* name, gr_surface* pSurface) { char resPath[256]; - BitMapFileHeader header; GGLSurface* surface = NULL; int result = 0; - - snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.bmp", name); + unsigned char header[8]; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + + snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name); resPath[sizeof(resPath)-1] = '\0'; - int fd = open(resPath, O_RDONLY); - if (fd == -1) { + FILE* fp = fopen(resPath, "rb"); + if (fp == NULL) { result = -1; goto exit; } - size_t bytesRead = read(fd, &header, sizeof(header)); + + size_t bytesRead = fread(header, 1, sizeof(header), fp); if (bytesRead != sizeof(header)) { result = -2; goto exit; } - if (header.bfType != BMP_SIGNATURE) { - result = -3; // Not a legal header + + if (png_sig_cmp(header, 0, sizeof(header))) { + result = -3; goto exit; } - if (header.biPlanes != 1) { + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { result = -4; goto exit; } - if (!(header.biBitCount == 24 || header.biBitCount == 32)) { + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { result = -5; goto exit; } - if (header.biCompression != 0) { + + if (setjmp(png_jmpbuf(png_ptr))) { result = -6; goto exit; } - size_t width = header.biWidth; - size_t height = header.biHeight; + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, sizeof(header)); + png_read_info(png_ptr, info_ptr); + + size_t width = info_ptr->width; + size_t height = info_ptr->height; size_t stride = 4 * width; size_t pixelSize = stride * height; - + + int color_type = info_ptr->color_type; + int bit_depth = info_ptr->bit_depth; + int channels = info_ptr->channels; + if (bit_depth != 8 || (channels != 3 && channels != 4) || + (color_type != PNG_COLOR_TYPE_RGB && + color_type != PNG_COLOR_TYPE_RGBA)) { + return -7; + goto exit; + } + surface = malloc(sizeof(GGLSurface) + pixelSize); if (surface == NULL) { - result = -7; + result = -8; goto exit; } unsigned char* pData = (unsigned char*) (surface + 1); @@ -128,63 +115,43 @@ int res_create_surface(const char* name, gr_surface* pSurface) { surface->height = height; surface->stride = width; /* Yes, pixels, not bytes */ surface->data = pData; - surface->format = (header.biBitCount == 24) ? + surface->format = (channels == 3) ? GGL_PIXEL_FORMAT_RGBX_8888 : GGL_PIXEL_FORMAT_RGBA_8888; - // Source pixel bytes are stored B G R {A} - - lseek(fd, header.bfOffBits, SEEK_SET); - size_t y; - if (header.biBitCount == 24) { // RGB - size_t inputStride = (((3 * width + 3) >> 2) << 2); - for (y = 0; y < height; y++) { - unsigned char* pRow = pData + (height - (y + 1)) * stride; - bytesRead = read(fd, pRow, inputStride); - if (bytesRead != inputStride) { - result = -8; - goto exit; - } + int y; + if (channels == 3) { + for (y = 0; y < height; ++y) { + unsigned char* pRow = pData + y * stride; + png_read_row(png_ptr, pRow, NULL); + int x; for(x = width - 1; x >= 0; x--) { int sx = x * 3; int dx = x * 4; - unsigned char b = pRow[sx]; + unsigned char r = pRow[sx]; unsigned char g = pRow[sx + 1]; - unsigned char r = pRow[sx + 2]; + unsigned char b = pRow[sx + 2]; unsigned char a = 0xff; pRow[dx ] = r; // r pRow[dx + 1] = g; // g - pRow[dx + 2] = b; // b; + pRow[dx + 2] = b; // b pRow[dx + 3] = a; } } - } else { // RGBA - for (y = 0; y < height; y++) { - unsigned char* pRow = pData + (height - (y + 1)) * stride; - bytesRead = read(fd, pRow, stride); - if (bytesRead != stride) { - result = -9; - goto exit; - } - size_t x; - for(x = 0; x < width; x++) { - size_t xx = x * 4; - unsigned char b = pRow[xx]; - unsigned char g = pRow[xx + 1]; - unsigned char r = pRow[xx + 2]; - unsigned char a = pRow[xx + 3]; - pRow[xx ] = r; - pRow[xx + 1] = g; - pRow[xx + 2] = b; - pRow[xx + 3] = a; - } + } else { + for (y = 0; y < height; ++y) { + unsigned char* pRow = pData + y * stride; + png_read_row(png_ptr, pRow, NULL); } } + *pSurface = (gr_surface) surface; exit: - if (fd >= 0) { - close(fd); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + if (fp != NULL) { + fclose(fp); } if (result < 0) { if (surface) { diff --git a/res/images/icon_error.bmp b/res/images/icon_error.bmp deleted file mode 100644 index 7eb2bbc77..000000000 Binary files a/res/images/icon_error.bmp and /dev/null differ diff --git a/res/images/icon_error.png b/res/images/icon_error.png new file mode 100644 index 000000000..7064c2e23 Binary files /dev/null and b/res/images/icon_error.png differ diff --git a/res/images/icon_firmware_error.bmp b/res/images/icon_firmware_error.bmp deleted file mode 100644 index 5b8649f17..000000000 Binary files a/res/images/icon_firmware_error.bmp and /dev/null differ diff --git a/res/images/icon_firmware_error.png b/res/images/icon_firmware_error.png new file mode 100644 index 000000000..0c32c9ede Binary files /dev/null and b/res/images/icon_firmware_error.png differ diff --git a/res/images/icon_firmware_install.bmp b/res/images/icon_firmware_install.bmp deleted file mode 100644 index b0f5f959b..000000000 Binary files a/res/images/icon_firmware_install.bmp and /dev/null differ diff --git a/res/images/icon_firmware_install.png b/res/images/icon_firmware_install.png new file mode 100644 index 000000000..ee2afac5d Binary files /dev/null and b/res/images/icon_firmware_install.png differ diff --git a/res/images/icon_installing.bmp b/res/images/icon_installing.bmp deleted file mode 100644 index fff99fd7e..000000000 Binary files a/res/images/icon_installing.bmp and /dev/null differ diff --git a/res/images/icon_installing.png b/res/images/icon_installing.png new file mode 100644 index 000000000..f24f2e33f Binary files /dev/null and b/res/images/icon_installing.png differ diff --git a/res/images/icon_unpacking.bmp b/res/images/icon_unpacking.bmp deleted file mode 100644 index ab6548c58..000000000 Binary files a/res/images/icon_unpacking.bmp and /dev/null differ diff --git a/res/images/icon_unpacking.png b/res/images/icon_unpacking.png new file mode 100644 index 000000000..9198e2eb1 Binary files /dev/null and b/res/images/icon_unpacking.png differ diff --git a/res/images/indeterminate1.bmp b/res/images/indeterminate1.bmp deleted file mode 100644 index 716c92568..000000000 Binary files a/res/images/indeterminate1.bmp and /dev/null differ diff --git a/res/images/indeterminate1.png b/res/images/indeterminate1.png new file mode 100644 index 000000000..264bf27e5 Binary files /dev/null and b/res/images/indeterminate1.png differ diff --git a/res/images/indeterminate2.bmp b/res/images/indeterminate2.bmp deleted file mode 100644 index 223cd3c1e..000000000 Binary files a/res/images/indeterminate2.bmp and /dev/null differ diff --git a/res/images/indeterminate2.png b/res/images/indeterminate2.png new file mode 100644 index 000000000..c30c049ab Binary files /dev/null and b/res/images/indeterminate2.png differ diff --git a/res/images/indeterminate3.bmp b/res/images/indeterminate3.bmp deleted file mode 100644 index fd9086a1f..000000000 Binary files a/res/images/indeterminate3.bmp and /dev/null differ diff --git a/res/images/indeterminate3.png b/res/images/indeterminate3.png new file mode 100644 index 000000000..891a00095 Binary files /dev/null and b/res/images/indeterminate3.png differ diff --git a/res/images/indeterminate4.bmp b/res/images/indeterminate4.bmp deleted file mode 100644 index 87b264034..000000000 Binary files a/res/images/indeterminate4.bmp and /dev/null differ diff --git a/res/images/indeterminate4.png b/res/images/indeterminate4.png new file mode 100644 index 000000000..7a6415149 Binary files /dev/null and b/res/images/indeterminate4.png differ diff --git a/res/images/indeterminate5.bmp b/res/images/indeterminate5.bmp deleted file mode 100644 index e16efb04c..000000000 Binary files a/res/images/indeterminate5.bmp and /dev/null differ diff --git a/res/images/indeterminate5.png b/res/images/indeterminate5.png new file mode 100644 index 000000000..cd6ab20a7 Binary files /dev/null and b/res/images/indeterminate5.png differ diff --git a/res/images/indeterminate6.bmp b/res/images/indeterminate6.bmp deleted file mode 100644 index 085ad951a..000000000 Binary files a/res/images/indeterminate6.bmp and /dev/null differ diff --git a/res/images/indeterminate6.png b/res/images/indeterminate6.png new file mode 100644 index 000000000..ddd9e7384 Binary files /dev/null and b/res/images/indeterminate6.png differ diff --git a/res/images/progress_bar_empty.bmp b/res/images/progress_bar_empty.bmp deleted file mode 100644 index 8e512fd92..000000000 Binary files a/res/images/progress_bar_empty.bmp and /dev/null differ diff --git a/res/images/progress_bar_empty.png b/res/images/progress_bar_empty.png new file mode 100644 index 000000000..9013f04ac Binary files /dev/null and b/res/images/progress_bar_empty.png differ diff --git a/res/images/progress_bar_empty_left_round.bmp b/res/images/progress_bar_empty_left_round.bmp deleted file mode 100644 index c4e2f44fc..000000000 Binary files a/res/images/progress_bar_empty_left_round.bmp and /dev/null differ diff --git a/res/images/progress_bar_empty_left_round.png b/res/images/progress_bar_empty_left_round.png new file mode 100644 index 000000000..dae7d5d13 Binary files /dev/null and b/res/images/progress_bar_empty_left_round.png differ diff --git a/res/images/progress_bar_empty_right_round.bmp b/res/images/progress_bar_empty_right_round.bmp deleted file mode 100644 index 1906f6209..000000000 Binary files a/res/images/progress_bar_empty_right_round.bmp and /dev/null differ diff --git a/res/images/progress_bar_empty_right_round.png b/res/images/progress_bar_empty_right_round.png new file mode 100644 index 000000000..542708823 Binary files /dev/null and b/res/images/progress_bar_empty_right_round.png differ diff --git a/res/images/progress_bar_fill.bmp b/res/images/progress_bar_fill.bmp deleted file mode 100644 index 8d57d8117..000000000 Binary files a/res/images/progress_bar_fill.bmp and /dev/null differ diff --git a/res/images/progress_bar_fill.png b/res/images/progress_bar_fill.png new file mode 100644 index 000000000..37c04b4f4 Binary files /dev/null and b/res/images/progress_bar_fill.png differ diff --git a/res/images/progress_bar_left_round.bmp b/res/images/progress_bar_left_round.bmp deleted file mode 100644 index 6d2df8d6a..000000000 Binary files a/res/images/progress_bar_left_round.bmp and /dev/null differ diff --git a/res/images/progress_bar_left_round.png b/res/images/progress_bar_left_round.png new file mode 100644 index 000000000..e72af47d4 Binary files /dev/null and b/res/images/progress_bar_left_round.png differ diff --git a/res/images/progress_bar_right_round.bmp b/res/images/progress_bar_right_round.bmp deleted file mode 100644 index 68bb6fe37..000000000 Binary files a/res/images/progress_bar_right_round.bmp and /dev/null differ diff --git a/res/images/progress_bar_right_round.png b/res/images/progress_bar_right_round.png new file mode 100644 index 000000000..d04c980b9 Binary files /dev/null and b/res/images/progress_bar_right_round.png differ -- cgit v1.2.3 From 1066d2c31990e3c2eacedbe0a70013da774fcf3e Mon Sep 17 00:00:00 2001 From: Doug Zongker <> Date: Wed, 1 Apr 2009 13:57:40 -0700 Subject: AI 144070: Add an option to wipe cache (only) to the recovery menu. Automated import of CL 144070 --- recovery.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/recovery.c b/recovery.c index 221ee2975..a0bae97ca 100644 --- a/recovery.c +++ b/recovery.c @@ -302,9 +302,11 @@ prompt_and_wait() #define ITEM_REBOOT 0 #define ITEM_APPLY_SDCARD 1 #define ITEM_WIPE_DATA 2 +#define ITEM_WIPE_CACHE 3 char* items[] = { "reboot system now [Home+Back]", "apply sdcard:update.zip [Alt+S]", "wipe data/factory reset [Alt+W]", + "wipe cache partition", NULL }; ui_start_menu(headers, items); @@ -357,6 +359,13 @@ prompt_and_wait() if (!ui_text_visible()) return; break; + case ITEM_WIPE_CACHE: + ui_print("\n-- Wiping cache...\n"); + erase_root("CACHE:"); + ui_print("Cache wipe complete.\n"); + if (!ui_text_visible()) return; + break; + case ITEM_APPLY_SDCARD: ui_print("\n-- Install from sdcard...\n"); int status = install_package(SDCARD_PACKAGE_FILE); -- cgit v1.2.3 From 49283858fb6565a5da0c5987e614f60254e61804 Mon Sep 17 00:00:00 2001 From: Doug Zongker <> Date: Wed, 1 Apr 2009 14:39:15 -0700 Subject: AI 144082: Remove the unused "unpacking" recovery icon. Automated import of CL 144082 --- common.h | 1 - res/images/icon_unpacking.png | Bin 7180 -> 0 bytes ui.c | 1 - 3 files changed, 2 deletions(-) delete mode 100644 res/images/icon_unpacking.png diff --git a/common.h b/common.h index e17f76a46..98ce86894 100644 --- a/common.h +++ b/common.h @@ -47,7 +47,6 @@ void ui_end_menu(); // Set the icon (normally the only thing visible besides the progress bar). enum { BACKGROUND_ICON_NONE, - BACKGROUND_ICON_UNPACKING, BACKGROUND_ICON_INSTALLING, BACKGROUND_ICON_ERROR, BACKGROUND_ICON_FIRMWARE_INSTALLING, diff --git a/res/images/icon_unpacking.png b/res/images/icon_unpacking.png deleted file mode 100644 index 9198e2eb1..000000000 Binary files a/res/images/icon_unpacking.png and /dev/null differ diff --git a/ui.c b/ui.c index 5d06561c5..b84f1722f 100644 --- a/ui.c +++ b/ui.c @@ -46,7 +46,6 @@ static gr_surface gProgressBarEmpty[NUM_SIDES]; static gr_surface gProgressBarFill[NUM_SIDES]; static const struct { gr_surface* surface; const char *name; } BITMAPS[] = { - { &gBackgroundIcon[BACKGROUND_ICON_UNPACKING], "icon_unpacking" }, { &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" }, { &gBackgroundIcon[BACKGROUND_ICON_ERROR], "icon_error" }, { &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_INSTALLING], -- cgit v1.2.3 From d1b19b9c98ac97db5c933d72dac5dca054a28353 Mon Sep 17 00:00:00 2001 From: Doug Zongker <> Date: Wed, 1 Apr 2009 15:48:46 -0700 Subject: AI 144130: Don't build OTA package keys into the recovery binary; read them from an external file in the recovery image. Use the test-keys for all builds. Automated import of CL 144130 --- Android.mk | 13 --------- install.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 88 insertions(+), 21 deletions(-) diff --git a/Android.mk b/Android.mk index 6198ab338..8c1de7366 100644 --- a/Android.mk +++ b/Android.mk @@ -33,21 +33,8 @@ LOCAL_STATIC_LIBRARIES := libminzip libunz libamend libmtdutils libmincrypt LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils LOCAL_STATIC_LIBRARIES += libstdc++ libc -# Specify a C-includable file containing the OTA public keys. -# This is built in config/Makefile. -# *** THIS IS A TOTAL HACK; EXECUTABLES MUST NOT CHANGE BETWEEN DIFFERENT -# PRODUCTS/BUILD TYPES. *** -# TODO: make recovery read the keys from an external file. -RECOVERY_INSTALL_OTA_KEYS_INC := \ - $(call intermediates-dir-for,PACKAGING,ota_keys_inc)/keys.inc -# Let install.c say #include "keys.inc" -LOCAL_C_INCLUDES += $(dir $(RECOVERY_INSTALL_OTA_KEYS_INC)) - include $(BUILD_EXECUTABLE) -# Depend on the generated keys.inc containing the OTA public keys. -$(intermediates)/install.o: $(RECOVERY_INSTALL_OTA_KEYS_INC) - include $(commands_recovery_local_path)/minui/Android.mk endif # TARGET_ARCH == arm diff --git a/install.c b/install.c index 069112080..4dcfe7536 100644 --- a/install.c +++ b/install.c @@ -31,12 +31,8 @@ #include "roots.h" #include "verifier.h" -/* List of public keys */ -static const RSAPublicKey keys[] = { -#include "keys.inc" -}; - #define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script" +#define PUBLIC_KEYS_FILE "/res/keys" static const ZipEntry * find_update_script(ZipArchive *zip) @@ -114,7 +110,8 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) } static int -handle_update_package(const char *path, ZipArchive *zip) +handle_update_package(const char *path, ZipArchive *zip, + const RSAPublicKey *keys, int numKeys) { // Give verification half the progress bar... ui_print("Verifying update package...\n"); @@ -122,7 +119,7 @@ handle_update_package(const char *path, ZipArchive *zip) VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); - if (!verify_jar_signature(zip, keys, sizeof(keys) / sizeof(keys[0]))) { + if (!verify_jar_signature(zip, keys, numKeys)) { LOGE("Verification failed\n"); return INSTALL_CORRUPT; } @@ -147,6 +144,80 @@ handle_update_package(const char *path, ZipArchive *zip) return ret; } +// Reads a file containing one or more public keys as produced by +// DumpPublicKey: this is an RSAPublicKey struct as it would appear +// as a C source literal, eg: +// +// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}" +// +// (Note that the braces and commas in this example are actual +// characters the parser expects to find in the file; the ellipses +// indicate more numbers omitted from this example.) +// +// The file may contain multiple keys in this format, separated by +// commas. The last key must not be followed by a comma. +// +// Returns NULL if the file failed to parse, or if it contain zero keys. +static RSAPublicKey* +load_keys(const char* filename, int* numKeys) { + RSAPublicKey* out = NULL; + *numKeys = 0; + + FILE* f = fopen(filename, "r"); + if (f == NULL) { + LOGE("opening %s: %s\n", filename, strerror(errno)); + goto exit; + } + + int i; + bool done = false; + while (!done) { + ++*numKeys; + out = realloc(out, *numKeys * sizeof(RSAPublicKey)); + RSAPublicKey* key = out + (*numKeys - 1); + if (fscanf(f, " { %i , %i , { %i", + &(key->len), &(key->n0inv), &(key->n[0])) != 3) { + goto exit; + } + if (key->len != RSANUMWORDS) { + LOGE("key length (%d) does not match expected size\n", key->len); + goto exit; + } + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %i", &(key->n[i])) != 1) goto exit; + } + if (fscanf(f, " } , { %i", &(key->rr[0])) != 1) goto exit; + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %i", &(key->rr[i])) != 1) goto exit; + } + fscanf(f, " } } "); + + // if the line ends in a comma, this file has more keys. + switch (fgetc(f)) { + case ',': + // more keys to come. + break; + + case EOF: + done = true; + break; + + default: + LOGE("unexpected character between keys\n"); + goto exit; + } + } + + fclose(f); + return out; + +exit: + if (f) fclose(f); + free(out); + *numKeys = 0; + return NULL; +} + int install_package(const char *root_path) { @@ -169,6 +240,14 @@ install_package(const char *root_path) ui_print("Opening update package...\n"); LOGI("Update file path: %s\n", path); + int numKeys; + RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); + if (loadedKeys == NULL) { + LOGE("Failed to load keys\n"); + return INSTALL_CORRUPT; + } + LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); + /* Try to open the package. */ ZipArchive zip; @@ -180,7 +259,8 @@ install_package(const char *root_path) /* Verify and install the contents of the package. */ - int status = handle_update_package(path, &zip); + int status = handle_update_package(path, &zip, loadedKeys, numKeys); mzCloseZipArchive(&zip); + free(loadedKeys); return status; } -- cgit v1.2.3 From 596271fa71d79e3eec03c7cf6ac76cb026dd8578 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 29 Apr 2009 16:52:04 -0700 Subject: handle short writes when unzipping files minzip fails if write() doesn't write all the data in one call. Apparently this was good enough before, but it causes OTAs to fail all the time now (maybe due to the recently-submitted kernel)? Change code to attempt continuing after short writes. --- minzip/Zip.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/minzip/Zip.c b/minzip/Zip.c index 100c833fe..ead899390 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -41,7 +41,7 @@ enum { CENSIZ = 20, CENLEN = 24, CENNAM = 28, - CENEXT = 30, + CENEXT = 30, CENCOM = 32, CENDSK = 34, CENATT = 36, @@ -66,13 +66,13 @@ enum { LOCSIG = 0x04034b50, // PK34 LOCHDR = 30, - + LOCVER = 4, LOCFLG = 6, LOCHOW = 8, LOCTIM = 10, LOCCRC = 14, - LOCSIZ = 18, + LOCSIZ = 18, LOCLEN = 22, LOCNAM = 26, LOCEXT = 28, @@ -757,7 +757,7 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry, { CopyProcessArgs args; bool ret; - + args.buf = buf; args.bufLen = bufLen; ret = mzProcessZipEntryContents(pArchive, pEntry, copyProcessFunction, @@ -772,13 +772,29 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry, static bool writeProcessFunction(const unsigned char *data, int dataLen, void *fd) { - ssize_t n = write((int)fd, data, dataLen); - if (n != dataLen) { - LOGE("Can't write %d bytes (only %ld) from zip file: %s\n", - dataLen, n, strerror(errno)); - return false; - } - return true; + int zeroWrites = 0; + ssize_t soFar = 0; + do { + ssize_t n = write((int)fd, data+soFar, dataLen-soFar); + if (n < 0) { + LOGE("Error writing %ld bytes from zip file: %s\n", + dataLen-soFar, strerror(errno)); + return false; + } else if (n > 0) { + soFar += n; + if (soFar == dataLen) return true; + if (soFar > dataLen) { + LOGE("write overrun? (%ld bytes instead of %d)\n", + soFar, dataLen); + return false; + } + zeroWrites = 0; + } else { + ++zeroWrites; + } + } while (zeroWrites < 5); + LOGE("too many consecutive zero-length writes\n"); + return false; } /* -- cgit v1.2.3 From 683c4628039a8cb6dad1a086fae23a7d71438414 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 5 May 2009 17:50:21 -0700 Subject: align data passed to write() on 32k boundaries In donut, OTA installation often encounters the write() system call doing short writes -- which is legal but unexpected -- or failing with ENOSPC when plenty of space is available. Passing aligned memory buffers to write() appears to prevent (or at least reduce the frequency) of these problems. b/1833052 has been filed to look at the underlying problem, but this change aligns buffers we use with write() so we can OTA for now (or see if this problem still occurs). --- minzip/Zip.c | 62 ++++++++++++++++++++++++++++++++++++++++++++---------------- minzip/Zip.h | 10 +++++++++- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/minzip/Zip.c b/minzip/Zip.c index ead899390..a601e7453 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -12,6 +12,7 @@ #include // for uintptr_t #include #include // for S_ISLNK() +#include #include #define LOG_TAG "minzip" @@ -84,6 +85,12 @@ enum { }; +/* The maximum zipped file write size we will align. */ +#define WRITE_SIZE 32768 +/* The boundary on which we will align it. */ +#define WRITE_ALIGNMENT 32768 + + /* * For debugging, dump the contents of a ZipEntry. */ @@ -770,17 +777,38 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry, } static bool writeProcessFunction(const unsigned char *data, int dataLen, - void *fd) + void *cookie) { - int zeroWrites = 0; + WriteInfo *wi = (WriteInfo*)cookie; + + if (dataLen <= WRITE_SIZE) { + memcpy(wi->aligned_buffer, data, dataLen); + data = wi->aligned_buffer; + } + ssize_t soFar = 0; - do { - ssize_t n = write((int)fd, data+soFar, dataLen-soFar); - if (n < 0) { - LOGE("Error writing %ld bytes from zip file: %s\n", - dataLen-soFar, strerror(errno)); + while (true) { + ssize_t n = write(wi->fd, data+soFar, dataLen-soFar); + if (n <= 0) { + LOGE("Error writing %ld bytes from zip file from %p: %s\n", + dataLen-soFar, data+soFar, strerror(errno)); + if (errno == ENOSPC) { + struct statfs sf; + if (statfs("/system", &sf) != 0) { + LOGE("failed to statfs /system: %s\n", strerror(errno)); + } else { + LOGE("statfs said: %ld * %ld = %ld\n", + (long)sf.f_bsize, (long)sf.f_bfree, + (long)sf.f_bsize * (long)sf.f_bfree); + } + } return false; } else if (n > 0) { + if (n < dataLen-soFar) { + LOGE("short write: %d bytes of %d from %p\n", + (int)n, (int)(dataLen-soFar), + data+soFar); + } soFar += n; if (soFar == dataLen) return true; if (soFar > dataLen) { @@ -788,23 +816,18 @@ static bool writeProcessFunction(const unsigned char *data, int dataLen, soFar, dataLen); return false; } - zeroWrites = 0; - } else { - ++zeroWrites; } - } while (zeroWrites < 5); - LOGE("too many consecutive zero-length writes\n"); - return false; + } } /* * Uncompress "pEntry" in "pArchive" to "fd" at the current offset. */ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, - const ZipEntry *pEntry, int fd) + const ZipEntry *pEntry, WriteInfo *wi) { bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction, - (void *)fd); + wi); if (!ret) { LOGE("Can't extract entry to file.\n"); return false; @@ -906,6 +929,11 @@ bool mzExtractRecursive(const ZipArchive *pArchive, return false; } + unsigned char* buffer = malloc(WRITE_SIZE+WRITE_ALIGNMENT); + WriteInfo wi; + wi.aligned_buffer = buffer + WRITE_ALIGNMENT - + ((long)buffer % WRITE_ALIGNMENT); + unsigned int zipDirLen; char *zpath; @@ -1086,7 +1114,8 @@ bool mzExtractRecursive(const ZipArchive *pArchive, break; } - bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd); + wi.fd = fd; + bool ok = mzExtractZipEntryToFile(pArchive, pEntry, &wi); close(fd); if (!ok) { LOGE("Error extracting \"%s\"\n", targetFile); @@ -1109,6 +1138,7 @@ bool mzExtractRecursive(const ZipArchive *pArchive, free(helper.buf); free(zpath); + free(buffer); return ok; } diff --git a/minzip/Zip.h b/minzip/Zip.h index 1c1df2fae..57c0abd2c 100644 --- a/minzip/Zip.h +++ b/minzip/Zip.h @@ -55,6 +55,14 @@ typedef struct { size_t len; } UnterminatedString; +/* + * The information we pass down to writeProcessFunction. + */ +typedef struct { + int fd; + unsigned char* aligned_buffer; +} WriteInfo; + /* * Open a Zip archive. * @@ -166,7 +174,7 @@ bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry); * Inflate and write an entry to a file. */ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, - const ZipEntry *pEntry, int fd); + const ZipEntry *pEntry, WriteInfo *wi); /* * Inflate all entries under zipDir to the directory specified by -- cgit v1.2.3 From 1c4ceae38f3fd7eb1e451d430acb5d99f257b0f9 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 8 May 2009 09:43:28 -0700 Subject: undo temporary alignment hack Remove the memory alignment that mysteriously made OTA installs work, in anticipation of a kernel that fixes the actual problem. Handle EINTR properly. --- minzip/Zip.c | 46 +++++++--------------------------------------- minzip/Zip.h | 10 +--------- 2 files changed, 8 insertions(+), 48 deletions(-) diff --git a/minzip/Zip.c b/minzip/Zip.c index a601e7453..8cdb89874 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -12,7 +12,6 @@ #include // for uintptr_t #include #include // for S_ISLNK() -#include #include #define LOG_TAG "minzip" @@ -85,12 +84,6 @@ enum { }; -/* The maximum zipped file write size we will align. */ -#define WRITE_SIZE 32768 -/* The boundary on which we will align it. */ -#define WRITE_ALIGNMENT 32768 - - /* * For debugging, dump the contents of a ZipEntry. */ @@ -779,36 +772,18 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry, static bool writeProcessFunction(const unsigned char *data, int dataLen, void *cookie) { - WriteInfo *wi = (WriteInfo*)cookie; - - if (dataLen <= WRITE_SIZE) { - memcpy(wi->aligned_buffer, data, dataLen); - data = wi->aligned_buffer; - } + int fd = (int)cookie; ssize_t soFar = 0; while (true) { - ssize_t n = write(wi->fd, data+soFar, dataLen-soFar); + ssize_t n = write(fd, data+soFar, dataLen-soFar); if (n <= 0) { LOGE("Error writing %ld bytes from zip file from %p: %s\n", dataLen-soFar, data+soFar, strerror(errno)); - if (errno == ENOSPC) { - struct statfs sf; - if (statfs("/system", &sf) != 0) { - LOGE("failed to statfs /system: %s\n", strerror(errno)); - } else { - LOGE("statfs said: %ld * %ld = %ld\n", - (long)sf.f_bsize, (long)sf.f_bfree, - (long)sf.f_bsize * (long)sf.f_bfree); - } + if (errno != EINTR) { + return false; } - return false; } else if (n > 0) { - if (n < dataLen-soFar) { - LOGE("short write: %d bytes of %d from %p\n", - (int)n, (int)(dataLen-soFar), - data+soFar); - } soFar += n; if (soFar == dataLen) return true; if (soFar > dataLen) { @@ -824,10 +799,10 @@ static bool writeProcessFunction(const unsigned char *data, int dataLen, * Uncompress "pEntry" in "pArchive" to "fd" at the current offset. */ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, - const ZipEntry *pEntry, WriteInfo *wi) + const ZipEntry *pEntry, int fd) { bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction, - wi); + (void*)fd); if (!ret) { LOGE("Can't extract entry to file.\n"); return false; @@ -929,11 +904,6 @@ bool mzExtractRecursive(const ZipArchive *pArchive, return false; } - unsigned char* buffer = malloc(WRITE_SIZE+WRITE_ALIGNMENT); - WriteInfo wi; - wi.aligned_buffer = buffer + WRITE_ALIGNMENT - - ((long)buffer % WRITE_ALIGNMENT); - unsigned int zipDirLen; char *zpath; @@ -1114,8 +1084,7 @@ bool mzExtractRecursive(const ZipArchive *pArchive, break; } - wi.fd = fd; - bool ok = mzExtractZipEntryToFile(pArchive, pEntry, &wi); + bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd); close(fd); if (!ok) { LOGE("Error extracting \"%s\"\n", targetFile); @@ -1138,7 +1107,6 @@ bool mzExtractRecursive(const ZipArchive *pArchive, free(helper.buf); free(zpath); - free(buffer); return ok; } diff --git a/minzip/Zip.h b/minzip/Zip.h index 57c0abd2c..1c1df2fae 100644 --- a/minzip/Zip.h +++ b/minzip/Zip.h @@ -55,14 +55,6 @@ typedef struct { size_t len; } UnterminatedString; -/* - * The information we pass down to writeProcessFunction. - */ -typedef struct { - int fd; - unsigned char* aligned_buffer; -} WriteInfo; - /* * Open a Zip archive. * @@ -174,7 +166,7 @@ bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry); * Inflate and write an entry to a file. */ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, - const ZipEntry *pEntry, WriteInfo *wi); + const ZipEntry *pEntry, int fd); /* * Inflate all entries under zipDir to the directory specified by -- cgit v1.2.3 From 07e1dca7068284c4f3013550335029eb72b39b82 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 28 May 2009 19:02:45 -0700 Subject: don't say "install complete" when it really isn't Change the recovery UI so that when there is a hboot or radio update pending (which the user most do a home+back reboot to actually install), the UI tells them so, instead of saying "Install from sdcard complete." --- firmware.c | 4 ++++ firmware.h | 3 +++ install.c | 2 +- recovery.c | 7 ++++++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/firmware.c b/firmware.c index 34b291835..e2e4fe630 100644 --- a/firmware.c +++ b/firmware.c @@ -39,6 +39,10 @@ int remember_firmware_update(const char *type, const char *data, int length) { return 0; } +// Return true if there is a firmware update pending. +int firmware_update_pending() { + return update_data != NULL && update_length > 0; +} /* Bootloader / Recovery Flow * diff --git a/firmware.h b/firmware.h index f3f7aab79..aeb8f97aa 100644 --- a/firmware.h +++ b/firmware.h @@ -23,6 +23,9 @@ */ int remember_firmware_update(const char *type, const char *data, int length); +/* Returns true if a firmware update has been saved. */ +int firmware_update_pending(); + /* If an update was saved, reboot into the bootloader now to install it. * Returns 0 if no radio image was defined, nonzero on error, * doesn't return at all on success... diff --git a/install.c b/install.c index 4dcfe7536..e7db2a8f2 100644 --- a/install.c +++ b/install.c @@ -105,7 +105,7 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) return INSTALL_ERROR; } - ui_print("Installation complete.\n"); + LOGI("Installation complete.\n"); return INSTALL_SUCCESS; } diff --git a/recovery.c b/recovery.c index a0bae97ca..e329db93d 100644 --- a/recovery.c +++ b/recovery.c @@ -375,7 +375,12 @@ prompt_and_wait() } else if (!ui_text_visible()) { return; // reboot if logs aren't visible } else { - ui_print("Install from sdcard complete.\n"); + if (firmware_update_pending()) { + ui_print("\nReboot via home+back or menu\n" + "to complete installation.\n"); + } else { + ui_print("\nInstall from sdcard complete.\n"); + } } break; } -- cgit v1.2.3 From f28c916e73ee9f643c67c70d059c70381d774cb0 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 2 Jun 2009 15:30:11 -0700 Subject: remove unused permissions scheme from amend Amend (aka the recovery command language) had a half-implemented scheme of limiting which commands OTA packages were allowed to execute. It's not clear what this was ever supposed to be good for. Remove it. --- amend/Android.mk | 4 +- amend/amend.c | 1 + amend/commands.c | 88 +++--------- amend/commands.h | 32 +---- amend/main.c | 6 - amend/permissions.c | 270 ------------------------------------ amend/permissions.h | 111 --------------- amend/register.c | 88 +++--------- amend/test_commands.c | 77 +---------- amend/test_permissions.c | 347 ----------------------------------------------- commands.c | 117 +++------------- recovery.c | 4 - 12 files changed, 69 insertions(+), 1076 deletions(-) delete mode 100644 amend/permissions.c delete mode 100644 amend/permissions.h delete mode 100644 amend/test_permissions.c diff --git a/amend/Android.mk b/amend/Android.mk index ae2d44ae1..c3b7c3277 100644 --- a/amend/Android.mk +++ b/amend/Android.mk @@ -10,13 +10,11 @@ amend_src_files := \ ast.c \ symtab.c \ commands.c \ - permissions.c \ execute.c amend_test_files := \ test_symtab.c \ - test_commands.c \ - test_permissions.c + test_commands.c # "-x c" forces the lex/yacc files to be compiled as c; # the build system otherwise forces them to be c++. diff --git a/amend/amend.c b/amend/amend.c index 49cd64edb..6f706d021 100644 --- a/amend/amend.c +++ b/amend/amend.c @@ -17,6 +17,7 @@ #include #include "amend.h" #include "lexer.h" +#include "parser.h" extern const AmCommandList *gCommands; diff --git a/amend/commands.c b/amend/commands.c index 75ff82840..78121adf1 100644 --- a/amend/commands.c +++ b/amend/commands.c @@ -152,37 +152,30 @@ getCommandArgumentType(Command *cmd) } static int -callCommandInternal(CommandEntry *entry, int argc, const char *argv[], - PermissionRequestList *permissions) +callCommandInternal(CommandEntry *entry, int argc, const char *argv[]) { if (entry != NULL && entry->argType == CMD_ARGS_WORDS && (argc == 0 || (argc > 0 && argv != NULL))) { - if (permissions == NULL) { - int i; - for (i = 0; i < argc; i++) { - if (argv[i] == NULL) { - goto bail; - } + int i; + for (i = 0; i < argc; i++) { + if (argv[i] == NULL) { + goto bail; } } TRACE("calling command %s\n", entry->name); - return entry->hook(entry->name, entry->cookie, argc, argv, permissions); -//xxx if permissions, make sure the entry has added at least one element. + return entry->hook(entry->name, entry->cookie, argc, argv); } bail: return -1; } static int -callBooleanCommandInternal(CommandEntry *entry, bool arg, - PermissionRequestList *permissions) +callBooleanCommandInternal(CommandEntry *entry, bool arg) { if (entry != NULL && entry->argType == CMD_ARGS_BOOLEAN) { TRACE("calling boolean command %s\n", entry->name); - return entry->hook(entry->name, entry->cookie, arg ? 1 : 0, NULL, - permissions); -//xxx if permissions, make sure the entry has added at least one element. + return entry->hook(entry->name, entry->cookie, arg ? 1 : 0, NULL); } return -1; } @@ -190,63 +183,37 @@ callBooleanCommandInternal(CommandEntry *entry, bool arg, int callCommand(Command *cmd, int argc, const char *argv[]) { - return callCommandInternal((CommandEntry *)cmd, argc, argv, NULL); + return callCommandInternal((CommandEntry *)cmd, argc, argv); } int callBooleanCommand(Command *cmd, bool arg) { - return callBooleanCommandInternal((CommandEntry *)cmd, arg, NULL); -} - -int -getCommandPermissions(Command *cmd, int argc, const char *argv[], - PermissionRequestList *permissions) -{ - if (permissions != NULL) { - return callCommandInternal((CommandEntry *)cmd, argc, argv, - permissions); - } - return -1; -} - -int -getBooleanCommandPermissions(Command *cmd, bool arg, - PermissionRequestList *permissions) -{ - if (permissions != NULL) { - return callBooleanCommandInternal((CommandEntry *)cmd, arg, - permissions); - } - return -1; + return callBooleanCommandInternal((CommandEntry *)cmd, arg); } int callFunctionInternal(CommandEntry *entry, int argc, const char *argv[], - char **result, size_t *resultLen, PermissionRequestList *permissions) + char **result, size_t *resultLen) { if (entry != NULL && entry->argType == CMD_ARGS_WORDS && (argc == 0 || (argc > 0 && argv != NULL))) { - if ((permissions == NULL && result != NULL) || - (permissions != NULL && result == NULL)) + if (result != NULL) { - if (permissions == NULL) { - /* This is the actual invocation of the function, - * which means that none of the arguments are allowed - * to be NULL. - */ - int i; - for (i = 0; i < argc; i++) { - if (argv[i] == NULL) { - goto bail; - } + /* This is the actual invocation of the function, + * which means that none of the arguments are allowed + * to be NULL. + */ + int i; + for (i = 0; i < argc; i++) { + if (argv[i] == NULL) { + goto bail; } } TRACE("calling function %s\n", entry->name); return ((FunctionHook)entry->hook)(entry->name, entry->cookie, - argc, argv, result, resultLen, permissions); -//xxx if permissions, make sure the entry has added at least one element. + argc, argv, result, resultLen); } } bail: @@ -258,16 +225,5 @@ callFunction(Function *fn, int argc, const char *argv[], char **result, size_t *resultLen) { return callFunctionInternal((CommandEntry *)fn, argc, argv, - result, resultLen, NULL); -} - -int -getFunctionPermissions(Function *fn, int argc, const char *argv[], - PermissionRequestList *permissions) -{ - if (permissions != NULL) { - return callFunctionInternal((CommandEntry *)fn, argc, argv, - NULL, NULL, permissions); - } - return -1; + result, resultLen); } diff --git a/amend/commands.h b/amend/commands.h index 38931c075..6c97e5587 100644 --- a/amend/commands.h +++ b/amend/commands.h @@ -14,31 +14,18 @@ * limitations under the License. */ +#include + #ifndef AMEND_COMMANDS_H_ #define AMEND_COMMANDS_H_ -#include "permissions.h" - -/* Invoke or dry-run a command. If "permissions" is non-NULL, - * the hook should fill it out with the list of files and operations that - * it would need to complete its operation. If "permissions" is NULL, - * the hook should do the actual work specified by its arguments. - * - * When a command is called with non-NULL "permissions", some arguments - * may be NULL. A NULL argument indicates that the argument is actually - * the output of another function, so is not known at permissions time. - * The permissions of leaf-node functions (those that have only literal - * strings as arguments) will get appended to the permissions of the - * functions that call them. However, to be completely safe, functions - * that receive a NULL argument should request the broadest-possible - * permissions for the range of the input argument. +/* Invoke a command. * * When a boolean command is called, "argc" is the boolean value and * "argv" is NULL. */ typedef int (*CommandHook)(const char *name, void *cookie, - int argc, const char *argv[], - PermissionRequestList *permissions); + int argc, const char *argv[]); int commandInit(void); void commandCleanup(void); @@ -66,19 +53,13 @@ CommandArgumentType getCommandArgumentType(Command *cmd); int callCommand(Command *cmd, int argc, const char *argv[]); int callBooleanCommand(Command *cmd, bool arg); -int getCommandPermissions(Command *cmd, int argc, const char *argv[], - PermissionRequestList *permissions); -int getBooleanCommandPermissions(Command *cmd, bool arg, - PermissionRequestList *permissions); - /* * Function management */ typedef int (*FunctionHook)(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions); + char **result, size_t *resultLen); struct Function; typedef struct Function Function; @@ -90,7 +71,4 @@ Function *findFunction(const char *name); int callFunction(Function *fn, int argc, const char *argv[], char **result, size_t *resultLen); -int getFunctionPermissions(Function *fn, int argc, const char *argv[], - PermissionRequestList *permissions); - #endif // AMEND_COMMANDS_H_ diff --git a/amend/main.c b/amend/main.c index 9bb0785de..bc9e58778 100644 --- a/amend/main.c +++ b/amend/main.c @@ -86,12 +86,6 @@ main(int argc, char *argv[]) fprintf(stderr, "test_cmd_fn() failed: %d\n", ret); exit(ret); } - extern int test_permissions(void); - ret = test_permissions(); - if (ret != 0) { - fprintf(stderr, "test_permissions() failed: %d\n", ret); - exit(ret); - } #endif argc--; diff --git a/amend/permissions.c b/amend/permissions.c deleted file mode 100644 index a642d0bb2..000000000 --- a/amend/permissions.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (C) 2007 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 -#include -#include "permissions.h" - -int -initPermissionRequestList(PermissionRequestList *list) -{ - if (list != NULL) { - list->requests = NULL; - list->numRequests = 0; - list->requestsAllocated = 0; - return 0; - } - return -1; -} - -int -addPermissionRequestToList(PermissionRequestList *list, - const char *path, bool recursive, unsigned int permissions) -{ - if (list == NULL || list->numRequests < 0 || - list->requestsAllocated < list->numRequests || path == NULL) - { - return -1; - } - - if (list->numRequests == list->requestsAllocated) { - int newSize; - PermissionRequest *newRequests; - - newSize = list->requestsAllocated * 2; - if (newSize < 16) { - newSize = 16; - } - newRequests = (PermissionRequest *)realloc(list->requests, - newSize * sizeof(PermissionRequest)); - if (newRequests == NULL) { - return -2; - } - list->requests = newRequests; - list->requestsAllocated = newSize; - } - - PermissionRequest *req; - req = &list->requests[list->numRequests++]; - req->path = strdup(path); - if (req->path == NULL) { - list->numRequests--; - return -3; - } - req->recursive = recursive; - req->requested = permissions; - req->allowed = 0; - - return 0; -} - -void -freePermissionRequestListElements(PermissionRequestList *list) -{ - if (list != NULL && list->numRequests >= 0 && - list->requestsAllocated >= list->numRequests) - { - int i; - for (i = 0; i < list->numRequests; i++) { - free((void *)list->requests[i].path); - } - free(list->requests); - initPermissionRequestList(list); - } -} - -/* - * Global permission table - */ - -static struct { - Permission *permissions; - int numPermissionEntries; - int allocatedPermissionEntries; - bool permissionStateInitialized; -} gPermissionState = { -#if 1 - NULL, 0, 0, false -#else - .permissions = NULL, - .numPermissionEntries = 0, - .allocatedPermissionEntries = 0, - .permissionStateInitialized = false -#endif -}; - -int -permissionInit() -{ - if (gPermissionState.permissionStateInitialized) { - return -1; - } - gPermissionState.permissions = NULL; - gPermissionState.numPermissionEntries = 0; - gPermissionState.allocatedPermissionEntries = 0; - gPermissionState.permissionStateInitialized = true; -//xxx maybe add an "namespace root gets no permissions" fallback by default - return 0; -} - -void -permissionCleanup() -{ - if (gPermissionState.permissionStateInitialized) { - gPermissionState.permissionStateInitialized = false; - if (gPermissionState.permissions != NULL) { - int i; - for (i = 0; i < gPermissionState.numPermissionEntries; i++) { - free((void *)gPermissionState.permissions[i].path); - } - free(gPermissionState.permissions); - } - } -} - -int -getPermissionCount() -{ - if (gPermissionState.permissionStateInitialized) { - return gPermissionState.numPermissionEntries; - } - return -1; -} - -const Permission * -getPermissionAt(int index) -{ - if (!gPermissionState.permissionStateInitialized) { - return NULL; - } - if (index < 0 || index >= gPermissionState.numPermissionEntries) { - return NULL; - } - return &gPermissionState.permissions[index]; -} - -int -getAllowedPermissions(const char *path, bool recursive, - unsigned int *outAllowed) -{ - if (!gPermissionState.permissionStateInitialized) { - return -2; - } - if (outAllowed == NULL) { - return -1; - } - *outAllowed = 0; - if (path == NULL) { - return -1; - } - //TODO: implement this for real. - recursive = false; - *outAllowed = PERMSET_ALL; - return 0; -} - -int -countPermissionConflicts(PermissionRequestList *requests, bool updateAllowed) -{ - if (!gPermissionState.permissionStateInitialized) { - return -2; - } - if (requests == NULL || requests->requests == NULL || - requests->numRequests < 0 || - requests->requestsAllocated < requests->numRequests) - { - return -1; - } - int conflicts = 0; - int i; - for (i = 0; i < requests->numRequests; i++) { - PermissionRequest *req; - unsigned int allowed; - int ret; - - req = &requests->requests[i]; - ret = getAllowedPermissions(req->path, req->recursive, &allowed); - if (ret < 0) { - return ret; - } - if ((req->requested & ~allowed) != 0) { - conflicts++; - } - if (updateAllowed) { - req->allowed = allowed; - } - } - return conflicts; -} - -int -registerPermissionSet(int count, Permission *set) -{ - if (!gPermissionState.permissionStateInitialized) { - return -2; - } - if (count < 0 || (count > 0 && set == NULL)) { - return -1; - } - if (count == 0) { - return 0; - } - - if (gPermissionState.numPermissionEntries + count >= - gPermissionState.allocatedPermissionEntries) - { - Permission *newList; - int newSize; - - newSize = (gPermissionState.allocatedPermissionEntries + count) * 2; - if (newSize < 16) { - newSize = 16; - } - newList = (Permission *)realloc(gPermissionState.permissions, - newSize * sizeof(Permission)); - if (newList == NULL) { - return -3; - } - gPermissionState.permissions = newList; - gPermissionState.allocatedPermissionEntries = newSize; - } - - Permission *p = &gPermissionState.permissions[ - gPermissionState.numPermissionEntries]; - int i; - for (i = 0; i < count; i++) { - *p = set[i]; - //TODO: cache the strlen of the path - //TODO: normalize; strip off trailing / - p->path = strdup(p->path); - if (p->path == NULL) { - /* If we can't add all of the entries, we don't - * add any of them. - */ - Permission *pp = &gPermissionState.permissions[ - gPermissionState.numPermissionEntries]; - while (pp != p) { - free((void *)pp->path); - pp++; - } - return -4; - } - p++; - } - gPermissionState.numPermissionEntries += count; - - return 0; -} diff --git a/amend/permissions.h b/amend/permissions.h deleted file mode 100644 index 5b1d14dc2..000000000 --- a/amend/permissions.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2007 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 AMEND_PERMISSIONS_H_ -#define AMEND_PERMISSIONS_H_ - -#include - -#define PERM_NONE (0) -#define PERM_STAT (1<<0) -#define PERM_READ (1<<1) -#define PERM_WRITE (1<<2) // including create, delete, mkdir, rmdir -#define PERM_CHMOD (1<<3) -#define PERM_CHOWN (1<<4) -#define PERM_CHGRP (1<<5) -#define PERM_SETUID (1<<6) -#define PERM_SETGID (1<<7) - -#define PERMSET_READ (PERM_STAT | PERM_READ) -#define PERMSET_WRITE (PERMSET_READ | PERM_WRITE) - -#define PERMSET_ALL \ - (PERM_STAT | PERM_READ | PERM_WRITE | PERM_CHMOD | \ - PERM_CHOWN | PERM_CHGRP | PERM_SETUID | PERM_SETGID) - -typedef struct { - unsigned int requested; - unsigned int allowed; - const char *path; - bool recursive; -} PermissionRequest; - -typedef struct { - PermissionRequest *requests; - int numRequests; - int requestsAllocated; -} PermissionRequestList; - -/* Properly clear out a PermissionRequestList. - * - * @return 0 if list is non-NULL, negative otherwise. - */ -int initPermissionRequestList(PermissionRequestList *list); - -/* Add a permission request to the list, allocating more space - * if necessary. - * - * @return 0 on success or a negative value on failure. - */ -int addPermissionRequestToList(PermissionRequestList *list, - const char *path, bool recursive, unsigned int permissions); - -/* Free anything allocated by addPermissionRequestToList(). The caller - * is responsible for freeing the actual PermissionRequestList. - */ -void freePermissionRequestListElements(PermissionRequestList *list); - - -/* - * Global permission table - */ - -typedef struct { - const char *path; - unsigned int allowed; -} Permission; - -int permissionInit(void); -void permissionCleanup(void); - -/* Returns the allowed permissions for the path in "outAllowed". - * Returns 0 if successful, negative if a parameter or global state - * is bad. - */ -int getAllowedPermissions(const char *path, bool recursive, - unsigned int *outAllowed); - -/* More-recently-registered permissions override older permissions. - */ -int registerPermissionSet(int count, Permission *set); - -/* Check to make sure that each request is allowed. - * - * @param requests The list of permission requests - * @param updateAllowed If true, update the "allowed" field in each - * element of the list - * @return the number of requests that were denied, or negative if - * an error occurred. - */ -int countPermissionConflicts(PermissionRequestList *requests, - bool updateAllowed); - -/* Inspection/testing/debugging functions - */ -int getPermissionCount(void); -const Permission *getPermissionAt(int index); - -#endif // AMEND_PERMISSIONS_H_ diff --git a/amend/register.c b/amend/register.c index 167dd32e5..0f44b7478 100644 --- a/amend/register.c +++ b/amend/register.c @@ -39,40 +39,15 @@ if (argc < 0) return -1; \ assert(argc == 0 || argv != NULL); \ if (argc != 0 && argv == NULL) return -1; \ - if (permissions != NULL) { \ - int CW_I_; \ - for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \ - assert(argv[CW_I_] != NULL); \ - if (argv[CW_I_] == NULL) return -1; \ - } \ - } \ } while (false) #define CHECK_FN() \ do { \ CHECK_WORDS(); \ - if (permissions != NULL) { \ - assert(result == NULL); \ - if (result != NULL) return -1; \ - } else { \ - assert(result != NULL); \ - if (result == NULL) return -1; \ - } \ + assert(result != NULL); \ + if (result == NULL) return -1; \ } while (false) -#define NO_PERMS(perms) \ - do { \ - PermissionRequestList *NP_PRL_ = (perms); \ - if (NP_PRL_ != NULL) { \ - int NP_RET_ = addPermissionRequestToList(NP_PRL_, \ - "", false, PERM_NONE); \ - if (NP_RET_ < 0) { \ - /* Returns from the calling function. \ - */ \ - return NP_RET_; \ - } \ - } \ - } while (false) /* * Command definitions @@ -81,13 +56,11 @@ /* assert */ static int -cmd_assert(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_assert(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); CHECK_BOOL(); - NO_PERMS(permissions); /* If our argument is false, return non-zero (failure) * If our argument is true, return zero (success) @@ -102,8 +75,7 @@ cmd_assert(const char *name, void *cookie, int argc, const char *argv[], /* format */ static int -cmd_format(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_format(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -115,8 +87,7 @@ cmd_format(const char *name, void *cookie, int argc, const char *argv[], /* copy_dir */ static int -cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -128,8 +99,7 @@ cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[], /* mark dirty|clean */ static int -cmd_mark(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_mark(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -144,8 +114,7 @@ cmd_mark(const char *name, void *cookie, int argc, const char *argv[], /* done */ static int -cmd_done(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_done(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -174,11 +143,6 @@ registerUpdateCommands() ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, NULL); if (ret < 0) return ret; -//xxx some way to fix permissions -//xxx could have "installperms" commands that build the fs_config list -//xxx along with a "commitperms", and any copy_dir etc. needs to see -// a commitperms before it will work - return 0; } @@ -194,13 +158,11 @@ registerUpdateCommands() */ static int fn_update_forced(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 0) { fprintf(stderr, "%s: wrong number of arguments (%d)\n", @@ -228,13 +190,11 @@ fn_update_forced(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_get_mark(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 1) { fprintf(stderr, "%s: wrong number of arguments (%d)\n", @@ -255,8 +215,7 @@ fn_get_mark(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { int ret = -1; @@ -273,23 +232,12 @@ fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], dir = argv[0]; } - if (permissions != NULL) { - if (dir == NULL) { - /* The argument is the result of another function. - * Assume the worst case, where the function returns - * the root. - */ - dir = "/"; - } - ret = addPermissionRequestToList(permissions, dir, true, PERM_READ); - } else { //xxx build and return the string - *result = strdup("hashvalue"); - if (resultLen != NULL) { - *resultLen = strlen(*result); - } - ret = 0; + *result = strdup("hashvalue"); + if (resultLen != NULL) { + *resultLen = strlen(*result); } + ret = 0; return ret; } @@ -302,13 +250,11 @@ fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_matches(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc < 2) { fprintf(stderr, "%s: not enough arguments (%d < 2)\n", @@ -339,13 +285,11 @@ fn_matches(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_concat(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); size_t totalLen = 0; int i; diff --git a/amend/test_commands.c b/amend/test_commands.c index be938ac26..452f808b0 100644 --- a/amend/test_commands.c +++ b/amend/test_commands.c @@ -27,34 +27,30 @@ static struct { void *cookie; int argc; const char **argv; - PermissionRequestList *permissions; int returnValue; char *functionResult; } gTestCommandState; static int -testCommand(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +testCommand(const char *name, void *cookie, int argc, const char *argv[]) { gTestCommandState.called = true; gTestCommandState.name = name; gTestCommandState.cookie = cookie; gTestCommandState.argc = argc; gTestCommandState.argv = argv; - gTestCommandState.permissions = permissions; return gTestCommandState.returnValue; } static int testFunction(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, PermissionRequestList *permissions) + char **result, size_t *resultLen) { gTestCommandState.called = true; gTestCommandState.name = name; gTestCommandState.cookie = cookie; gTestCommandState.argc = argc; gTestCommandState.argv = argv; - gTestCommandState.permissions = permissions; if (result != NULL) { *result = gTestCommandState.functionResult; if (resultLen != NULL) { @@ -187,7 +183,6 @@ test_commands() memset(&gTestCommandState, 0, sizeof(gTestCommandState)); gTestCommandState.called = false; gTestCommandState.returnValue = 25; - gTestCommandState.permissions = (PermissionRequestList *)1; ret = callCommand(cmd, argc, argv); //xxx also try calling with a null argv element (should fail) assert(ret == 25); @@ -196,7 +191,6 @@ test_commands() assert(gTestCommandState.cookie == &gTestCommandState); assert(gTestCommandState.argc == argc); assert(gTestCommandState.argv == argv); - assert(gTestCommandState.permissions == NULL); /* Make a boolean call and make sure that it occurred. */ @@ -206,7 +200,6 @@ test_commands() memset(&gTestCommandState, 0, sizeof(gTestCommandState)); gTestCommandState.called = false; gTestCommandState.returnValue = 12; - gTestCommandState.permissions = (PermissionRequestList *)1; ret = callBooleanCommand(cmd, false); assert(ret == 12); assert(gTestCommandState.called); @@ -214,12 +207,10 @@ test_commands() assert(gTestCommandState.cookie == &gTestCommandState); assert(gTestCommandState.argc == 0); assert(gTestCommandState.argv == NULL); - assert(gTestCommandState.permissions == NULL); memset(&gTestCommandState, 0, sizeof(gTestCommandState)); gTestCommandState.called = false; gTestCommandState.returnValue = 13; - gTestCommandState.permissions = (PermissionRequestList *)1; ret = callBooleanCommand(cmd, true); assert(ret == 13); assert(gTestCommandState.called); @@ -227,45 +218,6 @@ test_commands() assert(gTestCommandState.cookie == &gTestCommandState); assert(gTestCommandState.argc == 1); assert(gTestCommandState.argv == NULL); - assert(gTestCommandState.permissions == NULL); - - /* Try looking up permissions. - */ - PermissionRequestList permissions; - cmd = findCommand("one"); - assert(cmd != NULL); - memset(&gTestCommandState, 0, sizeof(gTestCommandState)); - gTestCommandState.called = false; - gTestCommandState.returnValue = 27; - gTestCommandState.permissions = (PermissionRequestList *)1; - argv[1] = NULL; // null out an arg, which should be ok - ret = getCommandPermissions(cmd, argc, argv, &permissions); - assert(ret == 27); - assert(gTestCommandState.called); - assert(strcmp(gTestCommandState.name, "one") == 0); - assert(gTestCommandState.cookie == &gTestCommandState); - assert(gTestCommandState.argc == argc); - assert(gTestCommandState.argv == argv); - assert(gTestCommandState.permissions == &permissions); - - /* Boolean command permissions - */ - cmd = findCommand("bool"); - assert(cmd != NULL); - memset(&gTestCommandState, 0, sizeof(gTestCommandState)); - gTestCommandState.called = false; - gTestCommandState.returnValue = 55; - gTestCommandState.permissions = (PermissionRequestList *)1; - // argv[1] is still NULL - ret = getBooleanCommandPermissions(cmd, true, &permissions); - assert(ret == 55); - assert(gTestCommandState.called); - assert(strcmp(gTestCommandState.name, "bool") == 0); - assert(gTestCommandState.cookie == &gTestCommandState); - assert(gTestCommandState.argc == 1); - assert(gTestCommandState.argv == NULL); - assert(gTestCommandState.permissions == &permissions); - /* Smoke test commandCleanup(). */ @@ -365,7 +317,6 @@ test_functions() gTestCommandState.called = false; gTestCommandState.returnValue = 25; gTestCommandState.functionResult = "1234"; - gTestCommandState.permissions = (PermissionRequestList *)1; functionResult = NULL; functionResultLen = 55; ret = callFunction(fn, argc, argv, @@ -378,29 +329,9 @@ test_functions() assert(gTestCommandState.cookie == &gTestCommandState); assert(gTestCommandState.argc == argc); assert(gTestCommandState.argv == argv); - assert(gTestCommandState.permissions == NULL); assert(strcmp(functionResult, "1234") == 0); assert(functionResultLen == strlen(functionResult)); - /* Try looking up permissions. - */ - PermissionRequestList permissions; - fn = findFunction("one"); - assert(fn != NULL); - memset(&gTestCommandState, 0, sizeof(gTestCommandState)); - gTestCommandState.called = false; - gTestCommandState.returnValue = 27; - gTestCommandState.permissions = (PermissionRequestList *)1; - argv[1] = NULL; // null out an arg, which should be ok - ret = getFunctionPermissions(fn, argc, argv, &permissions); - assert(ret == 27); - assert(gTestCommandState.called); - assert(strcmp(gTestCommandState.name, "one") == 0); - assert(gTestCommandState.cookie == &gTestCommandState); - assert(gTestCommandState.argc == argc); - assert(gTestCommandState.argv == argv); - assert(gTestCommandState.permissions == &permissions); - /* Smoke test commandCleanup(). */ commandCleanup(); @@ -470,7 +401,6 @@ test_interaction() memset(&gTestCommandState, 0, sizeof(gTestCommandState)); gTestCommandState.called = false; gTestCommandState.returnValue = 123; - gTestCommandState.permissions = (PermissionRequestList *)1; ret = callCommand(cmd, argc, argv); assert(ret == 123); assert(gTestCommandState.called); @@ -478,7 +408,6 @@ test_interaction() assert((int)gTestCommandState.cookie == 0xc1); assert(gTestCommandState.argc == argc); assert(gTestCommandState.argv == argv); - assert(gTestCommandState.permissions == NULL); /* Call the overlapping function and make sure that the cookie is correct. */ @@ -490,7 +419,6 @@ test_interaction() gTestCommandState.called = false; gTestCommandState.returnValue = 125; gTestCommandState.functionResult = "5678"; - gTestCommandState.permissions = (PermissionRequestList *)2; functionResult = NULL; functionResultLen = 66; ret = callFunction(fn, argc, argv, &functionResult, &functionResultLen); @@ -500,7 +428,6 @@ test_interaction() assert((int)gTestCommandState.cookie == 0xf1); assert(gTestCommandState.argc == argc); assert(gTestCommandState.argv == argv); - assert(gTestCommandState.permissions == NULL); assert(strcmp(functionResult, "5678") == 0); assert(functionResultLen == strlen(functionResult)); diff --git a/amend/test_permissions.c b/amend/test_permissions.c deleted file mode 100644 index c3894563e..000000000 --- a/amend/test_permissions.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (C) 2007 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 -#include -#include -#undef NDEBUG -#include -#include "permissions.h" - -static int -test_permission_list() -{ - PermissionRequestList list; - int ret; - int numRequests; - - /* Bad parameter - */ - ret = initPermissionRequestList(NULL); - assert(ret < 0); - - /* Good parameter - */ - ret = initPermissionRequestList(&list); - assert(ret == 0); - - /* Bad parameters - */ - ret = addPermissionRequestToList(NULL, NULL, false, 0); - assert(ret < 0); - - ret = addPermissionRequestToList(&list, NULL, false, 0); - assert(ret < 0); - - /* Good parameters - */ - numRequests = 0; - - ret = addPermissionRequestToList(&list, "one", false, 1); - assert(ret == 0); - numRequests++; - - ret = addPermissionRequestToList(&list, "two", false, 2); - assert(ret == 0); - numRequests++; - - ret = addPermissionRequestToList(&list, "three", false, 3); - assert(ret == 0); - numRequests++; - - ret = addPermissionRequestToList(&list, "recursive", true, 55); - assert(ret == 0); - numRequests++; - - /* Validate the list - */ - assert(list.requests != NULL); - assert(list.numRequests == numRequests); - assert(list.numRequests <= list.requestsAllocated); - bool sawOne = false; - bool sawTwo = false; - bool sawThree = false; - bool sawRecursive = false; - int i; - for (i = 0; i < list.numRequests; i++) { - PermissionRequest *req = &list.requests[i]; - assert(req->allowed == 0); - - /* Order isn't guaranteed, so we have to switch every time. - */ - if (strcmp(req->path, "one") == 0) { - assert(!sawOne); - assert(req->requested == 1); - assert(!req->recursive); - sawOne = true; - } else if (strcmp(req->path, "two") == 0) { - assert(!sawTwo); - assert(req->requested == 2); - assert(!req->recursive); - sawTwo = true; - } else if (strcmp(req->path, "three") == 0) { - assert(!sawThree); - assert(req->requested == 3); - assert(!req->recursive); - sawThree = true; - } else if (strcmp(req->path, "recursive") == 0) { - assert(!sawRecursive); - assert(req->requested == 55); - assert(req->recursive); - sawRecursive = true; - } else { - assert(false); - } - } - assert(sawOne); - assert(sawTwo); - assert(sawThree); - assert(sawRecursive); - - /* Smoke test the teardown - */ - freePermissionRequestListElements(&list); - - return 0; -} - -static int -test_permission_table() -{ - int ret; - - /* Test the global permissions table. - * Try calling functions without initializing first. - */ - ret = registerPermissionSet(0, NULL); - assert(ret < 0); - - ret = countPermissionConflicts((PermissionRequestList *)16, false); - assert(ret < 0); - - ret = getPermissionCount(); - assert(ret < 0); - - const Permission *p; - p = getPermissionAt(0); - assert(p == NULL); - - /* Initialize. - */ - ret = permissionInit(); - assert(ret == 0); - - /* Make sure we can't initialize twice. - */ - ret = permissionInit(); - assert(ret < 0); - - /* Test the inspection functions. - */ - ret = getPermissionCount(); - assert(ret == 0); - - p = getPermissionAt(-1); - assert(p == NULL); - - p = getPermissionAt(0); - assert(p == NULL); - - p = getPermissionAt(1); - assert(p == NULL); - - /* Test registerPermissionSet(). - * Try some bad parameter values. - */ - ret = registerPermissionSet(-1, NULL); - assert(ret < 0); - - ret = registerPermissionSet(1, NULL); - assert(ret < 0); - - /* Register some permissions. - */ - Permission p1; - p1.path = "one"; - p1.allowed = 1; - ret = registerPermissionSet(1, &p1); - assert(ret == 0); - ret = getPermissionCount(); - assert(ret == 1); - - Permission p2[2]; - p2[0].path = "two"; - p2[0].allowed = 2; - p2[1].path = "three"; - p2[1].allowed = 3; - ret = registerPermissionSet(2, p2); - assert(ret == 0); - ret = getPermissionCount(); - assert(ret == 3); - - ret = registerPermissionSet(0, NULL); - assert(ret == 0); - ret = getPermissionCount(); - assert(ret == 3); - - p1.path = "four"; - p1.allowed = 4; - ret = registerPermissionSet(1, &p1); - assert(ret == 0); - - /* Make sure the table looks correct. - * Order is important; more-recent additions - * should appear at higher indices. - */ - ret = getPermissionCount(); - assert(ret == 4); - - int i; - for (i = 0; i < ret; i++) { - const Permission *p; - p = getPermissionAt(i); - assert(p != NULL); - assert(p->allowed == (unsigned int)(i + 1)); - switch (i) { - case 0: - assert(strcmp(p->path, "one") == 0); - break; - case 1: - assert(strcmp(p->path, "two") == 0); - break; - case 2: - assert(strcmp(p->path, "three") == 0); - break; - case 3: - assert(strcmp(p->path, "four") == 0); - break; - default: - assert(!"internal error"); - break; - } - } - p = getPermissionAt(ret); - assert(p == NULL); - - /* Smoke test the teardown - */ - permissionCleanup(); - - return 0; -} - -static int -test_allowed_permissions() -{ - int ret; - int numPerms; - - /* Make sure these fail before initialization. - */ - ret = countPermissionConflicts((PermissionRequestList *)1, false); - assert(ret < 0); - - ret = getAllowedPermissions((const char *)1, false, (unsigned int *)1); - assert(ret < 0); - - /* Initialize. - */ - ret = permissionInit(); - assert(ret == 0); - - /* Make sure countPermissionConflicts() fails with bad parameters. - */ - ret = countPermissionConflicts(NULL, false); - assert(ret < 0); - - /* Register a set of permissions. - */ - Permission perms[] = { - { "/", PERM_NONE }, - { "/stat", PERM_STAT }, - { "/read", PERMSET_READ }, - { "/write", PERMSET_WRITE }, - { "/.stat", PERM_STAT }, - { "/.stat/.read", PERMSET_READ }, - { "/.stat/.read/.write", PERMSET_WRITE }, - { "/.stat/.write", PERMSET_WRITE }, - }; - numPerms = sizeof(perms) / sizeof(perms[0]); - ret = registerPermissionSet(numPerms, perms); - assert(ret == 0); - - /* Build a permission request list. - */ - PermissionRequestList list; - ret = initPermissionRequestList(&list); - assert(ret == 0); - - ret = addPermissionRequestToList(&list, "/stat", false, PERM_STAT); - assert(ret == 0); - - ret = addPermissionRequestToList(&list, "/read", false, PERM_READ); - assert(ret == 0); - - ret = addPermissionRequestToList(&list, "/write", false, PERM_WRITE); - assert(ret == 0); - - //TODO: cover more cases once the permission stuff has been implemented - - /* All of the requests in the list should be allowed. - */ - ret = countPermissionConflicts(&list, false); - assert(ret == 0); - - /* Add a request that will be denied. - */ - ret = addPermissionRequestToList(&list, "/stat", false, 1<<31 | PERM_STAT); - assert(ret == 0); - - ret = countPermissionConflicts(&list, false); - assert(ret == 1); - - //TODO: more tests - - permissionCleanup(); - - return 0; -} - -int -test_permissions() -{ - int ret; - - ret = test_permission_list(); - if (ret != 0) { - fprintf(stderr, "test_permission_list() failed: %d\n", ret); - return ret; - } - - ret = test_permission_table(); - if (ret != 0) { - fprintf(stderr, "test_permission_table() failed: %d\n", ret); - return ret; - } - - ret = test_allowed_permissions(); - if (ret != 0) { - fprintf(stderr, "test_permission_table() failed: %d\n", ret); - return ret; - } - - return 0; -} diff --git a/commands.c b/commands.c index 23ad91c03..b4678ba6f 100644 --- a/commands.c +++ b/commands.c @@ -57,39 +57,13 @@ static int gDidShowProgress = 0; if (argc < 0) return -1; \ assert(argc == 0 || argv != NULL); \ if (argc != 0 && argv == NULL) return -1; \ - if (permissions != NULL) { \ - int CW_I_; \ - for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \ - assert(argv[CW_I_] != NULL); \ - if (argv[CW_I_] == NULL) return -1; \ - } \ - } \ } while (false) #define CHECK_FN() \ do { \ CHECK_WORDS(); \ - if (permissions != NULL) { \ - assert(result == NULL); \ - if (result != NULL) return -1; \ - } else { \ - assert(result != NULL); \ - if (result == NULL) return -1; \ - } \ - } while (false) - -#define NO_PERMS(perms) \ - do { \ - PermissionRequestList *NP_PRL_ = (perms); \ - if (NP_PRL_ != NULL) { \ - int NP_RET_ = addPermissionRequestToList(NP_PRL_, \ - "", false, PERM_NONE); \ - if (NP_RET_ < 0) { \ - /* Returns from the calling function. \ - */ \ - return NP_RET_; \ - } \ - } \ + assert(result != NULL); \ + if (result == NULL) return -1; \ } while (false) /* @@ -99,13 +73,11 @@ static int gDidShowProgress = 0; /* assert */ static int -cmd_assert(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_assert(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); CHECK_BOOL(); - NO_PERMS(permissions); /* If our argument is false, return non-zero (failure) * If our argument is true, return zero (success) @@ -120,8 +92,7 @@ cmd_assert(const char *name, void *cookie, int argc, const char *argv[], /* format */ static int -cmd_format(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_format(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -151,8 +122,7 @@ cmd_format(const char *name, void *cookie, int argc, const char *argv[], * give up early. */ static int -cmd_delete(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_delete(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); @@ -166,7 +136,6 @@ cmd_delete(const char *name, void *cookie, int argc, const char *argv[], recurse = (strcmp(name, "delete_recursive") == 0); ui_print("Deleting files...\n"); -//xxx permissions int i; for (i = 0; i < argc; i++) { @@ -233,13 +202,11 @@ static void extract_cb(const char *fn, void *cookie) * or a fixed default timestamp will be supplied otherwise. */ static int -cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); CHECK_WORDS(); -//xxx permissions // To create a consistent system image, never use the clock for timestamps. struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default @@ -331,8 +298,7 @@ cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[], * Run an external program included in the update package. */ static int -cmd_run_program(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_run_program(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); @@ -407,8 +373,7 @@ cmd_run_program(const char *name, void *cookie, int argc, const char *argv[], * User, group, and modes must all be integer values (hex or octal OK). */ static int -cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); @@ -461,8 +426,7 @@ cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[], * if the actual rate of progress can be determined). */ static int -cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); @@ -499,8 +463,7 @@ cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[], * for the target filesystem (and may be relative). */ static int -cmd_symlink(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_symlink(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); @@ -554,7 +517,7 @@ static bool firmware_fn(const unsigned char *data, int data_len, void *cookie) */ static int cmd_write_firmware_image(const char *name, void *cookie, - int argc, const char *argv[], PermissionRequestList *permissions) + int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); @@ -634,11 +597,10 @@ static bool write_raw_image_process_fn( */ static int cmd_write_raw_image(const char *name, void *cookie, - int argc, const char *argv[], PermissionRequestList *permissions) + int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); -//xxx permissions if (argc != 2) { LOGE("Command %s requires exactly two arguments\n", name); @@ -726,8 +688,7 @@ cmd_write_raw_image(const char *name, void *cookie, /* mark dirty|clean */ static int -cmd_mark(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_mark(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -742,8 +703,7 @@ cmd_mark(const char *name, void *cookie, int argc, const char *argv[], /* done */ static int -cmd_done(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_done(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -764,13 +724,11 @@ cmd_done(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_compatible_with(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 1) { fprintf(stderr, "%s: wrong number of arguments (%d)\n", @@ -796,13 +754,11 @@ fn_compatible_with(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_update_forced(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 0) { fprintf(stderr, "%s: wrong number of arguments (%d)\n", @@ -830,13 +786,11 @@ fn_update_forced(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_get_mark(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 1) { fprintf(stderr, "%s: wrong number of arguments (%d)\n", @@ -857,8 +811,7 @@ fn_get_mark(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { int ret = -1; @@ -875,24 +828,6 @@ fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], dir = argv[0]; } - if (permissions != NULL) { - if (dir == NULL) { - /* The argument is the result of another function. - * Assume the worst case, where the function returns - * the root. - */ - dir = "/"; - } - ret = addPermissionRequestToList(permissions, dir, true, PERM_READ); - } else { -//xxx build and return the string - *result = strdup("hashvalue"); - if (resultLen != NULL) { - *resultLen = strlen(*result); - } - ret = 0; - } - return ret; } @@ -904,13 +839,11 @@ fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_matches(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc < 2) { fprintf(stderr, "%s: not enough arguments (%d < 2)\n", @@ -941,13 +874,11 @@ fn_matches(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_concat(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); size_t totalLen = 0; int i; @@ -977,12 +908,10 @@ fn_concat(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_getprop(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 1) { LOGE("Command %s requires exactly one argument\n", name); @@ -1005,12 +934,10 @@ fn_getprop(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_file_contains(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 2) { LOGE("Command %s requires exactly two arguments\n", name); diff --git a/recovery.c b/recovery.c index e329db93d..188d4de69 100644 --- a/recovery.c +++ b/recovery.c @@ -265,7 +265,6 @@ test_amend() { extern int test_symtab(void); extern int test_cmd_fn(void); - extern int test_permissions(void); int ret; LOGD("Testing symtab...\n"); ret = test_symtab(); @@ -273,9 +272,6 @@ test_amend() LOGD("Testing cmd_fn...\n"); ret = test_cmd_fn(); LOGD(" returned %d\n", ret); - LOGD("Testing permissions...\n"); - ret = test_permissions(); - LOGD(" returned %d\n", ret); } #endif // TEST_AMEND -- cgit v1.2.3 From b2ee9201be583b17ddbf0eaa69a37545f992b565 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 4 Jun 2009 10:24:53 -0700 Subject: allow OTA package to provide binary instead of script Allow installation of OTA packages which do not contain an update-script, but instead contain an update-binary. --- install.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 169 insertions(+), 1 deletion(-) diff --git a/install.c b/install.c index e7db2a8f2..eff9312b4 100644 --- a/install.c +++ b/install.c @@ -14,10 +14,13 @@ * limitations under the License. */ +#include #include #include #include #include +#include +#include #include "amend/amend.h" #include "common.h" @@ -30,8 +33,10 @@ #include "mtdutils/mtdutils.h" #include "roots.h" #include "verifier.h" +#include "firmware.h" #define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script" +#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" #define PUBLIC_KEYS_FILE "/res/keys" static const ZipEntry * @@ -95,7 +100,7 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) int ret = execCommandList((ExecContext *)1, commands); if (ret != 0) { int num = ret; - char *line, *next = script_data; + char *line = NULL, *next = script_data; while (next != NULL && ret-- > 0) { line = next; next = memchr(line, '\n', script_data + script_len - line); @@ -109,6 +114,159 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) return INSTALL_SUCCESS; } +// The update binary ask us to install a firmware file on reboot. Set +// that up. Takes ownership of type and filename. +static int +handle_firmware_update(char* type, char* filename) { + struct stat st_data; + if (stat(filename, &st_data) < 0) { + LOGE("Error stat'ing %s: %s\n", filename, strerror(errno)); + return INSTALL_ERROR; + } + + LOGI("type is [%s]\n", type); + + char* data = malloc(st_data.st_size); + if (data == NULL) { + LOGE("Can't allocate %d bytes for firmware data\n", st_data.st_size); + return INSTALL_ERROR; + } + + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + LOGE("Failed to open %s: %s\n", filename, strerror(errno)); + return INSTALL_ERROR; + } + if (fread(data, 1, st_data.st_size, f) != st_data.st_size) { + LOGE("Failed to read firmware data: %s\n", strerror(errno)); + return INSTALL_ERROR; + } + fclose(f); + + if (remember_firmware_update(type, data, st_data.st_size)) { + LOGE("Can't store %s image\n", type); + free(data); + return INSTALL_ERROR; + } + free(filename); + + return INSTALL_SUCCESS; +} + +// If the package contains an update binary, extract it and run it. +static int +try_update_binary(const char *path, ZipArchive *zip) { + const ZipEntry* binary_entry = + mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); + if (binary_entry == NULL) { + return INSTALL_CORRUPT; + } + + char* binary = "/tmp/update_binary"; + unlink(binary); + int fd = creat(binary, 0755); + if (fd < 0) { + LOGE("Can't make %s\n", binary); + return 1; + } + bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd); + close(fd); + + if (!ok) { + LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME); + return 1; + } + + int pipefd[2]; + pipe(pipefd); + + // When executing the update binary contained in the package, the + // arguments passed are: + // + // - the version number for this interface (currently 1) + // + // - an fd to which the program can write in order to update the + // progress bar. The program can write single-line commands: + // + // progress + // fill up of the progress bar over seconds. + // + // firmware <"hboot"|"radio"> + // arrange to install the contents of in the + // given partition on reboot. + // + // - the name of the package zip file. + // + + char** args = malloc(sizeof(char*) * 5); + args[0] = binary; + args[1] = "1"; + args[2] = malloc(10); + sprintf(args[2], "%d", pipefd[1]); + args[3] = (char*)path; + args[4] = NULL; + + pid_t pid = fork(); + if (pid == 0) { + close(pipefd[0]); + execv(binary, args); + fprintf(stderr, "E:Can't run %s (%s)\n", binary, strerror(errno)); + _exit(-1); + } + close(pipefd[1]); + + char* firmware_type = NULL; + char* firmware_filename = NULL; + + char buffer[81]; + FILE* from_child = fdopen(pipefd[0], "r"); + while (fgets(buffer, sizeof(buffer), from_child) != NULL) { + LOGI("read: %s", buffer); + + char* command = strtok(buffer, " \n"); + if (command == NULL) { + continue; + } else if (strcmp(command, "progress") == 0) { + char* fraction_s = strtok(NULL, " \n"); + char* seconds_s = strtok(NULL, " \n"); + + float fraction = strtof(fraction_s, NULL); + int seconds = strtol(seconds_s, NULL, 10); + + ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), + seconds); + } else if (strcmp(command, "firmware") == 0) { + char* type = strtok(NULL, " \n"); + char* filename = strtok(NULL, " \n"); + + if (type != NULL && filename != NULL) { + if (firmware_type != NULL) { + LOGE("ignoring attempt to do multiple firmware updates"); + } else { + firmware_type = strdup(type); + firmware_filename = strdup(filename); + } + } + } else { + LOGE("unknown command [%s]\n", command); + } + } + fclose(from_child); + + int status; + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + LOGE("Error in %s\n(Status %d)\n", path, status); + return INSTALL_ERROR; + } + + if (firmware_type != NULL) { + return handle_firmware_update(firmware_type, firmware_filename); + } else { + return INSTALL_SUCCESS; + } +} + static int handle_update_package(const char *path, ZipArchive *zip, const RSAPublicKey *keys, int numKeys) @@ -127,6 +285,16 @@ handle_update_package(const char *path, ZipArchive *zip, // Update should take the rest of the progress bar. ui_print("Installing update...\n"); + int result = try_update_binary(path, zip); + if (result == INSTALL_SUCCESS || result == INSTALL_ERROR) { + register_package_root(NULL, NULL); // Unregister package root + return result; + } + + // if INSTALL_CORRUPT is returned, this package doesn't have an + // update binary. Fall back to the older mechanism of looking for + // an update script. + const ZipEntry *script_entry; script_entry = find_update_script(zip); if (script_entry == NULL) { -- cgit v1.2.3 From 37bee62aefb5ac10716054edf482fd37078f1427 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 8 Jun 2009 17:35:39 -0700 Subject: core of edify, an eventual replacement for amend Edify is a simple scripting language for OTA installation, to be used when we move to OTAs being installed via binaries shipped with the package. --- edify/Android.mk | 40 ++++++++ edify/README | 108 +++++++++++++++++++++ edify/expr.c | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ edify/expr.h | 80 ++++++++++++++++ edify/lexer.l | 97 +++++++++++++++++++ edify/main.c | 164 ++++++++++++++++++++++++++++++++ edify/parser.y | 121 ++++++++++++++++++++++++ 7 files changed, 889 insertions(+) create mode 100644 edify/Android.mk create mode 100644 edify/README create mode 100644 edify/expr.c create mode 100644 edify/expr.h create mode 100644 edify/lexer.l create mode 100644 edify/main.c create mode 100644 edify/parser.y diff --git a/edify/Android.mk b/edify/Android.mk new file mode 100644 index 000000000..803fba207 --- /dev/null +++ b/edify/Android.mk @@ -0,0 +1,40 @@ +# Copyright 2009 The Android Open Source Project + +LOCAL_PATH := $(call my-dir) + +edify_src_files := \ + lexer.l \ + parser.y \ + expr.c + +# "-x c" forces the lex/yacc files to be compiled as c; +# the build system otherwise forces them to be c++. +edify_cflags := -x c + +# +# Build the host-side command line tool +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + $(edify_src_files) \ + main.c + +LOCAL_CFLAGS := $(edify_cflags) -g -O0 +LOCAL_MODULE := edify +LOCAL_YACCFLAGS := -v + +include $(BUILD_HOST_EXECUTABLE) + +# # +# # Build the device-side library +# # +# include $(CLEAR_VARS) + +# LOCAL_SRC_FILES := $(edify_src_files) +# LOCAL_SRC_FILES += $(edify_test_files) + +# LOCAL_CFLAGS := $(edify_cflags) +# LOCAL_MODULE := libedify + +# include $(BUILD_STATIC_LIBRARY) diff --git a/edify/README b/edify/README new file mode 100644 index 000000000..5ccb582e8 --- /dev/null +++ b/edify/README @@ -0,0 +1,108 @@ +Update scripts (from donut onwards) are written in a new little +scripting language ("edify") that is superficially somewhat similar to +the old one ("amend"). This is a brief overview of the new language. + +- The entire script is a single expression. + +- All expressions are string-valued. + +- String literals appear in double quotes. \n, \t, \", and \\ are + understood, as are hexadecimal escapes like \x4a. + +- String literals consisting of only letters, numbers, colons, + underscores, and slashes don't need to be in double quotes. + +- The following words are reserved: + + if then else endif + + They have special meaning when unquoted. (In quotes, they are just + string literals.) + +- When used as a boolean, the empty string is "false" and all other + strings are "true". + +- All functions are actually macros (in the Lisp sense); the body of + the function can control which (if any) of the arguments are + evaluated. This means that functions can act as control + structures. + +- Operators (like "&&" and "||") are just syntactic sugar for builtin + functions, so they can act as control structures as well. + +- ";" is a binary operator; evaluating it just means to first evaluate + the left side, then the right. It can also appear after any + expression. + +- Comments start with "#" and run to the end of the line. + + + +Some examples: + +- There's no distinction between quoted and unquoted strings; the + quotes are only needed if you want characters like whitespace to + appear in the string. The following expressions all evaluate to the + same string. + + "a b" + a + " " + b + "a" + " " + "b" + "a\x20b" + a + "\x20b" + concat(a, " ", "b") + "concat"(a, " ", "b") + + As shown in the last example, function names are just strings, + too. They must be string *literals*, however. This is not legal: + + ("con" + "cat")(a, " ", b) # syntax error! + + +- The ifelse() builtin takes three arguments: it evaluates exactly + one of the second and third, depending on whether the first one is + true. There is also some syntactic sugar to make expressions that + look like if/else statements: + + # these are all equivalent + ifelse(something(), "yes", "no") + if something() then yes else no endif + if something() then "yes" else "no" endif + + The else part is optional. + + if something() then "yes" endif # if something() is false, + # evaluates to false + + ifelse(condition(), "", abort()) # abort() only called if + # condition() is false + + The last example is equivalent to: + + assert(condition()) + + +- The && and || operators can be used similarly; they evaluate their + second argument only if it's needed to determine the truth of the + expression. Their value is the value of the last-evaluated + argument: + + file_exists("/data/system/bad") && delete("/data/system/bad") + + file_exists("/data/system/missing") || create("/data/system/missing") + + get_it() || "xxx" # returns value of get_it() if that value is + # true, otherwise returns "xxx" + + +- The purpose of ";" is to simulate imperative statements, of course, + but the operator can be used anywhere. Its value is the value of + its right side: + + concat(a;b;c, d, e;f) # evaluates to "cdf" + + A more useful example might be something like: + + ifelse(condition(), + (first_step(); second_step();), # second ; is optional + alternative_procedure()) diff --git a/edify/expr.c b/edify/expr.c new file mode 100644 index 000000000..b3b892711 --- /dev/null +++ b/edify/expr.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2009 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 +#include +#include +#include +#include + +#include "expr.h" + +// Functions should: +// +// - return a malloc()'d string +// - if Evaluate() on any argument returns NULL, return NULL. + +int BooleanString(const char* s) { + return s[0] != '\0'; +} + +char* Evaluate(void* cookie, Expr* expr) { + return expr->fn(expr->name, cookie, expr->argc, expr->argv); +} + +char* ConcatFn(const char* name, void* cookie, int argc, Expr* argv[]) { + if (argc == 0) { + return strdup(""); + } + char** strings = malloc(argc * sizeof(char*)); + int i; + for (i = 0; i < argc; ++i) { + strings[i] = NULL; + } + char* result = NULL; + int length = 0; + for (i = 0; i < argc; ++i) { + strings[i] = Evaluate(cookie, argv[i]); + if (strings[i] == NULL) { + goto done; + } + length += strlen(strings[i]); + } + + result = malloc(length+1); + int p = 0; + for (i = 0; i < argc; ++i) { + strcpy(result+p, strings[i]); + p += strlen(strings[i]); + } + result[p] = '\0'; + +done: + for (i = 0; i < argc; ++i) { + free(strings[i]); + } + return result; +} + +char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]) { + if (argc != 2 && argc != 3) { + return NULL; + } + char* cond = Evaluate(cookie, argv[0]); + if (cond == NULL) { + return NULL; + } + + if (BooleanString(cond) == true) { + free(cond); + return Evaluate(cookie, argv[1]); + } else { + if (argc == 3) { + free(cond); + return Evaluate(cookie, argv[2]); + } else { + return cond; + } + } +} + +char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]) { + return NULL; +} + +char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]) { + int i; + for (i = 0; i < argc; ++i) { + char* v = Evaluate(cookie, argv[i]); + if (v == NULL) { + return NULL; + } + int b = BooleanString(v); + free(v); + if (!b) { + return NULL; + } + } + return strdup(""); +} + +char* PrintFn(const char* name, void* cookie, int argc, Expr* argv[]) { + int i; + for (i = 0; i < argc; ++i) { + char* v = Evaluate(cookie, argv[i]); + if (v == NULL) { + return NULL; + } + fputs(v, stdout); + free(v); + } + return strdup(""); +} + +char* LogicalAndFn(const char* name, void* cookie, + int argc, Expr* argv[]) { + char* left = Evaluate(cookie, argv[0]); + if (left == NULL) return NULL; + if (BooleanString(left) == true) { + free(left); + return Evaluate(cookie, argv[1]); + } else { + return left; + } +} + +char* LogicalOrFn(const char* name, void* cookie, + int argc, Expr* argv[]) { + char* left = Evaluate(cookie, argv[0]); + if (left == NULL) return NULL; + if (BooleanString(left) == false) { + free(left); + return Evaluate(cookie, argv[1]); + } else { + return left; + } +} + +char* LogicalNotFn(const char* name, void* cookie, + int argc, Expr* argv[]) { + char* val = Evaluate(cookie, argv[0]); + if (val == NULL) return NULL; + bool bv = BooleanString(val); + free(val); + if (bv) { + return strdup(""); + } else { + return strdup("t"); + } +} + +char* SubstringFn(const char* name, void* cookie, + int argc, Expr* argv[]) { + char* needle = Evaluate(cookie, argv[0]); + if (needle == NULL) return NULL; + char* haystack = Evaluate(cookie, argv[1]); + if (haystack == NULL) { + free(needle); + return NULL; + } + + char* result = strdup(strstr(haystack, needle) ? "t" : ""); + free(needle); + free(haystack); + return result; +} + +char* EqualityFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* left = Evaluate(cookie, argv[0]); + if (left == NULL) return NULL; + char* right = Evaluate(cookie, argv[1]); + if (right == NULL) { + free(left); + return NULL; + } + + char* result = strdup(strcmp(left, right) == 0 ? "t" : ""); + free(left); + free(right); + return result; +} + +char* InequalityFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* left = Evaluate(cookie, argv[0]); + if (left == NULL) return NULL; + char* right = Evaluate(cookie, argv[1]); + if (right == NULL) { + free(left); + return NULL; + } + + char* result = strdup(strcmp(left, right) != 0 ? "t" : ""); + free(left); + free(right); + return result; +} + +char* SequenceFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* left = Evaluate(cookie, argv[0]); + if (left == NULL) return NULL; + free(left); + return Evaluate(cookie, argv[1]); +} + +char* Literal(const char* name, void* cookie, int argc, Expr* argv[]) { + return strdup(name); +} + +Expr* Build(Function fn, int count, ...) { + va_list v; + va_start(v, count); + Expr* e = malloc(sizeof(Expr)); + e->fn = fn; + e->name = "(operator)"; + e->argc = count; + e->argv = malloc(count * sizeof(Expr*)); + int i; + for (i = 0; i < count; ++i) { + e->argv[i] = va_arg(v, Expr*); + } + va_end(v); + return e; +} + +static int fn_entries = 0; +static int fn_size = 0; +NamedFunction* fn_table = NULL; + +void RegisterFunction(const char* name, Function fn) { + if (fn_entries <= fn_size) { + fn_size = fn_size*2 + 1; + fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction)); + } + fn_table[fn_entries].name = name; + fn_table[fn_entries].fn = fn; + ++fn_entries; +} + +static int fn_entry_compare(const void* a, const void* b) { + const char* na = ((const NamedFunction*)a)->name; + const char* nb = ((const NamedFunction*)b)->name; + return strcmp(na, nb); +} + +void FinishRegistration() { + qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare); +} + +Function FindFunction(const char* name) { + NamedFunction key; + key.name = name; + NamedFunction* nf = bsearch(&key, fn_table, fn_entries, sizeof(NamedFunction), + fn_entry_compare); + if (nf == NULL) { + return NULL; + } + return nf->fn; +} + +void RegisterBuiltins() { + RegisterFunction("ifelse", IfElseFn); + RegisterFunction("abort", AbortFn); + RegisterFunction("assert", AssertFn); + RegisterFunction("concat", ConcatFn); + RegisterFunction("is_substring", SubstringFn); + RegisterFunction("print", PrintFn); +} diff --git a/edify/expr.h b/edify/expr.h new file mode 100644 index 000000000..ac5df1869 --- /dev/null +++ b/edify/expr.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2009 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 _EXPRESSION_H +#define _EXPRESSION_H + +#define MAX_STRING_LEN 1024 + +typedef struct Expr Expr; + +typedef char* (*Function)(const char* name, void* cookie, + int argc, Expr* argv[]); + +struct Expr { + Function fn; + char* name; + int argc; + Expr** argv; +}; + +char* Evaluate(void* cookie, Expr* expr); + +// Glue to make an Expr out of a literal. +char* Literal(const char* name, void* cookie, int argc, Expr* argv[]); + +// Functions corresponding to various syntactic sugar operators. +// ("concat" is also available as a builtin function, to concatenate +// more than two strings.) +char* ConcatFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* LogicalAndFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* LogicalOrFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* LogicalNotFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* SubstringFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* EqualityFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* InequalityFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* SequenceFn(const char* name, void* cookie, int argc, Expr* argv[]); + +// Convenience function for building expressions with a fixed number +// of arguments. +Expr* Build(Function fn, int count, ...); + +// Global builtins, registered by RegisterBuiltins(). +char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]); + +typedef struct { + const char* name; + Function fn; +} NamedFunction; + +// Register a new function. The same Function may be registered under +// multiple names, but a given name should only be used once. +void RegisterFunction(const char* name, Function fn); + +// Register all the builtins. +void RegisterBuiltins(); + +// Call this after all calls to RegisterFunction() but before parsing +// any scripts to finish building the function table. +void FinishRegistration(); + +// Find the Function for a given name; return NULL if no such function +// exists. +Function FindFunction(const char* name); + +#endif // _EXPRESSION_H diff --git a/edify/lexer.l b/edify/lexer.l new file mode 100644 index 000000000..4faef5da0 --- /dev/null +++ b/edify/lexer.l @@ -0,0 +1,97 @@ +%{ +/* + * Copyright (C) 2009 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 "expr.h" +#include "parser.h" + +int gLine = 1; +int gColumn = 1; + +// TODO: enforce MAX_STRING_LEN during lexing +char string_buffer[MAX_STRING_LEN]; +char* string_pos; +%} + +%x STR + +%option noyywrap + +%% + + +\" { + ++gColumn; + BEGIN(STR); + string_pos = string_buffer; +} + +{ + \" { + ++gColumn; + BEGIN(INITIAL); + *string_pos = '\0'; + yylval.str = strdup(string_buffer); + return STRING; + } + + \\n { gColumn += yyleng; *string_pos++ = '\n'; } + \\t { gColumn += yyleng; *string_pos++ = '\t'; } + \\\" { gColumn += yyleng; *string_pos++ = '\"'; } + \\\\ { gColumn += yyleng; *string_pos++ = '\\'; } + + \\x[0-9a-fA-F]{2} { + gColumn += yyleng; + int val; + sscanf(yytext+2, "%x", &val); + *string_pos++ = val; + } + + \n { + ++gLine; + gColumn = 1; + *string_pos++ = yytext[0]; + } + + . { + ++gColumn; + *string_pos++ = yytext[0]; + } +} + +if { gColumn += yyleng; return IF; } +then { gColumn += yyleng; return THEN; } +else { gColumn += yyleng; return ELSE; } +endif { gColumn += yyleng; return ENDIF; } + +[a-zA-Z0-9_:/]+ { + gColumn += yyleng; + yylval.str = strdup(yytext); + return STRING; +} + +\&\& { gColumn += yyleng; return AND; } +\|\| { gColumn += yyleng; return OR; } +== { gColumn += yyleng; return EQ; } +!= { gColumn += yyleng; return NE; } + +[+(),!;] { gColumn += yyleng; return yytext[0]; } + +[ \t]+ gColumn += yyleng; + +(#.*)?\n { ++gLine; gColumn = 1; } + +. return BAD; diff --git a/edify/main.c b/edify/main.c new file mode 100644 index 000000000..4d65da2d8 --- /dev/null +++ b/edify/main.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2009 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 +#include +#include + +#include "expr.h" +#include "parser.h" + +int expect(const char* expr_str, const char* expected, int* errors) { + Expr* e; + int error; + char* result; + + printf("."); + + yy_scan_string(expr_str); + error = yyparse(&e); + if (error > 0) { + fprintf(stderr, "error parsing \"%s\"\n", expr_str); + ++*errors; + return 0; + } + + result = Evaluate(NULL, e); + if (result == NULL && expected != NULL) { + fprintf(stderr, "error evaluating \"%s\"\n", expr_str); + ++*errors; + return 0; + } + + if (result == NULL && expected == NULL) { + return 1; + } + + if (strcmp(result, expected) != 0) { + fprintf(stderr, "evaluating \"%s\": expected \"%s\", got \"%s\"\n", + expr_str, expected, result); + ++*errors; + free(result); + return 0; + } + + free(result); + return 1; +} + +int test() { + int errors = 0; + + expect("a", "a", &errors); + expect("\"a\"", "a", &errors); + expect("\"\\x61\"", "a", &errors); + expect("# this is a comment\n" + " a\n" + " \n", + "a", &errors); + + + // sequence operator + expect("a; b; c", "c", &errors); + + // string concat operator + expect("a + b", "ab", &errors); + expect("a + \n \"b\"", "ab", &errors); + expect("a + b +\nc\n", "abc", &errors); + + // string concat function + expect("concat(a, b)", "ab", &errors); + expect("concat(a,\n \"b\")", "ab", &errors); + expect("concat(a + b,\nc,\"d\")", "abcd", &errors); + expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors); + + // logical and + expect("a && b", "b", &errors); + expect("a && \"\"", "", &errors); + expect("\"\" && b", "", &errors); + expect("\"\" && \"\"", "", &errors); + expect("\"\" && abort()", "", &errors); // test short-circuiting + expect("t && abort()", NULL, &errors); + + // logical or + expect("a || b", "a", &errors); + expect("a || \"\"", "a", &errors); + expect("\"\" || b", "b", &errors); + expect("\"\" || \"\"", "", &errors); + expect("a || abort()", "a", &errors); // test short-circuiting + expect("\"\" || abort()", NULL, &errors); + + // logical not + expect("!a", "", &errors); + expect("! \"\"", "t", &errors); + expect("!!a", "t", &errors); + + // precedence + expect("\"\" == \"\" && b", "b", &errors); + expect("a + b == ab", "t", &errors); + expect("ab == a + b", "t", &errors); + expect("a + (b == ab)", "a", &errors); + expect("(ab == a) + b", "b", &errors); + + // substring function + expect("is_substring(cad, abracadabra)", "t", &errors); + expect("is_substring(abrac, abracadabra)", "t", &errors); + expect("is_substring(dabra, abracadabra)", "t", &errors); + expect("is_substring(cad, abracxadabra)", "", &errors); + expect("is_substring(abrac, axbracadabra)", "", &errors); + expect("is_substring(dabra, abracadabrxa)", "", &errors); + + // ifelse function + expect("ifelse(t, yes, no)", "yes", &errors); + expect("ifelse(!t, yes, no)", "no", &errors); + expect("ifelse(t, yes, abort())", "yes", &errors); + expect("ifelse(!t, abort(), no)", "no", &errors); + + // if "statements" + expect("if t then yes else no endif", "yes", &errors); + expect("if \"\" then yes else no endif", "no", &errors); + expect("if \"\" then yes endif", "", &errors); + expect("if \"\"; t then yes endif", "yes", &errors); + + printf("\n"); + + return errors; +} + +int main(int argc, char** argv) { + RegisterBuiltins(); + FinishRegistration(); + + if (argc == 1) { + return test() != 0; + } + + FILE* f = fopen(argv[1], "r"); + char buffer[8192]; + int size = fread(buffer, 1, 8191, f); + fclose(f); + buffer[size] = '\0'; + + Expr* root; + yy_scan_bytes(buffer, size); + int error = yyparse(&root); + printf("parse returned %d\n", error); + if (error == 0) { + char* result = Evaluate(NULL, root); + printf("result is [%s]\n", result == NULL ? "(NULL)" : result); + } + return 0; +} diff --git a/edify/parser.y b/edify/parser.y new file mode 100644 index 000000000..67a210fca --- /dev/null +++ b/edify/parser.y @@ -0,0 +1,121 @@ +%{ +/* + * Copyright (C) 2009 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 +#include +#include + +#include "expr.h" +#include "parser.h" + +extern int gLine; +extern int gColumn; + +void yyerror(Expr** root, const char* s); +int yyparse(Expr** root); + +%} + +%union { + char* str; + Expr* expr; + struct { + int argc; + Expr** argv; + } args; +} + +%token AND OR SUBSTR SUPERSTR EQ NE IF THEN ELSE ENDIF +%token STRING BAD +%type expr +%type arglist + +%parse-param {Expr** root} +%error-verbose + +/* declarations in increasing order of precedence */ +%left ';' +%left ',' +%left OR +%left AND +%left EQ NE +%left '+' +%right '!' + +%% + +input: expr { *root = $1; } +; + +expr: STRING { + $$ = malloc(sizeof(Expr)); + $$->fn = Literal; + $$->name = $1; + $$->argc = 0; + $$->argv = NULL; +} +| '(' expr ')' { $$ = $2; } +| expr ';' { $$ = $1; } +| expr ';' expr { $$ = Build(SequenceFn, 2, $1, $3); } +| error ';' expr { $$ = $3; } +| expr '+' expr { $$ = Build(ConcatFn, 2, $1, $3); } +| expr EQ expr { $$ = Build(EqualityFn, 2, $1, $3); } +| expr NE expr { $$ = Build(InequalityFn, 2, $1, $3); } +| expr AND expr { $$ = Build(LogicalAndFn, 2, $1, $3); } +| expr OR expr { $$ = Build(LogicalOrFn, 2, $1, $3); } +| '!' expr { $$ = Build(LogicalNotFn, 1, $2); } +| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, 2, $2, $4); } +| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, 3, $2, $4, $6); } +| STRING '(' arglist ')' { + $$ = malloc(sizeof(Expr)); + $$->fn = FindFunction($1); + if ($$->fn == NULL) { + char buffer[256]; + snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1); + yyerror(root, buffer); + YYERROR; + } + $$->name = $1; + $$->argc = $3.argc; + $$->argv = $3.argv; +} +; + +arglist: /* empty */ { + $$.argc = 0; + $$.argv = NULL; +} +| expr { + $$.argc = 1; + $$.argv = malloc(sizeof(Expr*)); + $$.argv[0] = $1; +} +| arglist ',' expr { + $$.argc = $1.argc + 1; + $$.argv = realloc($$.argv, $$.argc * sizeof(Expr*)); + $$.argv[$$.argc-1] = $3; +} +; + +%% + +void yyerror(Expr** root, const char* s) { + if (strlen(s) == 0) { + s = "syntax error"; + } + printf("line %d col %d: %s\n", gLine, gColumn, s); +} -- cgit v1.2.3 From 9931f7f3c1288171319e9ff7d053ebaad07db720 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 10 Jun 2009 14:11:53 -0700 Subject: edify extensions for OTA package installation, part 1 Adds the following edify functions: mount unmount format show_progress delete delete_recursive package_extract symlink set_perm set_perm_recursive This set is enough to extract and install the system part of a (full) OTA package. Adds the updater binary that extracts an edify script from the OTA package and then executes it. Minor changes to the edify core (adds a sleep() builtin for debugging, adds "." to the set of characters that can appear in an unquoted string). --- Android.mk | 2 + edify/Android.mk | 17 ++- edify/README | 2 +- edify/expr.c | 95 ++++++++++++++ edify/expr.h | 23 ++++ edify/lexer.l | 2 +- edify/main.c | 9 +- install.c | 2 +- updater/Android.mk | 24 ++++ updater/install.c | 370 +++++++++++++++++++++++++++++++++++++++++++++++++++++ updater/install.h | 22 ++++ updater/updater.c | 111 ++++++++++++++++ updater/updater.h | 28 ++++ 13 files changed, 694 insertions(+), 13 deletions(-) create mode 100644 updater/Android.mk create mode 100644 updater/install.c create mode 100644 updater/install.h create mode 100644 updater/updater.c create mode 100644 updater/updater.h diff --git a/Android.mk b/Android.mk index 8c1de7366..bfb1bedcb 100644 --- a/Android.mk +++ b/Android.mk @@ -44,4 +44,6 @@ include $(commands_recovery_local_path)/amend/Android.mk include $(commands_recovery_local_path)/minzip/Android.mk include $(commands_recovery_local_path)/mtdutils/Android.mk include $(commands_recovery_local_path)/tools/Android.mk +include $(commands_recovery_local_path)/edify/Android.mk +include $(commands_recovery_local_path)/updater/Android.mk commands_recovery_local_path := diff --git a/edify/Android.mk b/edify/Android.mk index 803fba207..fac0ba712 100644 --- a/edify/Android.mk +++ b/edify/Android.mk @@ -26,15 +26,14 @@ LOCAL_YACCFLAGS := -v include $(BUILD_HOST_EXECUTABLE) -# # -# # Build the device-side library -# # -# include $(CLEAR_VARS) +# +# Build the device-side library +# +include $(CLEAR_VARS) -# LOCAL_SRC_FILES := $(edify_src_files) -# LOCAL_SRC_FILES += $(edify_test_files) +LOCAL_SRC_FILES := $(edify_src_files) -# LOCAL_CFLAGS := $(edify_cflags) -# LOCAL_MODULE := libedify +LOCAL_CFLAGS := $(edify_cflags) +LOCAL_MODULE := libedify -# include $(BUILD_STATIC_LIBRARY) +include $(BUILD_STATIC_LIBRARY) diff --git a/edify/README b/edify/README index 5ccb582e8..810455cca 100644 --- a/edify/README +++ b/edify/README @@ -10,7 +10,7 @@ the old one ("amend"). This is a brief overview of the new language. understood, as are hexadecimal escapes like \x4a. - String literals consisting of only letters, numbers, colons, - underscores, and slashes don't need to be in double quotes. + underscores, slashes, and periods don't need to be in double quotes. - The following words are reserved: diff --git a/edify/expr.c b/edify/expr.c index b3b892711..129fbd96b 100644 --- a/edify/expr.c +++ b/edify/expr.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "expr.h" @@ -92,6 +93,12 @@ char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]) { } char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* msg = NULL; + if (argc > 0) { + msg = Evaluate(cookie, argv[0]); + } + SetError(msg == NULL ? "called abort()" : msg); + free(msg); return NULL; } @@ -105,12 +112,23 @@ char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]) { int b = BooleanString(v); free(v); if (!b) { + SetError("assert() failed"); return NULL; } } return strdup(""); } +char* SleepFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* val = Evaluate(cookie, argv[0]); + if (val == NULL) { + return NULL; + } + int v = strtol(val, NULL, 10); + sleep(v); + return val; +} + char* PrintFn(const char* name, void* cookie, int argc, Expr* argv[]) { int i; for (i = 0; i < argc; ++i) { @@ -234,6 +252,32 @@ Expr* Build(Function fn, int count, ...) { return e; } +// ----------------------------------------------------------------- +// error reporting +// ----------------------------------------------------------------- + +static char* error_message = NULL; + +void SetError(const char* message) { + if (error_message) { + free(error_message); + } + error_message = strdup(message); +} + +const char* GetError() { + return error_message; +} + +void ClearError() { + free(error_message); + error_message = NULL; +} + +// ----------------------------------------------------------------- +// the function table +// ----------------------------------------------------------------- + static int fn_entries = 0; static int fn_size = 0; NamedFunction* fn_table = NULL; @@ -276,4 +320,55 @@ void RegisterBuiltins() { RegisterFunction("concat", ConcatFn); RegisterFunction("is_substring", SubstringFn); RegisterFunction("print", PrintFn); + RegisterFunction("sleep", SleepFn); +} + + +// ----------------------------------------------------------------- +// convenience methods for functions +// ----------------------------------------------------------------- + +// Evaluate the expressions in argv, giving 'count' char* (the ... is +// zero or more char** to put them in). If any expression evaluates +// to NULL, free the rest and return -1. Return 0 on success. +int ReadArgs(void* cookie, Expr* argv[], int count, ...) { + char** args = malloc(count * sizeof(char*)); + va_list v; + va_start(v, count); + int i; + for (i = 0; i < count; ++i) { + args[i] = Evaluate(cookie, argv[i]); + if (args[i] == NULL) { + va_end(v); + int j; + for (j = 0; j < i; ++j) { + free(args[j]); + } + return -1; + } + *(va_arg(v, char**)) = args[i]; + } + va_end(v); + return 0; +} + +// Evaluate the expressions in argv, returning an array of char* +// results. If any evaluate to NULL, free the rest and return NULL. +// The caller is responsible for freeing the returned array and the +// strings it contains. +char** ReadVarArgs(void* cookie, int argc, Expr* argv[]) { + char** args = (char**)malloc(argc * sizeof(char*)); + int i = 0; + for (i = 0; i < argc; ++i) { + args[i] = Evaluate(cookie, argv[i]); + if (args[i] == NULL) { + int j; + for (j = 0; j < i; ++j) { + free(args[j]); + } + free(args); + return NULL; + } + } + return args; } diff --git a/edify/expr.h b/edify/expr.h index ac5df1869..cfbef903b 100644 --- a/edify/expr.h +++ b/edify/expr.h @@ -57,6 +57,14 @@ char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]); char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]); char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]); + +// For setting and getting the global error string (when returning +// NULL from a function). +void SetError(const char* message); // makes a copy +const char* GetError(); // retains ownership +void ClearError(); + + typedef struct { const char* name; Function fn; @@ -77,4 +85,19 @@ void FinishRegistration(); // exists. Function FindFunction(const char* name); + +// --- convenience functions for use in functions --- + +// Evaluate the expressions in argv, giving 'count' char* (the ... is +// zero or more char** to put them in). If any expression evaluates +// to NULL, free the rest and return -1. Return 0 on success. +int ReadArgs(void* cookie, Expr* argv[], int count, ...); + +// Evaluate the expressions in argv, returning an array of char* +// results. If any evaluate to NULL, free the rest and return NULL. +// The caller is responsible for freeing the returned array and the +// strings it contains. +char** ReadVarArgs(void* cookie, int argc, Expr* argv[]); + + #endif // _EXPRESSION_H diff --git a/edify/lexer.l b/edify/lexer.l index 4faef5da0..cb5eb318a 100644 --- a/edify/lexer.l +++ b/edify/lexer.l @@ -77,7 +77,7 @@ then { gColumn += yyleng; return THEN; } else { gColumn += yyleng; return ELSE; } endif { gColumn += yyleng; return ENDIF; } -[a-zA-Z0-9_:/]+ { +[a-zA-Z0-9_:/.]+ { gColumn += yyleng; yylval.str = strdup(yytext); return STRING; diff --git a/edify/main.c b/edify/main.c index 4d65da2d8..c95968376 100644 --- a/edify/main.c +++ b/edify/main.c @@ -158,7 +158,14 @@ int main(int argc, char** argv) { printf("parse returned %d\n", error); if (error == 0) { char* result = Evaluate(NULL, root); - printf("result is [%s]\n", result == NULL ? "(NULL)" : result); + if (result == NULL) { + char* errmsg = GetError(); + printf("result was NULL, message is: %s\n", + (errmsg == NULL ? "(NULL)" : errmsg)); + ClearError(); + } else { + printf("result is [%s]\n", result); + } } return 0; } diff --git a/install.c b/install.c index eff9312b4..0b5c04da9 100644 --- a/install.c +++ b/install.c @@ -256,7 +256,7 @@ try_update_binary(const char *path, ZipArchive *zip) { int status; waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - LOGE("Error in %s\n(Status %d)\n", path, status); + LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status)); return INSTALL_ERROR; } diff --git a/updater/Android.mk b/updater/Android.mk new file mode 100644 index 000000000..2716d4868 --- /dev/null +++ b/updater/Android.mk @@ -0,0 +1,24 @@ +# Copyright 2009 The Android Open Source Project + +LOCAL_PATH := $(call my-dir) + +updater_src_files := \ + install.c \ + updater.c + +# +# Build the device-side library +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(updater_src_files) + +LOCAL_STATIC_LIBRARIES := libedify libmtdutils libminzip libz +LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc +LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. + +LOCAL_MODULE := updater + +LOCAL_FORCE_STATIC_EXECUTABLE := true + +include $(BUILD_EXECUTABLE) diff --git a/updater/install.c b/updater/install.c new file mode 100644 index 000000000..2336f614d --- /dev/null +++ b/updater/install.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2009 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "edify/expr.h" +#include "minzip/DirUtil.h" +#include "mtdutils/mounts.h" +#include "mtdutils/mtdutils.h" +#include "updater.h" + +char* ErrorAbort(void* cookie, char* format, ...) { + char* buffer = malloc(4096); + va_list v; + va_start(v, format); + vsnprintf(buffer, 4096, format, v); + va_end(v); + SetError(buffer); + return NULL; +} + +// mount(type, location, mount_point) +// +// what: type="MTD" location="" to mount a yaffs2 filesystem +// type="vfat" location="/dev/block/" to mount a device +char* MountFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 3) { + return ErrorAbort(cookie, "%s() expects 3 args, got %d", name, argc); + } + char* type; + char* location; + char* mount_point; + if (ReadArgs(cookie, argv, 3, &type, &location, &mount_point) < 0) { + return NULL; + } + + if (strlen(type) == 0) { + ErrorAbort(cookie, "type argument to %s() can't be empty", name); + goto done; + } + if (strlen(location) == 0) { + ErrorAbort(cookie, "location argument to %s() can't be empty", name); + goto done; + } + if (strlen(mount_point) == 0) { + ErrorAbort(cookie, "mount_point argument to %s() can't be empty", name); + goto done; + } + + mkdir(mount_point, 0755); + + if (strcmp(type, "MTD") == 0) { + mtd_scan_partitions(); + const MtdPartition* mtd; + mtd = mtd_find_partition_by_name(location); + if (mtd == NULL) { + fprintf(stderr, "%s: no mtd partition named \"%s\"", + name, location); + result = strdup(""); + goto done; + } + if (mtd_mount_partition(mtd, mount_point, "yaffs2", 0 /* rw */) != 0) { + fprintf(stderr, "mtd mount of %s failed: %s\n", + location, strerror(errno)); + result = strdup(""); + goto done; + } + result = mount_point; + } else { + if (mount(location, mount_point, type, + MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) { + result = strdup(""); + } else { + result = mount_point; + } + } + +done: + free(type); + free(location); + if (result != mount_point) free(mount_point); + return result; +} + +char* UnmountFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 1) { + return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc); + } + char* mount_point; + if (ReadArgs(cookie, argv, 1, &mount_point) < 0) { + return NULL; + } + if (strlen(mount_point) == 0) { + ErrorAbort(cookie, "mount_point argument to unmount() can't be empty"); + goto done; + } + + scan_mounted_volumes(); + const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); + if (vol == NULL) { + fprintf(stderr, "unmount of %s failed; no such volume\n", mount_point); + result = strdup(""); + } else { + unmount_mounted_volume(vol); + result = mount_point; + } + +done: + if (result != mount_point) free(mount_point); + return result; +} +// format(type, location) +// +// type="MTD" location=partition +char* FormatFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 2) { + return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + } + char* type; + char* location; + if (ReadArgs(cookie, argv, 2, &type, &location) < 0) { + return NULL; + } + + if (strlen(type) == 0) { + ErrorAbort(cookie, "type argument to %s() can't be empty", name); + goto done; + } + if (strlen(location) == 0) { + ErrorAbort(cookie, "location argument to %s() can't be empty", name); + goto done; + } + + if (strcmp(type, "MTD") == 0) { + mtd_scan_partitions(); + const MtdPartition* mtd = mtd_find_partition_by_name(location); + if (mtd == NULL) { + fprintf(stderr, "%s: no mtd partition named \"%s\"", + name, location); + result = strdup(""); + goto done; + } + MtdWriteContext* ctx = mtd_write_partition(mtd); + if (ctx == NULL) { + fprintf(stderr, "%s: can't write \"%s\"", name, location); + result = strdup(""); + goto done; + } + if (mtd_erase_blocks(ctx, -1) == -1) { + mtd_write_close(ctx); + fprintf(stderr, "%s: failed to erase \"%s\"", name, location); + result = strdup(""); + goto done; + } + if (mtd_write_close(ctx) != 0) { + fprintf(stderr, "%s: failed to close \"%s\"", name, location); + result = strdup(""); + goto done; + } + result = location; + } else { + fprintf(stderr, "%s: unsupported type \"%s\"", name, type); + } + +done: + free(type); + if (result != location) free(location); + return result; +} + +char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char** paths = malloc(argc * sizeof(char*)); + int i; + for (i = 0; i < argc; ++i) { + paths[i] = Evaluate(cookie, argv[i]); + if (paths[i] == NULL) { + int j; + for (j = 0; j < i; ++i) { + free(paths[j]); + } + free(paths); + return NULL; + } + } + + bool recursive = (strcmp(name, "delete_recursive") == 0); + + int success = 0; + for (i = 0; i < argc; ++i) { + if ((recursive ? dirUnlinkHierarchy(paths[i]) : unlink(paths[i])) == 0) + ++success; + free(paths[i]); + } + free(paths); + + char buffer[10]; + sprintf(buffer, "%d", success); + return strdup(buffer); +} + +char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + } + char* frac_str; + char* sec_str; + if (ReadArgs(cookie, argv, 2, &frac_str, &sec_str) < 0) { + return NULL; + } + + double frac = strtod(frac_str, NULL); + int sec = strtol(sec_str, NULL, 10); + + UpdaterInfo* ui = (UpdaterInfo*)cookie; + fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec); + + free(frac_str); + free(sec_str); + return strdup(""); +} + +// package_extract package_path destination_path +char* PackageExtractFn(const char* name, void* cookie, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + } + char* zip_path; + char* dest_path; + if (ReadArgs(cookie, argv, 2, &zip_path, &dest_path) < 0) return NULL; + + ZipArchive* za = ((UpdaterInfo*)cookie)->package_zip; + + // To create a consistent system image, never use the clock for timestamps. + struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default + + bool success = mzExtractRecursive(za, zip_path, dest_path, + MZ_EXTRACT_FILES_ONLY, ×tamp, + NULL, NULL); + free(zip_path); + free(dest_path); + return strdup(success ? "t" : ""); +} + +// symlink target src1 src2 ... +char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) { + if (argc == 0) { + return ErrorAbort(cookie, "%s() expects 1+ args, got %d", name, argc); + } + char* target; + target = Evaluate(cookie, argv[0]); + if (target == NULL) return NULL; + + char** srcs = ReadVarArgs(cookie, argc-1, argv+1); + if (srcs == NULL) { + free(target); + return NULL; + } + + int i; + for (i = 0; i < argc-1; ++i) { + symlink(target, srcs[i]); + free(srcs[i]); + } + free(srcs); + return strdup(""); +} + +char* SetPermFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* result = NULL; + bool recursive = (strcmp(name, "set_perm_recursive") == 0); + + int min_args = 4 + (recursive ? 1 : 0); + if (argc < min_args) { + return ErrorAbort(cookie, "%s() expects %d+ args, got %d", name, argc); + } + + char** args = ReadVarArgs(cookie, argc, argv); + if (args == NULL) return NULL; + + char* end; + int i; + + int uid = strtoul(args[0], &end, 0); + if (*end != '\0' || args[0][0] == 0) { + ErrorAbort(cookie, "%s: \"%s\" not a valid uid", name, args[0]); + goto done; + } + + int gid = strtoul(args[1], &end, 0); + if (*end != '\0' || args[1][0] == 0) { + ErrorAbort(cookie, "%s: \"%s\" not a valid gid", name, args[1]); + goto done; + } + + if (recursive) { + int dir_mode = strtoul(args[2], &end, 0); + if (*end != '\0' || args[2][0] == 0) { + ErrorAbort(cookie, "%s: \"%s\" not a valid dirmode", name, args[2]); + goto done; + } + + int file_mode = strtoul(args[3], &end, 0); + if (*end != '\0' || args[3][0] == 0) { + ErrorAbort(cookie, "%s: \"%s\" not a valid filemode", + name, args[3]); + goto done; + } + + for (i = 4; i < argc; ++i) { + dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode); + } + } else { + int mode = strtoul(args[2], &end, 0); + if (*end != '\0' || args[2][0] == 0) { + ErrorAbort(cookie, "%s: \"%s\" not a valid mode", name, args[2]); + goto done; + } + + for (i = 4; i < argc; ++i) { + chown(args[i], uid, gid); + chmod(args[i], mode); + } + } + result = strdup(""); + +done: + for (i = 0; i < argc; ++i) { + free(args[i]); + } + free(args); + + return result; +} + +void RegisterInstallFunctions() { + RegisterFunction("mount", MountFn); + RegisterFunction("unmount", UnmountFn); + RegisterFunction("format", FormatFn); + RegisterFunction("show_progress", ShowProgressFn); + RegisterFunction("delete", DeleteFn); + RegisterFunction("delete_recursive", DeleteFn); + RegisterFunction("package_extract", PackageExtractFn); + RegisterFunction("symlink", SymlinkFn); + RegisterFunction("set_perm", SetPermFn); + RegisterFunction("set_perm_recursive", SetPermFn); +} diff --git a/updater/install.h b/updater/install.h new file mode 100644 index 000000000..94f344f8e --- /dev/null +++ b/updater/install.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2009 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_INSTALL_H_ +#define _UPDATER_INSTALL_H_ + +void RegisterInstallFunctions(); + +#endif diff --git a/updater/updater.c b/updater/updater.c new file mode 100644 index 000000000..aa03803a8 --- /dev/null +++ b/updater/updater.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2009 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 +#include +#include + +#include "edify/expr.h" +#include "updater.h" +#include "install.h" +#include "minzip/Zip.h" + +// Where in the package we expect to find the edify script to execute. +// (Note it's "updateR-script", not the older "update-script".) +#define SCRIPT_NAME "META-INF/com/google/android/updater-script" + +int main(int argc, char** argv) { + if (argc != 4) { + fprintf(stderr, "unexpected number of arguments (%d)\n", argc); + return 1; + } + + char* version = argv[1]; + if (version[0] != '1' || version[1] != '\0') { + fprintf(stderr, "wrong updater binary API; expected 1, got %s\n", + version); + return 2; + } + + // Set up the pipe for sending commands back to the parent process. + + int fd = atoi(argv[2]); + FILE* cmd_pipe = fdopen(fd, "wb"); + setlinebuf(cmd_pipe); + + // Extract the script from the package. + + char* package_data = argv[3]; + ZipArchive za; + int err; + err = mzOpenZipArchive(package_data, &za); + if (err != 0) { + fprintf(stderr, "failed to open package %s: %s\n", + package_data, strerror(err)); + return 3; + } + + const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME); + if (script_entry == NULL) { + fprintf(stderr, "failed to find %s in %s\n", SCRIPT_NAME, package_data); + return 4; + } + + char* script = malloc(script_entry->uncompLen+1); + if (!mzReadZipEntry(&za, script_entry, script, script_entry->uncompLen)) { + fprintf(stderr, "failed to read script from package\n"); + return 5; + } + script[script_entry->uncompLen] = '\0'; + + // Configure edify's functions. + + RegisterBuiltins(); + RegisterInstallFunctions(); + FinishRegistration(); + + // Parse the script. + + Expr* root; + yy_scan_string(script); + int error = yyparse(&root); + if (error != 0) { + fprintf(stderr, "%d parse errors\n", error); + return 6; + } + + // Evaluate the parsed script. + + UpdaterInfo updater_info; + updater_info.cmd_pipe = cmd_pipe; + updater_info.package_zip = &za; + + char* result = Evaluate(&updater_info, root); + if (result == NULL) { + const char* errmsg = GetError(); + fprintf(stderr, "script aborted with error: %s\n", + errmsg == NULL ? "(none)" : errmsg); + ClearError(); + return 7; + } else { + fprintf(stderr, "script result was [%s]\n", result); + free(result); + } + + mzCloseZipArchive(&za); + + return 0; +} diff --git a/updater/updater.h b/updater/updater.h new file mode 100644 index 000000000..22fbfd285 --- /dev/null +++ b/updater/updater.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2009 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_UPDATER_H_ +#define _UPDATER_UPDATER_H_ + +#include +#include "minzip/Zip.h" + +typedef struct { + FILE* cmd_pipe; + ZipArchive* package_zip; +} UpdaterInfo; + +#endif -- cgit v1.2.3 From 9dbc027b5f540bcf23c968398f8a70e92abd56cd Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 11 Jun 2009 17:32:55 -0700 Subject: fix sim build in donut, too --- Android.mk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Android.mk b/Android.mk index bfb1bedcb..6ab498e7e 100644 --- a/Android.mk +++ b/Android.mk @@ -36,10 +36,6 @@ LOCAL_STATIC_LIBRARIES += libstdc++ libc include $(BUILD_EXECUTABLE) include $(commands_recovery_local_path)/minui/Android.mk - -endif # TARGET_ARCH == arm -endif # !TARGET_SIMULATOR - include $(commands_recovery_local_path)/amend/Android.mk include $(commands_recovery_local_path)/minzip/Android.mk include $(commands_recovery_local_path)/mtdutils/Android.mk @@ -47,3 +43,7 @@ include $(commands_recovery_local_path)/tools/Android.mk include $(commands_recovery_local_path)/edify/Android.mk include $(commands_recovery_local_path)/updater/Android.mk commands_recovery_local_path := + +endif # TARGET_ARCH == arm +endif # !TARGET_SIMULATOR + -- cgit v1.2.3 From 8edb00c990e563e6f91b278a212f2edf877cf763 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 11 Jun 2009 17:21:44 -0700 Subject: edify extensions for OTA package installation, part 2 Adds more edify functions for OTAs: is_mounted getprop apply_patch apply_patch_check apply_patch_space write_raw_image write_firmware_image package_extract_file This allows us to install radios, hboots, boot images, and install incremental OTA packages. Fixes a couple of dumb bugs in edify itself: - we were doubling the size of the function table each time it was *not* full, rather than each time it was full - "no such function" errors weren't visible to the parser, so they didn't prevent execution of the script. --- edify/expr.c | 2 +- edify/main.c | 7 +- edify/parser.y | 10 +- install.c | 3 +- updater/Android.mk | 3 +- updater/install.c | 275 ++++++++++++++++++++++++++++++++++++++++++++++++++++- updater/updater.c | 7 +- 7 files changed, 291 insertions(+), 16 deletions(-) diff --git a/edify/expr.c b/edify/expr.c index 129fbd96b..5470a2bac 100644 --- a/edify/expr.c +++ b/edify/expr.c @@ -283,7 +283,7 @@ static int fn_size = 0; NamedFunction* fn_table = NULL; void RegisterFunction(const char* name, Function fn) { - if (fn_entries <= fn_size) { + if (fn_entries >= fn_size) { fn_size = fn_size*2 + 1; fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction)); } diff --git a/edify/main.c b/edify/main.c index c95968376..7da89e2ea 100644 --- a/edify/main.c +++ b/edify/main.c @@ -153,10 +153,11 @@ int main(int argc, char** argv) { buffer[size] = '\0'; Expr* root; + int error_count = 0; yy_scan_bytes(buffer, size); - int error = yyparse(&root); - printf("parse returned %d\n", error); - if (error == 0) { + int error = yyparse(&root, &error_count); + printf("parse returned %d; %d errors encountered\n", error, error_count); + if (error == 0 || error_count > 0) { char* result = Evaluate(NULL, root); if (result == NULL) { char* errmsg = GetError(); diff --git a/edify/parser.y b/edify/parser.y index 67a210fca..cf163c026 100644 --- a/edify/parser.y +++ b/edify/parser.y @@ -25,8 +25,8 @@ extern int gLine; extern int gColumn; -void yyerror(Expr** root, const char* s); -int yyparse(Expr** root); +void yyerror(Expr** root, int* error_count, const char* s); +int yyparse(Expr** root, int* error_count); %} @@ -45,6 +45,7 @@ int yyparse(Expr** root); %type arglist %parse-param {Expr** root} +%parse-param {int* error_count} %error-verbose /* declarations in increasing order of precedence */ @@ -86,7 +87,7 @@ expr: STRING { if ($$->fn == NULL) { char buffer[256]; snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1); - yyerror(root, buffer); + yyerror(root, error_count, buffer); YYERROR; } $$->name = $1; @@ -113,9 +114,10 @@ arglist: /* empty */ { %% -void yyerror(Expr** root, const char* s) { +void yyerror(Expr** root, int* error_count, const char* s) { if (strlen(s) == 0) { s = "syntax error"; } printf("line %d col %d: %s\n", gLine, gColumn, s); + ++*error_count; } diff --git a/install.c b/install.c index 0b5c04da9..cca940021 100644 --- a/install.c +++ b/install.c @@ -124,7 +124,8 @@ handle_firmware_update(char* type, char* filename) { return INSTALL_ERROR; } - LOGI("type is [%s]\n", type); + LOGI("type is %s; size is %d; file is %s\n", + type, (int)st_data.st_size, filename); char* data = malloc(st_data.st_size); if (data == NULL) { diff --git a/updater/Android.mk b/updater/Android.mk index 2716d4868..1159d7bb8 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -13,7 +13,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(updater_src_files) -LOCAL_STATIC_LIBRARIES := libedify libmtdutils libminzip libz +LOCAL_STATIC_LIBRARIES := libapplypatch libedify libmtdutils libminzip libz +LOCAL_STATIC_LIBRARIES += libmincrypt libbz LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. diff --git a/updater/install.c b/updater/install.c index 2336f614d..2e965cee5 100644 --- a/updater/install.c +++ b/updater/install.c @@ -24,6 +24,8 @@ #include #include +#include "cutils/misc.h" +#include "cutils/properties.h" #include "edify/expr.h" #include "minzip/DirUtil.h" #include "mtdutils/mounts.h" @@ -40,6 +42,7 @@ char* ErrorAbort(void* cookie, char* format, ...) { return NULL; } + // mount(type, location, mount_point) // // what: type="MTD" location="" to mount a yaffs2 filesystem @@ -104,6 +107,36 @@ done: return result; } + +// is_mounted(mount_point) +char* IsMountedFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 1) { + return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc); + } + char* mount_point; + if (ReadArgs(cookie, argv, 1, &mount_point) < 0) { + return NULL; + } + if (strlen(mount_point) == 0) { + ErrorAbort(cookie, "mount_point argument to unmount() can't be empty"); + goto done; + } + + scan_mounted_volumes(); + const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); + if (vol == NULL) { + result = strdup(""); + } else { + result = mount_point; + } + +done: + if (result != mount_point) free(mount_point); + return result; +} + + char* UnmountFn(const char* name, void* cookie, int argc, Expr* argv[]) { char* result = NULL; if (argc != 1) { @@ -132,6 +165,8 @@ done: if (result != mount_point) free(mount_point); return result; } + + // format(type, location) // // type="MTD" location=partition @@ -192,6 +227,7 @@ done: return result; } + char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) { char** paths = malloc(argc * sizeof(char*)); int i; @@ -222,6 +258,7 @@ char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) { return strdup(buffer); } + char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) { if (argc != 2) { return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); @@ -243,8 +280,9 @@ char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) { return strdup(""); } -// package_extract package_path destination_path -char* PackageExtractFn(const char* name, void* cookie, int argc, Expr* argv[]) { +// package_extract_dir(package_path, destination_path) +char* PackageExtractDirFn(const char* name, void* cookie, + int argc, Expr* argv[]) { if (argc != 2) { return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); } @@ -265,6 +303,42 @@ char* PackageExtractFn(const char* name, void* cookie, int argc, Expr* argv[]) { return strdup(success ? "t" : ""); } + +// package_extract_file(package_path, destination_path) +char* PackageExtractFileFn(const char* name, void* cookie, + int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + } + char* zip_path; + char* dest_path; + if (ReadArgs(cookie, argv, 2, &zip_path, &dest_path) < 0) return NULL; + + bool success = false; + + ZipArchive* za = ((UpdaterInfo*)cookie)->package_zip; + const ZipEntry* entry = mzFindZipEntry(za, zip_path); + if (entry == NULL) { + fprintf(stderr, "%s: no %s in package\n", name, zip_path); + goto done; + } + + FILE* f = fopen(dest_path, "wb"); + if (f == NULL) { + fprintf(stderr, "%s: can't open %s for write: %s\n", + name, dest_path, strerror(errno)); + goto done; + } + success = mzExtractZipEntryToFile(za, entry, fileno(f)); + fclose(f); + + done: + free(zip_path); + free(dest_path); + return strdup(success ? "t" : ""); +} + + // symlink target src1 src2 ... char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) { if (argc == 0) { @@ -289,6 +363,7 @@ char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) { return strdup(""); } + char* SetPermFn(const char* name, void* cookie, int argc, Expr* argv[]) { char* result = NULL; bool recursive = (strcmp(name, "set_perm_recursive") == 0); @@ -356,15 +431,209 @@ done: return result; } + +char* GetPropFn(const char* name, void* cookie, int argc, Expr* argv[]) { + if (argc != 1) { + return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc); + } + char* key; + key = Evaluate(cookie, argv[0]); + if (key == NULL) return NULL; + + char value[PROPERTY_VALUE_MAX]; + property_get(key, value, ""); + free(key); + + return strdup(value); +} + + +static bool write_raw_image_cb(const unsigned char* data, + int data_len, void* ctx) { + int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len); + if (r == data_len) return true; + fprintf(stderr, "%s\n", strerror(errno)); + return false; +} + +// write_raw_image(file, partition) +char* WriteRawImageFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* result = NULL; + + char* partition; + char* filename; + if (ReadArgs(cookie, argv, 2, &filename, &partition) < 0) { + return NULL; + } + + if (strlen(partition) == 0) { + ErrorAbort(cookie, "partition argument to %s can't be empty", name); + goto done; + } + if (strlen(filename) == 0) { + ErrorAbort(cookie, "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; + + 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); + if (!success) { + fprintf(stderr, "mtd_write_data to %s failed: %s\n", + partition, strerror(errno)); + } + } + free(buffer); + fclose(f); + + printf("%s %s partition from %s\n", + success ? "wrote" : "failed to write", partition, filename); + + result = success ? partition : strdup(""); + +done: + if (result != partition) free(partition); + free(filename); + return result; +} + +// write_firmware_image(file, partition) +// +// partition is "radio" or "hboot" +// file is not used until after updater exits +// +// TODO: this should live in some HTC-specific library +char* WriteFirmwareImageFn(const char* name, void* cookie, + int argc, Expr* argv[]) { + char* result = NULL; + + char* partition; + char* filename; + if (ReadArgs(cookie, argv, 2, &filename, &partition) < 0) { + return NULL; + } + + if (strlen(partition) == 0) { + ErrorAbort(cookie, "partition argument to %s can't be empty", name); + goto done; + } + if (strlen(filename) == 0) { + ErrorAbort(cookie, "file argument to %s can't be empty", name); + goto done; + } + + FILE* cmd = ((UpdaterInfo*)cookie)->cmd_pipe; + fprintf(cmd, "firmware %s %s\n", partition, filename); + + printf("will write %s firmware from %s\n", partition, filename); + result = partition; + +done: + if (result != partition) free(partition); + free(filename); + return result; +} + + +extern int applypatch(int argc, char** argv); + +// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...) +// apply_patch_check(file, sha1, ...) +// apply_patch_space(bytes) +char* ApplyPatchFn(const char* name, void* cookie, int argc, Expr* argv[]) { + printf("in applypatchfn (%s)\n", name); + + char* prepend = NULL; + if (strstr(name, "check") != NULL) { + prepend = "-c"; + } else if (strstr(name, "space") != NULL) { + prepend = "-s"; + } + + char** args = ReadVarArgs(cookie, argc, argv); + if (args == NULL) return NULL; + + // insert the "program name" argv[0] and a copy of the "prepend" + // string (if any) at the start of the args. + + int extra = 1 + (prepend != NULL ? 1 : 0); + char** temp = malloc((argc+extra) * sizeof(char*)); + memcpy(temp+extra, args, argc * sizeof(char*)); + temp[0] = strdup("updater"); + if (prepend) { + temp[1] = strdup(prepend); + } + free(args); + args = temp; + argc += extra; + + printf("calling applypatch\n"); + fflush(stdout); + int result = applypatch(argc, args); + printf("applypatch returned %d\n", result); + + int i; + for (i = 0; i < argc; ++i) { + free(args[i]); + } + free(args); + + switch (result) { + case 0: return strdup("t"); + case 1: return strdup(""); + default: return ErrorAbort(cookie, "applypatch couldn't parse args"); + } +} + + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); + RegisterFunction("is_mounted", IsMountedFn); RegisterFunction("unmount", UnmountFn); RegisterFunction("format", FormatFn); RegisterFunction("show_progress", ShowProgressFn); RegisterFunction("delete", DeleteFn); RegisterFunction("delete_recursive", DeleteFn); - RegisterFunction("package_extract", PackageExtractFn); + RegisterFunction("package_extract_dir", PackageExtractDirFn); + RegisterFunction("package_extract_file", PackageExtractFileFn); RegisterFunction("symlink", SymlinkFn); RegisterFunction("set_perm", SetPermFn); RegisterFunction("set_perm_recursive", SetPermFn); + + RegisterFunction("getprop", GetPropFn); + RegisterFunction("write_raw_image", WriteRawImageFn); + RegisterFunction("write_firmware_image", WriteFirmwareImageFn); + + RegisterFunction("apply_patch", ApplyPatchFn); + RegisterFunction("apply_patch_check", ApplyPatchFn); + RegisterFunction("apply_patch_space", ApplyPatchFn); } diff --git a/updater/updater.c b/updater/updater.c index aa03803a8..09776256b 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -80,10 +80,11 @@ int main(int argc, char** argv) { // Parse the script. Expr* root; + int error_count = 0; yy_scan_string(script); - int error = yyparse(&root); - if (error != 0) { - fprintf(stderr, "%d parse errors\n", error); + int error = yyparse(&root, &error_count); + if (error != 0 || error_count > 0) { + fprintf(stderr, "%d parse errors\n", error_count); return 6; } -- cgit v1.2.3 From d9c9d10d9da76f067d3955bea71f7bb39e859fa5 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 12 Jun 2009 12:24:39 -0700 Subject: fixes to edify and updater script A few more changes to edify: - fix write_raw_image(); my last change neglected to close the write context, so the written image was corrupt. - each expression tracks the span of the source code from which it was compiled, so that assert()'s error message can include the source of the expression that failed. - the 'cookie' argument to each Function is replaced with a State object, which contains the cookie, the source script (for use with the above spans), and the current error message (replacing the global variables that were used for this purpose). - in the recovery image, a new command "ui_print" can be sent back through the command pipe to cause text to appear on the screen. Add a new ui_print() function to print things from scripts. Rename existing "print" function to "stdout". --- edify/expr.c | 506 +++++++++++++++++++++++++++--------------------------- edify/expr.h | 61 ++++--- edify/lexer.l | 47 +++-- edify/main.c | 301 +++++++++++++++++--------------- edify/parser.y | 31 ++-- edify/yydefs.h | 36 ++++ install.c | 10 ++ updater/install.c | 166 +++++++++++------- updater/updater.c | 25 ++- 9 files changed, 674 insertions(+), 509 deletions(-) create mode 100644 edify/yydefs.h diff --git a/edify/expr.c b/edify/expr.c index 5470a2bac..406c67ea6 100644 --- a/edify/expr.c +++ b/edify/expr.c @@ -29,249 +29,241 @@ // - if Evaluate() on any argument returns NULL, return NULL. int BooleanString(const char* s) { - return s[0] != '\0'; + return s[0] != '\0'; } -char* Evaluate(void* cookie, Expr* expr) { - return expr->fn(expr->name, cookie, expr->argc, expr->argv); +char* Evaluate(State* state, Expr* expr) { + return expr->fn(expr->name, state, expr->argc, expr->argv); } -char* ConcatFn(const char* name, void* cookie, int argc, Expr* argv[]) { - if (argc == 0) { - return strdup(""); - } - char** strings = malloc(argc * sizeof(char*)); - int i; - for (i = 0; i < argc; ++i) { - strings[i] = NULL; - } - char* result = NULL; - int length = 0; - for (i = 0; i < argc; ++i) { - strings[i] = Evaluate(cookie, argv[i]); - if (strings[i] == NULL) { - goto done; +char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc == 0) { + return strdup(""); + } + char** strings = malloc(argc * sizeof(char*)); + int i; + for (i = 0; i < argc; ++i) { + strings[i] = NULL; + } + char* result = NULL; + int length = 0; + for (i = 0; i < argc; ++i) { + strings[i] = Evaluate(state, argv[i]); + if (strings[i] == NULL) { + goto done; + } + length += strlen(strings[i]); + } + + result = malloc(length+1); + int p = 0; + for (i = 0; i < argc; ++i) { + strcpy(result+p, strings[i]); + p += strlen(strings[i]); + } + result[p] = '\0'; + + done: + for (i = 0; i < argc; ++i) { + free(strings[i]); } - length += strlen(strings[i]); - } - - result = malloc(length+1); - int p = 0; - for (i = 0; i < argc; ++i) { - strcpy(result+p, strings[i]); - p += strlen(strings[i]); - } - result[p] = '\0'; - -done: - for (i = 0; i < argc; ++i) { - free(strings[i]); - } - return result; + return result; } -char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]) { - if (argc != 2 && argc != 3) { - return NULL; - } - char* cond = Evaluate(cookie, argv[0]); - if (cond == NULL) { - return NULL; - } - - if (BooleanString(cond) == true) { - free(cond); - return Evaluate(cookie, argv[1]); - } else { - if (argc == 3) { - free(cond); - return Evaluate(cookie, argv[2]); +char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2 && argc != 3) { + return NULL; + } + char* cond = Evaluate(state, argv[0]); + if (cond == NULL) { + return NULL; + } + + if (BooleanString(cond) == true) { + free(cond); + return Evaluate(state, argv[1]); } else { - return cond; + if (argc == 3) { + free(cond); + return Evaluate(state, argv[2]); + } else { + return cond; + } } - } } -char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]) { - char* msg = NULL; - if (argc > 0) { - msg = Evaluate(cookie, argv[0]); - } - SetError(msg == NULL ? "called abort()" : msg); - free(msg); - return NULL; +char* AbortFn(const char* name, State* state, int argc, Expr* argv[]) { + char* msg = NULL; + if (argc > 0) { + msg = Evaluate(state, argv[0]); + } + free(state->errmsg); + if (msg) { + state->errmsg = msg; + } else { + state->errmsg = strdup("called abort()"); + } + return NULL; } -char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]) { - int i; - for (i = 0; i < argc; ++i) { - char* v = Evaluate(cookie, argv[i]); - if (v == NULL) { - return NULL; - } - int b = BooleanString(v); - free(v); - if (!b) { - SetError("assert() failed"); - return NULL; +char* AssertFn(const char* name, State* state, int argc, Expr* argv[]) { + int i; + for (i = 0; i < argc; ++i) { + char* v = Evaluate(state, argv[i]); + if (v == NULL) { + return NULL; + } + int b = BooleanString(v); + free(v); + if (!b) { + int prefix_len; + int len = argv[i]->end - argv[i]->start; + char* err_src = malloc(len + 20); + strcpy(err_src, "assert failed: "); + prefix_len = strlen(err_src); + memcpy(err_src + prefix_len, state->script + argv[i]->start, len); + err_src[prefix_len + len] = '\0'; + free(state->errmsg); + state->errmsg = err_src; + return NULL; + } } - } - return strdup(""); + return strdup(""); } -char* SleepFn(const char* name, void* cookie, int argc, Expr* argv[]) { - char* val = Evaluate(cookie, argv[0]); - if (val == NULL) { - return NULL; - } - int v = strtol(val, NULL, 10); - sleep(v); - return val; +char* SleepFn(const char* name, State* state, int argc, Expr* argv[]) { + char* val = Evaluate(state, argv[0]); + if (val == NULL) { + return NULL; + } + int v = strtol(val, NULL, 10); + sleep(v); + return val; } -char* PrintFn(const char* name, void* cookie, int argc, Expr* argv[]) { - int i; - for (i = 0; i < argc; ++i) { - char* v = Evaluate(cookie, argv[i]); - if (v == NULL) { - return NULL; +char* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) { + int i; + for (i = 0; i < argc; ++i) { + char* v = Evaluate(state, argv[i]); + if (v == NULL) { + return NULL; + } + fputs(v, stdout); + free(v); } - fputs(v, stdout); - free(v); - } - return strdup(""); + return strdup(""); } -char* LogicalAndFn(const char* name, void* cookie, +char* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]) { - char* left = Evaluate(cookie, argv[0]); - if (left == NULL) return NULL; - if (BooleanString(left) == true) { - free(left); - return Evaluate(cookie, argv[1]); - } else { - return left; - } + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + if (BooleanString(left) == true) { + free(left); + return Evaluate(state, argv[1]); + } else { + return left; + } } -char* LogicalOrFn(const char* name, void* cookie, +char* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]) { - char* left = Evaluate(cookie, argv[0]); - if (left == NULL) return NULL; - if (BooleanString(left) == false) { - free(left); - return Evaluate(cookie, argv[1]); - } else { - return left; - } + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + if (BooleanString(left) == false) { + free(left); + return Evaluate(state, argv[1]); + } else { + return left; + } } -char* LogicalNotFn(const char* name, void* cookie, - int argc, Expr* argv[]) { - char* val = Evaluate(cookie, argv[0]); - if (val == NULL) return NULL; - bool bv = BooleanString(val); - free(val); - if (bv) { - return strdup(""); - } else { - return strdup("t"); - } +char* LogicalNotFn(const char* name, State* state, + int argc, Expr* argv[]) { + char* val = Evaluate(state, argv[0]); + if (val == NULL) return NULL; + bool bv = BooleanString(val); + free(val); + if (bv) { + return strdup(""); + } else { + return strdup("t"); + } } -char* SubstringFn(const char* name, void* cookie, +char* SubstringFn(const char* name, State* state, int argc, Expr* argv[]) { - char* needle = Evaluate(cookie, argv[0]); - if (needle == NULL) return NULL; - char* haystack = Evaluate(cookie, argv[1]); - if (haystack == NULL) { - free(needle); - return NULL; - } + char* needle = Evaluate(state, argv[0]); + if (needle == NULL) return NULL; + char* haystack = Evaluate(state, argv[1]); + if (haystack == NULL) { + free(needle); + return NULL; + } - char* result = strdup(strstr(haystack, needle) ? "t" : ""); - free(needle); - free(haystack); - return result; + char* result = strdup(strstr(haystack, needle) ? "t" : ""); + free(needle); + free(haystack); + return result; } -char* EqualityFn(const char* name, void* cookie, int argc, Expr* argv[]) { - char* left = Evaluate(cookie, argv[0]); - if (left == NULL) return NULL; - char* right = Evaluate(cookie, argv[1]); - if (right == NULL) { - free(left); - return NULL; - } - - char* result = strdup(strcmp(left, right) == 0 ? "t" : ""); - free(left); - free(right); - return result; -} +char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + char* right = Evaluate(state, argv[1]); + if (right == NULL) { + free(left); + return NULL; + } -char* InequalityFn(const char* name, void* cookie, int argc, Expr* argv[]) { - char* left = Evaluate(cookie, argv[0]); - if (left == NULL) return NULL; - char* right = Evaluate(cookie, argv[1]); - if (right == NULL) { + char* result = strdup(strcmp(left, right) == 0 ? "t" : ""); free(left); - return NULL; - } - - char* result = strdup(strcmp(left, right) != 0 ? "t" : ""); - free(left); - free(right); - return result; + free(right); + return result; } -char* SequenceFn(const char* name, void* cookie, int argc, Expr* argv[]) { - char* left = Evaluate(cookie, argv[0]); - if (left == NULL) return NULL; - free(left); - return Evaluate(cookie, argv[1]); -} - -char* Literal(const char* name, void* cookie, int argc, Expr* argv[]) { - return strdup(name); -} +char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + char* right = Evaluate(state, argv[1]); + if (right == NULL) { + free(left); + return NULL; + } -Expr* Build(Function fn, int count, ...) { - va_list v; - va_start(v, count); - Expr* e = malloc(sizeof(Expr)); - e->fn = fn; - e->name = "(operator)"; - e->argc = count; - e->argv = malloc(count * sizeof(Expr*)); - int i; - for (i = 0; i < count; ++i) { - e->argv[i] = va_arg(v, Expr*); - } - va_end(v); - return e; + char* result = strdup(strcmp(left, right) != 0 ? "t" : ""); + free(left); + free(right); + return result; } -// ----------------------------------------------------------------- -// error reporting -// ----------------------------------------------------------------- - -static char* error_message = NULL; - -void SetError(const char* message) { - if (error_message) { - free(error_message); - } - error_message = strdup(message); +char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + free(left); + return Evaluate(state, argv[1]); } -const char* GetError() { - return error_message; +char* Literal(const char* name, State* state, int argc, Expr* argv[]) { + return strdup(name); } -void ClearError() { - free(error_message); - error_message = NULL; +Expr* Build(Function fn, YYLTYPE loc, int count, ...) { + va_list v; + va_start(v, count); + Expr* e = malloc(sizeof(Expr)); + e->fn = fn; + e->name = "(operator)"; + e->argc = count; + e->argv = malloc(count * sizeof(Expr*)); + int i; + for (i = 0; i < count; ++i) { + e->argv[i] = va_arg(v, Expr*); + } + va_end(v); + e->start = loc.start; + e->end = loc.end; + return e; } // ----------------------------------------------------------------- @@ -283,44 +275,44 @@ static int fn_size = 0; NamedFunction* fn_table = NULL; void RegisterFunction(const char* name, Function fn) { - if (fn_entries >= fn_size) { - fn_size = fn_size*2 + 1; - fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction)); - } - fn_table[fn_entries].name = name; - fn_table[fn_entries].fn = fn; - ++fn_entries; + if (fn_entries >= fn_size) { + fn_size = fn_size*2 + 1; + fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction)); + } + fn_table[fn_entries].name = name; + fn_table[fn_entries].fn = fn; + ++fn_entries; } static int fn_entry_compare(const void* a, const void* b) { - const char* na = ((const NamedFunction*)a)->name; - const char* nb = ((const NamedFunction*)b)->name; - return strcmp(na, nb); + const char* na = ((const NamedFunction*)a)->name; + const char* nb = ((const NamedFunction*)b)->name; + return strcmp(na, nb); } void FinishRegistration() { - qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare); + qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare); } Function FindFunction(const char* name) { - NamedFunction key; - key.name = name; - NamedFunction* nf = bsearch(&key, fn_table, fn_entries, sizeof(NamedFunction), - fn_entry_compare); - if (nf == NULL) { - return NULL; - } - return nf->fn; + NamedFunction key; + key.name = name; + NamedFunction* nf = bsearch(&key, fn_table, fn_entries, + sizeof(NamedFunction), fn_entry_compare); + if (nf == NULL) { + return NULL; + } + return nf->fn; } void RegisterBuiltins() { - RegisterFunction("ifelse", IfElseFn); - RegisterFunction("abort", AbortFn); - RegisterFunction("assert", AssertFn); - RegisterFunction("concat", ConcatFn); - RegisterFunction("is_substring", SubstringFn); - RegisterFunction("print", PrintFn); - RegisterFunction("sleep", SleepFn); + RegisterFunction("ifelse", IfElseFn); + RegisterFunction("abort", AbortFn); + RegisterFunction("assert", AssertFn); + RegisterFunction("concat", ConcatFn); + RegisterFunction("is_substring", SubstringFn); + RegisterFunction("stdout", StdoutFn); + RegisterFunction("sleep", SleepFn); } @@ -331,44 +323,44 @@ void RegisterBuiltins() { // Evaluate the expressions in argv, giving 'count' char* (the ... is // zero or more char** to put them in). If any expression evaluates // to NULL, free the rest and return -1. Return 0 on success. -int ReadArgs(void* cookie, Expr* argv[], int count, ...) { - char** args = malloc(count * sizeof(char*)); - va_list v; - va_start(v, count); - int i; - for (i = 0; i < count; ++i) { - args[i] = Evaluate(cookie, argv[i]); - if (args[i] == NULL) { - va_end(v); - int j; - for (j = 0; j < i; ++j) { - free(args[j]); - } - return -1; +int ReadArgs(State* state, Expr* argv[], int count, ...) { + char** args = malloc(count * sizeof(char*)); + va_list v; + va_start(v, count); + int i; + for (i = 0; i < count; ++i) { + args[i] = Evaluate(state, argv[i]); + if (args[i] == NULL) { + va_end(v); + int j; + for (j = 0; j < i; ++j) { + free(args[j]); + } + return -1; + } + *(va_arg(v, char**)) = args[i]; } - *(va_arg(v, char**)) = args[i]; - } - va_end(v); - return 0; + va_end(v); + return 0; } // Evaluate the expressions in argv, returning an array of char* // results. If any evaluate to NULL, free the rest and return NULL. // The caller is responsible for freeing the returned array and the // strings it contains. -char** ReadVarArgs(void* cookie, int argc, Expr* argv[]) { - char** args = (char**)malloc(argc * sizeof(char*)); - int i = 0; - for (i = 0; i < argc; ++i) { - args[i] = Evaluate(cookie, argv[i]); - if (args[i] == NULL) { - int j; - for (j = 0; j < i; ++j) { - free(args[j]); - } - free(args); - return NULL; +char** ReadVarArgs(State* state, int argc, Expr* argv[]) { + char** args = (char**)malloc(argc * sizeof(char*)); + int i = 0; + for (i = 0; i < argc; ++i) { + args[i] = Evaluate(state, argv[i]); + if (args[i] == NULL) { + int j; + for (j = 0; j < i; ++j) { + free(args[j]); + } + free(args); + return NULL; + } } - } - return args; + return args; } diff --git a/edify/expr.h b/edify/expr.h index cfbef903b..671b499b5 100644 --- a/edify/expr.h +++ b/edify/expr.h @@ -17,45 +17,64 @@ #ifndef _EXPRESSION_H #define _EXPRESSION_H +#include "yydefs.h" + #define MAX_STRING_LEN 1024 typedef struct Expr Expr; -typedef char* (*Function)(const char* name, void* cookie, +typedef struct { + // Optional pointer to app-specific data; the core of edify never + // uses this value. + void* cookie; + + // The source of the original script. Must be NULL-terminated, + // and in writable memory (Evaluate may make temporary changes to + // it but will restore it when done). + char* script; + + // The error message (if any) returned if the evaluation aborts. + // Should be NULL initially, will be either NULL or a malloc'd + // pointer after Evaluate() returns. + char* errmsg; +} State; + +typedef char* (*Function)(const char* name, State* state, int argc, Expr* argv[]); struct Expr { - Function fn; - char* name; - int argc; - Expr** argv; + Function fn; + char* name; + int argc; + Expr** argv; + int start, end; }; -char* Evaluate(void* cookie, Expr* expr); +char* Evaluate(State* state, Expr* expr); // Glue to make an Expr out of a literal. -char* Literal(const char* name, void* cookie, int argc, Expr* argv[]); +char* Literal(const char* name, State* state, int argc, Expr* argv[]); // Functions corresponding to various syntactic sugar operators. // ("concat" is also available as a builtin function, to concatenate // more than two strings.) -char* ConcatFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* LogicalAndFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* LogicalOrFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* LogicalNotFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* SubstringFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* EqualityFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* InequalityFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* SequenceFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]); +char* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]); +char* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]); +char* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]); +char* SubstringFn(const char* name, State* state, int argc, Expr* argv[]); +char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]); +char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]); +char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]); // Convenience function for building expressions with a fixed number // of arguments. -Expr* Build(Function fn, int count, ...); +Expr* Build(Function fn, YYLTYPE loc, int count, ...); // Global builtins, registered by RegisterBuiltins(). -char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]); +char* AssertFn(const char* name, State* state, int argc, Expr* argv[]); +char* AbortFn(const char* name, State* state, int argc, Expr* argv[]); // For setting and getting the global error string (when returning @@ -91,13 +110,13 @@ Function FindFunction(const char* name); // Evaluate the expressions in argv, giving 'count' char* (the ... is // zero or more char** to put them in). If any expression evaluates // to NULL, free the rest and return -1. Return 0 on success. -int ReadArgs(void* cookie, Expr* argv[], int count, ...); +int ReadArgs(State* state, Expr* argv[], int count, ...); // Evaluate the expressions in argv, returning an array of char* // results. If any evaluate to NULL, free the rest and return NULL. // The caller is responsible for freeing the returned array and the // strings it contains. -char** ReadVarArgs(void* cookie, int argc, Expr* argv[]); +char** ReadVarArgs(State* state, int argc, Expr* argv[]); #endif // _EXPRESSION_H diff --git a/edify/lexer.l b/edify/lexer.l index cb5eb318a..2c4489cc6 100644 --- a/edify/lexer.l +++ b/edify/lexer.l @@ -16,14 +16,20 @@ */ #include "expr.h" +#include "yydefs.h" #include "parser.h" int gLine = 1; int gColumn = 1; +int gPos = 0; // TODO: enforce MAX_STRING_LEN during lexing char string_buffer[MAX_STRING_LEN]; char* string_pos; + +#define ADVANCE do {yylloc.start=gPos; yylloc.end=gPos+yyleng; \ + gColumn+=yyleng; gPos+=yyleng;} while(0) + %} %x STR @@ -34,27 +40,32 @@ char* string_pos; \" { - ++gColumn; BEGIN(STR); string_pos = string_buffer; + yylloc.start = gPos; + ++gColumn; + ++gPos; } { \" { ++gColumn; + ++gPos; BEGIN(INITIAL); *string_pos = '\0'; yylval.str = strdup(string_buffer); + yylloc.end = gPos; return STRING; } - \\n { gColumn += yyleng; *string_pos++ = '\n'; } - \\t { gColumn += yyleng; *string_pos++ = '\t'; } - \\\" { gColumn += yyleng; *string_pos++ = '\"'; } - \\\\ { gColumn += yyleng; *string_pos++ = '\\'; } + \\n { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\n'; } + \\t { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\t'; } + \\\" { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\"'; } + \\\\ { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\\'; } \\x[0-9a-fA-F]{2} { gColumn += yyleng; + gPos += yyleng; int val; sscanf(yytext+2, "%x", &val); *string_pos++ = val; @@ -62,36 +73,38 @@ char* string_pos; \n { ++gLine; + ++gPos; gColumn = 1; *string_pos++ = yytext[0]; } . { ++gColumn; + ++gPos; *string_pos++ = yytext[0]; } } -if { gColumn += yyleng; return IF; } -then { gColumn += yyleng; return THEN; } -else { gColumn += yyleng; return ELSE; } -endif { gColumn += yyleng; return ENDIF; } +if ADVANCE; return IF; +then ADVANCE; return THEN; +else ADVANCE; return ELSE; +endif ADVANCE; return ENDIF; [a-zA-Z0-9_:/.]+ { - gColumn += yyleng; + ADVANCE; yylval.str = strdup(yytext); return STRING; } -\&\& { gColumn += yyleng; return AND; } -\|\| { gColumn += yyleng; return OR; } -== { gColumn += yyleng; return EQ; } -!= { gColumn += yyleng; return NE; } +\&\& ADVANCE; return AND; +\|\| ADVANCE; return OR; +== ADVANCE; return EQ; +!= ADVANCE; return NE; -[+(),!;] { gColumn += yyleng; return yytext[0]; } +[+(),!;] ADVANCE; return yytext[0]; -[ \t]+ gColumn += yyleng; +[ \t]+ ADVANCE; -(#.*)?\n { ++gLine; gColumn = 1; } +(#.*)?\n gPos += yyleng; ++gLine; gColumn = 1; . return BAD; diff --git a/edify/main.c b/edify/main.c index 7da89e2ea..03eefc69e 100644 --- a/edify/main.c +++ b/edify/main.c @@ -21,152 +21,183 @@ #include "expr.h" #include "parser.h" -int expect(const char* expr_str, const char* expected, int* errors) { - Expr* e; - int error; - char* result; +extern int yyparse(Expr** root, int* error_count); - printf("."); +int expect(const char* expr_str, const char* expected, int* errors) { + Expr* e; + int error; + char* result; + + printf("."); + + yy_scan_string(expr_str); + int error_count = 0; + error = yyparse(&e, &error_count); + if (error > 0 || error_count > 0) { + fprintf(stderr, "error parsing \"%s\" (%d errors)\n", + expr_str, error_count); + ++*errors; + return 0; + } - yy_scan_string(expr_str); - error = yyparse(&e); - if (error > 0) { - fprintf(stderr, "error parsing \"%s\"\n", expr_str); - ++*errors; - return 0; - } + State state; + state.cookie = NULL; + state.script = expr_str; + state.errmsg = NULL; + + result = Evaluate(&state, e); + free(state.errmsg); + if (result == NULL && expected != NULL) { + fprintf(stderr, "error evaluating \"%s\"\n", expr_str); + ++*errors; + return 0; + } - result = Evaluate(NULL, e); - if (result == NULL && expected != NULL) { - fprintf(stderr, "error evaluating \"%s\"\n", expr_str); - ++*errors; - return 0; - } + if (result == NULL && expected == NULL) { + return 1; + } - if (result == NULL && expected == NULL) { - return 1; - } + if (strcmp(result, expected) != 0) { + fprintf(stderr, "evaluating \"%s\": expected \"%s\", got \"%s\"\n", + expr_str, expected, result); + ++*errors; + free(result); + return 0; + } - if (strcmp(result, expected) != 0) { - fprintf(stderr, "evaluating \"%s\": expected \"%s\", got \"%s\"\n", - expr_str, expected, result); - ++*errors; free(result); - return 0; - } - - free(result); - return 1; + return 1; } int test() { - int errors = 0; - - expect("a", "a", &errors); - expect("\"a\"", "a", &errors); - expect("\"\\x61\"", "a", &errors); - expect("# this is a comment\n" - " a\n" - " \n", - "a", &errors); - - - // sequence operator - expect("a; b; c", "c", &errors); - - // string concat operator - expect("a + b", "ab", &errors); - expect("a + \n \"b\"", "ab", &errors); - expect("a + b +\nc\n", "abc", &errors); - - // string concat function - expect("concat(a, b)", "ab", &errors); - expect("concat(a,\n \"b\")", "ab", &errors); - expect("concat(a + b,\nc,\"d\")", "abcd", &errors); - expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors); - - // logical and - expect("a && b", "b", &errors); - expect("a && \"\"", "", &errors); - expect("\"\" && b", "", &errors); - expect("\"\" && \"\"", "", &errors); - expect("\"\" && abort()", "", &errors); // test short-circuiting - expect("t && abort()", NULL, &errors); - - // logical or - expect("a || b", "a", &errors); - expect("a || \"\"", "a", &errors); - expect("\"\" || b", "b", &errors); - expect("\"\" || \"\"", "", &errors); - expect("a || abort()", "a", &errors); // test short-circuiting - expect("\"\" || abort()", NULL, &errors); - - // logical not - expect("!a", "", &errors); - expect("! \"\"", "t", &errors); - expect("!!a", "t", &errors); - - // precedence - expect("\"\" == \"\" && b", "b", &errors); - expect("a + b == ab", "t", &errors); - expect("ab == a + b", "t", &errors); - expect("a + (b == ab)", "a", &errors); - expect("(ab == a) + b", "b", &errors); - - // substring function - expect("is_substring(cad, abracadabra)", "t", &errors); - expect("is_substring(abrac, abracadabra)", "t", &errors); - expect("is_substring(dabra, abracadabra)", "t", &errors); - expect("is_substring(cad, abracxadabra)", "", &errors); - expect("is_substring(abrac, axbracadabra)", "", &errors); - expect("is_substring(dabra, abracadabrxa)", "", &errors); - - // ifelse function - expect("ifelse(t, yes, no)", "yes", &errors); - expect("ifelse(!t, yes, no)", "no", &errors); - expect("ifelse(t, yes, abort())", "yes", &errors); - expect("ifelse(!t, abort(), no)", "no", &errors); - - // if "statements" - expect("if t then yes else no endif", "yes", &errors); - expect("if \"\" then yes else no endif", "no", &errors); - expect("if \"\" then yes endif", "", &errors); - expect("if \"\"; t then yes endif", "yes", &errors); - - printf("\n"); - - return errors; + int errors = 0; + + expect("a", "a", &errors); + expect("\"a\"", "a", &errors); + expect("\"\\x61\"", "a", &errors); + expect("# this is a comment\n" + " a\n" + " \n", + "a", &errors); + + + // sequence operator + expect("a; b; c", "c", &errors); + + // string concat operator + expect("a + b", "ab", &errors); + expect("a + \n \"b\"", "ab", &errors); + expect("a + b +\nc\n", "abc", &errors); + + // string concat function + expect("concat(a, b)", "ab", &errors); + expect("concat(a,\n \"b\")", "ab", &errors); + expect("concat(a + b,\nc,\"d\")", "abcd", &errors); + expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors); + + // logical and + expect("a && b", "b", &errors); + expect("a && \"\"", "", &errors); + expect("\"\" && b", "", &errors); + expect("\"\" && \"\"", "", &errors); + expect("\"\" && abort()", "", &errors); // test short-circuiting + expect("t && abort()", NULL, &errors); + + // logical or + expect("a || b", "a", &errors); + expect("a || \"\"", "a", &errors); + expect("\"\" || b", "b", &errors); + expect("\"\" || \"\"", "", &errors); + expect("a || abort()", "a", &errors); // test short-circuiting + expect("\"\" || abort()", NULL, &errors); + + // logical not + expect("!a", "", &errors); + expect("! \"\"", "t", &errors); + expect("!!a", "t", &errors); + + // precedence + expect("\"\" == \"\" && b", "b", &errors); + expect("a + b == ab", "t", &errors); + expect("ab == a + b", "t", &errors); + expect("a + (b == ab)", "a", &errors); + expect("(ab == a) + b", "b", &errors); + + // substring function + expect("is_substring(cad, abracadabra)", "t", &errors); + expect("is_substring(abrac, abracadabra)", "t", &errors); + expect("is_substring(dabra, abracadabra)", "t", &errors); + expect("is_substring(cad, abracxadabra)", "", &errors); + expect("is_substring(abrac, axbracadabra)", "", &errors); + expect("is_substring(dabra, abracadabrxa)", "", &errors); + + // ifelse function + expect("ifelse(t, yes, no)", "yes", &errors); + expect("ifelse(!t, yes, no)", "no", &errors); + expect("ifelse(t, yes, abort())", "yes", &errors); + expect("ifelse(!t, abort(), no)", "no", &errors); + + // if "statements" + expect("if t then yes else no endif", "yes", &errors); + expect("if \"\" then yes else no endif", "no", &errors); + expect("if \"\" then yes endif", "", &errors); + expect("if \"\"; t then yes endif", "yes", &errors); + + printf("\n"); + + return errors; +} + +void ExprDump(int depth, Expr* n, char* script) { + printf("%*s", depth*2, ""); + char temp = script[n->end]; + script[n->end] = '\0'; + printf("%s %p (%d-%d) \"%s\"\n", + n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end, + script+n->start); + script[n->end] = temp; + int i; + for (i = 0; i < n->argc; ++i) { + ExprDump(depth+1, n->argv[i], script); + } } int main(int argc, char** argv) { - RegisterBuiltins(); - FinishRegistration(); - - if (argc == 1) { - return test() != 0; - } - - FILE* f = fopen(argv[1], "r"); - char buffer[8192]; - int size = fread(buffer, 1, 8191, f); - fclose(f); - buffer[size] = '\0'; - - Expr* root; - int error_count = 0; - yy_scan_bytes(buffer, size); - int error = yyparse(&root, &error_count); - printf("parse returned %d; %d errors encountered\n", error, error_count); - if (error == 0 || error_count > 0) { - char* result = Evaluate(NULL, root); - if (result == NULL) { - char* errmsg = GetError(); - printf("result was NULL, message is: %s\n", - (errmsg == NULL ? "(NULL)" : errmsg)); - ClearError(); - } else { - printf("result is [%s]\n", result); + RegisterBuiltins(); + FinishRegistration(); + + if (argc == 1) { + return test() != 0; + } + + FILE* f = fopen(argv[1], "r"); + char buffer[8192]; + int size = fread(buffer, 1, 8191, f); + fclose(f); + buffer[size] = '\0'; + + Expr* root; + int error_count = 0; + yy_scan_bytes(buffer, size); + int error = yyparse(&root, &error_count); + printf("parse returned %d; %d errors encountered\n", error, error_count); + if (error == 0 || error_count > 0) { + + ExprDump(0, root, buffer); + + State state; + state.cookie = NULL; + state.script = buffer; + state.errmsg = NULL; + + char* result = Evaluate(&state, root); + if (result == NULL) { + printf("result was NULL, message is: %s\n", + (state.errmsg == NULL ? "(NULL)" : state.errmsg)); + free(state.errmsg); + } else { + printf("result is [%s]\n", result); + } } - } - return 0; + return 0; } diff --git a/edify/parser.y b/edify/parser.y index cf163c026..3f9ade144 100644 --- a/edify/parser.y +++ b/edify/parser.y @@ -20,6 +20,7 @@ #include #include "expr.h" +#include "yydefs.h" #include "parser.h" extern int gLine; @@ -30,6 +31,8 @@ int yyparse(Expr** root, int* error_count); %} +%locations + %union { char* str; Expr* expr; @@ -68,19 +71,21 @@ expr: STRING { $$->name = $1; $$->argc = 0; $$->argv = NULL; + $$->start = @$.start; + $$->end = @$.end; } -| '(' expr ')' { $$ = $2; } -| expr ';' { $$ = $1; } -| expr ';' expr { $$ = Build(SequenceFn, 2, $1, $3); } -| error ';' expr { $$ = $3; } -| expr '+' expr { $$ = Build(ConcatFn, 2, $1, $3); } -| expr EQ expr { $$ = Build(EqualityFn, 2, $1, $3); } -| expr NE expr { $$ = Build(InequalityFn, 2, $1, $3); } -| expr AND expr { $$ = Build(LogicalAndFn, 2, $1, $3); } -| expr OR expr { $$ = Build(LogicalOrFn, 2, $1, $3); } -| '!' expr { $$ = Build(LogicalNotFn, 1, $2); } -| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, 2, $2, $4); } -| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, 3, $2, $4, $6); } +| '(' expr ')' { $$ = $2; $$->start=@$.start; $$->end=@$.end; } +| expr ';' { $$ = $1; $$->start=@1.start; $$->end=@1.end; } +| expr ';' expr { $$ = Build(SequenceFn, @$, 2, $1, $3); } +| error ';' expr { $$ = $3; $$->start=@$.start; $$->end=@$.end; } +| expr '+' expr { $$ = Build(ConcatFn, @$, 2, $1, $3); } +| expr EQ expr { $$ = Build(EqualityFn, @$, 2, $1, $3); } +| expr NE expr { $$ = Build(InequalityFn, @$, 2, $1, $3); } +| expr AND expr { $$ = Build(LogicalAndFn, @$, 2, $1, $3); } +| expr OR expr { $$ = Build(LogicalOrFn, @$, 2, $1, $3); } +| '!' expr { $$ = Build(LogicalNotFn, @$, 1, $2); } +| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, @$, 2, $2, $4); } +| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); } | STRING '(' arglist ')' { $$ = malloc(sizeof(Expr)); $$->fn = FindFunction($1); @@ -93,6 +98,8 @@ expr: STRING { $$->name = $1; $$->argc = $3.argc; $$->argv = $3.argv; + $$->start = @$.start; + $$->end = @$.end; } ; diff --git a/edify/yydefs.h b/edify/yydefs.h new file mode 100644 index 000000000..625786255 --- /dev/null +++ b/edify/yydefs.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009 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 _YYDEFS_H_ +#define _YYDEFS_H_ + +#define YYLTYPE YYLTYPE +typedef struct { + int start, end; +} YYLTYPE; + +#define YYLLOC_DEFAULT(Current, Rhs, N) \ + do { \ + if (N) { \ + (Current).start = YYRHSLOC(Rhs, 1).start; \ + (Current).end = YYRHSLOC(Rhs, N).end; \ + } else { \ + (Current).start = YYRHSLOC(Rhs, 0).start; \ + (Current).end = YYRHSLOC(Rhs, 0).end; \ + } \ + } while (0) + +#endif diff --git a/install.c b/install.c index cca940021..c2e1385b0 100644 --- a/install.c +++ b/install.c @@ -196,6 +196,9 @@ try_update_binary(const char *path, ZipArchive *zip) { // arrange to install the contents of in the // given partition on reboot. // + // ui_print + // display on the screen. + // // - the name of the package zip file. // @@ -248,6 +251,13 @@ try_update_binary(const char *path, ZipArchive *zip) { firmware_filename = strdup(filename); } } + } else if (strcmp(command, "ui_print") == 0) { + char* str = strtok(NULL, "\n"); + if (str) { + ui_print(str); + } else { + ui_print("\n"); + } } else { LOGE("unknown command [%s]\n", command); } diff --git a/updater/install.c b/updater/install.c index 2e965cee5..616cb2c8b 100644 --- a/updater/install.c +++ b/updater/install.c @@ -32,13 +32,14 @@ #include "mtdutils/mtdutils.h" #include "updater.h" -char* ErrorAbort(void* cookie, char* format, ...) { +char* ErrorAbort(State* state, char* format, ...) { char* buffer = malloc(4096); va_list v; va_start(v, format); vsnprintf(buffer, 4096, format, v); va_end(v); - SetError(buffer); + free(state->errmsg); + state->errmsg = buffer; return NULL; } @@ -47,28 +48,28 @@ char* ErrorAbort(void* cookie, char* format, ...) { // // what: type="MTD" location="" to mount a yaffs2 filesystem // type="vfat" location="/dev/block/" to mount a device -char* MountFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* MountFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 3) { - return ErrorAbort(cookie, "%s() expects 3 args, got %d", name, argc); + return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc); } char* type; char* location; char* mount_point; - if (ReadArgs(cookie, argv, 3, &type, &location, &mount_point) < 0) { + if (ReadArgs(state, argv, 3, &type, &location, &mount_point) < 0) { return NULL; } if (strlen(type) == 0) { - ErrorAbort(cookie, "type argument to %s() can't be empty", name); + ErrorAbort(state, "type argument to %s() can't be empty", name); goto done; } if (strlen(location) == 0) { - ErrorAbort(cookie, "location argument to %s() can't be empty", name); + ErrorAbort(state, "location argument to %s() can't be empty", name); goto done; } if (strlen(mount_point) == 0) { - ErrorAbort(cookie, "mount_point argument to %s() can't be empty", name); + ErrorAbort(state, "mount_point argument to %s() can't be empty", name); goto done; } @@ -109,17 +110,17 @@ done: // is_mounted(mount_point) -char* IsMountedFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 1) { - return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc); + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); } char* mount_point; - if (ReadArgs(cookie, argv, 1, &mount_point) < 0) { + if (ReadArgs(state, argv, 1, &mount_point) < 0) { return NULL; } if (strlen(mount_point) == 0) { - ErrorAbort(cookie, "mount_point argument to unmount() can't be empty"); + ErrorAbort(state, "mount_point argument to unmount() can't be empty"); goto done; } @@ -137,17 +138,17 @@ done: } -char* UnmountFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 1) { - return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc); + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); } char* mount_point; - if (ReadArgs(cookie, argv, 1, &mount_point) < 0) { + if (ReadArgs(state, argv, 1, &mount_point) < 0) { return NULL; } if (strlen(mount_point) == 0) { - ErrorAbort(cookie, "mount_point argument to unmount() can't be empty"); + ErrorAbort(state, "mount_point argument to unmount() can't be empty"); goto done; } @@ -170,23 +171,23 @@ done: // format(type, location) // // type="MTD" location=partition -char* FormatFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 2) { - return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); } char* type; char* location; - if (ReadArgs(cookie, argv, 2, &type, &location) < 0) { + if (ReadArgs(state, argv, 2, &type, &location) < 0) { return NULL; } if (strlen(type) == 0) { - ErrorAbort(cookie, "type argument to %s() can't be empty", name); + ErrorAbort(state, "type argument to %s() can't be empty", name); goto done; } if (strlen(location) == 0) { - ErrorAbort(cookie, "location argument to %s() can't be empty", name); + ErrorAbort(state, "location argument to %s() can't be empty", name); goto done; } @@ -228,11 +229,11 @@ done: } -char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { char** paths = malloc(argc * sizeof(char*)); int i; for (i = 0; i < argc; ++i) { - paths[i] = Evaluate(cookie, argv[i]); + paths[i] = Evaluate(state, argv[i]); if (paths[i] == NULL) { int j; for (j = 0; j < i; ++i) { @@ -259,20 +260,20 @@ char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) { } -char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { - return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); } char* frac_str; char* sec_str; - if (ReadArgs(cookie, argv, 2, &frac_str, &sec_str) < 0) { + if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) { return NULL; } double frac = strtod(frac_str, NULL); int sec = strtol(sec_str, NULL, 10); - UpdaterInfo* ui = (UpdaterInfo*)cookie; + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec); free(frac_str); @@ -281,16 +282,16 @@ char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) { } // package_extract_dir(package_path, destination_path) -char* PackageExtractDirFn(const char* name, void* cookie, +char* PackageExtractDirFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { - return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); } char* zip_path; char* dest_path; - if (ReadArgs(cookie, argv, 2, &zip_path, &dest_path) < 0) return NULL; + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; - ZipArchive* za = ((UpdaterInfo*)cookie)->package_zip; + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; // To create a consistent system image, never use the clock for timestamps. struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default @@ -305,18 +306,18 @@ char* PackageExtractDirFn(const char* name, void* cookie, // package_extract_file(package_path, destination_path) -char* PackageExtractFileFn(const char* name, void* cookie, +char* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { - return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); } char* zip_path; char* dest_path; - if (ReadArgs(cookie, argv, 2, &zip_path, &dest_path) < 0) return NULL; + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; bool success = false; - ZipArchive* za = ((UpdaterInfo*)cookie)->package_zip; + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; const ZipEntry* entry = mzFindZipEntry(za, zip_path); if (entry == NULL) { fprintf(stderr, "%s: no %s in package\n", name, zip_path); @@ -340,15 +341,15 @@ char* PackageExtractFileFn(const char* name, void* cookie, // symlink target src1 src2 ... -char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc == 0) { - return ErrorAbort(cookie, "%s() expects 1+ args, got %d", name, argc); + return ErrorAbort(state, "%s() expects 1+ args, got %d", name, argc); } char* target; - target = Evaluate(cookie, argv[0]); + target = Evaluate(state, argv[0]); if (target == NULL) return NULL; - char** srcs = ReadVarArgs(cookie, argc-1, argv+1); + char** srcs = ReadVarArgs(state, argc-1, argv+1); if (srcs == NULL) { free(target); return NULL; @@ -364,16 +365,16 @@ char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) { } -char* SetPermFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; bool recursive = (strcmp(name, "set_perm_recursive") == 0); int min_args = 4 + (recursive ? 1 : 0); if (argc < min_args) { - return ErrorAbort(cookie, "%s() expects %d+ args, got %d", name, argc); + return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc); } - char** args = ReadVarArgs(cookie, argc, argv); + char** args = ReadVarArgs(state, argc, argv); if (args == NULL) return NULL; char* end; @@ -381,26 +382,26 @@ char* SetPermFn(const char* name, void* cookie, int argc, Expr* argv[]) { int uid = strtoul(args[0], &end, 0); if (*end != '\0' || args[0][0] == 0) { - ErrorAbort(cookie, "%s: \"%s\" not a valid uid", name, args[0]); + ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]); goto done; } int gid = strtoul(args[1], &end, 0); if (*end != '\0' || args[1][0] == 0) { - ErrorAbort(cookie, "%s: \"%s\" not a valid gid", name, args[1]); + ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]); goto done; } if (recursive) { int dir_mode = strtoul(args[2], &end, 0); if (*end != '\0' || args[2][0] == 0) { - ErrorAbort(cookie, "%s: \"%s\" not a valid dirmode", name, args[2]); + ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]); goto done; } int file_mode = strtoul(args[3], &end, 0); if (*end != '\0' || args[3][0] == 0) { - ErrorAbort(cookie, "%s: \"%s\" not a valid filemode", + ErrorAbort(state, "%s: \"%s\" not a valid filemode", name, args[3]); goto done; } @@ -411,7 +412,7 @@ char* SetPermFn(const char* name, void* cookie, int argc, Expr* argv[]) { } else { int mode = strtoul(args[2], &end, 0); if (*end != '\0' || args[2][0] == 0) { - ErrorAbort(cookie, "%s: \"%s\" not a valid mode", name, args[2]); + ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]); goto done; } @@ -432,12 +433,12 @@ done: } -char* GetPropFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 1) { - return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc); + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); } char* key; - key = Evaluate(cookie, argv[0]); + key = Evaluate(state, argv[0]); if (key == NULL) return NULL; char value[PROPERTY_VALUE_MAX]; @@ -457,21 +458,21 @@ static bool write_raw_image_cb(const unsigned char* data, } // write_raw_image(file, partition) -char* WriteRawImageFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; char* partition; char* filename; - if (ReadArgs(cookie, argv, 2, &filename, &partition) < 0) { + if (ReadArgs(state, argv, 2, &filename, &partition) < 0) { return NULL; } if (strlen(partition) == 0) { - ErrorAbort(cookie, "partition argument to %s can't be empty", name); + ErrorAbort(state, "partition argument to %s can't be empty", name); goto done; } if (strlen(filename) == 0) { - ErrorAbort(cookie, "file argument to %s can't be empty", name); + ErrorAbort(state, "file argument to %s can't be empty", name); goto done; } @@ -515,6 +516,13 @@ char* WriteRawImageFn(const char* name, void* cookie, int argc, Expr* argv[]) { free(buffer); fclose(f); + 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 from %s\n", success ? "wrote" : "failed to write", partition, filename); @@ -532,26 +540,26 @@ done: // file is not used until after updater exits // // TODO: this should live in some HTC-specific library -char* WriteFirmwareImageFn(const char* name, void* cookie, +char* WriteFirmwareImageFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; char* partition; char* filename; - if (ReadArgs(cookie, argv, 2, &filename, &partition) < 0) { + if (ReadArgs(state, argv, 2, &filename, &partition) < 0) { return NULL; } if (strlen(partition) == 0) { - ErrorAbort(cookie, "partition argument to %s can't be empty", name); + ErrorAbort(state, "partition argument to %s can't be empty", name); goto done; } if (strlen(filename) == 0) { - ErrorAbort(cookie, "file argument to %s can't be empty", name); + ErrorAbort(state, "file argument to %s can't be empty", name); goto done; } - FILE* cmd = ((UpdaterInfo*)cookie)->cmd_pipe; + FILE* cmd = ((UpdaterInfo*)(state->cookie))->cmd_pipe; fprintf(cmd, "firmware %s %s\n", partition, filename); printf("will write %s firmware from %s\n", partition, filename); @@ -569,7 +577,7 @@ extern int applypatch(int argc, char** argv); // apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...) // apply_patch_check(file, sha1, ...) // apply_patch_space(bytes) -char* ApplyPatchFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { printf("in applypatchfn (%s)\n", name); char* prepend = NULL; @@ -579,7 +587,7 @@ char* ApplyPatchFn(const char* name, void* cookie, int argc, Expr* argv[]) { prepend = "-s"; } - char** args = ReadVarArgs(cookie, argc, argv); + char** args = ReadVarArgs(state, argc, argv); if (args == NULL) return NULL; // insert the "program name" argv[0] and a copy of the "prepend" @@ -610,10 +618,42 @@ char* ApplyPatchFn(const char* name, void* cookie, int argc, Expr* argv[]) { switch (result) { case 0: return strdup("t"); case 1: return strdup(""); - default: return ErrorAbort(cookie, "applypatch couldn't parse args"); + default: return ErrorAbort(state, "applypatch couldn't parse args"); } } +char* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) { + char** args = ReadVarArgs(state, argc, argv); + if (args == NULL) { + return NULL; + } + + int size = 0; + int i; + for (i = 0; i < argc; ++i) { + size += strlen(args[i]); + } + char* buffer = malloc(size+1); + size = 0; + for (i = 0; i < argc; ++i) { + strcpy(buffer+size, args[i]); + size += strlen(args[i]); + free(args[i]); + } + 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"); + + return buffer; +} + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); @@ -636,4 +676,6 @@ void RegisterInstallFunctions() { RegisterFunction("apply_patch", ApplyPatchFn); RegisterFunction("apply_patch_check", ApplyPatchFn); RegisterFunction("apply_patch_space", ApplyPatchFn); + + RegisterFunction("ui_print", UIPrintFn); } diff --git a/updater/updater.c b/updater/updater.c index 09776256b..5a2ed2ccf 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -94,12 +94,26 @@ int main(int argc, char** argv) { updater_info.cmd_pipe = cmd_pipe; updater_info.package_zip = &za; - char* result = Evaluate(&updater_info, root); + State state; + state.cookie = &updater_info; + state.script = script; + state.errmsg = NULL; + + char* result = Evaluate(&state, root); if (result == NULL) { - const char* errmsg = GetError(); - fprintf(stderr, "script aborted with error: %s\n", - errmsg == NULL ? "(none)" : errmsg); - ClearError(); + if (state.errmsg == NULL) { + fprintf(stderr, "script aborted (no error message)\n"); + fprintf(cmd_pipe, "ui_print script aborted (no error message)\n"); + } else { + fprintf(stderr, "script aborted: %s\n", state.errmsg); + char* line = strtok(state.errmsg, "\n"); + while (line) { + fprintf(cmd_pipe, "ui_print %s\n", line); + line = strtok(NULL, "\n"); + } + fprintf(cmd_pipe, "ui_print\n"); + } + free(state.errmsg); return 7; } else { fprintf(stderr, "script result was [%s]\n", result); @@ -107,6 +121,7 @@ int main(int argc, char** argv) { } mzCloseZipArchive(&za); + free(script); return 0; } -- cgit v1.2.3 From e3da02e7bcfd85c543419e7590a3c86f64d8cc8a Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 12 Jun 2009 16:13:52 -0700 Subject: add less_than_int, greater_than_int to edify Add functions less_than_int() and greater_than_int() that interpret their args as ints and do the comparison. ("<" and ">" operators, if implemented, should do string comparison.) This lets us do the build time check currently done by the check_prereq binary. --- edify/expr.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ edify/main.c | 10 ++++++++++ 2 files changed, 63 insertions(+) diff --git a/edify/expr.c b/edify/expr.c index 406c67ea6..f1c555552 100644 --- a/edify/expr.c +++ b/edify/expr.c @@ -72,6 +72,8 @@ char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) { char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2 && argc != 3) { + free(state->errmsg); + state->errmsg = strdup("ifelse expects 2 or 3 arguments"); return NULL; } char* cond = Evaluate(state, argv[0]); @@ -244,6 +246,54 @@ char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) { return Evaluate(state, argv[1]); } +char* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + free(state->errmsg); + state->errmsg = strdup("less_than_int expects 2 arguments"); + return NULL; + } + + char* left; + char* right; + if (ReadArgs(state, argv, 2, &left, &right) < 0) return NULL; + + bool result = false; + char* end; + + long l_int = strtol(left, &end, 10); + if (left[0] == '\0' || *end != '\0') { + fprintf(stderr, "[%s] is not an int\n", left); + goto done; + } + + long r_int = strtol(right, &end, 10); + if (right[0] == '\0' || *end != '\0') { + fprintf(stderr, "[%s] is not an int\n", right); + goto done; + } + + result = l_int < r_int; + + done: + free(left); + free(right); + return strdup(result ? "t" : ""); +} + +char* GreaterThanIntFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + free(state->errmsg); + state->errmsg = strdup("greater_than_int expects 2 arguments"); + return NULL; + } + + Expr* temp[2]; + temp[0] = argv[1]; + temp[1] = argv[0]; + + return LessThanIntFn(name, state, 2, temp); +} + char* Literal(const char* name, State* state, int argc, Expr* argv[]) { return strdup(name); } @@ -313,6 +363,9 @@ void RegisterBuiltins() { RegisterFunction("is_substring", SubstringFn); RegisterFunction("stdout", StdoutFn); RegisterFunction("sleep", SleepFn); + + RegisterFunction("less_than_int", LessThanIntFn); + RegisterFunction("greater_than_int", GreaterThanIntFn); } diff --git a/edify/main.c b/edify/main.c index 03eefc69e..0e3610847 100644 --- a/edify/main.c +++ b/edify/main.c @@ -143,6 +143,16 @@ int test() { expect("if \"\" then yes endif", "", &errors); expect("if \"\"; t then yes endif", "yes", &errors); + // numeric comparisons + expect("less_than_int(3, 14)", "t", &errors); + expect("less_than_int(14, 3)", "", &errors); + expect("less_than_int(x, 3)", "", &errors); + expect("less_than_int(3, x)", "", &errors); + expect("greater_than_int(3, 14)", "", &errors); + expect("greater_than_int(14, 3)", "t", &errors); + expect("greater_than_int(x, 3)", "", &errors); + expect("greater_than_int(3, x)", "", &errors); + printf("\n"); return errors; -- cgit v1.2.3 From fb2e3af3f915c0e3f2b4b027ef26777267ad46dc Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 17 Jun 2009 17:29:40 -0700 Subject: let the "firmware" command take the file straight from the package To do a firmware-install-on-reboot, the update binary tells recovery what file to install before rebooting. Let this file be specified as "PACKAGE:" to indicate taking the file out of the OTA package, avoiding an extra copy to /tmp. Bump the API version number to reflect this change. --- Android.mk | 3 +++ common.h | 3 +++ install.c | 66 ++++++++++++++++++++++++++++++++++++------------------- recovery.c | 3 ++- updater/updater.c | 7 +++--- 5 files changed, 56 insertions(+), 26 deletions(-) diff --git a/Android.mk b/Android.mk index 6ab498e7e..0367fef70 100644 --- a/Android.mk +++ b/Android.mk @@ -22,6 +22,9 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true +RECOVERY_API_VERSION := 2 +LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) + # This binary is in the recovery ramdisk, which is otherwise a copy of root. # It gets copied there in config/Makefile. LOCAL_MODULE_TAGS suppresses # a (redundant) copy of the binary in /system/bin for user builds. diff --git a/common.h b/common.h index 98ce86894..67611592a 100644 --- a/common.h +++ b/common.h @@ -90,4 +90,7 @@ void ui_reset_progress(); #define LOGD(...) do {} while (0) #endif +#define STRINGIFY(x) #x +#define EXPAND(x) STRINGIFY(x) + #endif // RECOVERY_COMMON_H diff --git a/install.c b/install.c index c2e1385b0..cbb35805c 100644 --- a/install.c +++ b/install.c @@ -117,34 +117,54 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) // The update binary ask us to install a firmware file on reboot. Set // that up. Takes ownership of type and filename. static int -handle_firmware_update(char* type, char* filename) { - struct stat st_data; - if (stat(filename, &st_data) < 0) { - LOGE("Error stat'ing %s: %s\n", filename, strerror(errno)); - return INSTALL_ERROR; +handle_firmware_update(char* type, char* filename, ZipArchive* zip) { + unsigned int data_size; + const ZipEntry* entry = NULL; + + if (strncmp(filename, "PACKAGE:", 8) == 0) { + entry = mzFindZipEntry(zip, filename+8); + if (entry == NULL) { + LOGE("Failed to find \"%s\" in package", filename+8); + return INSTALL_ERROR; + } + data_size = entry->uncompLen; + } else { + struct stat st_data; + if (stat(filename, &st_data) < 0) { + LOGE("Error stat'ing %s: %s\n", filename, strerror(errno)); + return INSTALL_ERROR; + } + data_size = st_data.st_size; } LOGI("type is %s; size is %d; file is %s\n", - type, (int)st_data.st_size, filename); + type, data_size, filename); - char* data = malloc(st_data.st_size); + char* data = malloc(data_size); if (data == NULL) { - LOGE("Can't allocate %d bytes for firmware data\n", st_data.st_size); + LOGI("Can't allocate %d bytes for firmware data\n", data_size); return INSTALL_ERROR; } - FILE* f = fopen(filename, "rb"); - if (f == NULL) { - LOGE("Failed to open %s: %s\n", filename, strerror(errno)); - return INSTALL_ERROR; - } - if (fread(data, 1, st_data.st_size, f) != st_data.st_size) { - LOGE("Failed to read firmware data: %s\n", strerror(errno)); - return INSTALL_ERROR; + if (entry) { + if (mzReadZipEntry(zip, entry, data, data_size) == false) { + LOGE("Failed to read \"%s\" from package", filename+8); + return INSTALL_ERROR; + } + } else { + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + LOGE("Failed to open %s: %s\n", filename, strerror(errno)); + return INSTALL_ERROR; + } + if (fread(data, 1, data_size, f) != data_size) { + LOGE("Failed to read firmware data: %s\n", strerror(errno)); + return INSTALL_ERROR; + } + fclose(f); } - fclose(f); - if (remember_firmware_update(type, data, st_data.st_size)) { + if (remember_firmware_update(type, data, data_size)) { LOGE("Can't store %s image\n", type); free(data); return INSTALL_ERROR; @@ -184,7 +204,7 @@ try_update_binary(const char *path, ZipArchive *zip) { // When executing the update binary contained in the package, the // arguments passed are: // - // - the version number for this interface (currently 1) + // - the version number for this interface // // - an fd to which the program can write in order to update the // progress bar. The program can write single-line commands: @@ -194,7 +214,9 @@ try_update_binary(const char *path, ZipArchive *zip) { // // firmware <"hboot"|"radio"> // arrange to install the contents of in the - // given partition on reboot. + // given partition on reboot. (API v2: may + // start with "PACKAGE:" to indicate taking a file from + // the OTA package.) // // ui_print // display on the screen. @@ -204,7 +226,7 @@ try_update_binary(const char *path, ZipArchive *zip) { char** args = malloc(sizeof(char*) * 5); args[0] = binary; - args[1] = "1"; + args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk args[2] = malloc(10); sprintf(args[2], "%d", pipefd[1]); args[3] = (char*)path; @@ -272,7 +294,7 @@ try_update_binary(const char *path, ZipArchive *zip) { } if (firmware_type != NULL) { - return handle_firmware_update(firmware_type, firmware_filename); + return handle_firmware_update(firmware_type, firmware_filename, zip); } else { return INSTALL_SUCCESS; } diff --git a/recovery.c b/recovery.c index 188d4de69..5ccd38f2c 100644 --- a/recovery.c +++ b/recovery.c @@ -287,7 +287,8 @@ erase_root(const char *root) static void prompt_and_wait() { - char* headers[] = { "Android system recovery utility", + char* headers[] = { "Android system recovery <" + EXPAND(RECOVERY_API_VERSION) ">", "", "Use trackball to highlight;", "click to select.", diff --git a/updater/updater.c b/updater/updater.c index 5a2ed2ccf..31d93ae96 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -34,9 +34,10 @@ int main(int argc, char** argv) { } char* version = argv[1]; - if (version[0] != '1' || version[1] != '\0') { - fprintf(stderr, "wrong updater binary API; expected 1, got %s\n", - version); + if ((version[0] != '1' && version[0] != '2') || version[1] != '\0') { + // We support version "1" or "2". + fprintf(stderr, "wrong updater binary API; expected 1 or 2, got %s\n", + argv[1]); return 2; } -- cgit v1.2.3 From 47cace98369f60df2351a65801c8065bb7f9dbf0 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 18 Jun 2009 10:11:50 -0700 Subject: add file_getprop() to updater Add a function to read a property from a ".prop"-formatted file (key=value pairs, one per line, ignore # comment lines and blank lines). Move ErrorAbort to the core of edify; it's not specific to updater now that errors aren't stored in the app cookie. --- edify/expr.c | 13 +++++++ edify/expr.h | 4 ++ updater/install.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 117 insertions(+), 11 deletions(-) diff --git a/edify/expr.c b/edify/expr.c index f1c555552..72e5100f3 100644 --- a/edify/expr.c +++ b/edify/expr.c @@ -417,3 +417,16 @@ char** ReadVarArgs(State* state, int argc, Expr* argv[]) { } return args; } + +// Use printf-style arguments to compose an error message to put into +// *state. Returns NULL. +char* ErrorAbort(State* state, char* format, ...) { + char* buffer = malloc(4096); + va_list v; + va_start(v, format); + vsnprintf(buffer, 4096, format, v); + va_end(v); + free(state->errmsg); + state->errmsg = buffer; + return NULL; +} diff --git a/edify/expr.h b/edify/expr.h index 671b499b5..d2e739201 100644 --- a/edify/expr.h +++ b/edify/expr.h @@ -118,5 +118,9 @@ int ReadArgs(State* state, Expr* argv[], int count, ...); // strings it contains. char** ReadVarArgs(State* state, int argc, Expr* argv[]); +// Use printf-style arguments to compose an error message to put into +// *state. Returns NULL. +char* ErrorAbort(State* state, char* format, ...); + #endif // _EXPRESSION_H diff --git a/updater/install.c b/updater/install.c index 616cb2c8b..0bd09399c 100644 --- a/updater/install.c +++ b/updater/install.c @@ -32,17 +32,6 @@ #include "mtdutils/mtdutils.h" #include "updater.h" -char* ErrorAbort(State* state, char* format, ...) { - char* buffer = malloc(4096); - va_list v; - va_start(v, format); - vsnprintf(buffer, 4096, format, v); - va_end(v); - free(state->errmsg); - state->errmsg = buffer; - return NULL; -} - // mount(type, location, mount_point) // @@ -449,6 +438,105 @@ char* 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). +char* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + char* buffer = NULL; + char* filename; + char* key; + if (ReadArgs(state, argv, 2, &filename, &key) < 0) { + return NULL; + } + + struct stat st; + if (stat(filename, &st) < 0) { + ErrorAbort(state, "%s: failed to stat \"%s\": %s", + name, filename, strerror(errno)); + goto done; + } + +#define MAX_FILE_GETPROP_SIZE 65536 + + if (st.st_size > MAX_FILE_GETPROP_SIZE) { + ErrorAbort(state, "%s too large for %s (max %d)", + filename, name, MAX_FILE_GETPROP_SIZE); + goto done; + } + + buffer = malloc(st.st_size+1); + if (buffer == NULL) { + ErrorAbort(state, "%s: failed to alloc %d bytes", name, st.st_size+1); + goto done; + } + + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + ErrorAbort(state, "%s: failed to open %s: %s", + name, filename, strerror(errno)); + goto done; + } + + if (fread(buffer, 1, st.st_size, f) != st.st_size) { + ErrorAbort(state, "%s: failed to read %d bytes from %s", + name, st.st_size+1, filename); + fclose(f); + goto done; + } + buffer[st.st_size] = '\0'; + + fclose(f); + + char* line = strtok(buffer, "\n"); + do { + // skip whitespace at start of line + while (*line && isspace(*line)) ++line; + + // comment or blank line: skip to next line + if (*line == '\0' || *line == '#') continue; + + char* equal = strchr(line, '='); + if (equal == NULL) { + ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?", + name, line, filename); + goto done; + } + + // trim whitespace between key and '=' + char* key_end = equal-1; + while (key_end > line && isspace(*key_end)) --key_end; + key_end[1] = '\0'; + + // not the key we're looking for + if (strcmp(key, line) != 0) continue; + + // skip whitespace after the '=' to the start of the value + char* val_start = equal+1; + while(*val_start && isspace(*val_start)) ++val_start; + + // trim trailing whitespace + char* val_end = val_start + strlen(val_start)-1; + while (val_end > val_start && isspace(*val_end)) --val_end; + val_end[1] = '\0'; + + result = strdup(val_start); + break; + + } while ((line = strtok(NULL, "\n"))); + + if (result == NULL) result = strdup(""); + + done: + free(filename); + free(key); + free(buffer); + return result; +} + + static bool write_raw_image_cb(const unsigned char* data, int data_len, void* ctx) { int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len); @@ -670,6 +758,7 @@ void RegisterInstallFunctions() { RegisterFunction("set_perm_recursive", SetPermFn); RegisterFunction("getprop", GetPropFn); + RegisterFunction("file_getprop", FileGetPropFn); RegisterFunction("write_raw_image", WriteRawImageFn); RegisterFunction("write_firmware_image", WriteFirmwareImageFn); -- cgit v1.2.3 From fbf3c10e45c20f8fe6bd1ac49ffe220035b9c454 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 24 Jun 2009 09:36:20 -0700 Subject: improve updater progress bar Let recovery accept set_progress commands to control progress over the 'current segment' of the bar. Add a set_progress() builtin to the updater binary. --- install.c | 14 +++++++++++++- updater/install.c | 24 +++++++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/install.c b/install.c index cbb35805c..ab19478cf 100644 --- a/install.c +++ b/install.c @@ -210,7 +210,15 @@ try_update_binary(const char *path, ZipArchive *zip) { // progress bar. The program can write single-line commands: // // progress - // fill up of the progress bar over seconds. + // fill up the next part of of the progress bar + // over seconds. If is zero, use + // set_progress commands to manually control the + // progress of this segment of the bar + // + // set_progress + // should be between 0.0 and 1.0; sets the + // progress bar within the segment defined by the most + // recent progress command. // // firmware <"hboot"|"radio"> // arrange to install the contents of in the @@ -261,6 +269,10 @@ try_update_binary(const char *path, ZipArchive *zip) { ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds); + } else if (strcmp(command, "set_progress") == 0) { + char* fraction_s = strtok(NULL, " \n"); + float fraction = strtof(fraction_s, NULL); + ui_set_progress(fraction); } else if (strcmp(command, "firmware") == 0) { char* type = strtok(NULL, " \n"); char* filename = strtok(NULL, " \n"); diff --git a/updater/install.c b/updater/install.c index 0bd09399c..e1f3c9acf 100644 --- a/updater/install.c +++ b/updater/install.c @@ -14,9 +14,10 @@ * limitations under the License. */ -#include +#include #include #include +#include #include #include #include @@ -265,9 +266,25 @@ char* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) { UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec); - free(frac_str); free(sec_str); - return strdup(""); + return frac_str; +} + +char* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 1) { + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + } + char* frac_str; + if (ReadArgs(state, argv, 1, &frac_str) < 0) { + return NULL; + } + + double frac = strtod(frac_str, NULL); + + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + fprintf(ui->cmd_pipe, "set_progress %f\n", frac); + + return frac_str; } // package_extract_dir(package_path, destination_path) @@ -749,6 +766,7 @@ void RegisterInstallFunctions() { RegisterFunction("unmount", UnmountFn); RegisterFunction("format", FormatFn); RegisterFunction("show_progress", ShowProgressFn); + RegisterFunction("set_progress", SetProgressFn); RegisterFunction("delete", DeleteFn); RegisterFunction("delete_recursive", DeleteFn); RegisterFunction("package_extract_dir", PackageExtractDirFn); -- cgit v1.2.3 From 0bbfe3d901885c1f0ab006e8d4cc1029c44a7376 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 25 Jun 2009 13:37:31 -0700 Subject: fix off-by-one error in set_perm() We were inadvertently skipping over the first filename in the list of arguments. --- updater/install.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updater/install.c b/updater/install.c index e1f3c9acf..c4f5e0341 100644 --- a/updater/install.c +++ b/updater/install.c @@ -422,7 +422,7 @@ char* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } - for (i = 4; i < argc; ++i) { + for (i = 3; i < argc; ++i) { chown(args[i], uid, gid); chmod(args[i], mode); } -- cgit v1.2.3 From ad3db099d5ee17b1b46fee3131b9561b73b36703 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 26 Jun 2009 13:38:55 -0700 Subject: remove updater from the user system image updater (which is only needed in OTA packages) is getting included in /system/bin, where it just takes up (quite a bit of) space. Use the hack of including it only in eng builds so it's not there for user builds. --- updater/Android.mk | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/updater/Android.mk b/updater/Android.mk index 1159d7bb8..897b9d74c 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -7,10 +7,15 @@ updater_src_files := \ updater.c # -# Build the device-side library +# Build a statically-linked binary to include in OTA packages # include $(CLEAR_VARS) +# Build only in eng, so we don't end up with a copy of this in /system +# on user builds. (TODO: find a better way to build device binaries +# needed only for OTA packages.) +LOCAL_MODULE_TAGS := eng + LOCAL_SRC_FILES := $(updater_src_files) LOCAL_STATIC_LIBRARIES := libapplypatch libedify libmtdutils libminzip libz -- cgit v1.2.3 From bec02d57fb85cc7dd0196a54b0e9530e306623ac Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 1 Jul 2009 12:09:29 -0700 Subject: skip over all-zero blocks when reading MTD partition We fail to detect certain bad blocks (marked in the factory as bad, I think?) when reading mtd partitions. These come back as a block of all zeros. Since it's fairly unlikely a legitimate boot or recovery block will contain 128k of zeros, change mtdutils to skip over such blocks. Arve says https://review.source.android.com/10535 may be a long-term fix for this, but he isn't yet sure. --- mtdutils/mtdutils.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c index 2b0106f14..fc067669b 100644 --- a/mtdutils/mtdutils.c +++ b/mtdutils/mtdutils.c @@ -297,7 +297,14 @@ static int read_block(const MtdPartition *partition, int fd, char *data) after.corrected - before.corrected, after.failed - before.failed, pos); } else { - return 0; // Success! + int i; + for (i = 0; i < size; ++i) { + if (data[i] != 0) { + return 0; // Success! + } + } + fprintf(stderr, "mtd: read all-zero block at 0x%08lx; skipping\n", + pos); } pos += partition->erase_size; @@ -326,6 +333,10 @@ ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len) read += ctx->partition->erase_size; } + if (read >= len) { + return read; + } + // Read the next block into the buffer if (ctx->consumed == ctx->partition->erase_size && read < (int) len) { if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1; -- cgit v1.2.3