diff --git a/core/Makefile b/core/Makefile index ff7e26a67..6e81784e4 100644 --- a/core/Makefile +++ b/core/Makefile @@ -679,10 +679,11 @@ $(kernel_notice_file): \ # before the rules that use that variable to build the image. ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_ETC)/security/otacerts.zip $(TARGET_OUT_ETC)/security/otacerts.zip: KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR) -$(TARGET_OUT_ETC)/security/otacerts.zip: $(addsuffix .x509.pem,$(DEFAULT_KEY_CERT_PAIR)) +$(TARGET_OUT_ETC)/security/otacerts.zip: $(addsuffix .x509.pem,$(DEFAULT_KEY_CERT_PAIR)) | $(ZIPTIME) $(hide) rm -f $@ $(hide) mkdir -p $(dir $@) - $(hide) zip -qj $@ $< + $(hide) zip -qjX $@ $< + $(remove-timestamps-from-package) .PHONY: otacerts otacerts: $(TARGET_OUT_ETC)/security/otacerts.zip @@ -940,9 +941,10 @@ $(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) $(MINIGZIP) \ $(call build-recoveryimage-target, $@) ifneq ($(BOARD_USES_FULL_RECOVERY_IMAGE),true) -$(RECOVERY_RESOURCE_ZIP): $(INSTALLED_RECOVERYIMAGE_TARGET) +$(RECOVERY_RESOURCE_ZIP): $(INSTALLED_RECOVERYIMAGE_TARGET) | $(ZIPTIME) $(hide) mkdir -p $(dir $@) - $(hide) find $(TARGET_RECOVERY_ROOT_OUT)/res -type f | sort | zip -0qrj $@ -@ + $(hide) find $(TARGET_RECOVERY_ROOT_OUT)/res -type f | sort | zip -0qrjX $@ -@ + $(remove-timestamps-from-package) endif .PHONY: recoveryimage-nodeps @@ -1130,23 +1132,24 @@ stnod: systemtarball-nodeps ## Files under out dir will be rejected to prevent possible conflicts with other rules. PDK_PLATFORM_ZIP_PRODUCT_BINARIES := $(filter-out $(OUT_DIR)/%,$(PDK_PLATFORM_ZIP_PRODUCT_BINARIES)) INSTALLED_PLATFORM_ZIP := $(PRODUCT_OUT)/platform.zip -$(INSTALLED_PLATFORM_ZIP) : $(INTERNAL_SYSTEMIMAGE_FILES) +$(INSTALLED_PLATFORM_ZIP) : $(INTERNAL_SYSTEMIMAGE_FILES) | $(ZIPTIME) $(call pretty,"Platform zip package: $(INSTALLED_PLATFORM_ZIP)") $(hide) rm -f $@ - $(hide) cd $(dir $@) && zip -qry $(notdir $@) \ + $(hide) cd $(dir $@) && zip -qryX $(notdir $@) \ $(TARGET_COPY_OUT_SYSTEM) \ $(patsubst $(PRODUCT_OUT)/%, %, $(TARGET_OUT_NOTICE_FILES)) \ $(addprefix symbols/,$(PDK_SYMBOL_FILES_LIST)) ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE - $(hide) cd $(dir $@) && zip -qry $(notdir $@) \ + $(hide) cd $(dir $@) && zip -qryX $(notdir $@) \ $(TARGET_COPY_OUT_VENDOR) endif ifneq ($(PDK_PLATFORM_JAVA_ZIP_CONTENTS),) - $(hide) cd $(OUT_DIR) && zip -qry $(patsubst $(OUT_DIR)/%,%,$@) $(PDK_PLATFORM_JAVA_ZIP_CONTENTS) + $(hide) cd $(OUT_DIR) && zip -qryX $(patsubst $(OUT_DIR)/%,%,$@) $(PDK_PLATFORM_JAVA_ZIP_CONTENTS) endif ifneq ($(PDK_PLATFORM_ZIP_PRODUCT_BINARIES),) - $(hide) zip -qry $@ $(PDK_PLATFORM_ZIP_PRODUCT_BINARIES) + $(hide) zip -qryX $@ $(PDK_PLATFORM_ZIP_PRODUCT_BINARIES) endif + $(remove-timestamps-from-package) .PHONY: platform platform: $(INSTALLED_PLATFORM_ZIP) @@ -1405,7 +1408,7 @@ otatools: $(OTATOOLS) BUILT_OTATOOLS_PACKAGE := $(PRODUCT_OUT)/otatools.zip $(BUILT_OTATOOLS_PACKAGE): zip_root := $(call intermediates-dir-for,PACKAGING,otatools)/otatools -$(BUILT_OTATOOLS_PACKAGE): $(OTATOOLS) | $(ACP) +$(BUILT_OTATOOLS_PACKAGE): $(OTATOOLS) | $(ACP) $(ZIPTIME) @echo "Package OTA tools: $@" $(hide) rm -rf $@ $(zip_root) $(hide) mkdir -p $(dir $@) $(zip_root)/bin $(zip_root)/framework $(zip_root)/releasetools $(zip_root)/system/extras/verity @@ -1414,9 +1417,10 @@ $(BUILT_OTATOOLS_PACKAGE): $(OTATOOLS) | $(ACP) $(hide) $(ACP) -p system/extras/verity/build_verity_metadata.py $(zip_root)/system/extras/verity/ $(hide) $(ACP) -r -d -p build/tools/releasetools/* $(zip_root)/releasetools $(hide) rm -rf $@ $(zip_root)/releasetools/*.pyc - $(hide) (cd $(zip_root) && zip -qry $(abspath $@) *) - $(hide) zip -qry $(abspath $@) build/target/product/security/ - $(hide) find device vendor -name \*.pk8 -o -name \*.x509.pem -o -name oem.prop | xargs zip -qry $(abspath $@)>/dev/null || true + $(hide) (cd $(zip_root) && zip -qryX $(abspath $@) *) + $(hide) zip -qryX $(abspath $@) build/target/product/security/ + $(hide) find device vendor -name \*.pk8 -o -name \*.x509.pem -o -name oem.prop | xargs zip -qryX $(abspath $@)>/dev/null || true + $(remove-timestamps-from-package) .PHONY: otatools-package otatools-package: $(BUILT_OTATOOLS_PACKAGE) @@ -1493,7 +1497,7 @@ $(BUILT_TARGET_FILES_PACKAGE): \ $(SELINUX_FC) \ $(APKCERTS_FILE) \ $(HOST_OUT_EXECUTABLES)/fs_config \ - | $(ACP) + | $(ACP) $(ZIPTIME) @echo "Package target files: $@" $(hide) rm -rf $@ $(zip_root) $(hide) mkdir -p $(dir $@) $(zip_root) @@ -1638,7 +1642,7 @@ ifeq ($(BREAKPAD_GENERATE_SYMBOLS),true) $(hide) $(ACP) -r $(TARGET_OUT_BREAKPAD) $(zip_root)/BREAKPAD endif @# Zip everything up, preserving symlinks - $(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .) + $(hide) (cd $(zip_root) && zip -qryX ../$(notdir $@) .) @# Run fs_config on all the system, vendor, boot ramdisk, @# and recovery ramdisk files in the zip, and save the output $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM/" } /^SYSTEM\// {print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) > $(zip_root)/META/filesystem_config.txt @@ -1651,9 +1655,10 @@ endif ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),) $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="RECOVERY/RAMDISK/" } /^RECOVERY\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) > $(zip_root)/META/recovery_filesystem_config.txt endif - $(hide) (cd $(zip_root) && zip -q ../$(notdir $@) META/*filesystem_config.txt) + $(hide) (cd $(zip_root) && zip -qX ../$(notdir $@) META/*filesystem_config.txt) $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH MKBOOTIMG=$(MKBOOTIMG) \ ./build/tools/releasetools/add_img_to_target_files -v -p $(HOST_OUT) $@ + $(remove-timestamps-from-package) .PHONY: target-files-package target-files-package: $(BUILT_TARGET_FILES_PACKAGE) @@ -1727,11 +1732,12 @@ SYMBOLS_ZIP := $(PRODUCT_OUT)/$(name).zip ifndef TARGET_BUILD_APPS $(SYMBOLS_ZIP): $(INSTALLED_SYSTEMIMAGE) $(INSTALLED_BOOTIMAGE_TARGET) endif -$(SYMBOLS_ZIP): +$(SYMBOLS_ZIP): | $(ZIPTIME) @echo "Package symbols: $@" $(hide) rm -rf $@ $(hide) mkdir -p $(dir $@) $(TARGET_OUT_UNSTRIPPED) - $(hide) zip -qr $@ $(TARGET_OUT_UNSTRIPPED) + $(hide) zip -qrX $@ $(TARGET_OUT_UNSTRIPPED) + $(remove-timestamps-from-package) # ----------------------------------------------------------------- # A zip of the Android Apps. Not keeping full path so that we don't @@ -1744,7 +1750,7 @@ endif name := $(name)-apps-$(FILE_NAME_TAG) APPS_ZIP := $(PRODUCT_OUT)/$(name).zip -$(APPS_ZIP): $(INSTALLED_SYSTEMIMAGE) +$(APPS_ZIP): $(INSTALLED_SYSTEMIMAGE) | $(ZIPTIME) @echo "Package apps: $@" $(hide) rm -rf $@ $(hide) mkdir -p $(dir $@) @@ -1753,8 +1759,9 @@ $(APPS_ZIP): $(INSTALLED_SYSTEMIMAGE) echo "No apps to zip up. Generating empty apps archive." ; \ a=$$(mktemp /tmp/XXXXXXX) && touch $$a && zip $@ $$a && zip -d $@ $$a; \ else \ - zip -qj $@ $$apps_to_zip; \ + zip -qjX $@ $$apps_to_zip; \ fi + $(remove-timestamps-from-package) #------------------------------------------------------------------ # A zip of emma code coverage meta files. Generated for fully emma @@ -1763,10 +1770,12 @@ $(APPS_ZIP): $(INSTALLED_SYSTEMIMAGE) ifeq (true,$(EMMA_INSTRUMENT)) EMMA_META_ZIP := $(PRODUCT_OUT)/emma_meta.zip # the dependency will be set up later in build/core/main.mk. +$(EMMA_META_ZIP): | $(ZIPTIME) $(EMMA_META_ZIP) : @echo "Collecting Emma coverage meta files." $(hide) find $(TARGET_COMMON_OUT_ROOT) $(HOST_COMMON_OUT_ROOT) -name "coverage.em" | \ - zip -@ -q $@ + zip -@ -qX $@ + $(remove-timestamps-from-package) endif # EMMA_INSTRUMENT=true @@ -1777,18 +1786,20 @@ endif # EMMA_INSTRUMENT=true ifdef TARGET_BUILD_APPS PROGUARD_DICT_ZIP := $(PRODUCT_OUT)/$(TARGET_PRODUCT)-proguard-dict-$(FILE_NAME_TAG).zip # the dependency will be set up later in build/core/main.mk. +$(PROGUARD_DICT_ZIP): | $(ZIPTIME) $(PROGUARD_DICT_ZIP) : @echo "Packaging Proguard obfuscation dictionary files." $(hide) dict_files=`find $(TARGET_OUT_COMMON_INTERMEDIATES)/APPS -name proguard_dictionary`; \ if [ -n "$$dict_files" ]; then \ unobfuscated_jars=$${dict_files//proguard_dictionary/classes.jar}; \ - zip -q $@ $$dict_files $$unobfuscated_jars; \ + zip -qX $@ $$dict_files $$unobfuscated_jars; \ else \ touch $(dir $@)/zipdummy; \ (cd $(dir $@) && zip -q $(notdir $@) zipdummy); \ zip -qd $@ zipdummy; \ rm $(dir $@)/zipdummy; \ fi + $(remove-timestamps-from-package) endif # TARGET_BUILD_APPS @@ -1811,9 +1822,10 @@ name := $(TARGET_PRODUCT)-emulator-$(FILE_NAME_TAG) INTERNAL_EMULATOR_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip -$(INTERNAL_EMULATOR_PACKAGE_TARGET): $(INTERNAL_EMULATOR_PACKAGE_FILES) +$(INTERNAL_EMULATOR_PACKAGE_TARGET): $(INTERNAL_EMULATOR_PACKAGE_FILES) | $(ZIPTIME) @echo "Package: $@" - $(hide) zip -qj $@ $(INTERNAL_EMULATOR_PACKAGE_FILES) + $(hide) zip -qjX $@ $(INTERNAL_EMULATOR_PACKAGE_FILES) + $(remove-timestamps-from-package) endif # ----------------------------------------------------------------- @@ -1913,7 +1925,7 @@ $(INTERNAL_SDK_TARGET): PRIVATE_INPUT_FILES := $(sdk_atree_files) # #SDK_GNU_ERROR := true -$(INTERNAL_SDK_TARGET): $(deps) +$(INTERNAL_SDK_TARGET): $(deps) | $(ZIPTIME) @echo "Package SDK: $@" $(hide) rm -rf $(PRIVATE_DIR) $@ $(hide) for f in $(target_gnu_MODULES); do \ @@ -1948,8 +1960,9 @@ $(INTERNAL_SDK_TARGET): $(deps) HOST_OUT_EXECUTABLES=$(HOST_OUT_EXECUTABLES) HOST_OS=$(HOST_OS) \ development/build/tools/sdk_clean.sh $(PRIVATE_DIR) && \ chmod -R ug+rwX $(PRIVATE_DIR) && \ - cd $(dir $@) && zip -rq $(notdir $@) $(PRIVATE_NAME) \ + cd $(dir $@) && zip -rqX $(notdir $@) $(PRIVATE_NAME) \ ) || ( rm -rf $(PRIVATE_DIR) $@ && exit 44 ) + $(remove-timestamps-from-package) # Is a Windows SDK requested? If so, we need some definitions from here diff --git a/core/config.mk b/core/config.mk index 34628acb6..483943851 100644 --- a/core/config.mk +++ b/core/config.mk @@ -524,6 +524,7 @@ ACP := $(BUILD_OUT_EXECUTABLES)/acp$(BUILD_EXECUTABLE_SUFFIX) # dx is java behind a shell script; no .exe necessary. DX := $(HOST_OUT_EXECUTABLES)/dx ZIPALIGN := $(HOST_OUT_EXECUTABLES)/zipalign$(HOST_EXECUTABLE_SUFFIX) +ZIPTIME := $(HOST_OUT_EXECUTABLES)/ziptime$(HOST_EXECUTABLE_SUFFIX) # relocation packer RELOCATION_PACKER := prebuilts/misc/$(BUILD_OS)-$(HOST_PREBUILT_ARCH)/relocation_packer/relocation_packer diff --git a/core/definitions.mk b/core/definitions.mk index ce7602bf7..ad686883a 100644 --- a/core/definitions.mk +++ b/core/definitions.mk @@ -2094,13 +2094,13 @@ $(hide) mkdir -p $(addprefix $(dir $@)lib/,$(PRIVATE_JNI_SHARED_LIBRARIES_ABI)) $(foreach abi,$(PRIVATE_JNI_SHARED_LIBRARIES_ABI),\ $(call _add-jni-shared-libs-to-package-per-abi,$(abi),\ $(patsubst $(abi):%,%,$(filter $(abi):%,$(PRIVATE_JNI_SHARED_LIBRARIES))))) -$(hide) (cd $(dir $@) && zip -qr $(JNI_COMPRESS_FLAGS) $(notdir $@) lib) +$(hide) (cd $(dir $@) && zip -qrX $(JNI_COMPRESS_FLAGS) $(notdir $@) lib) $(hide) rm -rf $(dir $@)lib endef #TODO: update the manifest to point to the dex file define add-dex-to-package -$(hide) zip -qj $@ $(dir $(PRIVATE_DEX_FILE))classes*.dex +$(hide) zip -qjX $@ $(dir $(PRIVATE_DEX_FILE))classes*.dex endef # Add java resources added by the current module. @@ -2147,6 +2147,12 @@ $(hide) $(ZIPALIGN) \ $(hide) mv $@.aligned $@ endef +# Remove dynamic timestamps from packages +# +define remove-timestamps-from-package +$(hide) $(ZIPTIME) $@ +endef + # Uncompress shared libraries embedded in an apk. # define uncompress-shared-libs @@ -2154,7 +2160,7 @@ $(hide) if unzip -l $@ $(PRIVATE_EMBEDDED_JNI_LIBS) >/dev/null ; then \ rm -rf $(dir $@)uncompressedlibs && mkdir $(dir $@)uncompressedlibs; \ unzip $@ $(PRIVATE_EMBEDDED_JNI_LIBS) -d $(dir $@)uncompressedlibs && \ zip -d $@ 'lib/*.so' && \ - ( cd $(dir $@)uncompressedlibs && zip -D -r -0 ../$(notdir $@) lib ) && \ + ( cd $(dir $@)uncompressedlibs && zip -D -r -X -0 ../$(notdir $@) lib ) && \ rm -rf $(dir $@)uncompressedlibs; \ fi endef diff --git a/core/droiddoc.mk b/core/droiddoc.mk index cc2a915d8..c0a27326e 100644 --- a/core/droiddoc.mk +++ b/core/droiddoc.mk @@ -237,11 +237,12 @@ ifeq ($(strip $(LOCAL_UNINSTALLABLE_MODULE)),) # Define a rule to create a zip of these docs. out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip $(out_zip): PRIVATE_DOCS_DIR := $(out_dir) -$(out_zip): $(full_target) +$(out_zip): $(full_target) | $(ZIPTIME) @echo Package docs: $@ @rm -f $@ @mkdir -p $(dir $@) - $(hide) ( F=$$(pwd)/$@ ; cd $(PRIVATE_DOCS_DIR) && zip -rq $$F * ) + $(hide) ( F=$$(pwd)/$@ ; cd $(PRIVATE_DOCS_DIR) && zip -rqX $$F * ) + $(remove-timestamps-from-package) $(LOCAL_MODULE)-docs.zip : $(out_zip) diff --git a/core/java_library.mk b/core/java_library.mk index 5a2d19bff..dc6186f3f 100644 --- a/core/java_library.mk +++ b/core/java_library.mk @@ -81,7 +81,7 @@ else # !LOCAL_IS_STATIC_JAVA_LIBRARY $(common_javalib.jar): PRIVATE_DEX_FILE := $(built_dex) $(common_javalib.jar): PRIVATE_SOURCE_ARCHIVE := $(full_classes_jarjar_jar) $(common_javalib.jar): PRIVATE_DONT_DELETE_JAR_DIRS := $(LOCAL_DONT_DELETE_JAR_DIRS) -$(common_javalib.jar) : $(built_dex) $(java_resource_sources) +$(common_javalib.jar) : $(built_dex) $(java_resource_sources) | $(ZIPTIME) @echo "target Jar: $(PRIVATE_MODULE) ($@)" ifdef LOCAL_JACK_ENABLED $(create-empty-package) @@ -92,6 +92,7 @@ endif ifdef LOCAL_JACK_ENABLED $(add-carried-jack-resources) endif + $(remove-timestamps-from-package) ifdef LOCAL_DEX_PREOPT ifneq ($(dexpreopt_boot_jar_module),) # boot jar diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk index 56a7f6fd4..2050fc732 100644 --- a/core/tasks/cts.mk +++ b/core/tasks/cts.mk @@ -385,9 +385,10 @@ $(INTERNAL_CTS_TARGET): PRIVATE_NAME := $(cts_name) $(INTERNAL_CTS_TARGET): PRIVATE_CTS_DIR := $(cts_dir) $(INTERNAL_CTS_TARGET): PRIVATE_DIR := $(cts_dir)/$(cts_name) $(INTERNAL_CTS_TARGET): TMP_DIR := $(cts_dir)/temp -$(INTERNAL_CTS_TARGET): $(cts_dir)/all_cts_files_stamp $(DEFAULT_TEST_PLAN) +$(INTERNAL_CTS_TARGET): $(cts_dir)/all_cts_files_stamp $(DEFAULT_TEST_PLAN) | $(ZIPTIME) $(hide) echo "Package CTS: $@" - $(hide) cd $(dir $@) && zip -rq $(notdir $@) $(PRIVATE_NAME) + $(hide) cd $(dir $@) && zip -rqX $(notdir $@) $(PRIVATE_NAME) + $(remove-timestamps-from-package) .PHONY: cts cts: $(INTERNAL_CTS_TARGET) adb diff --git a/core/tasks/sdk-addon.mk b/core/tasks/sdk-addon.mk index 5ac9b7d47..0062f7e9a 100644 --- a/core/tasks/sdk-addon.mk +++ b/core/tasks/sdk-addon.mk @@ -104,20 +104,22 @@ $(full_target): PRIVATE_DOCS_DIRS := $(addprefix $(OUT_DOCS)/, $(doc_modules)) $(full_target): PRIVATE_STAGING_DIR := $(call append-path,$(staging),$(addon_dir_leaf)) -$(full_target): $(sdk_addon_deps) | $(ACP) +$(full_target): $(sdk_addon_deps) | $(ACP) $(ZIPTIME) @echo Packaging SDK Addon: $@ $(hide) mkdir -p $(PRIVATE_STAGING_DIR)/docs $(hide) for d in $(PRIVATE_DOCS_DIRS); do \ $(ACP) -r $$d $(PRIVATE_STAGING_DIR)/docs ;\ done $(hide) mkdir -p $(dir $@) - $(hide) ( F=$$(pwd)/$@ ; cd $(PRIVATE_STAGING_DIR)/.. && zip -rq $$F $(notdir $(PRIVATE_STAGING_DIR)) ) + $(hide) ( F=$$(pwd)/$@ ; cd $(PRIVATE_STAGING_DIR)/.. && zip -rqX $$F $(notdir $(PRIVATE_STAGING_DIR)) ) + $(remove-timestamps-from-package) $(full_target_img): PRIVATE_STAGING_DIR := $(call append-path,$(staging),$(addon_dir_img))/images/$(TARGET_CPU_ABI) -$(full_target_img): $(full_target) $(addon_img_source_prop) +$(full_target_img): $(full_target) $(addon_img_source_prop) | $(ZIPTIME) @echo Packaging SDK Addon System-Image: $@ $(hide) mkdir -p $(dir $@) - $(hide) ( F=$$(pwd)/$@ ; cd $(PRIVATE_STAGING_DIR)/.. && zip -rq $$F $(notdir $(PRIVATE_STAGING_DIR)) ) + $(hide) ( F=$$(pwd)/$@ ; cd $(PRIVATE_STAGING_DIR)/.. && zip -rqX $$F $(notdir $(PRIVATE_STAGING_DIR)) ) + $(remove-timestamps-from-package) .PHONY: sdk_addon diff --git a/core/tasks/tools/package-modules.mk b/core/tasks/tools/package-modules.mk index a70e644e4..2e1ad21b5 100644 --- a/core/tasks/tools/package-modules.mk +++ b/core/tasks/tools/package-modules.mk @@ -47,7 +47,7 @@ endef my_package_zip := $(my_staging_dir)/$(my_package_name).zip $(my_package_zip): PRIVATE_COPY_PAIRS := $(my_copy_pairs) $(my_package_zip): PRIVATE_PICKUP_FILES := $(my_pickup_files) -$(my_package_zip) : $(my_built_modules) +$(my_package_zip) : $(my_built_modules) | $(ZIPTIME) @echo "Package $@" @rm -rf $(dir $@) && mkdir -p $(dir $@) $(call copy-tests-in-batch,$(wordlist 1,200,$(PRIVATE_COPY_PAIRS))) @@ -59,4 +59,5 @@ $(my_package_zip) : $(my_built_modules) $(call copy-tests-in-batch,$(wordlist 1201,9999,$(PRIVATE_COPY_PAIRS))) $(hide) $(foreach f, $(PRIVATE_PICKUP_FILES),\ cp -RfL $(f) $(dir $@);) - $(hide) cd $(dir $@) && zip -rq $(notdir $@) * + $(hide) cd $(dir $@) && zip -rqX $(notdir $@) * + $(remove-timestamps-from-package) diff --git a/tools/ziptime/Android.mk b/tools/ziptime/Android.mk new file mode 100644 index 000000000..3575229eb --- /dev/null +++ b/tools/ziptime/Android.mk @@ -0,0 +1,32 @@ +# +# Copyright 2015 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. +# + +# +# Zip timestamp removal tool +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + ZipTime.cpp \ + ZipEntry.cpp \ + ZipFile.cpp + +LOCAL_MODULE := ziptime +LOCAL_MODULE_HOST_OS := darwin linux windows + +include $(BUILD_HOST_EXECUTABLE) diff --git a/tools/ziptime/README.txt b/tools/ziptime/README.txt new file mode 100644 index 000000000..8a101e9af --- /dev/null +++ b/tools/ziptime/README.txt @@ -0,0 +1,10 @@ +ziptime -- zip timestamp tool + +usage: ziptime file.zip + + file.zip is an existing Zip archive to rewrite + + +This tools replaces the timestamps in the zip headers with a static time +(Jan 1 2008). The extra fields are not changed, so you'll need to use the +-X option to zip so that it doesn't create the 'universal time' extra. diff --git a/tools/ziptime/ZipEntry.cpp b/tools/ziptime/ZipEntry.cpp new file mode 100644 index 000000000..bdbdd3224 --- /dev/null +++ b/tools/ziptime/ZipEntry.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access to entries in a Zip archive. +// + +#include "ZipEntry.h" + +#include +#include +#include + +using namespace android; + +#define LOG(...) fprintf(stderr, __VA_ARGS__) + +/* Jan 01 2008 */ +#define STATIC_DATE (28 << 9 | 1 << 5 | 1) +#define STATIC_TIME 0 + +/* + * Initialize a new ZipEntry structure from a FILE* positioned at a + * CentralDirectoryEntry. Rewrites the headers to remove the dynamic + * timestamps. + * + * On exit, the file pointer will be at the start of the next CDE or + * at the EOCD. + */ +status_t ZipEntry::initAndRewriteFromCDE(FILE* fp) +{ + status_t result; + long posn; + + /* read the CDE */ + result = mCDE.rewrite(fp); + if (result != 0) { + LOG("mCDE.rewrite failed\n"); + return result; + } + + /* using the info in the CDE, go load up the LFH */ + posn = ftell(fp); + if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { + LOG("local header seek failed (%ld)\n", + mCDE.mLocalHeaderRelOffset); + return -1; + } + + result = mLFH.rewrite(fp); + if (result != 0) { + LOG("mLFH.rewrite failed\n"); + return result; + } + + if (fseek(fp, posn, SEEK_SET) != 0) + return -1; + + return 0; +} + +/* + * =========================================================================== + * ZipEntry::LocalFileHeader + * =========================================================================== + */ + +/* + * Rewrite a local file header. + * + * On entry, "fp" points to the signature at the start of the header. + */ +status_t ZipEntry::LocalFileHeader::rewrite(FILE* fp) +{ + status_t result = 0; + unsigned char buf[kLFHLen]; + + if (fread(buf, 1, kLFHLen, fp) != kLFHLen) + return -1; + + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { + LOG("whoops: didn't find expected signature\n"); + return -1; + } + + ZipEntry::putShortLE(&buf[0x0a], STATIC_TIME); + ZipEntry::putShortLE(&buf[0x0c], STATIC_DATE); + + if (fseek(fp, -kLFHLen, SEEK_CUR) != 0) + return -1; + + if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) + return -1; + + return 0; +} + +/* + * =========================================================================== + * ZipEntry::CentralDirEntry + * =========================================================================== + */ + +/* + * Read and rewrite the central dir entry that appears next in the file. + * + * On entry, "fp" should be positioned on the signature bytes for the + * entry. On exit, "fp" will point at the signature word for the next + * entry or for the EOCD. + */ +status_t ZipEntry::CentralDirEntry::rewrite(FILE* fp) +{ + status_t result = 0; + unsigned char buf[kCDELen]; + unsigned short fileNameLength, extraFieldLength, fileCommentLength; + + if (fread(buf, 1, kCDELen, fp) != kCDELen) + return -1; + + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { + LOG("Whoops: didn't find expected signature\n"); + return -1; + } + + ZipEntry::putShortLE(&buf[0x0c], STATIC_TIME); + ZipEntry::putShortLE(&buf[0x0e], STATIC_DATE); + + fileNameLength = ZipEntry::getShortLE(&buf[0x1c]); + extraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); + fileCommentLength = ZipEntry::getShortLE(&buf[0x20]); + mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); + + if (fseek(fp, -kCDELen, SEEK_CUR) != 0) + return -1; + + if (fwrite(buf, 1, kCDELen, fp) != kCDELen) + return -1; + + if (fseek(fp, fileNameLength + extraFieldLength + fileCommentLength, SEEK_CUR) != 0) + return -1; + + return 0; +} diff --git a/tools/ziptime/ZipEntry.h b/tools/ziptime/ZipEntry.h new file mode 100644 index 000000000..beea20c07 --- /dev/null +++ b/tools/ziptime/ZipEntry.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Zip archive entries. +// +// The ZipEntry class is tightly meshed with the ZipFile class. +// +#ifndef __LIBS_ZIPENTRY_H +#define __LIBS_ZIPENTRY_H + +#include +#include + +typedef int status_t; + +namespace android { + +class ZipFile; + +/* + * ZipEntry objects represent a single entry in a Zip archive. + * + * File information is stored in two places: next to the file data (the Local + * File Header, and possibly a Data Descriptor), and at the end of the file + * (the Central Directory Entry). The two must be kept in sync. + */ +class ZipEntry { +public: + friend class ZipFile; + + ZipEntry(void) {} + ~ZipEntry(void) {} + + /* + * Some basic functions for raw data manipulation. "LE" means + * Little Endian. + */ + static inline unsigned short getShortLE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8); + } + static inline unsigned long getLongLE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + static inline void putShortLE(unsigned char* buf, short val) { + buf[0] = (unsigned char) val; + buf[1] = (unsigned char) (val >> 8); + } + +protected: + /* + * Initialize the structure from the file, which is pointing at + * our Central Directory entry. And rewrite it. + */ + status_t initAndRewriteFromCDE(FILE* fp); + +private: + /* these are private and not defined */ + ZipEntry(const ZipEntry& src); + ZipEntry& operator=(const ZipEntry& src); + + /* + * Every entry in the Zip archive starts off with one of these. + */ + class LocalFileHeader { + public: + LocalFileHeader(void) {} + + status_t rewrite(FILE* fp); + + enum { + kSignature = 0x04034b50, + kLFHLen = 30, // LocalFileHdr len, excl. var fields + }; + }; + + /* + * Every entry in the Zip archive has one of these in the "central + * directory" at the end of the file. + */ + class CentralDirEntry { + public: + CentralDirEntry(void) : + mLocalHeaderRelOffset(0) + {} + + status_t rewrite(FILE* fp); + + unsigned long mLocalHeaderRelOffset; + + enum { + kSignature = 0x02014b50, + kCDELen = 46, // CentralDirEnt len, excl. var fields + }; + }; + + LocalFileHeader mLFH; + CentralDirEntry mCDE; +}; + +}; // namespace android + +#endif // __LIBS_ZIPENTRY_H diff --git a/tools/ziptime/ZipFile.cpp b/tools/ziptime/ZipFile.cpp new file mode 100644 index 000000000..c4c898e9b --- /dev/null +++ b/tools/ziptime/ZipFile.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access to Zip archives. +// + +#include "ZipFile.h" + +#include +#include +#include +#include + +using namespace android; + +#define LOG(...) fprintf(stderr, __VA_ARGS__) + +/* + * Open a file and rewrite the headers + */ +status_t ZipFile::rewrite(const char* zipFileName) +{ + assert(mZipFp == NULL); // no reopen + + /* open the file */ + mZipFp = fopen(zipFileName, "r+b"); + if (mZipFp == NULL) { + int err = errno; + LOG("fopen failed: %d\n", err); + return -1; + } + + /* + * Load the central directory. If that fails, then this probably + * isn't a Zip archive. + */ + return rewriteCentralDir(); +} + +/* + * Find the central directory, read and rewrite the contents. + * + * The fun thing about ZIP archives is that they may or may not be + * readable from start to end. In some cases, notably for archives + * that were written to stdout, the only length information is in the + * central directory at the end of the file. + * + * Of course, the central directory can be followed by a variable-length + * comment field, so we have to scan through it backwards. The comment + * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff + * itself, plus apparently sometimes people throw random junk on the end + * just for the fun of it. + * + * This is all a little wobbly. If the wrong value ends up in the EOCD + * area, we're hosed. This appears to be the way that everbody handles + * it though, so we're in pretty good company if this fails. + */ +status_t ZipFile::rewriteCentralDir(void) +{ + status_t result = 0; + unsigned char* buf = NULL; + off_t fileLength, seekStart; + long readAmount; + int i; + + fseek(mZipFp, 0, SEEK_END); + fileLength = ftell(mZipFp); + rewind(mZipFp); + + /* too small to be a ZIP archive? */ + if (fileLength < EndOfCentralDir::kEOCDLen) { + LOG("Length is %ld -- too small\n", (long)fileLength); + result = -1; + goto bail; + } + + buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; + if (buf == NULL) { + LOG("Failure allocating %d bytes for EOCD search", + EndOfCentralDir::kMaxEOCDSearch); + result = -1; + goto bail; + } + + if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { + seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; + readAmount = EndOfCentralDir::kMaxEOCDSearch; + } else { + seekStart = 0; + readAmount = (long) fileLength; + } + if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { + LOG("Failure seeking to end of zip at %ld", (long) seekStart); + result = -1; + goto bail; + } + + /* read the last part of the file into the buffer */ + if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { + LOG("short file? wanted %ld\n", readAmount); + result = -1; + goto bail; + } + + /* find the end-of-central-dir magic */ + for (i = readAmount - 4; i >= 0; i--) { + if (buf[i] == 0x50 && + ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) + { + break; + } + } + if (i < 0) { + LOG("EOCD not found, not Zip\n"); + result = -1; + goto bail; + } + + /* extract eocd values */ + result = mEOCD.readBuf(buf + i, readAmount - i); + if (result != 0) { + LOG("Failure reading %ld bytes of EOCD values", readAmount - i); + goto bail; + } + + /* + * So far so good. "mCentralDirSize" is the size in bytes of the + * central directory, so we can just seek back that far to find it. + * We can also seek forward mCentralDirOffset bytes from the + * start of the file. + * + * We're not guaranteed to have the rest of the central dir in the + * buffer, nor are we guaranteed that the central dir will have any + * sort of convenient size. We need to skip to the start of it and + * read the header, then the other goodies. + * + * The only thing we really need right now is the file comment, which + * we're hoping to preserve. + */ + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + LOG("Failure seeking to central dir offset %ld\n", + mEOCD.mCentralDirOffset); + result = -1; + goto bail; + } + + /* + * Loop through and read the central dir entries. + */ + int entry; + for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { + ZipEntry* pEntry = new ZipEntry; + + result = pEntry->initAndRewriteFromCDE(mZipFp); + if (result != 0) { + LOG("initFromCDE failed\n"); + delete pEntry; + goto bail; + } + + delete pEntry; + } + + + /* + * If all went well, we should now be back at the EOCD. + */ + unsigned char checkBuf[4]; + if (fread(checkBuf, 1, 4, mZipFp) != 4) { + LOG("EOCD check read failed\n"); + result = -1; + goto bail; + } + if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { + LOG("EOCD read check failed\n"); + result = -1; + goto bail; + } + +bail: + delete[] buf; + return result; +} + +/* + * =========================================================================== + * ZipFile::EndOfCentralDir + * =========================================================================== + */ + +/* + * Read the end-of-central-dir fields. + * + * "buf" should be positioned at the EOCD signature, and should contain + * the entire EOCD area including the comment. + */ +status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len) +{ + unsigned short diskNumber, diskWithCentralDir, numEntries; + + if (len < kEOCDLen) { + /* looks like ZIP file got truncated */ + LOG(" Zip EOCD: expected >= %d bytes, found %d\n", + kEOCDLen, len); + return -1; + } + + /* this should probably be an assert() */ + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) + return -1; + + diskNumber = ZipEntry::getShortLE(&buf[0x04]); + diskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); + numEntries = ZipEntry::getShortLE(&buf[0x08]); + mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); + mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); + + if (diskNumber != 0 || diskWithCentralDir != 0 || + numEntries != mTotalNumEntries) + { + LOG("Archive spanning not supported\n"); + return -1; + } + + return 0; +} diff --git a/tools/ziptime/ZipFile.h b/tools/ziptime/ZipFile.h new file mode 100644 index 000000000..50ca92321 --- /dev/null +++ b/tools/ziptime/ZipFile.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Class to rewrite zip file headers to remove dynamic timestamps. +// +#ifndef __LIBS_ZIPFILE_H +#define __LIBS_ZIPFILE_H + +#include + +#include "ZipEntry.h" + +namespace android { + +/* + * Manipulate a Zip archive. + */ +class ZipFile { +public: + ZipFile(void) : mZipFp(NULL) {} + ~ZipFile(void) { + if (mZipFp != NULL) + fclose(mZipFp); + } + + /* + * Rewrite an archive's headers to remove dynamic timestamps. + */ + status_t rewrite(const char* zipFileName); + +private: + /* these are private and not defined */ + ZipFile(const ZipFile& src); + ZipFile& operator=(const ZipFile& src); + + class EndOfCentralDir { + public: + EndOfCentralDir(void) : mTotalNumEntries(0), mCentralDirOffset(0) {} + + status_t readBuf(const unsigned char* buf, int len); + + unsigned short mTotalNumEntries; + unsigned long mCentralDirOffset; // offset from first disk + + enum { + kSignature = 0x06054b50, + kEOCDLen = 22, // EndOfCentralDir len, excl. comment + + kMaxCommentLen = 65535, // longest possible in ushort + kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen, + + }; + }; + + /* read all entries in the central dir */ + status_t rewriteCentralDir(void); + + /* + * We use stdio FILE*, which gives us buffering but makes dealing + * with files >2GB awkward. Until we support Zip64, we're fine. + */ + FILE* mZipFp; // Zip file pointer + + /* one of these per file */ + EndOfCentralDir mEOCD; +}; + +}; // namespace android + +#endif // __LIBS_ZIPFILE_H diff --git a/tools/ziptime/ZipTime.cpp b/tools/ziptime/ZipTime.cpp new file mode 100644 index 000000000..99d32310b --- /dev/null +++ b/tools/ziptime/ZipTime.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 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. + */ + +/* + * Zip tool to remove dynamic timestamps + */ +#include "ZipFile.h" + +#include +#include + +using namespace android; + +static void usage(void) +{ + fprintf(stderr, "Zip timestamp utility\n"); + fprintf(stderr, "Copyright (C) 2015 The Android Open Source Project\n\n"); + fprintf(stderr, "Usage: ziptime file.zip\n"); +} + +int main(int argc, char* const argv[]) +{ + if (argc != 2) { + usage(); + return 2; + } + + ZipFile zip; + if (zip.rewrite(argv[1]) != 0) { + fprintf(stderr, "Unable to rewrite '%s' as zip archive\n", argv[1]); + return 1; + } + + return 0; +}