From bac7fba02763ae5e78e8e4ba0bea727330ad953e Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 10 Apr 2013 11:32:17 -0700 Subject: [PATCH 01/20] verifier: update to support certificates using SHA-256 Change-Id: Ifd5a29d459acf101311fa1c220f728c3d0ac2e4e --- applypatch/applypatch.c | 2 +- install.cpp | 2 +- testdata/otasigned_f4_sha256.zip | Bin 0 -> 5319 bytes testdata/otasigned_sha256.zip | Bin 0 -> 5326 bytes testdata/test_f4_sha256.x509.pem | 25 +++++ testdata/testkey.pk8 | Bin 0 -> 1217 bytes testdata/testkey.x509.pem | 27 ++++++ testdata/testkey_sha256.x509.pem | 27 ++++++ updater/install.c | 2 +- verifier.cpp | 80 ++++++++++++---- verifier.h | 9 +- verifier_test.cpp | 151 ++++++++++++++++--------------- verifier_test.sh | 38 ++++---- 13 files changed, 254 insertions(+), 109 deletions(-) create mode 100644 testdata/otasigned_f4_sha256.zip create mode 100644 testdata/otasigned_sha256.zip create mode 100644 testdata/test_f4_sha256.x509.pem create mode 100644 testdata/testkey.pk8 create mode 100644 testdata/testkey.x509.pem create mode 100644 testdata/testkey_sha256.x509.pem diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 69f8633a..259fa581 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -101,7 +101,7 @@ int LoadFileContents(const char* filename, FileContents* file, } } - SHA(file->data, file->size, file->sha1); + SHA_hash(file->data, file->size, file->sha1); return 0; } diff --git a/install.cpp b/install.cpp index 0f3298f1..0cb5cc7d 100644 --- a/install.cpp +++ b/install.cpp @@ -190,7 +190,7 @@ really_install_package(const char *path, int* wipe_cache) ui->Print("Opening update package...\n"); int numKeys; - RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); + Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); if (loadedKeys == NULL) { LOGE("Failed to load keys\n"); return INSTALL_CORRUPT; diff --git a/testdata/otasigned_f4_sha256.zip b/testdata/otasigned_f4_sha256.zip new file mode 100644 index 0000000000000000000000000000000000000000..3af408c402bb636a9b75d67b456d51a32b342fb0 GIT binary patch literal 5319 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 Rv;E^@Rf%uFL}DNK{{V--V{-ri literal 0 HcmV?d00001 diff --git a/testdata/test_f4_sha256.x509.pem b/testdata/test_f4_sha256.x509.pem new file mode 100644 index 00000000..9d5376b4 --- /dev/null +++ b/testdata/test_f4_sha256.x509.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIJAKhkCO1dDYMaMA0GCSqGSIb3DQEBCwUAMG8xCzAJBgNV +BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBW +aWV3MQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMT +B1Rlc3QxMjMwHhcNMTMwNDEwMTcyMzUyWhcNMTMwNTEwMTcyMzUyWjBvMQswCQYD +VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g +VmlldzEPMA0GA1UEChMGR29vZ2xlMRAwDgYDVQQLEwdBbmRyb2lkMRAwDgYDVQQD +EwdUZXN0MTIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu8WwMN9x +4Mz7YgkG2qy9g8/kl5ZoYrUM0ApHhaITAcL7RXLZaNipCf0w/YjYTQgj+75MK30x +TsnPeWNOEwA62gkHrZyyWfxBRO6kBYuIuI4roGDBJOmKQ1OEaDeIRKu7q5V8v3Cs +0wQDAQWTbhpxBZr9UYFgJUg8XWBfPrGJLVwsoiy4xrMhoTlNZKHfwOMMqVtSHkZX +qydYrcIzyjh+TO0e/xSNQ8MMRRbtqWgCHN6Rzhog3IHZu0RaPoukariopjXM/s0V +gTm3rHDHCOpna2pNblyiFlvbkoCs769mtNmx/yrDShO30jg/xaG8RypKDvTChzOT +oWW/XQ5VEXjbHwIDAQABo4HUMIHRMB0GA1UdDgQWBBRlT2dEZJY1tmUM8mZ0xnhS +GdD9TTCBoQYDVR0jBIGZMIGWgBRlT2dEZJY1tmUM8mZ0xnhSGdD9TaFzpHEwbzEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50 +YWluIFZpZXcxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMHQW5kcm9pZDEQMA4G +A1UEAxMHVGVzdDEyM4IJAKhkCO1dDYMaMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN +AQELBQADggEBAKWWQ9S0V9wWjrMJe8exj1gklwD1Ysi0vi+h2tfixahelrpsNkWi +EFjoUSHEkW9ThLmtui646uAlwSiWtSn1XkGGmIJ3s+gmAFUcMc0CaK0dgoq/M9zn +fQ0Vkzc1tK4MLsf+CbPDywPycb6+T3dBkerbWn9GUpjGl1ANWlciXZZ3657m61sL +HhwUOBxbZZ6sYP4ed2SVCf45GgMyJ0VoUg5yI2JzPAgOkGfeEIPVXE1M94edJY4G +8eHYvXovJZwXvKFI+ZyS0KBPx8cpfw89RB9qmkxqNBIm8qWb3qBiuBEIPj+NF/7w +sC/Fv8NNXkVquy0xa0qdyJBABzWE18zGcXs= +-----END CERTIFICATE----- diff --git a/testdata/testkey.pk8 b/testdata/testkey.pk8 new file mode 100644 index 0000000000000000000000000000000000000000..586c1bd5cf96f9358f36b37ea98fef93f4d0a8e3 GIT binary patch literal 1217 zcmV;y1U~yPf&{$+0RS)!1_>&LNQUrr!ay9qXGc{0)hbn0M?Tk1m4CA zB(d$sV&K`>B|bjQZ0jB3{|1bx=)9=SYtwjcrwXTR<_^0LnKfDe**XXo? z!`w>qP$CUb2nRCYfR`8UY6UY{=BAYFEVJ^2prmHVdX(5ZF$p?nH!d$^62NY4Lh)D_ z-~I!E%nZ~-+HBp!xFlE^NCC4YWD`%n-k8ZKcSzgyXKGXpfmF!5$_KgPM^!Ts#A}-O zZ{F<8j%nw}qR@_bCLokQnR0zR1pacF9hu*ot~j8w7k{H2TWbF49|8db0)hbn0FMm2 zSd@eZZOil8LF+r^ZYh5=o$?dxssvrd@O{PJN8(UP#IeNlLarW*N_QWU=dJbtwUa9MNB6mRRfiD@u-F4570O|hs88oP`H<{-WtCyW#0(F+HjcFBTNtPVs zqOAxo7-A$eO)H3r6sgRe?MH7gF5FJ$Tk3bck@E0NKQy`twmC#7`?TOyTgJ~$><(c= zegFe}?aLXyS&y+x7cyW?K{hgS`ZOu%e`OsJB7tT%e$vUP84FySkKHZd#R8E)%>sde z0Gxnk4`^q&#Rvr|X}T^DSmg_(X~iRQqj#>*`b>&xVy7{s?@%B>l*r?UT zWofQjN z+qr0%Y;#)55J7EtGjdx74aSIRNZLvuA3cfg=kcW}3-?Ohe|UTbd3%Dz{Ubm)=h@$h z^0YwLsa02~o`+_B=3mQ)WXwP_fX>WJrp<9hszYeD7Xa?hso~qjW(SiJU+Bp}E&;YH zGXjBtV=ArL8r`H~*_EbpVp|2xsYW66=~;8`kD4v(aiPJi8`ByM)}o1ZhZ|~p>uyzH zw4p3uqoir?dUseWHOKf>xwmY}&%k#`MgFNpFR|mPj0O9er&4Gc#l%UD4Z+(RrHVV$ f`+data, args[0]->size, digest); + SHA_hash(args[0]->data, args[0]->size, digest); FreeValue(args[0]); if (argc == 1) { diff --git a/verifier.cpp b/verifier.cpp index 5f4c981e..782a8386 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -20,6 +20,7 @@ #include "mincrypt/rsa.h" #include "mincrypt/sha.h" +#include "mincrypt/sha256.h" #include #include @@ -34,7 +35,7 @@ extern RecoveryUI* ui; // Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered // or no key matches the signature). -int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys) { +int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys) { ui->SetProgress(0.0); FILE* f = fopen(path, "rb"); @@ -68,6 +69,7 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey } if (footer[2] != 0xff || footer[3] != 0xff) { + LOGE("footer is wrong\n"); fclose(f); return VERIFY_FAILURE; } @@ -139,8 +141,19 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey #define BUFFER_SIZE 4096 - SHA_CTX ctx; - SHA_init(&ctx); + bool need_sha1 = false; + bool need_sha256 = false; + for (i = 0; i < numKeys; ++i) { + switch (pKeys[i].hash_len) { + case SHA_DIGEST_SIZE: need_sha1 = true; break; + case SHA256_DIGEST_SIZE: need_sha256 = true; break; + } + } + + SHA_CTX sha1_ctx; + SHA256_CTX sha256_ctx; + SHA_init(&sha1_ctx); + SHA256_init(&sha256_ctx); unsigned char* buffer = (unsigned char*)malloc(BUFFER_SIZE); if (buffer == NULL) { LOGE("failed to alloc memory for sha1 buffer\n"); @@ -159,7 +172,8 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey fclose(f); return VERIFY_FAILURE; } - SHA_update(&ctx, buffer, size); + if (need_sha1) SHA_update(&sha1_ctx, buffer, size); + if (need_sha256) SHA256_update(&sha256_ctx, buffer, size); so_far += size; double f = so_far / (double)signed_len; if (f > frac + 0.02 || size == so_far) { @@ -170,12 +184,21 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey fclose(f); free(buffer); - const uint8_t* sha1 = SHA_final(&ctx); + const uint8_t* sha1 = SHA_final(&sha1_ctx); + const uint8_t* sha256 = SHA256_final(&sha256_ctx); + for (i = 0; i < numKeys; ++i) { + const uint8_t* hash; + switch (pKeys[i].hash_len) { + case SHA_DIGEST_SIZE: hash = sha1; break; + case SHA256_DIGEST_SIZE: hash = sha256; break; + default: continue; + } + // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that // the signing tool appends after the signature itself. - if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES, - RSANUMBYTES, sha1)) { + if (RSA_verify(pKeys[i].public_key, eocd + eocd_size - 6 - RSANUMBYTES, + RSANUMBYTES, hash, pKeys[i].hash_len)) { LOGI("whole-file signature verified against key %d\n", i); free(eocd); return VERIFY_SUCCESS; @@ -207,10 +230,19 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey // The file may contain multiple keys in this format, separated by // commas. The last key must not be followed by a comma. // +// A Certificate is a pair of an RSAPublicKey and a particular hash +// (we support SHA-1 and SHA-256; we store the hash length to signify +// which is being used). The hash used is implied by the version number. +// +// 1: 2048-bit RSA key with e=3 and SHA-1 hash +// 2: 2048-bit RSA key with e=65537 and SHA-1 hash +// 3: 2048-bit RSA key with e=3 and SHA-256 hash +// 4: 2048-bit RSA key with e=65537 and SHA-256 hash +// // Returns NULL if the file failed to parse, or if it contain zero keys. -RSAPublicKey* +Certificate* load_keys(const char* filename, int* numKeys) { - RSAPublicKey* out = NULL; + Certificate* out = NULL; *numKeys = 0; FILE* f = fopen(filename, "r"); @@ -224,24 +256,38 @@ load_keys(const char* filename, int* numKeys) { bool done = false; while (!done) { ++*numKeys; - out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey)); - RSAPublicKey* key = out + (*numKeys - 1); + out = (Certificate*)realloc(out, *numKeys * sizeof(Certificate)); + Certificate* cert = out + (*numKeys - 1); + cert->public_key = (RSAPublicKey*)malloc(sizeof(RSAPublicKey)); char start_char; if (fscanf(f, " %c", &start_char) != 1) goto exit; if (start_char == '{') { // a version 1 key has no version specifier. - key->exponent = 3; + cert->public_key->exponent = 3; + cert->hash_len = SHA_DIGEST_SIZE; } else if (start_char == 'v') { int version; if (fscanf(f, "%d {", &version) != 1) goto exit; - if (version == 2) { - key->exponent = 65537; - } else { - goto exit; + switch (version) { + case 2: + cert->public_key->exponent = 65537; + cert->hash_len = SHA_DIGEST_SIZE; + break; + case 3: + cert->public_key->exponent = 3; + cert->hash_len = SHA256_DIGEST_SIZE; + break; + case 4: + cert->public_key->exponent = 65537; + cert->hash_len = SHA256_DIGEST_SIZE; + break; + default: + goto exit; } } + RSAPublicKey* key = cert->public_key; if (fscanf(f, " %i , 0x%x , { %u", &(key->len), &(key->n0inv), &(key->n[0])) != 3) { goto exit; @@ -274,7 +320,7 @@ load_keys(const char* filename, int* numKeys) { goto exit; } - LOGI("read key e=%d\n", key->exponent); + LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len); } } diff --git a/verifier.h b/verifier.h index e9ef3b72..6ce1b44d 100644 --- a/verifier.h +++ b/verifier.h @@ -19,12 +19,17 @@ #include "mincrypt/rsa.h" +typedef struct Certificate { + int hash_len; // SHA_DIGEST_SIZE (SHA-1) or SHA256_DIGEST_SIZE (SHA-256) + RSAPublicKey* public_key; +} Certificate; + /* Look in the file for a signature footer, and verify that it * matches one of the given keys. Return one of the constants below. */ -int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys); +int verify_file(const char* path, const Certificate *pKeys, unsigned int numKeys); -RSAPublicKey* load_keys(const char* filename, int* numKeys); +Certificate* load_keys(const char* filename, int* numKeys); #define VERIFY_SUCCESS 0 #define VERIFY_FAILURE 1 diff --git a/verifier_test.cpp b/verifier_test.cpp index 79c55783..7fab5474 100644 --- a/verifier_test.cpp +++ b/verifier_test.cpp @@ -20,80 +20,82 @@ #include "verifier.h" #include "ui.h" +#include "mincrypt/sha.h" +#include "mincrypt/sha256.h" // This is build/target/product/security/testkey.x509.pem after being // dumped out by dumpkey.jar. RSAPublicKey test_key = { 64, 0xc926ad21, - { 1795090719, 2141396315, 950055447, -1713398866, - -26044131, 1920809988, 546586521, -795969498, - 1776797858, -554906482, 1805317999, 1429410244, - 129622599, 1422441418, 1783893377, 1222374759, - -1731647369, 323993566, 28517732, 609753416, - 1826472888, 215237850, -33324596, -245884705, - -1066504894, 774857746, 154822455, -1797768399, - -1536767878, -1275951968, -1500189652, 87251430, - -1760039318, 120774784, 571297800, -599067824, - -1815042109, -483341846, -893134306, -1900097649, - -1027721089, 950095497, 555058928, 414729973, - 1136544882, -1250377212, 465547824, -236820568, - -1563171242, 1689838846, -404210357, 1048029507, - 895090649, 247140249, 178744550, -747082073, - -1129788053, 109881576, -350362881, 1044303212, - -522594267, -1309816990, -557446364, -695002876}, - { -857949815, -510492167, -1494742324, -1208744608, - 251333580, 2131931323, 512774938, 325948880, - -1637480859, 2102694287, -474399070, 792812816, - 1026422502, 2053275343, -1494078096, -1181380486, - 165549746, -21447327, -229719404, 1902789247, - 772932719, -353118870, -642223187, 216871947, - -1130566647, 1942378755, -298201445, 1055777370, - 964047799, 629391717, -2062222979, -384408304, - 191868569, -1536083459, -612150544, -1297252564, - -1592438046, -724266841, -518093464, -370899750, - -739277751, -1536141862, 1323144535, 61311905, - 1997411085, 376844204, 213777604, -217643712, - 9135381, 1625809335, -1490225159, -1342673351, - 1117190829, -57654514, 1825108855, -1281819325, - 1111251351, -1726129724, 1684324211, -1773988491, - 367251975, 810756730, -1941182952, 1175080310 }, + { 0x6afee91fu, 0x7fa31d5bu, 0x38a0b217u, 0x99df9baeu, + 0xfe72991du, 0x727d3c04u, 0x20943f99u, 0xd08e7826u, + 0x69e7c8a2u, 0xdeeccc8eu, 0x6b9af76fu, 0x553311c4u, + 0x07b9e247u, 0x54c8bbcau, 0x6a540d81u, 0x48dbf567u, + 0x98c92877u, 0x134fbfdeu, 0x01b32564u, 0x24581948u, + 0x6cddc3b8u, 0x0cd444dau, 0xfe0381ccu, 0xf15818dfu, + 0xc06e6d42u, 0x2e2f6412u, 0x093a6737u, 0x94d83b31u, + 0xa466c87au, 0xb3f284a0u, 0xa694ec2cu, 0x053359e6u, + 0x9717ee6au, 0x0732e080u, 0x220d5008u, 0xdc4af350u, + 0x93d0a7c3u, 0xe330c9eau, 0xcac3da1eu, 0x8ebecf8fu, + 0xc2be387fu, 0x38a14e89u, 0x211586f0u, 0x18b846f5u, + 0x43be4c72u, 0xb578c204u, 0x1bbfb230u, 0xf1e267a8u, + 0xa2d3e656u, 0x64b8e4feu, 0xe7e83d4bu, 0x3e77a943u, + 0x3559ffd9u, 0x0ebb0f99u, 0x0aa76ce6u, 0xd3786ea7u, + 0xbca8cd6bu, 0x068ca8e8u, 0xeb1de2ffu, 0x3e3ecd6cu, + 0xe0d9d825u, 0xb1edc762u, 0xdec60b24u, 0xd6931904u}, + { 0xccdcb989u, 0xe19281f9u, 0xa6e80accu, 0xb7f40560u, + 0x0efb0bccu, 0x7f12b0bbu, 0x1e90531au, 0x136d95d0u, + 0x9e660665u, 0x7d54918fu, 0xe3b93ea2u, 0x2f415d10u, + 0x3d2df6e6u, 0x7a627ecfu, 0xa6f22d70u, 0xb995907au, + 0x09de16b2u, 0xfeb8bd61u, 0xf24ec294u, 0x716a427fu, + 0x2e12046fu, 0xeaf3d56au, 0xd9b873adu, 0x0ced340bu, + 0xbc9cec09u, 0x73c65903u, 0xee39ce9bu, 0x3eede25au, + 0x397633b7u, 0x2583c165u, 0x8514f97du, 0xe9166510u, + 0x0b6fae99u, 0xa47139fdu, 0xdb8352f0u, 0xb2ad7f2cu, + 0xa11552e2u, 0xd4d490a7u, 0xe11e8568u, 0xe9e484dau, + 0xd3ef8449u, 0xa47055dau, 0x4edd9557u, 0x03a78ba1u, + 0x770e130du, 0x16762facu, 0x0cbdfcc4u, 0xf3070540u, + 0x008b6515u, 0x60e7e1b7u, 0xa72cf7f9u, 0xaff86e39u, + 0x4296faadu, 0xfc90430eu, 0x6cc8f377u, 0xb398fd43u, + 0x423c5997u, 0x991d59c4u, 0x6464bf73u, 0x96431575u, + 0x15e3d207u, 0x30532a7au, 0x8c4be618u, 0x460a4d76u }, 3 }; RSAPublicKey test_f4_key = { 64, 0xc9bd1f21, - { 293133087u, 3210546773u, 865313125u, 250921607u, - 3158780490u, 943703457u, 1242806226u, 2986289859u, - 2942743769u, 2457906415u, 2719374299u, 1783459420u, - 149579627u, 3081531591u, 3440738617u, 2788543742u, - 2758457512u, 1146764939u, 3699497403u, 2446203424u, - 1744968926u, 1159130537u, 2370028300u, 3978231572u, - 3392699980u, 1487782451u, 1180150567u, 2841334302u, - 3753960204u, 961373345u, 3333628321u, 748825784u, - 2978557276u, 1566596926u, 1613056060u, 2600292737u, - 1847226629u, 50398611u, 1890374404u, 2878700735u, - 2286201787u, 1401186359u, 619285059u, 731930817u, - 2340993166u, 1156490245u, 2992241729u, 151498140u, - 318782170u, 3480838990u, 2100383433u, 4223552555u, - 3628927011u, 4247846280u, 1759029513u, 4215632601u, - 2719154626u, 3490334597u, 1751299340u, 3487864726u, - 3668753795u, 4217506054u, 3748782284u, 3150295088u }, - { 1772626313u, 445326068u, 3477676155u, 1758201194u, - 2986784722u, 491035581u, 3922936562u, 702212696u, - 2979856666u, 3324974564u, 2488428922u, 3056318590u, - 1626954946u, 664714029u, 398585816u, 3964097931u, - 3356701905u, 2298377729u, 2040082097u, 3025491477u, - 539143308u, 3348777868u, 2995302452u, 3602465520u, - 212480763u, 2691021393u, 1307177300u, 704008044u, - 2031136606u, 1054106474u, 3838318865u, 2441343869u, - 1477566916u, 700949900u, 2534790355u, 3353533667u, - 336163563u, 4106790558u, 2701448228u, 1571536379u, - 1103842411u, 3623110423u, 1635278839u, 1577828979u, - 910322800u, 715583630u, 138128831u, 1017877531u, - 2289162787u, 447994798u, 1897243165u, 4121561445u, - 4150719842u, 2131821093u, 2262395396u, 3305771534u, - 980753571u, 3256525190u, 3128121808u, 1072869975u, - 3507939515u, 4229109952u, 118381341u, 2209831334u }, + { 0x1178db1fu, 0xbf5d0e55u, 0x3393a165u, 0x0ef4c287u, + 0xbc472a4au, 0x383fc5a1u, 0x4a13b7d2u, 0xb1ff2ac3u, + 0xaf66b4d9u, 0x9280acefu, 0xa2165bdbu, 0x6a4d6e5cu, + 0x08ea676bu, 0xb7ac70c7u, 0xcd158139u, 0xa635ccfeu, + 0xa46ab8a8u, 0x445a3e8bu, 0xdc81d9bbu, 0x91ce1a20u, + 0x68021cdeu, 0x4516eda9u, 0x8d43c30cu, 0xed1eff14u, + 0xca387e4cu, 0x58adc233u, 0x4657ab27u, 0xa95b521eu, + 0xdfc0e30cu, 0x394d64a1u, 0xc6b321a1u, 0x2ca22cb8u, + 0xb1892d5cu, 0x5d605f3eu, 0x6025483cu, 0x9afd5181u, + 0x6e1a7105u, 0x03010593u, 0x70acd304u, 0xab957cbfu, + 0x8844abbbu, 0x53846837u, 0x24e98a43u, 0x2ba060c1u, + 0x8b88b88eu, 0x44eea405u, 0xb259fc41u, 0x0907ad9cu, + 0x13003adau, 0xcf79634eu, 0x7d314ec9u, 0xfbbe4c2bu, + 0xd84d0823u, 0xfd30fd88u, 0x68d8a909u, 0xfb4572d9u, + 0xa21301c2u, 0xd00a4785u, 0x6862b50cu, 0xcfe49796u, + 0xdaacbd83u, 0xfb620906u, 0xdf71e0ccu, 0xbbc5b030u }, + { 0x69a82189u, 0x1a8b22f4u, 0xcf49207bu, 0x68cc056au, + 0xb206b7d2u, 0x1d449bbdu, 0xe9d342f2u, 0x29daea58u, + 0xb19d011au, 0xc62f15e4u, 0x9452697au, 0xb62bb87eu, + 0x60f95cc2u, 0x279ebb2du, 0x17c1efd8u, 0xec47558bu, + 0xc81334d1u, 0x88fe7601u, 0x79992eb1u, 0xb4555615u, + 0x2022ac8cu, 0xc79a4b8cu, 0xb288b034u, 0xd6b942f0u, + 0x0caa32fbu, 0xa065ba51u, 0x4de9f154u, 0x29f64f6cu, + 0x7910af5eu, 0x3ed4636au, 0xe4c81911u, 0x9183f37du, + 0x5811e1c4u, 0x29c7a58cu, 0x9715d4d3u, 0xc7e2dce3u, + 0x140972ebu, 0xf4c8a69eu, 0xa104d424u, 0x5dabbdfbu, + 0x41cb4c6bu, 0xd7f44717u, 0x61785ff7u, 0x5e0bc273u, + 0x36426c70u, 0x2aa6f08eu, 0x083badbfu, 0x3cab941bu, + 0x8871da23u, 0x1ab3dbaeu, 0x7115a21du, 0xf5aa0965u, + 0xf766f562u, 0x7f110225u, 0x86d96a04u, 0xc50a120eu, + 0x3a751ca3u, 0xc21aa186u, 0xba7359d0u, 0x3ff2b257u, + 0xd116e8bbu, 0xfc1318c0u, 0x070e5b1du, 0x83b759a6u }, 65537 }; @@ -130,30 +132,37 @@ class FakeUI : public RecoveryUI { int main(int argc, char **argv) { if (argc < 2 || argc > 4) { - fprintf(stderr, "Usage: %s [-f4 | -file ] \n", argv[0]); + fprintf(stderr, "Usage: %s [-sha256] [-f4 | -file ] \n", argv[0]); return 2; } - RSAPublicKey* key = &test_key; + Certificate default_cert; + Certificate* cert = &default_cert; + cert->public_key = &test_key; + cert->hash_len = SHA_DIGEST_SIZE; int num_keys = 1; ++argv; + if (strcmp(argv[0], "-sha256") == 0) { + ++argv; + cert->hash_len = SHA256_DIGEST_SIZE; + } if (strcmp(argv[0], "-f4") == 0) { ++argv; - key = &test_f4_key; + cert->public_key = &test_f4_key; } else if (strcmp(argv[0], "-file") == 0) { ++argv; - key = load_keys(argv[0], &num_keys); + cert = load_keys(argv[0], &num_keys); ++argv; } ui = new FakeUI(); - int result = verify_file(*argv, key, num_keys); + int result = verify_file(*argv, cert, num_keys); if (result == VERIFY_SUCCESS) { - printf("SUCCESS\n"); + printf("VERIFIED\n"); return 0; } else if (result == VERIFY_FAILURE) { - printf("FAILURE\n"); + printf("NOT VERIFIED\n"); return 1; } else { printf("bad return value\n"); diff --git a/verifier_test.sh b/verifier_test.sh index 378b0e5f..65f77f40 100755 --- a/verifier_test.sh +++ b/verifier_test.sh @@ -64,33 +64,39 @@ $ADB push $ANDROID_PRODUCT_OUT/system/bin/verifier_test \ expect_succeed() { testname "$1 (should succeed)" $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip - run_command $WORK_DIR/verifier_test $WORK_DIR/package.zip || fail + shift + run_command $WORK_DIR/verifier_test "$@" $WORK_DIR/package.zip || fail } expect_fail() { testname "$1 (should fail)" $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip - run_command $WORK_DIR/verifier_test $WORK_DIR/package.zip && fail -} - -expect_succeed_f4() { - testname "$1 (should succeed)" - $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip - run_command $WORK_DIR/verifier_test -f4 $WORK_DIR/package.zip || fail -} - -expect_fail_f4() { - testname "$1 (should fail)" - $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip - run_command $WORK_DIR/verifier_test -f4 $WORK_DIR/package.zip && fail + shift + run_command $WORK_DIR/verifier_test "$@" $WORK_DIR/package.zip && fail } +# not signed at all expect_fail unsigned.zip +# signed in the pre-donut way expect_fail jarsigned.zip + +# success cases expect_succeed otasigned.zip -expect_fail_f4 otasigned.zip -expect_succeed_f4 otasigned_f4.zip +expect_succeed otasigned_f4.zip -f4 +expect_succeed otasigned_sha256.zip -sha256 +expect_succeed otasigned_f4_sha256.zip -sha256 -f4 + +# verified against different key +expect_fail otasigned.zip -f4 expect_fail otasigned_f4.zip + +# verified against right key but wrong hash algorithm +expect_fail otasigned.zip -sha256 +expect_fail otasigned_f4.zip -sha256 -f4 +expect_fail otasigned_sha256.zip +expect_fail otasigned_f4_sha256.zip -f4 + +# various other cases expect_fail random.zip expect_fail fake-eocd.zip expect_fail alter-metadata.zip From 596b342a0476629badb41b840494254a19c57dae Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 14 May 2013 11:03:02 -0700 Subject: [PATCH 02/20] recovery: turn on text display for install errors in debug builds Hopefully this will reduce the number of OTA "bugs" reported that are really just someone having changed their system partition, invalidating future incremental OTAs. Also fixes a longstanding TODO about putting LOGE() output in the on-screen display. Change-Id: I44e5be65b2dee7ebce2cce28ccd920dc3d6e522e --- common.h | 6 ++++-- recovery.cpp | 35 +++++++++++++++++++++++++++++++++-- verifier_test.cpp | 14 ++++++++++---- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/common.h b/common.h index 3587a31f..768f499f 100644 --- a/common.h +++ b/common.h @@ -18,13 +18,13 @@ #define RECOVERY_COMMON_H #include +#include #ifdef __cplusplus extern "C" { #endif -// TODO: restore ui_print for LOGE -#define LOGE(...) fprintf(stdout, "E:" __VA_ARGS__) +#define LOGE(...) ui_print("E:" __VA_ARGS__) #define LOGW(...) fprintf(stdout, "W:" __VA_ARGS__) #define LOGI(...) fprintf(stdout, "I:" __VA_ARGS__) @@ -44,6 +44,8 @@ typedef struct fstab_rec Volume; // fopen a file, mounting volumes and making parent dirs as necessary. FILE* fopen_path(const char *path, const char *mode); +void ui_print(const char* format, ...); + #ifdef __cplusplus } #endif diff --git a/recovery.cpp b/recovery.cpp index 7002cb8a..a84d8333 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -15,11 +15,13 @@ */ #include +#include #include #include #include #include #include +#include #include #include #include @@ -27,7 +29,6 @@ #include #include #include -#include #include "bootloader.h" #include "common.h" @@ -801,6 +802,24 @@ load_locale_from_cache() { } } +static RecoveryUI* gCurrentUI = NULL; + +void +ui_print(const char* format, ...) { + char buffer[256]; + + va_list ap; + va_start(ap, format); + vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + + if (gCurrentUI != NULL) { + gCurrentUI->Print("%s", buffer); + } else { + fputs(buffer, stdout); + } +} + int main(int argc, char **argv) { time_t start = time(NULL); @@ -856,6 +875,7 @@ main(int argc, char **argv) { Device* device = make_device(); ui = device->GetUI(); + gCurrentUI = ui; ui->Init(); ui->SetLocale(locale); @@ -909,7 +929,18 @@ main(int argc, char **argv) { LOGE("Cache wipe (requested by package) failed."); } } - if (status != INSTALL_SUCCESS) ui->Print("Installation aborted.\n"); + if (status != INSTALL_SUCCESS) { + ui->Print("Installation aborted.\n"); + + // If this is an eng or userdebug build, then automatically + // turn the text display on if the script fails so the error + // message is visible. + char buffer[PROPERTY_VALUE_MAX+1]; + property_get("ro.build.fingerprint", buffer, ""); + if (strstr(buffer, ":userdebug/") || strstr(buffer, ":eng/")) { + ui->ShowText(true); + } + } } else if (wipe_data) { if (device->WipeData()) status = INSTALL_ERROR; if (erase_volume("/data")) status = INSTALL_ERROR; diff --git a/verifier_test.cpp b/verifier_test.cpp index 7fab5474..1063cbae 100644 --- a/verifier_test.cpp +++ b/verifier_test.cpp @@ -18,6 +18,7 @@ #include #include +#include "common.h" #include "verifier.h" #include "ui.h" #include "mincrypt/sha.h" @@ -115,13 +116,10 @@ class FakeUI : public RecoveryUI { bool IsTextVisible() { return false; } bool WasTextEverVisible() { return false; } void Print(const char* fmt, ...) { - char buf[256]; va_list ap; va_start(ap, fmt); - vsnprintf(buf, 256, fmt, ap); + vfprintf(stderr, fmt, ap); va_end(ap); - - fputs(buf, stderr); } void StartMenu(const char* const * headers, const char* const * items, @@ -130,6 +128,14 @@ class FakeUI : public RecoveryUI { void EndMenu() { } }; +void +ui_print(const char* format, ...) { + va_list ap; + va_start(ap, format); + vfprintf(stdout, format, ap); + va_end(ap); +} + int main(int argc, char **argv) { if (argc < 2 || argc > 4) { fprintf(stderr, "Usage: %s [-sha256] [-f4 | -file ] \n", argv[0]); From 46bee63afcd1e2817cdc75a6a8cefdcfdc3e8429 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 16 May 2013 11:23:48 -0700 Subject: [PATCH 03/20] recovery: save logs from the last few invocations of recovery Extends the last_log mechanism to save logs from the last six invocations of recovery, so that we're more likely to have useful logs even if the device has repeatedly booted into recovery. Change-Id: I08ae7a09553ada45f9e0733fe1e55e5a22efd9f9 --- recovery.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/recovery.cpp b/recovery.cpp index a84d8333..840e63c4 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -59,10 +59,11 @@ static const struct option OPTIONS[] = { { NULL, 0, NULL, 0 }, }; +#define LAST_LOG_FILE "/cache/recovery/last_log" + static const char *COMMAND_FILE = "/cache/recovery/command"; static const char *INTENT_FILE = "/cache/recovery/intent"; static const char *LOG_FILE = "/cache/recovery/log"; -static const char *LAST_LOG_FILE = "/cache/recovery/last_log"; static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install"; static const char *LOCALE_FILE = "/cache/recovery/last_locale"; static const char *CACHE_ROOT = "/cache"; @@ -260,6 +261,21 @@ copy_log_file(const char* source, const char* destination, int append) { } } +// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max +// Overwrites any existing last_log.$max. +static void +rotate_last_logs(int max) { + char oldfn[256]; + char newfn[256]; + + int i; + for (i = max-1; i >= 0; --i) { + snprintf(oldfn, sizeof(oldfn), (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i); + snprintf(newfn, sizeof(newfn), LAST_LOG_FILE ".%d", i+1); + // ignore errors + rename(oldfn, newfn); + } +} // clear the recovery command and prepare to boot a (hopefully working) system, // copy our log file to cache as well (for the system to read), and @@ -843,6 +859,8 @@ main(int argc, char **argv) { printf("Starting recovery on %s", ctime(&start)); load_volume_table(); + ensure_path_mounted(LAST_LOG_FILE); + rotate_last_logs(5); get_args(&argc, &argv); int previous_runs = 0; From 2f2c98869b5391310965c7d154c68f4b28e0ccfb Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 4 Jun 2013 13:11:44 -0700 Subject: [PATCH 04/20] start healthd in recovery Change-Id: I16e3e0ddb8ca062431deb4be83c5be5eb786d76f --- etc/init.rc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/etc/init.rc b/etc/init.rc index abc7b318..b26d2ae7 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -2,6 +2,7 @@ import /init.recovery.${ro.hardware}.rc on early-init start ueventd + start healthd on init export PATH /sbin @@ -40,6 +41,9 @@ on boot service ueventd /sbin/ueventd critical +service healthd /sbin/healthd -n + critical + service recovery /sbin/recovery service adbd /sbin/adbd recovery From f24fd7e8479d54eaa2b73db5a3a3ad076a13f72d Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 2 Jul 2013 11:43:25 -0700 Subject: [PATCH 05/20] recovery: copy logs to cache more aggressively Copy logs to /cache immediately upon a package installation failure; don't wait for recovery to finish. (If the user reboots without exiting recovery the "right" way, the logs never get copied at all.) Change-Id: Iee342944e7ded63da5a4af33d11ebc876f6c0835 --- recovery.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index c82844d2..b7fb616c 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -283,6 +283,19 @@ rotate_last_logs(int max) { } } +static void +copy_logs() { + // Copy logs to cache so the system can find out what happened. + copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true); + copy_log_file(TEMPORARY_LOG_FILE, LAST_LOG_FILE, false); + copy_log_file(TEMPORARY_INSTALL_FILE, LAST_INSTALL_FILE, false); + chmod(LOG_FILE, 0600); + chown(LOG_FILE, 1000, 1000); // system user + chmod(LAST_LOG_FILE, 0640); + chmod(LAST_INSTALL_FILE, 0644); + sync(); +} + // clear the recovery command and prepare to boot a (hopefully working) system, // copy our log file to cache as well (for the system to read), and // record any intent we were asked to communicate back to the system. @@ -312,14 +325,7 @@ finish_recovery(const char *send_intent) { check_and_fclose(fp, LOCALE_FILE); } - // Copy logs to cache so the system can find out what happened. - copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true); - copy_log_file(TEMPORARY_LOG_FILE, LAST_LOG_FILE, false); - copy_log_file(TEMPORARY_INSTALL_FILE, LAST_INSTALL_FILE, false); - chmod(LOG_FILE, 0600); - chown(LOG_FILE, 1000, 1000); // system user - chmod(LAST_LOG_FILE, 0640); - chmod(LAST_INSTALL_FILE, 0644); + copy_logs(); // Reset to normal system boot so recovery won't cycle indefinitely. struct bootloader_message boot; @@ -789,6 +795,7 @@ prompt_and_wait(Device* device, int status) { if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); + copy_logs(); } else if (!ui->IsTextVisible()) { return; // reboot if logs aren't visible } else { @@ -866,7 +873,7 @@ main(int argc, char **argv) { load_volume_table(); ensure_path_mounted(LAST_LOG_FILE); - rotate_last_logs(5); + rotate_last_logs(10); get_args(&argc, &argv); int previous_runs = 0; @@ -979,6 +986,7 @@ main(int argc, char **argv) { } if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) { + copy_logs(); ui->SetBackground(RecoveryUI::ERROR); } if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { From 5b468fc9305bf3adef681fa1e56364fc51761af8 Mon Sep 17 00:00:00 2001 From: yetta_wu Date: Tue, 25 Jun 2013 15:03:11 +0800 Subject: [PATCH 06/20] recovery: init backgroundIcon properly to avoid recovery mode crash We met factory issue that some devices would crash in recovery mode because the backgroundIcon array did not reset to NULL when initializing. Bug: 9568624 Change-Id: I13c7a7cc1053a7ffdbadd71740c1a2b4a2af6bba Signed-off-by: yetta_wu Signed-off-by: Iliyan Malchev --- screen_ui.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/screen_ui.cpp b/screen_ui.cpp index 222de00e..93e26093 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -82,6 +82,10 @@ ScreenRecoveryUI::ScreenRecoveryUI() : install_overlay_offset_y(190), overlay_offset_x(-1), overlay_offset_y(-1) { + + for (int i = 0; i < 5; i++) + backgroundIcon[i] = NULL; + pthread_mutex_init(&updateMutex, NULL); self = this; } From fafc85b4ad7a5679c6b562bed64460732e05fd1e Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 9 Jul 2013 12:29:45 -0700 Subject: [PATCH 07/20] recovery: move log output to stdout Recovery currently has a random mix of messages printed to stdout and messages printed to stderr, which can make logs hard to read. Move everything to stdout. Change-Id: Ie33bd4a9e1272e731302569cdec918e0534c48a6 --- edify/expr.c | 4 +-- edify/main.c | 10 +++---- install.cpp | 1 + minui/graphics.c | 4 +-- mtdutils/mtdutils.c | 26 ++++++++-------- recovery.cpp | 3 +- updater/install.c | 72 ++++++++++++++++++++++----------------------- updater/updater.c | 24 +++++++-------- 8 files changed, 72 insertions(+), 72 deletions(-) diff --git a/edify/expr.c b/edify/expr.c index 07a8ceb6..a2f1f99d 100644 --- a/edify/expr.c +++ b/edify/expr.c @@ -287,13 +287,13 @@ Value* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) { long l_int = strtol(left, &end, 10); if (left[0] == '\0' || *end != '\0') { - fprintf(stderr, "[%s] is not an int\n", left); + printf("[%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); + printf("[%s] is not an int\n", right); goto done; } diff --git a/edify/main.c b/edify/main.c index 85570438..9e6bab7c 100644 --- a/edify/main.c +++ b/edify/main.c @@ -34,8 +34,8 @@ int expect(const char* expr_str, const char* expected, int* errors) { 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); + printf("error parsing \"%s\" (%d errors)\n", + expr_str, error_count); ++*errors; return 0; } @@ -49,7 +49,7 @@ int expect(const char* expr_str, const char* expected, int* errors) { free(state.errmsg); free(state.script); if (result == NULL && expected != NULL) { - fprintf(stderr, "error evaluating \"%s\"\n", expr_str); + printf("error evaluating \"%s\"\n", expr_str); ++*errors; return 0; } @@ -59,8 +59,8 @@ int expect(const char* expr_str, const char* expected, int* errors) { } if (strcmp(result, expected) != 0) { - fprintf(stderr, "evaluating \"%s\": expected \"%s\", got \"%s\"\n", - expr_str, expected, result); + printf("evaluating \"%s\": expected \"%s\", got \"%s\"\n", + expr_str, expected, result); ++*errors; free(result); return 0; diff --git a/install.cpp b/install.cpp index 0cb5cc7d..e1ab848f 100644 --- a/install.cpp +++ b/install.cpp @@ -154,6 +154,7 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { } else { ui->Print("\n"); } + fflush(stdout); } else if (strcmp(command, "wipe_cache") == 0) { *wipe_cache = 1; } else if (strcmp(command, "clear_display") == 0) { diff --git a/minui/graphics.c b/minui/graphics.c index 4968eac7..d7571653 100644 --- a/minui/graphics.c +++ b/minui/graphics.c @@ -385,8 +385,8 @@ int gr_init(void) get_memory_surface(&gr_mem_surface); - fprintf(stderr, "framebuffer: fd %d (%d x %d)\n", - gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height); + printf("framebuffer: fd %d (%d x %d)\n", + gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height); /* start with 0 as front (displayed) and 1 as back (drawing) */ gr_active_fb = 0; diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c index 107cbb9a..d04b26ef 100644 --- a/mtdutils/mtdutils.c +++ b/mtdutils/mtdutils.c @@ -289,7 +289,7 @@ static int read_block(const MtdPartition *partition, int fd, char *data) { struct mtd_ecc_stats before, after; if (ioctl(fd, ECCGETSTATS, &before)) { - fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno)); + printf("mtd: ECCGETSTATS error (%s)\n", strerror(errno)); return -1; } @@ -300,13 +300,13 @@ static int read_block(const MtdPartition *partition, int fd, char *data) while (pos + size <= (int) partition->size) { if (lseek64(fd, pos, SEEK_SET) != pos || read(fd, data, size) != size) { - fprintf(stderr, "mtd: read error at 0x%08llx (%s)\n", + printf("mtd: read error at 0x%08llx (%s)\n", pos, strerror(errno)); } else if (ioctl(fd, ECCGETSTATS, &after)) { - fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno)); + printf("mtd: ECCGETSTATS error (%s)\n", strerror(errno)); return -1; } else if (after.failed != before.failed) { - fprintf(stderr, "mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n", + printf("mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n", after.corrected - before.corrected, after.failed - before.failed, pos); // copy the comparison baseline for the next read. @@ -431,39 +431,39 @@ static int write_block(MtdWriteContext *ctx, const char *data) int retry; for (retry = 0; retry < 2; ++retry) { if (ioctl(fd, MEMERASE, &erase_info) < 0) { - fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n", + printf("mtd: erase failure at 0x%08lx (%s)\n", pos, strerror(errno)); continue; } if (lseek(fd, pos, SEEK_SET) != pos || write(fd, data, size) != size) { - fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n", + printf("mtd: write error at 0x%08lx (%s)\n", pos, strerror(errno)); } char verify[size]; if (lseek(fd, pos, SEEK_SET) != pos || read(fd, verify, size) != size) { - fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n", + printf("mtd: re-read error at 0x%08lx (%s)\n", pos, strerror(errno)); continue; } if (memcmp(data, verify, size) != 0) { - fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n", + printf("mtd: verification error at 0x%08lx (%s)\n", pos, strerror(errno)); continue; } if (retry > 0) { - fprintf(stderr, "mtd: wrote block after %d retries\n", retry); + printf("mtd: wrote block after %d retries\n", retry); } - fprintf(stderr, "mtd: successfully wrote block at %lx\n", pos); + printf("mtd: successfully wrote block at %lx\n", pos); return 0; // Success! } // Try to erase it once more as we give up on this block add_bad_block_offset(ctx, pos); - fprintf(stderr, "mtd: skipping write block at 0x%08lx\n", pos); + printf("mtd: skipping write block at 0x%08lx\n", pos); ioctl(fd, MEMERASE, &erase_info); pos += partition->erase_size; } @@ -526,7 +526,7 @@ off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks) while (blocks-- > 0) { loff_t bpos = pos; if (ioctl(ctx->fd, MEMGETBADBLOCK, &bpos) > 0) { - fprintf(stderr, "mtd: not erasing bad block at 0x%08lx\n", pos); + printf("mtd: not erasing bad block at 0x%08lx\n", pos); pos += ctx->partition->erase_size; continue; // Don't try to erase known factory-bad blocks. } @@ -535,7 +535,7 @@ off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks) erase_info.start = pos; erase_info.length = ctx->partition->erase_size; if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) { - fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos); + printf("mtd: erase failure at 0x%08lx\n", pos); } pos += ctx->partition->erase_size; } diff --git a/recovery.cpp b/recovery.cpp index b7fb616c..d9533973 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -920,8 +920,7 @@ main(int argc, char **argv) { sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); if (!sehandle) { - fprintf(stderr, "Warning: No file_contexts\n"); - ui->Print("Warning: No file_contexts\n"); + ui->Print("Warning: No file_contexts\n"); } device->StartRecovery(); diff --git a/updater/install.c b/updater/install.c index 1fc4fd39..9fa06a22 100644 --- a/updater/install.c +++ b/updater/install.c @@ -97,13 +97,13 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { const MtdPartition* mtd; mtd = mtd_find_partition_by_name(location); if (mtd == NULL) { - fprintf(stderr, "%s: no mtd partition named \"%s\"", + printf("%s: no mtd partition named \"%s\"", name, location); result = strdup(""); goto done; } if (mtd_mount_partition(mtd, mount_point, fs_type, 0 /* rw */) != 0) { - fprintf(stderr, "mtd mount of %s failed: %s\n", + printf("mtd mount of %s failed: %s\n", location, strerror(errno)); result = strdup(""); goto done; @@ -112,7 +112,7 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { } else { if (mount(location, mount_point, fs_type, MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) { - fprintf(stderr, "%s: failed to mount %s at %s: %s\n", + printf("%s: failed to mount %s at %s: %s\n", name, location, mount_point, strerror(errno)); result = strdup(""); } else { @@ -175,7 +175,7 @@ Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) { scan_mounted_volumes(); const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); if (vol == NULL) { - fprintf(stderr, "unmount of %s failed; no such volume\n", mount_point); + printf("unmount of %s failed; no such volume\n", mount_point); result = strdup(""); } else { unmount_mounted_volume(vol); @@ -233,25 +233,25 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { mtd_scan_partitions(); const MtdPartition* mtd = mtd_find_partition_by_name(location); if (mtd == NULL) { - fprintf(stderr, "%s: no mtd partition named \"%s\"", + printf("%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); + printf("%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); + printf("%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); + printf("%s: failed to close \"%s\"", name, location); result = strdup(""); goto done; } @@ -260,7 +260,7 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { } else if (strcmp(fs_type, "ext4") == 0) { int status = make_ext4fs(location, atoll(fs_size), mount_point, sehandle); if (status != 0) { - fprintf(stderr, "%s: make_ext4fs failed (%d) on %s", + printf("%s: make_ext4fs failed (%d) on %s", name, status, location); result = strdup(""); goto done; @@ -268,7 +268,7 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { result = location; #endif } else { - fprintf(stderr, "%s: unsupported fs_type \"%s\" partition_type \"%s\"", + printf("%s: unsupported fs_type \"%s\" partition_type \"%s\"", name, fs_type, partition_type); } @@ -394,13 +394,13 @@ Value* PackageExtractFileFn(const char* name, State* state, 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); + printf("%s: no %s in package\n", name, zip_path); goto done2; } FILE* f = fopen(dest_path, "wb"); if (f == NULL) { - fprintf(stderr, "%s: can't open %s for write: %s\n", + printf("%s: can't open %s for write: %s\n", name, dest_path, strerror(errno)); goto done2; } @@ -426,14 +426,14 @@ Value* PackageExtractFileFn(const char* name, State* state, 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); + printf("%s: no %s in package\n", name, zip_path); goto done1; } v->size = mzGetZipEntryUncompLen(entry); v->data = malloc(v->size); if (v->data == NULL) { - fprintf(stderr, "%s: failed to allocate %ld bytes for %s\n", + printf("%s: failed to allocate %ld bytes for %s\n", name, (long)v->size, zip_path); goto done1; } @@ -460,13 +460,13 @@ static int make_parents(char* name) { *p = '\0'; if (make_parents(name) < 0) return -1; int result = mkdir(name, 0700); - if (result == 0) fprintf(stderr, "symlink(): created [%s]\n", name); + if (result == 0) printf("symlink(): created [%s]\n", name); *p = '/'; if (result == 0 || errno == EEXIST) { // successfully created or already existed; we're done return 0; } else { - fprintf(stderr, "failed to mkdir %s: %s\n", name, strerror(errno)); + printf("failed to mkdir %s: %s\n", name, strerror(errno)); return -1; } } @@ -494,18 +494,18 @@ Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { for (i = 0; i < argc-1; ++i) { if (unlink(srcs[i]) < 0) { if (errno != ENOENT) { - fprintf(stderr, "%s: failed to remove %s: %s\n", + printf("%s: failed to remove %s: %s\n", name, srcs[i], strerror(errno)); ++bad; } } if (make_parents(srcs[i])) { - fprintf(stderr, "%s: failed to symlink %s to %s: making parents failed\n", + printf("%s: failed to symlink %s to %s: making parents failed\n", name, srcs[i], target); ++bad; } if (symlink(target, srcs[i]) < 0) { - fprintf(stderr, "%s: failed to symlink %s to %s: %s\n", + printf("%s: failed to symlink %s to %s: %s\n", name, srcs[i], target, strerror(errno)); ++bad; } @@ -574,12 +574,12 @@ Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { for (i = 3; i < argc; ++i) { if (chown(args[i], uid, gid) < 0) { - fprintf(stderr, "%s: chown of %s to %d %d failed: %s\n", + printf("%s: chown of %s to %d %d failed: %s\n", name, args[i], uid, gid, strerror(errno)); ++bad; } if (chmod(args[i], mode) < 0) { - fprintf(stderr, "%s: chmod of %s to %o failed: %s\n", + printf("%s: chmod of %s to %o failed: %s\n", name, args[i], mode, strerror(errno)); ++bad; } @@ -720,7 +720,7 @@ 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)); + printf("%s\n", strerror(errno)); return false; } @@ -752,14 +752,14 @@ Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { 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); + printf("%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", + printf("%s: can't write mtd partition \"%s\"\n", name, partition); result = strdup(""); goto done; @@ -772,7 +772,7 @@ Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { char* filename = contents->data; FILE* f = fopen(filename, "rb"); if (f == NULL) { - fprintf(stderr, "%s: can't open %s: %s\n", + printf("%s: can't open %s: %s\n", name, filename, strerror(errno)); result = strdup(""); goto done; @@ -793,15 +793,15 @@ Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { success = (wrote == contents->size); } if (!success) { - fprintf(stderr, "mtd_write_data to %s failed: %s\n", + printf("mtd_write_data to %s failed: %s\n", partition, strerror(errno)); } if (mtd_erase_blocks(ctx, -1) == -1) { - fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition); + printf("%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: error closing write of %s\n", name, partition); } printf("%s %s partition\n", @@ -988,23 +988,23 @@ Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) { memcpy(args2, args, sizeof(char*) * argc); args2[argc] = NULL; - fprintf(stderr, "about to run program [%s] with %d args\n", args2[0], argc); + printf("about to run program [%s] with %d args\n", args2[0], argc); pid_t child = fork(); if (child == 0) { execv(args2[0], args2); - fprintf(stderr, "run_program: execv failed: %s\n", strerror(errno)); + printf("run_program: execv failed: %s\n", strerror(errno)); _exit(1); } int status; waitpid(child, &status, 0); if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) { - fprintf(stderr, "run_program: child exited with status %d\n", + printf("run_program: child exited with status %d\n", WEXITSTATUS(status)); } } else if (WIFSIGNALED(status)) { - fprintf(stderr, "run_program: child terminated by signal %d\n", + printf("run_program: child terminated by signal %d\n", WTERMSIG(status)); } @@ -1053,7 +1053,7 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { } if (args[0]->size < 0) { - fprintf(stderr, "%s(): no file contents received", name); + printf("%s(): no file contents received", name); return StringValue(strdup("")); } uint8_t digest[SHA_DIGEST_SIZE]; @@ -1068,12 +1068,12 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { uint8_t* arg_digest = malloc(SHA_DIGEST_SIZE); for (i = 1; i < argc; ++i) { if (args[i]->type != VAL_STRING) { - fprintf(stderr, "%s(): arg %d is not a string; skipping", + printf("%s(): arg %d is not a string; skipping", name, i); } else if (ParseSha1(args[i]->data, arg_digest) != 0) { // Warn about bad args and skip them. - fprintf(stderr, "%s(): error parsing \"%s\" as sha-1; skipping", - name, args[i]->data); + printf("%s(): error parsing \"%s\" as sha-1; skipping", + name, args[i]->data); } else if (memcmp(digest, arg_digest, SHA_DIGEST_SIZE) == 0) { break; } diff --git a/updater/updater.c b/updater/updater.c index 58ac27f9..c7009fea 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -36,13 +36,14 @@ struct selabel_handle *sehandle; int main(int argc, char** argv) { // Various things log information to stdout or stderr more or less - // at random. The log file makes more sense if buffering is - // turned off so things appear in the right order. + // at random (though we've tried to standardize on stdout). The + // log file makes more sense if buffering is turned off so things + // appear in the right order. setbuf(stdout, NULL); setbuf(stderr, NULL); if (argc != 4) { - fprintf(stderr, "unexpected number of arguments (%d)\n", argc); + printf("unexpected number of arguments (%d)\n", argc); return 1; } @@ -50,7 +51,7 @@ int main(int argc, char** argv) { if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') { // We support version 1, 2, or 3. - fprintf(stderr, "wrong updater binary API; expected 1, 2, or 3; " + printf("wrong updater binary API; expected 1, 2, or 3; " "got %s\n", argv[1]); return 2; @@ -69,20 +70,20 @@ int main(int argc, char** argv) { int err; err = mzOpenZipArchive(package_data, &za); if (err != 0) { - fprintf(stderr, "failed to open package %s: %s\n", + printf("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); + printf("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"); + printf("failed to read script from package\n"); return 5; } script[script_entry->uncompLen] = '\0'; @@ -101,7 +102,7 @@ int main(int argc, char** argv) { yy_scan_string(script); int error = yyparse(&root, &error_count); if (error != 0 || error_count > 0) { - fprintf(stderr, "%d parse errors\n", error_count); + printf("%d parse errors\n", error_count); return 6; } @@ -112,7 +113,6 @@ int main(int argc, char** argv) { sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); if (!sehandle) { - fprintf(stderr, "Warning: No file_contexts\n"); fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n"); } @@ -131,10 +131,10 @@ int main(int argc, char** argv) { char* result = Evaluate(&state, root); if (result == NULL) { if (state.errmsg == NULL) { - fprintf(stderr, "script aborted (no error message)\n"); + printf("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); + printf("script aborted: %s\n", state.errmsg); char* line = strtok(state.errmsg, "\n"); while (line) { fprintf(cmd_pipe, "ui_print %s\n", line); @@ -145,7 +145,7 @@ int main(int argc, char** argv) { free(state.errmsg); return 7; } else { - fprintf(stderr, "script result was [%s]\n", result); + fprintf(cmd_pipe, "ui_print script succeeded: result was [%s]\n", result); free(result); } From 6d0d7ac051a0338c0e07e239e742b92e5ab8ea07 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 9 Jul 2013 13:34:55 -0700 Subject: [PATCH 08/20] recovery: preserve recovery logs across cache wipes When doing a cache wipe or a factory reset (which includes a cache wipe), save any last* log files in the /cache/recovery directory and write them back after reformatting the partition, so that wiping data doesn't lose useful log information. Change-Id: I1f52ae9131760b5e752e136645c19f71b7b166ee --- recovery.cpp | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index b7fb616c..82cfc3f8 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -61,6 +61,7 @@ static const struct option OPTIONS[] = { #define LAST_LOG_FILE "/cache/recovery/last_log" +static const char *CACHE_LOG_DIR = "/cache/recovery"; static const char *COMMAND_FILE = "/cache/recovery/command"; static const char *INTENT_FILE = "/cache/recovery/intent"; static const char *LOG_FILE = "/cache/recovery/log"; @@ -342,22 +343,95 @@ finish_recovery(const char *send_intent) { sync(); // For good measure. } +typedef struct _saved_log_file { + char* name; + struct stat st; + unsigned char* data; + struct _saved_log_file* next; +} saved_log_file; + static int erase_volume(const char *volume) { + bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); + ui->SetBackground(RecoveryUI::ERASING); ui->SetProgressType(RecoveryUI::INDETERMINATE); + + saved_log_file* head = NULL; + + if (is_cache) { + // If we're reformatting /cache, we load any + // "/cache/recovery/last*" files into memory, so we can restore + // them after the reformat. + + ensure_path_mounted(volume); + + DIR* d; + struct dirent* de; + d = opendir(CACHE_LOG_DIR); + if (d) { + char path[PATH_MAX]; + strcpy(path, CACHE_LOG_DIR); + strcat(path, "/"); + int path_len = strlen(path); + while ((de = readdir(d)) != NULL) { + if (strncmp(de->d_name, "last", 4) == 0) { + saved_log_file* p = (saved_log_file*) malloc(sizeof(saved_log_file)); + strcpy(path+path_len, de->d_name); + p->name = strdup(path); + if (stat(path, &(p->st)) == 0) { + // truncate files to 512kb + if (p->st.st_size > (1 << 19)) { + p->st.st_size = 1 << 19; + } + p->data = (unsigned char*) malloc(p->st.st_size); + FILE* f = fopen(path, "rb"); + fread(p->data, 1, p->st.st_size, f); + fclose(f); + p->next = head; + head = p; + } else { + free(p); + } + } + } + closedir(d); + } else { + if (errno != ENOENT) { + printf("opendir failed: %s\n", strerror(errno)); + } + } + } + ui->Print("Formatting %s...\n", volume); ensure_path_unmounted(volume); + int result = format_volume(volume); + + if (is_cache) { + while (head) { + FILE* f = fopen_path(head->name, "wb"); + if (f) { + fwrite(head->data, 1, head->st.st_size, f); + fclose(f); + chmod(head->name, head->st.st_mode); + chown(head->name, head->st.st_uid, head->st.st_gid); + } + free(head->name); + free(head->data); + saved_log_file* temp = head->next; + free(head); + head = temp; + } - if (strcmp(volume, "/cache") == 0) { // Any part of the log we'd copied to cache is now gone. // Reset the pointer so we copy from the beginning of the temp // log. tmplog_offset = 0; + copy_logs(); } - return format_volume(volume); + return result; } static char* From 627eb30f73c29257acaeb6568f3da38880784f7c Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 17 Jul 2013 19:01:37 -0700 Subject: [PATCH 09/20] Update OTA installer to understand SELinux filesystem labels Modify the OTA installer to understand SELinux filesystem labels. We do this by introducing new set_perm2 / set_perm2_recursive calls, which understand SELinux filesystem labels. These filesystem labels are applied at the same time that we apply the UID / GID / permission changes. For compatibility, we preserve the behavior of the existing set_perm / set_perm_recursive calls. If the destination kernel doesn't support security labels, don't fail. SELinux isn't enabled on all kernels. Bug: 8985290 Change-Id: I99800499f01784199e4918a82e3e2db1089cf25b --- minzip/DirUtil.c | 9 +++++++-- minzip/DirUtil.h | 2 +- updater/install.c | 29 ++++++++++++++++++++++++----- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/minzip/DirUtil.c b/minzip/DirUtil.c index 8dd5da1d..c120fa3c 100644 --- a/minzip/DirUtil.c +++ b/minzip/DirUtil.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "DirUtil.h" @@ -237,7 +238,7 @@ dirUnlinkHierarchy(const char *path) int dirSetHierarchyPermissions(const char *path, - int uid, int gid, int dirMode, int fileMode) + int uid, int gid, int dirMode, int fileMode, const char* secontext) { struct stat st; if (lstat(path, &st)) { @@ -255,6 +256,10 @@ dirSetHierarchyPermissions(const char *path, return -1; } + if ((secontext != NULL) && lsetfilecon(path, secontext) && (errno != ENOTSUP)) { + return -1; + } + /* recurse over directory components */ if (S_ISDIR(st.st_mode)) { DIR *dir = opendir(path); @@ -271,7 +276,7 @@ dirSetHierarchyPermissions(const char *path, char dn[PATH_MAX]; snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name); - if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) { + if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode, secontext)) { errno = 0; } else if (errno == 0) { errno = -1; diff --git a/minzip/DirUtil.h b/minzip/DirUtil.h index a5cfa761..3e12a0bf 100644 --- a/minzip/DirUtil.h +++ b/minzip/DirUtil.h @@ -54,7 +54,7 @@ int dirUnlinkHierarchy(const char *path); * Sets directories to and files to . Skips symlinks. */ int dirSetHierarchyPermissions(const char *path, - int uid, int gid, int dirMode, int fileMode); + int uid, int gid, int dirMode, int fileMode, const char* secontext); #ifdef __cplusplus } diff --git a/updater/install.c b/updater/install.c index 9fa06a22..c81bbb59 100644 --- a/updater/install.c +++ b/updater/install.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "cutils/misc.h" #include "cutils/properties.h" @@ -521,9 +522,10 @@ Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; - bool recursive = (strcmp(name, "set_perm_recursive") == 0); + bool recursive = (strcmp(name, "set_perm_recursive") == 0) || (strcmp(name, "set_perm2_recursive") == 0); + bool has_selabel = (strcmp(name, "set_perm2") == 0) || (strcmp(name, "set_perm2_recursive") == 0); - int min_args = 4 + (recursive ? 1 : 0); + int min_args = 4 + (has_selabel ? 1 : 0) + (recursive ? 1 : 0); if (argc < min_args) { return ErrorAbort(state, "%s() expects %d+ args, got %d", name, min_args, argc); @@ -562,8 +564,13 @@ Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } - for (i = 4; i < argc; ++i) { - dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode); + char* secontext = NULL; + if (has_selabel) { + secontext = args[4]; + } + + for (i = 4 + (has_selabel ? 1 : 0); i < argc; ++i) { + dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode, secontext); } } else { int mode = strtoul(args[2], &end, 0); @@ -572,7 +579,12 @@ Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } - for (i = 3; i < argc; ++i) { + char* secontext = NULL; + if (has_selabel) { + secontext = args[3]; + } + + for (i = 3 + (has_selabel ? 1 : 0); i < argc; ++i) { if (chown(args[i], uid, gid) < 0) { printf("%s: chown of %s to %d %d failed: %s\n", name, args[i], uid, gid, strerror(errno)); @@ -583,6 +595,11 @@ Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { name, args[i], mode, strerror(errno)); ++bad; } + if (has_selabel && lsetfilecon(args[i], secontext) && (errno != ENOTSUP)) { + printf("%s: lsetfilecon of %s to %s failed: %s\n", + name, args[i], secontext, strerror(errno)); + ++bad; + } } } result = strdup(""); @@ -1135,6 +1152,8 @@ void RegisterInstallFunctions() { RegisterFunction("symlink", SymlinkFn); RegisterFunction("set_perm", SetPermFn); RegisterFunction("set_perm_recursive", SetPermFn); + RegisterFunction("set_perm2", SetPermFn); + RegisterFunction("set_perm2_recursive", SetPermFn); RegisterFunction("getprop", GetPropFn); RegisterFunction("file_getprop", FileGetPropFn); From c0441d171914e59941ec4f815ae0aabf56d6504f Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 31 Jul 2013 11:28:24 -0700 Subject: [PATCH 10/20] notify about pending long press Recovery changes: - add a method to the UI class that is called when a key is held down long enough to be a "long press" (but before it is released). Device-specific subclasses can override this to indicate a long press. - do color selection for ScreenRecoveryUI's menu-and-log drawing function. Subclasses can override this to customize the colors they use for various elements. - Include the value of ro.build.display.id in the menu headers, so you can see on the screen what version of recovery you are running. Change-Id: I426a6daf892b9011638e2035aebfa2831d4f596d --- recovery.cpp | 14 ++++++-------- screen_ui.cpp | 48 ++++++++++++++++++++++++++++++++++++++---------- screen_ui.h | 5 +++++ ui.cpp | 41 +++++++++++++++++++++++++++++++++-------- ui.h | 21 ++++++++++++++++++++- 5 files changed, 102 insertions(+), 27 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index c5a589cc..38366b65 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -75,6 +75,7 @@ static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload"; RecoveryUI* ui = NULL; char* locale = NULL; +char recovery_version[PROPERTY_VALUE_MAX+1]; /* * The recovery tool communicates with the main system through /cache files. @@ -526,21 +527,17 @@ copy_sideloaded_package(const char* original_path) { static const char** prepend_title(const char* const* headers) { - const char* title[] = { "Android system recovery <" - EXPAND(RECOVERY_API_VERSION) "e>", - "", - NULL }; - // count the number of lines in our title, plus the // caller-provided headers. - int count = 0; + int count = 3; // our title has 3 lines const char* const* p; - for (p = title; *p; ++p, ++count); for (p = headers; *p; ++p, ++count); const char** new_headers = (const char**)malloc((count+1) * sizeof(char*)); const char** h = new_headers; - for (p = title; *p; ++p, ++h) *h = *p; + *(h++) = "Android system recovery <" EXPAND(RECOVERY_API_VERSION) "e>"; + *(h++) = recovery_version; + *(h++) = ""; for (p = headers; *p; ++p, ++h) *h = *p; *h = NULL; @@ -1022,6 +1019,7 @@ main(int argc, char **argv) { printf("\n"); property_list(print_property, NULL); + property_get("ro.build.display.id", recovery_version, ""); printf("\n"); int status = INSTALL_SUCCESS; diff --git a/screen_ui.cpp b/screen_ui.cpp index 93e26093..6a638582 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -196,9 +196,29 @@ void ScreenRecoveryUI::draw_progress_locked() } } -#define C_HEADER 247,0,6 -#define C_MENU 0,106,157 -#define C_LOG 249,194,0 +void ScreenRecoveryUI::SetColor(UIElement e) { + switch (e) { + case HEADER: + gr_color(247, 0, 6, 255); + break; + case MENU: + case MENU_SEL_BG: + gr_color(0, 106, 157, 255); + break; + case MENU_SEL_FG: + gr_color(255, 255, 255, 255); + break; + case LOG: + gr_color(249, 194, 0, 255); + break; + case TEXT_FILL: + gr_color(0, 0, 0, 160); + break; + default: + gr_color(255, 255, 255, 255); + break; + } +} // Redraw everything on the screen. Does not flip pages. // Should only be called with updateMutex locked. @@ -208,37 +228,38 @@ void ScreenRecoveryUI::draw_screen_locked() draw_progress_locked(); if (show_text) { - gr_color(0, 0, 0, 160); + SetColor(TEXT_FILL); gr_fill(0, 0, gr_fb_width(), gr_fb_height()); int y = 0; int i = 0; if (show_menu) { - gr_color(C_HEADER, 255); + SetColor(HEADER); for (; i < menu_top + menu_items; ++i) { - if (i == menu_top) gr_color(C_MENU, 255); + if (i == menu_top) SetColor(MENU); if (i == menu_top + menu_sel) { // draw the highlight bar + SetColor(MENU_SEL_BG); gr_fill(0, y-2, gr_fb_width(), y+char_height+2); // white text of selected item - gr_color(255, 255, 255, 255); + SetColor(MENU_SEL_FG); if (menu[i][0]) gr_text(4, y, menu[i], 1); - gr_color(C_MENU, 255); + SetColor(MENU); } else { if (menu[i][0]) gr_text(4, y, menu[i], i < menu_top); } y += char_height+4; } - gr_color(C_MENU, 255); + SetColor(MENU); y += 4; gr_fill(0, y, gr_fb_width(), y+2); y += 4; ++i; } - gr_color(C_LOG, 255); + SetColor(LOG); // display from the bottom up, until we hit the top of the // screen, the bottom of the menu, or we've displayed the @@ -585,3 +606,10 @@ void ScreenRecoveryUI::ShowText(bool visible) update_screen_locked(); pthread_mutex_unlock(&updateMutex); } + +void ScreenRecoveryUI::Redraw() +{ + pthread_mutex_lock(&updateMutex); + update_screen_locked(); + pthread_mutex_unlock(&updateMutex); +} diff --git a/screen_ui.h b/screen_ui.h index fe0de46e..0bd220f7 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -53,6 +53,11 @@ class ScreenRecoveryUI : public RecoveryUI { int SelectMenu(int sel); void EndMenu(); + void Redraw(); + + enum UIElement { HEADER, MENU, MENU_SEL_BG, MENU_SEL_FG, LOG, TEXT_FILL }; + virtual void SetColor(UIElement e); + private: Icon currentIcon; int installingFrame; diff --git a/ui.cpp b/ui.cpp index 65f40282..cece02d3 100644 --- a/ui.cpp +++ b/ui.cpp @@ -46,7 +46,8 @@ static RecoveryUI* self = NULL; RecoveryUI::RecoveryUI() : key_queue_len(0), key_last_down(-1), - key_down_time(0) { + key_long_press(false), + key_down_count(0) { pthread_mutex_init(&key_queue_mutex, NULL); pthread_cond_init(&key_queue_cond, NULL); self = this; @@ -112,19 +113,22 @@ void RecoveryUI::process_key(int key_code, int updown) { bool register_key = false; bool long_press = false; - const long long_threshold = CLOCKS_PER_SEC * 750 / 1000; - pthread_mutex_lock(&key_queue_mutex); key_pressed[key_code] = updown; if (updown) { + ++key_down_count; key_last_down = key_code; - key_down_time = clock(); + key_long_press = false; + pthread_t th; + key_timer_t* info = new key_timer_t; + info->ui = this; + info->key_code = key_code; + info->count = key_down_count; + pthread_create(&th, NULL, &RecoveryUI::time_key_helper, info); + pthread_detach(th); } else { if (key_last_down == key_code) { - long duration = clock() - key_down_time; - if (duration > long_threshold) { - long_press = true; - } + long_press = key_long_press; register_key = true; } key_last_down = -1; @@ -152,6 +156,24 @@ void RecoveryUI::process_key(int key_code, int updown) { } } +void* RecoveryUI::time_key_helper(void* cookie) { + key_timer_t* info = (key_timer_t*) cookie; + info->ui->time_key(info->key_code, info->count); + delete info; + return NULL; +} + +void RecoveryUI::time_key(int key_code, int count) { + usleep(750000); // 750 ms == "long" + bool long_press = false; + pthread_mutex_lock(&key_queue_mutex); + if (key_last_down == key_code && key_down_count == count) { + long_press = key_long_press = true; + } + pthread_mutex_unlock(&key_queue_mutex); + if (long_press) KeyLongPress(key_code); +} + void RecoveryUI::EnqueueKey(int key_code) { pthread_mutex_lock(&key_queue_mutex); const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]); @@ -242,3 +264,6 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) { void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) { } + +void RecoveryUI::KeyLongPress(int key) { +} diff --git a/ui.h b/ui.h index aca7b7b8..6c8987a3 100644 --- a/ui.h +++ b/ui.h @@ -80,8 +80,17 @@ class RecoveryUI { enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE }; virtual KeyAction CheckKey(int key); + // Called immediately before each call to CheckKey(), tell you if + // the key was long-pressed. virtual void NextCheckKeyIsLong(bool is_long_press); + // Called when a key is held down long enough to have been a + // long-press (but before the key is released). This means that + // if the key is eventually registered (released without any other + // keys being pressed in the meantime), NextCheckKeyIsLong() will + // be called with "true". + virtual void KeyLongPress(int key); + // --- menu display --- // Display some header text followed by a menu of items, which appears @@ -108,15 +117,25 @@ private: int key_queue[256], key_queue_len; char key_pressed[KEY_MAX + 1]; // under key_queue_mutex int key_last_down; // under key_queue_mutex - clock_t key_down_time; // under key_queue_mutex + bool key_long_press; // under key_queue_mutex + int key_down_count; // under key_queue_mutex int rel_sum; + typedef struct { + RecoveryUI* ui; + int key_code; + int count; + } key_timer_t; + pthread_t input_t; static void* input_thread(void* cookie); static int input_callback(int fd, short revents, void* data); void process_key(int key_code, int updown); bool usb_connected(); + + static void* time_key_helper(void* cookie); + void time_key(int key_code, int count); }; #endif // RECOVERY_UI_H From 239ac6abac4524be93fce710360c0512c6cc2ab3 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 20 Aug 2013 16:03:25 -0700 Subject: [PATCH 11/20] recovery: install packages in a known mount environment When installing a package, we should have /tmp and /cache mounted and nothing else. Ensure this is true by explicitly mounting them and unmounting everything else as the first step of every install. Also fix an error in the progress bar that crops up when you do multiple package installs in one instance of recovery. Change-Id: I4837ed707cb419ddd3d9f6188b6355ba1bcfe2b2 --- install.cpp | 15 ++++++++++----- recovery.cpp | 5 ----- roots.cpp | 19 +++++++++++++++++++ roots.h | 4 ++++ screen_ui.cpp | 3 ++- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/install.cpp b/install.cpp index e1ab848f..797a525f 100644 --- a/install.cpp +++ b/install.cpp @@ -180,7 +180,9 @@ really_install_package(const char *path, int* wipe_cache) { ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); ui->Print("Finding update package...\n"); - ui->SetProgressType(RecoveryUI::INDETERMINATE); + // Give verification half the progress bar... + ui->SetProgressType(RecoveryUI::DETERMINATE); + ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); LOGI("Update location: %s\n", path); if (ensure_path_mounted(path) != 0) { @@ -198,10 +200,7 @@ really_install_package(const char *path, int* wipe_cache) } LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); - // Give verification half the progress bar... ui->Print("Verifying update package...\n"); - ui->SetProgressType(RecoveryUI::DETERMINATE); - ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); int err; err = verify_file(path, loadedKeys, numKeys); @@ -237,7 +236,13 @@ install_package(const char* path, int* wipe_cache, const char* install_file) } else { LOGE("failed to open last_install: %s\n", strerror(errno)); } - int result = really_install_package(path, wipe_cache); + int result; + if (setup_install_mounts() != 0) { + LOGE("failed to set up expected mounts for install; aborting\n"); + result = INSTALL_ERROR; + } else { + result = really_install_package(path, wipe_cache); + } if (install_log) { fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log); fputc('\n', install_log); diff --git a/recovery.cpp b/recovery.cpp index 38366b65..654a6652 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -811,10 +811,6 @@ prompt_and_wait(Device* device, int status) { break; case Device::APPLY_EXT: - // Some packages expect /cache to be mounted (eg, - // standard incremental packages expect to use /cache - // as scratch space). - ensure_path_mounted(CACHE_ROOT); status = update_directory(SDCARD_ROOT, SDCARD_ROOT, &wipe_cache, device); if (status == INSTALL_SUCCESS && wipe_cache) { ui->Print("\n-- Wiping cache (at package request)...\n"); @@ -860,7 +856,6 @@ prompt_and_wait(Device* device, int status) { break; case Device::APPLY_ADB_SIDELOAD: - ensure_path_mounted(CACHE_ROOT); status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE); if (status >= 0) { if (status != INSTALL_SUCCESS) { diff --git a/roots.cpp b/roots.cpp index 09471225..113dba1b 100644 --- a/roots.cpp +++ b/roots.cpp @@ -202,3 +202,22 @@ int format_volume(const char* volume) { LOGE("format_volume: fs_type \"%s\" unsupported\n", v->fs_type); return -1; } + +int setup_install_mounts() { + if (fstab == NULL) { + LOGE("can't set up install mounts: no fstab loaded\n"); + return -1; + } + for (int i = 0; i < fstab->num_entries; ++i) { + Volume* v = fstab->recs + i; + + if (strcmp(v->mount_point, "/tmp") == 0 || + strcmp(v->mount_point, "/cache") == 0) { + if (ensure_path_mounted(v->mount_point) != 0) return -1; + + } else { + if (ensure_path_unmounted(v->mount_point) != 0) return -1; + } + } + return 0; +} diff --git a/roots.h b/roots.h index 8abe18fb..230d9ded 100644 --- a/roots.h +++ b/roots.h @@ -42,6 +42,10 @@ int ensure_path_unmounted(const char* path); // it is mounted. int format_volume(const char* volume); +// Ensure that all and only the volumes that packages expect to find +// mounted (/tmp and /cache) are mounted. Returns 0 on success. +int setup_install_mounts(); + #ifdef __cplusplus } #endif diff --git a/screen_ui.cpp b/screen_ui.cpp index 6a638582..8376341c 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -467,10 +467,11 @@ void ScreenRecoveryUI::SetProgressType(ProgressType type) pthread_mutex_lock(&updateMutex); if (progressBarType != type) { progressBarType = type; - update_progress_locked(); } progressScopeStart = 0; + progressScopeSize = 0; progress = 0; + update_progress_locked(); pthread_mutex_unlock(&updateMutex); } From 77ea71d6a85a93c9bf423466e87661b1bf67c512 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 30 Aug 2013 12:20:16 -0700 Subject: [PATCH 12/20] recovery: fix rebooting Change I84c0513acb549720cb0e8c9fcbda0050f5c396f5 moved reboot functionality into init but did not update the recovery partition; so "adb reboot" and /system/bin/reboot in recovery are both broken. Change-Id: Ie2d14627a686ffb5064256b6c399723636dff116 --- etc/init.rc | 3 +++ recovery.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/etc/init.rc b/etc/init.rc index b26d2ae7..9d1da1d6 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -38,6 +38,9 @@ on boot class_start default +on property:sys.powerctl=* + powerctl ${sys.powerctl} + service ueventd /sbin/ueventd critical diff --git a/recovery.cpp b/recovery.cpp index 654a6652..b7833931 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1062,6 +1062,6 @@ main(int argc, char **argv) { // Otherwise, get ready to boot the main system... finish_recovery(send_intent); ui->Print("Rebooting...\n"); - android_reboot(ANDROID_RB_RESTART, 0, 0); + property_set(ANDROID_RB_PROPERTY, ""); return EXIT_SUCCESS; } From 3b5a987cd7fd76c038e9875b430028216d21ace3 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 3 Sep 2013 14:29:54 -0700 Subject: [PATCH 13/20] recovery: fix use of init reboot method We need to set the system property to "reboot,", not an empty string. Bug: 10605007 Change-Id: I776e0d273764cf254651ab2b25c2743395b990e0 --- recovery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recovery.cpp b/recovery.cpp index b7833931..d803cadf 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1062,6 +1062,6 @@ main(int argc, char **argv) { // Otherwise, get ready to boot the main system... finish_recovery(send_intent); ui->Print("Rebooting...\n"); - property_set(ANDROID_RB_PROPERTY, ""); + property_set(ANDROID_RB_PROPERTY, "reboot,"); return EXIT_SUCCESS; } From 15ae0e7867507f3bde3cd7061fbad933d1fe059c Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 3 Sep 2013 14:29:54 -0700 Subject: [PATCH 14/20] recovery: fix use of init reboot method We need to set the system property to "reboot,", not an empty string. Bug: 10605007 Change-Id: I776e0d273764cf254651ab2b25c2743395b990e0 --- recovery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recovery.cpp b/recovery.cpp index b7833931..d803cadf 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1062,6 +1062,6 @@ main(int argc, char **argv) { // Otherwise, get ready to boot the main system... finish_recovery(send_intent); ui->Print("Rebooting...\n"); - property_set(ANDROID_RB_PROPERTY, ""); + property_set(ANDROID_RB_PROPERTY, "reboot,"); return EXIT_SUCCESS; } From 3328e3bc81161c2a57ea94d304162276facdd826 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Mon, 9 Sep 2013 10:47:14 -0700 Subject: [PATCH 15/20] Revert "Update OTA installer to understand SELinux filesystem labels" This reverts commit 627eb30f73c29257acaeb6568f3da38880784f7c. Bug: 10183961 Bug: 10186213 --- minzip/DirUtil.c | 9 ++------- minzip/DirUtil.h | 2 +- updater/install.c | 29 +++++------------------------ 3 files changed, 8 insertions(+), 32 deletions(-) diff --git a/minzip/DirUtil.c b/minzip/DirUtil.c index c120fa3c..8dd5da1d 100644 --- a/minzip/DirUtil.c +++ b/minzip/DirUtil.c @@ -23,7 +23,6 @@ #include #include #include -#include #include "DirUtil.h" @@ -238,7 +237,7 @@ dirUnlinkHierarchy(const char *path) int dirSetHierarchyPermissions(const char *path, - int uid, int gid, int dirMode, int fileMode, const char* secontext) + int uid, int gid, int dirMode, int fileMode) { struct stat st; if (lstat(path, &st)) { @@ -256,10 +255,6 @@ dirSetHierarchyPermissions(const char *path, return -1; } - if ((secontext != NULL) && lsetfilecon(path, secontext) && (errno != ENOTSUP)) { - return -1; - } - /* recurse over directory components */ if (S_ISDIR(st.st_mode)) { DIR *dir = opendir(path); @@ -276,7 +271,7 @@ dirSetHierarchyPermissions(const char *path, char dn[PATH_MAX]; snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name); - if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode, secontext)) { + if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) { errno = 0; } else if (errno == 0) { errno = -1; diff --git a/minzip/DirUtil.h b/minzip/DirUtil.h index 3e12a0bf..a5cfa761 100644 --- a/minzip/DirUtil.h +++ b/minzip/DirUtil.h @@ -54,7 +54,7 @@ int dirUnlinkHierarchy(const char *path); * Sets directories to and files to . Skips symlinks. */ int dirSetHierarchyPermissions(const char *path, - int uid, int gid, int dirMode, int fileMode, const char* secontext); + int uid, int gid, int dirMode, int fileMode); #ifdef __cplusplus } diff --git a/updater/install.c b/updater/install.c index c81bbb59..9fa06a22 100644 --- a/updater/install.c +++ b/updater/install.c @@ -27,7 +27,6 @@ #include #include #include -#include #include "cutils/misc.h" #include "cutils/properties.h" @@ -522,10 +521,9 @@ Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; - bool recursive = (strcmp(name, "set_perm_recursive") == 0) || (strcmp(name, "set_perm2_recursive") == 0); - bool has_selabel = (strcmp(name, "set_perm2") == 0) || (strcmp(name, "set_perm2_recursive") == 0); + bool recursive = (strcmp(name, "set_perm_recursive") == 0); - int min_args = 4 + (has_selabel ? 1 : 0) + (recursive ? 1 : 0); + int min_args = 4 + (recursive ? 1 : 0); if (argc < min_args) { return ErrorAbort(state, "%s() expects %d+ args, got %d", name, min_args, argc); @@ -564,13 +562,8 @@ Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } - char* secontext = NULL; - if (has_selabel) { - secontext = args[4]; - } - - for (i = 4 + (has_selabel ? 1 : 0); i < argc; ++i) { - dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode, secontext); + for (i = 4; i < argc; ++i) { + dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode); } } else { int mode = strtoul(args[2], &end, 0); @@ -579,12 +572,7 @@ Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } - char* secontext = NULL; - if (has_selabel) { - secontext = args[3]; - } - - for (i = 3 + (has_selabel ? 1 : 0); i < argc; ++i) { + for (i = 3; i < argc; ++i) { if (chown(args[i], uid, gid) < 0) { printf("%s: chown of %s to %d %d failed: %s\n", name, args[i], uid, gid, strerror(errno)); @@ -595,11 +583,6 @@ Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { name, args[i], mode, strerror(errno)); ++bad; } - if (has_selabel && lsetfilecon(args[i], secontext) && (errno != ENOTSUP)) { - printf("%s: lsetfilecon of %s to %s failed: %s\n", - name, args[i], secontext, strerror(errno)); - ++bad; - } } } result = strdup(""); @@ -1152,8 +1135,6 @@ void RegisterInstallFunctions() { RegisterFunction("symlink", SymlinkFn); RegisterFunction("set_perm", SetPermFn); RegisterFunction("set_perm_recursive", SetPermFn); - RegisterFunction("set_perm2", SetPermFn); - RegisterFunction("set_perm2_recursive", SetPermFn); RegisterFunction("getprop", GetPropFn); RegisterFunction("file_getprop", FileGetPropFn); From 5dbdef0e5b8a841fadc64d016d10ce81a962b284 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Sat, 7 Sep 2013 14:41:06 -0700 Subject: [PATCH 16/20] updater: introduce and set_metadata and set_metadata_recursive Introduce two new updater functions: * set_metadata * set_metadata_recursive Long term, these functions are intended to be more flexible replacements for the following methods: * set_perm * set_perm_recursive Usage: set_metadata("filename", "key1", "value1", "key2", "value2", ...) set_metadata_recursive("dirname", "key1", "value1", "key2", "value2", ...) Description: set_metadata() and set_metadata_recursive() set the attributes on a file/directory according to the key/value pairs provided. Today, the following keys are supported: * uid * gid * mode (set_perm_extd only) * fmode (set_perm_extd_recursive only) * dmode (set_perm_extd_recursive only) * selabel * capabilities Unknown keys are logged as warnings, but are not fatal errors. Examples: * set_metadata("/system/bin/netcfg", "selabel", "u:object_r:system_file:s0"); This sets the SELinux label of /system/bin/netcfg to u:object_r:system_file:s0. No other changes occur. * set_metadata("/system/bin/netcfg", "uid", 0, "gid", 3003, "mode", 02750, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0); This sets /system/bin/netcfg to uid=0, gid=3003, mode=02750, selinux label=u:object_r:system_file:s0, and clears the capabilities associated with the file. * set_metadata_recursive("/system", "uid", 0, "gid", 0, "fmode", 0644, "dmode", 0755, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0); All files and directories under /system are set to uid=0, gid=0, and selinux label=u:object_r:system_file:s0. Directories are set to mode=0755. Files are set to mode=0644 and all capabilities are cleared. Bug: 10183961 Bug: 10186213 Bug: 8985290 Change-Id: Ifdcf186a7ed45265511dc493c4036e1ac5e3d0af --- updater/install.c | 274 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) diff --git a/updater/install.c b/updater/install.c index 9fa06a22..770dbd09 100644 --- a/updater/install.c +++ b/updater/install.c @@ -27,6 +27,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include "cutils/misc.h" #include "cutils/properties.h" @@ -600,6 +606,259 @@ done: return StringValue(result); } +struct perm_parsed_args { + bool has_uid; + uid_t uid; + bool has_gid; + gid_t gid; + bool has_mode; + mode_t mode; + bool has_fmode; + mode_t fmode; + bool has_dmode; + mode_t dmode; + bool has_selabel; + char* selabel; + bool has_capabilities; + uint64_t capabilities; +}; + +static struct perm_parsed_args ParsePermArgs(int argc, char** args) { + int i; + struct perm_parsed_args parsed; + int bad = 0; + static int max_warnings = 20; + + memset(&parsed, 0, sizeof(parsed)); + + for (i = 1; i < argc; i += 2) { + if (strcmp("uid", args[i]) == 0) { + int64_t uid; + if (sscanf(args[i+1], "%" SCNd64, &uid) == 1) { + parsed.uid = uid; + parsed.has_uid = true; + } else { + printf("ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]); + bad++; + } + continue; + } + if (strcmp("gid", args[i]) == 0) { + int64_t gid; + if (sscanf(args[i+1], "%" SCNd64, &gid) == 1) { + parsed.gid = gid; + parsed.has_gid = true; + } else { + printf("ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]); + bad++; + } + continue; + } + if (strcmp("mode", args[i]) == 0) { + int32_t mode; + if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) { + parsed.mode = mode; + parsed.has_mode = true; + } else { + printf("ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]); + bad++; + } + continue; + } + if (strcmp("dmode", args[i]) == 0) { + int32_t mode; + if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) { + parsed.dmode = mode; + parsed.has_dmode = true; + } else { + printf("ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]); + bad++; + } + continue; + } + if (strcmp("fmode", args[i]) == 0) { + int32_t mode; + if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) { + parsed.fmode = mode; + parsed.has_fmode = true; + } else { + printf("ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]); + bad++; + } + continue; + } + if (strcmp("capabilities", args[i]) == 0) { + int64_t capabilities; + if (sscanf(args[i+1], "%" SCNi64, &capabilities) == 1) { + parsed.capabilities = capabilities; + parsed.has_capabilities = true; + } else { + printf("ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]); + bad++; + } + continue; + } + if (strcmp("selabel", args[i]) == 0) { + if (args[i+1][0] != '\0') { + parsed.selabel = args[i+1]; + parsed.has_selabel = true; + } else { + printf("ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]); + bad++; + } + continue; + } + if (max_warnings != 0) { + printf("ParsedPermArgs: unknown key \"%s\", ignoring\n", args[i]); + max_warnings--; + if (max_warnings == 0) { + printf("ParsedPermArgs: suppressing further warnings\n"); + } + } + } + return parsed; +} + +static int ApplyParsedPerms( + const char* filename, + const struct stat *statptr, + struct perm_parsed_args parsed) +{ + int bad = 0; + + if (parsed.has_uid) { + if (chown(filename, parsed.uid, -1) < 0) { + printf("ApplyParsedPerms: chown of %s to %d failed: %s\n", + filename, parsed.uid, strerror(errno)); + bad++; + } + } + + if (parsed.has_gid) { + if (chown(filename, -1, parsed.gid) < 0) { + printf("ApplyParsedPerms: chgrp of %s to %d failed: %s\n", + filename, parsed.gid, strerror(errno)); + bad++; + } + } + + if (parsed.has_mode) { + if (chmod(filename, parsed.mode) < 0) { + printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n", + filename, parsed.mode, strerror(errno)); + bad++; + } + } + + if (parsed.has_dmode && S_ISDIR(statptr->st_mode)) { + if (chmod(filename, parsed.dmode) < 0) { + printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n", + filename, parsed.dmode, strerror(errno)); + bad++; + } + } + + if (parsed.has_fmode && S_ISREG(statptr->st_mode)) { + if (chmod(filename, parsed.fmode) < 0) { + printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n", + filename, parsed.fmode, strerror(errno)); + bad++; + } + } + + if (parsed.has_selabel) { + // TODO: Don't silently ignore ENOTSUP + if (lsetfilecon(filename, parsed.selabel) && (errno != ENOTSUP)) { + printf("ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n", + filename, parsed.selabel, strerror(errno)); + bad++; + } + } + + if (parsed.has_capabilities && S_ISREG(statptr->st_mode)) { + if (parsed.capabilities == 0) { + if ((removexattr(filename, XATTR_NAME_CAPS) == -1) && (errno != ENODATA)) { + // Report failure unless it's ENODATA (attribute not set) + printf("ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n", + filename, parsed.capabilities, strerror(errno)); + bad++; + } + } else { + struct vfs_cap_data cap_data; + memset(&cap_data, 0, sizeof(cap_data)); + cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE; + cap_data.data[0].permitted = (uint32_t) (parsed.capabilities & 0xffffffff); + cap_data.data[0].inheritable = 0; + cap_data.data[1].permitted = (uint32_t) (parsed.capabilities >> 32); + cap_data.data[1].inheritable = 0; + if (setxattr(filename, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) < 0) { + printf("ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n", + filename, parsed.capabilities, strerror(errno)); + bad++; + } + } + } + + return bad; +} + +// nftw doesn't allow us to pass along context, so we need to use +// global variables. *sigh* +static struct perm_parsed_args recursive_parsed_args; + +static int do_SetMetadataRecursive(const char* filename, const struct stat *statptr, + int fileflags, struct FTW *pfwt) { + return ApplyParsedPerms(filename, statptr, recursive_parsed_args); +} + +static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv[]) { + int i; + int bad = 0; + static int nwarnings = 0; + struct stat sb; + Value* result = NULL; + + bool recursive = (strcmp(name, "set_metadata_recursive") == 0); + + if ((argc % 2) != 1) { + return ErrorAbort(state, "%s() expects an odd number of arguments, got %d", + name, argc); + } + + char** args = ReadVarArgs(state, argc, argv); + if (args == NULL) return NULL; + + if (lstat(args[0], &sb) == -1) { + result = ErrorAbort(state, "%s: Error on lstat of \"%s\": %s", name, args[0], strerror(errno)); + goto done; + } + + struct perm_parsed_args parsed = ParsePermArgs(argc, args); + + if (recursive) { + recursive_parsed_args = parsed; + bad += nftw(args[0], do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS); + memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args)); + } else { + bad += ApplyParsedPerms(args[0], &sb, parsed); + } + +done: + for (i = 0; i < argc; ++i) { + free(args[i]); + } + free(args); + + if (result != NULL) { + return result; + } + + if (bad > 0) { + return ErrorAbort(state, "%s: some changes failed", name); + } + + return StringValue(strdup("")); +} Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 1) { @@ -1133,9 +1392,24 @@ void RegisterInstallFunctions() { RegisterFunction("package_extract_dir", PackageExtractDirFn); RegisterFunction("package_extract_file", PackageExtractFileFn); RegisterFunction("symlink", SymlinkFn); + + // Maybe, at some future point, we can delete these functions? They have been + // replaced by perm_set and perm_set_recursive. RegisterFunction("set_perm", SetPermFn); RegisterFunction("set_perm_recursive", SetPermFn); + // Usage: + // set_metadata("filename", "key1", "value1", "key2", "value2", ...) + // Example: + // set_metadata("/system/bin/netcfg", "uid", 0, "gid", 3003, "mode", 02750, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0); + RegisterFunction("set_metadata", SetMetadataFn); + + // Usage: + // set_metadata_recursive("dirname", "key1", "value1", "key2", "value2", ...) + // Example: + // set_metadata_recursive("/system", "uid", 0, "gid", 0, "fmode", 0644, "dmode", 0755, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0); + RegisterFunction("set_metadata_recursive", SetMetadataFn); + RegisterFunction("getprop", GetPropFn); RegisterFunction("file_getprop", FileGetPropFn); RegisterFunction("write_raw_image", WriteRawImageFn); From e461251e2caa5561cf6a315bffaebfd4eb896b1d Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Tue, 10 Sep 2013 15:34:19 -0700 Subject: [PATCH 17/20] Don't apply permission changes to symlink. Bug: 10183961 Bug: 10186213 Bug: 8985290 Change-Id: I57cb14af59682c5f25f1e091564548bdbf20f74e --- updater/install.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/updater/install.c b/updater/install.c index 770dbd09..0a859452 100644 --- a/updater/install.c +++ b/updater/install.c @@ -726,6 +726,11 @@ static int ApplyParsedPerms( { int bad = 0; + /* ignore symlinks */ + if (S_ISLNK(statptr->st_mode)) { + return 0; + } + if (parsed.has_uid) { if (chown(filename, parsed.uid, -1) < 0) { printf("ApplyParsedPerms: chown of %s to %d failed: %s\n", From d456944f02cf41af63f4a32a974721c8dd6a0f66 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Tue, 10 Sep 2013 15:34:19 -0700 Subject: [PATCH 18/20] Don't apply permission changes to symlink. Bug: 10183961 Bug: 10186213 Bug: 8985290 Change-Id: I57cb14af59682c5f25f1e091564548bdbf20f74e --- updater/install.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/updater/install.c b/updater/install.c index 770dbd09..0a859452 100644 --- a/updater/install.c +++ b/updater/install.c @@ -726,6 +726,11 @@ static int ApplyParsedPerms( { int bad = 0; + /* ignore symlinks */ + if (S_ISLNK(statptr->st_mode)) { + return 0; + } + if (parsed.has_uid) { if (chown(filename, parsed.uid, -1) < 0) { printf("ApplyParsedPerms: chown of %s to %d failed: %s\n", From cc2958fd7f32f79d1a034eee005e04afeb310a87 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 11 Sep 2013 13:24:32 -0700 Subject: [PATCH 19/20] fix secure adb in recovery Recovery's init.rc was missing a line (added to the main system's init.rc in change Ic97fd464440ff4a29fc9da7ad15949ac5215ade3) is required for secure adb to work. Change-Id: Id79b94d2abb4cbe3cca7cabeb4bc5faf7205e56b --- etc/init.rc | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/init.rc b/etc/init.rc index 9d1da1d6..17548906 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -51,6 +51,7 @@ service recovery /sbin/recovery service adbd /sbin/adbd recovery disabled + socket adbd stream 660 system system # Always start adbd on userdebug and eng builds on property:ro.debuggable=1 From a2a1ce823b8c76451ee2b38155b347b0c2ced714 Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Wed, 2 Oct 2013 16:17:37 -0700 Subject: [PATCH 20/20] Allow child classes to override the overlay location for the update image. b/10952479 Change-Id: I59bb834f271f702fb529054dab7926b816fa35cc --- screen_ui.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/screen_ui.h b/screen_ui.h index 0bd220f7..fc35d95b 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -58,6 +58,9 @@ class ScreenRecoveryUI : public RecoveryUI { enum UIElement { HEADER, MENU, MENU_SEL_BG, MENU_SEL_FG, LOG, TEXT_FILL }; virtual void SetColor(UIElement e); + protected: + int install_overlay_offset_x, install_overlay_offset_y; + private: Icon currentIcon; int installingFrame; @@ -99,7 +102,6 @@ class ScreenRecoveryUI : public RecoveryUI { int animation_fps; int indeterminate_frames; int installing_frames; - int install_overlay_offset_x, install_overlay_offset_y; int overlay_offset_x, overlay_offset_y; void draw_install_overlay_locked(int frame);