diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/acp/Android.mk | 3 | ||||
-rw-r--r-- | tools/atree/files.cpp | 9 | ||||
-rw-r--r-- | tools/atree/fs.cpp | 7 | ||||
-rwxr-xr-x | tools/buildinfo.sh | 7 | ||||
-rwxr-xr-x | tools/post_process_props.py | 19 | ||||
-rwxr-xr-x | tools/product_debug.py | 1 | ||||
-rw-r--r-- | tools/releasetools/blockimgdiff.py | 2 | ||||
-rwxr-xr-x | tools/releasetools/build_image.py | 11 | ||||
-rw-r--r-- | tools/releasetools/common.py | 63 | ||||
-rwxr-xr-x | tools/releasetools/sign_target_files_apks | 8 | ||||
-rw-r--r-- | tools/signapk/SignApk.java | 22 | ||||
-rw-r--r-- | tools/signtos/Android.mk | 25 | ||||
-rw-r--r-- | tools/signtos/SignTos.java | 314 | ||||
-rw-r--r-- | tools/signtos/SignTos.mf | 1 | ||||
-rw-r--r-- | tools/zipalign/Android.mk | 2 | ||||
-rw-r--r-- | tools/zipalign/README.txt | 5 | ||||
-rw-r--r-- | tools/zipalign/ZipAlign.cpp | 52 | ||||
-rw-r--r-- | tools/zipalign/ZipEntry.cpp | 4 |
18 files changed, 469 insertions, 86 deletions
diff --git a/tools/acp/Android.mk b/tools/acp/Android.mk index 33c5567..2b41bc1 100644 --- a/tools/acp/Android.mk +++ b/tools/acp/Android.mk @@ -4,6 +4,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_SRC_FILES := \ acp.c @@ -21,5 +22,7 @@ LOCAL_STATIC_LIBRARIES := libhost LOCAL_C_INCLUDES := build/libs/host/include LOCAL_MODULE := acp LOCAL_ACP_UNAVAILABLE := true +LOCAL_CXX_STL := none +LOCAL_ADDRESS_SANITIZER := false include $(BUILD_HOST_EXECUTABLE) diff --git a/tools/atree/files.cpp b/tools/atree/files.cpp index df3e987..d945f58 100644 --- a/tools/atree/files.cpp +++ b/tools/atree/files.cpp @@ -447,14 +447,7 @@ list_dir(const string& path, const FileRecord& rec, continue; } string entry = path_append(path, ent->d_name); -#ifdef HAVE_DIRENT_D_TYPE - bool is_directory = (ent->d_type == DT_DIR); -#else - // If dirent.d_type is missing, then use stat instead - struct stat stat_buf; - stat(entry.c_str(), &stat_buf); - bool is_directory = S_ISDIR(stat_buf.st_mode); -#endif + bool is_directory = (ent->d_type == DT_DIR); add_more(entry, is_directory, rec, more); if (is_directory) { dirs.push_back(entry); diff --git a/tools/atree/fs.cpp b/tools/atree/fs.cpp index 9468cfd..6cd080e 100644 --- a/tools/atree/fs.cpp +++ b/tools/atree/fs.cpp @@ -63,14 +63,7 @@ remove_recursively(const string& path) string full = path; full += '/'; full += ent->d_name; -#ifdef HAVE_DIRENT_D_TYPE bool is_directory = (ent->d_type == DT_DIR); -#else - // If dirent.d_type is missing, then use stat instead - struct stat stat_buf; - stat(full.c_str(), &stat_buf); - bool is_directory = S_ISDIR(stat_buf.st_mode); -#endif if (is_directory) { dirs.push_back(full); } else { diff --git a/tools/buildinfo.sh b/tools/buildinfo.sh index a80b2db..dcd9ab5 100755 --- a/tools/buildinfo.sh +++ b/tools/buildinfo.sh @@ -36,11 +36,8 @@ echo "ro.product.cpu.abilist32=$TARGET_CPU_ABI_LIST_32_BIT" echo "ro.product.cpu.abilist64=$TARGET_CPU_ABI_LIST_64_BIT" echo "ro.product.manufacturer=$PRODUCT_MANUFACTURER" -if [ -n "$PRODUCT_DEFAULT_LANGUAGE" ] ; then - echo "ro.product.locale.language=$PRODUCT_DEFAULT_LANGUAGE" -fi -if [ -n "$PRODUCT_DEFAULT_REGION" ] ; then - echo "ro.product.locale.region=$PRODUCT_DEFAULT_REGION" +if [ -n "$PRODUCT_DEFAULT_LOCALE" ] ; then + echo "ro.product.locale=$PRODUCT_DEFAULT_LOCALE" fi echo "ro.wifi.channels=$PRODUCT_DEFAULT_WIFI_CHANNELS" echo "ro.board.platform=$TARGET_BOARD_PLATFORM" diff --git a/tools/post_process_props.py b/tools/post_process_props.py index 030826d..fa6106f 100755 --- a/tools/post_process_props.py +++ b/tools/post_process_props.py @@ -56,8 +56,6 @@ def validate(prop): """ check_pass = True buildprops = prop.to_dict() - dev_build = buildprops.get("ro.build.version.incremental", - "").startswith("eng") for key, value in buildprops.iteritems(): # Check build properties' length. if len(key) > PROP_NAME_MAX: @@ -66,19 +64,10 @@ def validate(prop): (key, PROP_NAME_MAX)) sys.stderr.write("%s (%d)\n" % (key, len(key))) if len(value) > PROP_VALUE_MAX: - # If dev build, show a warning message, otherwise fail the - # build with error message - if dev_build: - sys.stderr.write("warning: %s exceeds %d bytes: " % - (key, PROP_VALUE_MAX)) - sys.stderr.write("%s (%d)\n" % (value, len(value))) - sys.stderr.write("warning: This will cause the %s " % key) - sys.stderr.write("property return as empty at runtime\n") - else: - check_pass = False - sys.stderr.write("error: %s cannot exceed %d bytes: " % - (key, PROP_VALUE_MAX)) - sys.stderr.write("%s (%d)\n" % (value, len(value))) + check_pass = False + sys.stderr.write("error: %s cannot exceed %d bytes: " % + (key, PROP_VALUE_MAX)) + sys.stderr.write("%s (%d)\n" % (value, len(value))) return check_pass class PropFile: diff --git a/tools/product_debug.py b/tools/product_debug.py index 661c5b7..ff2657c 100755 --- a/tools/product_debug.py +++ b/tools/product_debug.py @@ -89,7 +89,6 @@ def main(argv): "PRODUCT_COPY_FILES", "PRODUCT_PACKAGES", "PRODUCT_LOCALES", - "PRODUCT_FACTORY_RAMDISK_MODULES", "PRODUCT_PROPERTY_OVERRIDES", ) diff --git a/tools/releasetools/blockimgdiff.py b/tools/releasetools/blockimgdiff.py index 8b179d5..7181475 100644 --- a/tools/releasetools/blockimgdiff.py +++ b/tools/releasetools/blockimgdiff.py @@ -277,6 +277,8 @@ class BlockImageDiff(object): if stashed_blocks > max_stashed_blocks: max_stashed_blocks = stashed_blocks + free_string = [] + if self.version == 1: src_string = xf.src_ranges.to_string_raw() elif self.version == 2: diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py index 24d5665..692ec93 100755 --- a/tools/releasetools/build_image.py +++ b/tools/releasetools/build_image.py @@ -248,10 +248,19 @@ def BuildImage(in_dir, prop_dict, out_file, build_command.extend(["-C", fs_config]) if block_list is not None: build_command.extend(["-B", block_list]) + build_command.extend(["-L", prop_dict["mount_point"]]) if fc_config is not None: build_command.append(fc_config) elif "selinux_fc" in prop_dict: build_command.append(prop_dict["selinux_fc"]) + elif fs_type.startswith("squash"): + build_command = ["mksquashfsimage.sh"] + build_command.extend([in_dir, out_file]) + build_command.extend(["-m", prop_dict["mount_point"]]) + if fc_config is not None: + build_command.extend(["-c", fc_config]) + elif "selinux_fc" in prop_dict: + build_command.extend(["-c", prop_dict["selinux_fc"]]) elif fs_type.startswith("f2fs"): build_command = ["mkf2fsuserimg.sh"] build_command.extend([out_file, prop_dict["partition_size"]]) @@ -320,6 +329,8 @@ def ImagePropFromGlobalDict(glob_dict, mount_point): d["mount_point"] = mount_point if mount_point == "system": copy_prop("fs_type", "fs_type") + # Copy the generic sysetem fs type first, override with specific one if available. + copy_prop("system_fs_type", "fs_type") copy_prop("system_size", "partition_size") copy_prop("system_journal_size", "journal_size") copy_prop("system_verity_block_device", "verity_block_device") diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py index a596c26..39c9b3d 100644 --- a/tools/releasetools/common.py +++ b/tools/releasetools/common.py @@ -1058,29 +1058,38 @@ class BlockDifference: self._WriteUpdate(script, output_zip) def WriteVerifyScript(self, script): + partition = self.partition if not self.src: - script.Print("Image %s will be patched unconditionally." % (self.partition,)) + script.Print("Image %s will be patched unconditionally." % (partition,)) else: + if self.version >= 3: + script.AppendExtra(('if block_image_verify("%s", ' + 'package_extract_file("%s.transfer.list"), ' + '"%s.new.dat", "%s.patch.dat") then') % + (self.device, partition, partition, partition)) + else: + script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % + (self.device, self.src.care_map.to_string_raw(), + self.src.TotalSha1())) + script.Print('Verified %s image...' % (partition,)) + script.AppendExtra('else'); + + # When generating incrementals for the system and vendor partitions, + # explicitly check the first block (which contains the superblock) of + # the partition to see if it's what we expect. If this check fails, + # give an explicit log message about the partition having been + # remounted R/W (the most likely explanation) and the need to flash to + # get OTAs working again. if self.check_first_block: self._CheckFirstBlock(script) - script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % - (self.device, self.src.care_map.to_string_raw(), - self.src.TotalSha1())) - script.Print("Verified %s image..." % (self.partition,)) - # Abort the OTA update if it doesn't support resumable OTA (i.e. version<3) - # and the checksum doesn't match the one in the source partition. - if self.version < 3: - script.AppendExtra(('else\n' - ' abort("%s partition has unexpected contents");\n' - 'endif;') % (self.partition)) - else: - script.AppendExtra(('else\n' - ' (range_sha1("%s", "%s") == "%s") ||\n' - ' abort("%s partition has unexpected contents");\n' - 'endif;') % - (self.device, self.tgt.care_map.to_string_raw(), - self.tgt.TotalSha1(), self.partition)) + # Abort the OTA update. Note that the incremental OTA cannot be applied + # even if it may match the checksum of the target partition. + # a) If version < 3, operations like move and erase will make changes + # unconditionally and damage the partition. + # b) If version >= 3, it won't even reach here. + script.AppendExtra(('abort("%s partition has unexpected contents");\n' + 'endif;') % (partition,)) def _WriteUpdate(self, script, output_zip): partition = self.partition @@ -1098,18 +1107,24 @@ class BlockDifference: (self.device, partition, partition, partition)) script.AppendExtra(script._WordWrap(call)) + def _HashBlocks(self, source, ranges): + data = source.ReadRangeSet(ranges) + ctx = sha1() + + for p in data: + ctx.update(p) + + return ctx.hexdigest() + def _CheckFirstBlock(self, script): r = RangeSet((0, 1)) - h = sha1() - for data in self.src.ReadRangeSet(r): - h.update(data) - h = h.hexdigest() + srchash = self._HashBlocks(self.src, r); script.AppendExtra(('(range_sha1("%s", "%s") == "%s") || ' 'abort("%s has been remounted R/W; ' 'reflash device to reenable OTA updates");') - % (self.device, r.to_string_raw(), h, self.device)) - + % (self.device, r.to_string_raw(), srchash, + self.device)) DataImage = blockimgdiff.DataImage diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks index 931acb8..9e61051 100755 --- a/tools/releasetools/sign_target_files_apks +++ b/tools/releasetools/sign_target_files_apks @@ -179,7 +179,8 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, ReplaceVerityPrivateKey(input_tf_zip, output_tf_zip, misc_info, OPTIONS.replace_verity_private_key[1]) elif (info.filename == "BOOT/RAMDISK/verity_key" and OPTIONS.replace_verity_public_key): - ReplaceVerityPublicKey(output_tf_zip, OPTIONS.replace_verity_public_key[1]) + new_data = ReplaceVerityPublicKey(output_tf_zip, OPTIONS.replace_verity_public_key[1]) + write_to_temp(info.filename, info.external_attr, new_data) elif (info.filename.startswith("BOOT/") or info.filename.startswith("RECOVERY/") or info.filename.startswith("META/") or @@ -392,7 +393,9 @@ def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): def ReplaceVerityPublicKey(targetfile_zip, key_path): print "Replacing verity public key with %s" % key_path with open(key_path) as f: - common.ZipWriteStr(targetfile_zip, "BOOT/RAMDISK/verity_key", f.read()) + data = f.read() + common.ZipWriteStr(targetfile_zip, "BOOT/RAMDISK/verity_key", data) + return data def ReplaceVerityPrivateKey(targetfile_input_zip, targetfile_output_zip, misc_info, key_path): print "Replacing verity private key with %s" % key_path @@ -400,6 +403,7 @@ def ReplaceVerityPrivateKey(targetfile_input_zip, targetfile_output_zip, misc_in original_misc_info = targetfile_input_zip.read("META/misc_info.txt") new_misc_info = original_misc_info.replace(current_key, key_path) common.ZipWriteStr(targetfile_output_zip, "META/misc_info.txt", new_misc_info) + misc_info["verity_key"] = key_path def BuildKeyMap(misc_info, key_mapping_options): for s, d in key_mapping_options: diff --git a/tools/signapk/SignApk.java b/tools/signapk/SignApk.java index e661e50..88f486a 100644 --- a/tools/signapk/SignApk.java +++ b/tools/signapk/SignApk.java @@ -35,6 +35,7 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.util.encoders.Base64; +import java.io.Console; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -166,18 +167,17 @@ class SignApk { } /** - * Reads the password from stdin and returns it as a string. + * Reads the password from console and returns it as a string. * * @param keyFile The file containing the private key. Used to prompt the user. */ private static String readPassword(File keyFile) { - // TODO: use Console.readPassword() when it's available. - System.out.print("Enter password for " + keyFile + " (password will not be hidden): "); - System.out.flush(); - BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); - try { - return stdin.readLine(); - } catch (IOException ex) { + Console console; + char[] pwd; + if((console = System.console()) != null && + (pwd = console.readPassword("[%s]", "Enter password for " + keyFile)) != null){ + return String.valueOf(pwd); + } else { return null; } } @@ -621,8 +621,12 @@ class SignApk { this.type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()); } + /** + * This should actually return byte[] or something similar, but nothing + * actually checks it currently. + */ public Object getContent() { - throw new UnsupportedOperationException(); + return this; } public ASN1ObjectIdentifier getContentType() { diff --git a/tools/signtos/Android.mk b/tools/signtos/Android.mk new file mode 100644 index 0000000..94ab944 --- /dev/null +++ b/tools/signtos/Android.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH := $(call my-dir) + +# the signtos tool - signs Trusty images +# ============================================================ +include $(CLEAR_VARS) +LOCAL_MODULE := signtos +LOCAL_SRC_FILES := SignTos.java +LOCAL_JAR_MANIFEST := SignTos.mf +LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host bouncycastle-bcpkix-host +include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/tools/signtos/SignTos.java b/tools/signtos/SignTos.java new file mode 100644 index 0000000..485ad2f --- /dev/null +++ b/tools/signtos/SignTos.java @@ -0,0 +1,314 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.signtos; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.security.GeneralSecurityException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Security; +import java.security.Signature; +import java.security.interfaces.ECKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Arrays; + +import javax.crypto.Cipher; +import javax.crypto.EncryptedPrivateKeyInfo; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +/** + * Signs Trusty images for use with operating systems that support it. + */ +public class SignTos { + /** Size of the signature footer in bytes. */ + private static final int SIGNATURE_BLOCK_SIZE = 256; + + /** Current signature version code we use. */ + private static final int VERSION_CODE = 1; + + /** Size of the header on the file to skip. */ + private static final int HEADER_SIZE = 512; + + private static BouncyCastleProvider sBouncyCastleProvider; + + /** + * Reads the password from stdin and returns it as a string. + * + * @param keyFile The file containing the private key. Used to prompt the user. + */ + private static String readPassword(File keyFile) { + // TODO: use Console.readPassword() when it's available. + System.out.print("Enter password for " + keyFile + " (password will not be hidden): "); + System.out.flush(); + BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); + try { + return stdin.readLine(); + } catch (IOException ex) { + return null; + } + } + + /** + * Decrypt an encrypted PKCS#8 format private key. + * + * Based on ghstark's post on Aug 6, 2006 at + * http://forums.sun.com/thread.jspa?threadID=758133&messageID=4330949 + * + * @param encryptedPrivateKey The raw data of the private key + * @param keyFile The file containing the private key + */ + private static PKCS8EncodedKeySpec decryptPrivateKey(byte[] encryptedPrivateKey, File keyFile) + throws GeneralSecurityException { + EncryptedPrivateKeyInfo epkInfo; + try { + epkInfo = new EncryptedPrivateKeyInfo(encryptedPrivateKey); + } catch (IOException ex) { + // Probably not an encrypted key. + return null; + } + + char[] password = readPassword(keyFile).toCharArray(); + + SecretKeyFactory skFactory = SecretKeyFactory.getInstance(epkInfo.getAlgName()); + Key key = skFactory.generateSecret(new PBEKeySpec(password)); + + Cipher cipher = Cipher.getInstance(epkInfo.getAlgName()); + cipher.init(Cipher.DECRYPT_MODE, key, epkInfo.getAlgParameters()); + + try { + return epkInfo.getKeySpec(cipher); + } catch (InvalidKeySpecException ex) { + System.err.println("signapk: Password for " + keyFile + " may be bad."); + throw ex; + } + } + + /** Read a PKCS#8 format private key. */ + private static PrivateKey readPrivateKey(File file) throws IOException, + GeneralSecurityException { + DataInputStream input = new DataInputStream(new FileInputStream(file)); + try { + byte[] bytes = new byte[(int) file.length()]; + input.read(bytes); + + /* Check to see if this is in an EncryptedPrivateKeyInfo structure. */ + PKCS8EncodedKeySpec spec = decryptPrivateKey(bytes, file); + if (spec == null) { + spec = new PKCS8EncodedKeySpec(bytes); + } + + /* + * Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm + * OID and use that to construct a KeyFactory. + */ + ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded())); + PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject()); + String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId(); + + return KeyFactory.getInstance(algOid).generatePrivate(spec); + } finally { + input.close(); + } + } + + /** + * Tries to load a JSE Provider by class name. This is for custom PrivateKey + * types that might be stored in PKCS#11-like storage. + */ + private static void loadProviderIfNecessary(String providerClassName) { + if (providerClassName == null) { + return; + } + + final Class<?> klass; + try { + final ClassLoader sysLoader = ClassLoader.getSystemClassLoader(); + if (sysLoader != null) { + klass = sysLoader.loadClass(providerClassName); + } else { + klass = Class.forName(providerClassName); + } + } catch (ClassNotFoundException e) { + e.printStackTrace(); + System.exit(1); + return; + } + + Constructor<?> constructor = null; + for (Constructor<?> c : klass.getConstructors()) { + if (c.getParameterTypes().length == 0) { + constructor = c; + break; + } + } + if (constructor == null) { + System.err.println("No zero-arg constructor found for " + providerClassName); + System.exit(1); + return; + } + + final Object o; + try { + o = constructor.newInstance(); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + return; + } + if (!(o instanceof Provider)) { + System.err.println("Not a Provider class: " + providerClassName); + System.exit(1); + } + + Security.insertProviderAt((Provider) o, 1); + } + + private static String getSignatureAlgorithm(Key key) { + if ("EC".equals(key.getAlgorithm())) { + ECKey ecKey = (ECKey) key; + int curveSize = ecKey.getParams().getOrder().bitLength(); + if (curveSize <= 256) { + return "SHA256withECDSA"; + } else if (curveSize <= 384) { + return "SHA384withECDSA"; + } else { + return "SHA512withECDSA"; + } + } else { + throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm()); + } + } + + /** + * @param inputFilename + * @param outputFilename + */ + private static void signWholeFile(InputStream input, OutputStream output, PrivateKey signingKey) + throws Exception { + Signature sig = Signature.getInstance(getSignatureAlgorithm(signingKey)); + sig.initSign(signingKey); + + byte[] buffer = new byte[8192]; + + /* Skip the header. */ + int skippedBytes = 0; + while (skippedBytes != HEADER_SIZE) { + int bytesRead = input.read(buffer, 0, HEADER_SIZE - skippedBytes); + output.write(buffer, 0, bytesRead); + skippedBytes += bytesRead; + } + + int totalBytes = 0; + for (;;) { + int bytesRead = input.read(buffer); + if (bytesRead == -1) { + break; + } + totalBytes += bytesRead; + sig.update(buffer, 0, bytesRead); + output.write(buffer, 0, bytesRead); + } + + byte[] sigBlock = new byte[SIGNATURE_BLOCK_SIZE]; + sigBlock[0] = VERSION_CODE; + sig.sign(sigBlock, 1, sigBlock.length - 1); + + output.write(sigBlock); + } + + private static void usage() { + System.err.println("Usage: signtos " + + "[-providerClass <className>] " + + " privatekey.pk8 " + + "input.img output.img"); + System.exit(2); + } + + public static void main(String[] args) throws Exception { + if (args.length < 3) { + usage(); + } + + String providerClass = null; + String providerArg = null; + + int argstart = 0; + while (argstart < args.length && args[argstart].startsWith("-")) { + if ("-providerClass".equals(args[argstart])) { + if (argstart + 1 >= args.length) { + usage(); + } + providerClass = args[++argstart]; + ++argstart; + } else { + usage(); + } + } + + /* + * Should only be "<privatekey> <input> <output>" left. + */ + if (argstart != args.length - 3) { + usage(); + } + + sBouncyCastleProvider = new BouncyCastleProvider(); + Security.addProvider(sBouncyCastleProvider); + + loadProviderIfNecessary(providerClass); + + String keyFilename = args[args.length - 3]; + String inputFilename = args[args.length - 2]; + String outputFilename = args[args.length - 1]; + + PrivateKey privateKey = readPrivateKey(new File(keyFilename)); + + InputStream input = new BufferedInputStream(new FileInputStream(inputFilename)); + OutputStream output = new BufferedOutputStream(new FileOutputStream(outputFilename)); + try { + SignTos.signWholeFile(input, output, privateKey); + } finally { + input.close(); + output.close(); + } + + System.out.println("Successfully signed: " + outputFilename); + } +} diff --git a/tools/signtos/SignTos.mf b/tools/signtos/SignTos.mf new file mode 100644 index 0000000..d860296 --- /dev/null +++ b/tools/signtos/SignTos.mf @@ -0,0 +1 @@ +Main-Class: com.android.signtos.SignTos diff --git a/tools/zipalign/Android.mk b/tools/zipalign/Android.mk index 7986798..4194f81 100644 --- a/tools/zipalign/Android.mk +++ b/tools/zipalign/Android.mk @@ -26,7 +26,7 @@ ifeq ($(HOST_OS),linux) LOCAL_LDLIBS += -lrt endif -ifneq ($(strip $(USE_MINGW)),) +ifdef USE_MINGW LOCAL_STATIC_LIBRARIES += libz else LOCAL_LDLIBS += -lz diff --git a/tools/zipalign/README.txt b/tools/zipalign/README.txt index 9c7d07e..0b80b35 100644 --- a/tools/zipalign/README.txt +++ b/tools/zipalign/README.txt @@ -5,6 +5,7 @@ usage: zipalign [-f] [-v] <align> infile.zip outfile.zip -c : check alignment only (does not modify file) -f : overwrite existing outfile.zip + -p : page align stored shared object files -v : verbose output <align> is in bytes, e.g. "4" provides 32-bit alignment infile.zip is an existing Zip archive @@ -33,3 +34,7 @@ By default, zipalign will not overwrite an existing output file. With the You can use the "-c" flag to test whether a zip archive is properly aligned. +The "-p" flag aligns any file with a ".so" extension, and which is stored +uncompressed in the zip archive, to a 4096-byte page boundary. This +facilitates directly loading shared libraries from inside a zip archive. + diff --git a/tools/zipalign/ZipAlign.cpp b/tools/zipalign/ZipAlign.cpp index dc2826b..a2dfd02 100644 --- a/tools/zipalign/ZipAlign.cpp +++ b/tools/zipalign/ZipAlign.cpp @@ -32,20 +32,39 @@ void usage(void) fprintf(stderr, "Zip alignment utility\n"); fprintf(stderr, "Copyright (C) 2009 The Android Open Source Project\n\n"); fprintf(stderr, - "Usage: zipalign [-f] [-v] [-z] <align> infile.zip outfile.zip\n" + "Usage: zipalign [-f] [-p] [-v] [-z] <align> infile.zip outfile.zip\n" " zipalign -c [-v] <align> infile.zip\n\n" ); fprintf(stderr, " <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\n"); fprintf(stderr, " -c: check alignment only (does not modify file)\n"); fprintf(stderr, " -f: overwrite existing outfile.zip\n"); + fprintf(stderr, " -p: page align stored shared object files\n"); fprintf(stderr, " -v: verbose output\n"); fprintf(stderr, " -z: recompress using Zopfli\n"); } +static int getAlignment(bool pageAlignSharedLibs, int defaultAlignment, + ZipEntry* pEntry) { + + static const int kPageAlignment = 4096; + + if (!pageAlignSharedLibs) { + return defaultAlignment; + } + + const char* ext = strrchr(pEntry->getFileName(), '.'); + if (ext && strcmp(ext, ".so") == 0) { + return kPageAlignment; + } + + return defaultAlignment; +} + /* * Copy all entries from "pZin" to "pZout", aligning as needed. */ -static int copyAndAlign(ZipFile* pZin, ZipFile* pZout, int alignment, bool zopfli) +static int copyAndAlign(ZipFile* pZin, ZipFile* pZout, int alignment, bool zopfli, + bool pageAlignSharedLibs) { int numEntries = pZin->getNumEntries(); ZipEntry* pEntry; @@ -75,13 +94,15 @@ static int copyAndAlign(ZipFile* pZin, ZipFile* pZout, int alignment, bool zopfl status = pZout->add(pZin, pEntry, padding, &pNewEntry); } } else { + const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry); + /* * Copy the entry, adjusting as required. We assume that the * file position in the new file will be equal to the file * position in the original. */ long newOffset = pEntry->getFileOffset() + bias; - padding = (alignment - (newOffset % alignment)) % alignment; + padding = (alignTo - (newOffset % alignTo)) % alignTo; //printf("--- %s: orig at %ld(+%d) len=%ld, adding pad=%d\n", // pEntry->getFileName(), (long) pEntry->getFileOffset(), @@ -105,7 +126,7 @@ static int copyAndAlign(ZipFile* pZin, ZipFile* pZout, int alignment, bool zopfl * output file exists and "force" wasn't specified. */ static int process(const char* inFileName, const char* outFileName, - int alignment, bool force, bool zopfli) + int alignment, bool force, bool zopfli, bool pageAlignSharedLibs) { ZipFile zin, zout; @@ -136,7 +157,7 @@ static int process(const char* inFileName, const char* outFileName, return 1; } - int result = copyAndAlign(&zin, &zout, alignment, zopfli); + int result = copyAndAlign(&zin, &zout, alignment, zopfli, pageAlignSharedLibs); if (result != 0) { printf("zipalign: failed rewriting '%s' to '%s'\n", inFileName, outFileName); @@ -147,7 +168,8 @@ static int process(const char* inFileName, const char* outFileName, /* * Verify the alignment of a zip archive. */ -static int verify(const char* fileName, int alignment, bool verbose) +static int verify(const char* fileName, int alignment, bool verbose, + bool pageAlignSharedLibs) { ZipFile zipFile; bool foundBad = false; @@ -172,11 +194,12 @@ static int verify(const char* fileName, int alignment, bool verbose) } } else { long offset = pEntry->getFileOffset(); - if ((offset % alignment) != 0) { + const int alignTo = getAlignment(pageAlignSharedLibs, alignment, pEntry); + if ((offset % alignTo) != 0) { if (verbose) { printf("%8ld %s (BAD - %ld)\n", (long) offset, pEntry->getFileName(), - offset % alignment); + offset % alignTo); } foundBad = true; } else { @@ -204,6 +227,7 @@ int main(int argc, char* const argv[]) bool force = false; bool verbose = false; bool zopfli = false; + bool pageAlignSharedLibs = false; int result = 1; int alignment; char* endp; @@ -233,6 +257,9 @@ int main(int argc, char* const argv[]) case 'z': zopfli = true; break; + case 'p': + pageAlignSharedLibs = true; + break; default: fprintf(stderr, "ERROR: unknown flag -%c\n", *cp); wantUsage = true; @@ -260,14 +287,15 @@ int main(int argc, char* const argv[]) if (check) { /* check existing archive for correct alignment */ - result = verify(argv[1], alignment, verbose); + result = verify(argv[1], alignment, verbose, pageAlignSharedLibs); } else { /* create the new archive */ - result = process(argv[1], argv[2], alignment, force, zopfli); + result = process(argv[1], argv[2], alignment, force, zopfli, pageAlignSharedLibs); /* trust, but verify */ - if (result == 0) - result = verify(argv[2], alignment, verbose); + if (result == 0) { + result = verify(argv[2], alignment, verbose, pageAlignSharedLibs); + } } bail: diff --git a/tools/zipalign/ZipEntry.cpp b/tools/zipalign/ZipEntry.cpp index d4d366d..b2270cb 100644 --- a/tools/zipalign/ZipEntry.cpp +++ b/tools/zipalign/ZipEntry.cpp @@ -356,7 +356,7 @@ time_t ZipEntry::getModWhen(void) const */ void ZipEntry::setModWhen(time_t when) { -#ifdef HAVE_LOCALTIME_R +#if !defined(_WIN32) struct tm tmResult; #endif time_t even; @@ -368,7 +368,7 @@ void ZipEntry::setModWhen(time_t when) even = (time_t)(((unsigned long)(when) + 1) & (~1)); /* expand */ -#ifdef HAVE_LOCALTIME_R +#if !defined(_WIN32) ptm = localtime_r(&even, &tmResult); #else ptm = localtime(&even); |