/* * Find an entry by name. */ ZipEntry* ZipFile::getEntryByName(const char* fileName) const { /* * Do a stupid linear string-compare search. * * There are various ways to speed this up, especially since it's rare * to intermingle changes to the archive with "get by name" calls. We * don't want to sort the mEntries vector itself, however, because * it's used to recreate the Central Directory. * * (Hash table works, parallel list of pointers in sorted order is good.) */ int idx; for (idx = mEntries.size()-1; idx >= 0; idx--) { ZipEntry* pEntry = mEntries[idx]; if (!pEntry->getDeleted() && strcmp(fileName, pEntry->getFileName()) == 0) { return pEntry; } } return NULL; }
/* * Crunch deleted files out of an archive by shifting the later files down. * * Because we're not using a temp file, we do the operation inside the * current file. */ status_t ZipFile::crunchArchive(void) { status_t result = NO_ERROR; int i, count; long delCount, adjust; #if 0 printf("CONTENTS:\n"); for (i = 0; i < (int) mEntries.size(); i++) { printf(" %d: lfhOff=%ld del=%d\n", i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); } printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset); #endif /* * Roll through the set of files, shifting them as appropriate. We * could probably get a slight performance improvement by sliding * multiple files down at once (because we could use larger reads * when operating on batches of small files), but it's not that useful. */ count = mEntries.size(); delCount = adjust = 0; for (i = 0; i < count; i++) { ZipEntry* pEntry = mEntries[i]; long span; if (pEntry->getLFHOffset() != 0) { long nextOffset; /* Get the length of this entry by finding the offset * of the next entry. Directory entries don't have * file offsets, so we need to find the next non-directory * entry. */ nextOffset = 0; for (int ii = i+1; nextOffset == 0 && ii < count; ii++) nextOffset = mEntries[ii]->getLFHOffset(); if (nextOffset == 0) nextOffset = mEOCD.mCentralDirOffset; span = nextOffset - pEntry->getLFHOffset(); assert(span >= ZipEntry::LocalFileHeader::kLFHLen); } else { /* This is a directory entry. It doesn't have * any actual file contents, so there's no need to * move anything. */ span = 0; } //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); if (pEntry->getDeleted()) { adjust += span; delCount++; delete pEntry; mEntries.removeAt(i); /* adjust loop control */ count--; i--; } else if (span != 0 && adjust > 0) { /* shuffle this entry back */ //printf("+++ Shuffling '%s' back %ld\n", // pEntry->getFileName(), adjust); result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, pEntry->getLFHOffset(), span); if (result != NO_ERROR) { /* this is why you use a temp file */ LOGE("error during crunch - archive is toast\n"); return result; } pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); } } /* * Fix EOCD info. We have to wait until the end to do some of this * because we use mCentralDirOffset to determine "span" for the * last entry. */ mEOCD.mCentralDirOffset -= adjust; mEOCD.mNumEntries -= delCount; mEOCD.mTotalNumEntries -= delCount; mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); assert(mEOCD.mNumEntries == count); return result; }
/* * The directory hierarchy looks like this: * "outputDir" and "assetRoot" are existing directories. * * On success, "bundle->numPackages" will be the number of Zip packages * we created. */ status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>& outputSet) { #if BENCHMARK fprintf(stdout, "BENCHMARK: Starting APK Bundling \n"); long startAPKTime = clock(); #endif /* BENCHMARK */ status_t result = NO_ERROR; ZipFile* zip = NULL; int count; //bundle->setPackageCount(0); /* * Prep the Zip archive. * * If the file already exists, fail unless "update" or "force" is set. * If "update" is set, update the contents of the existing archive. * Else, if "force" is set, remove the existing archive. */ FileType fileType = getFileType(outputFile.string()); if (fileType == kFileTypeNonexistent) { // okay, create it below } else if (fileType == kFileTypeRegular) { if (bundle->getUpdate()) { // okay, open it below } else if (bundle->getForce()) { if (unlink(outputFile.string()) != 0) { fprintf(stderr, "ERROR: unable to remove '%s': %s\n", outputFile.string(), strerror(errno)); goto bail; } } else { fprintf(stderr, "ERROR: '%s' exists (use '-f' to force overwrite)\n", outputFile.string()); goto bail; } } else { fprintf(stderr, "ERROR: '%s' exists and is not a regular file\n", outputFile.string()); goto bail; } if (bundle->getVerbose()) { printf("%s '%s'\n", (fileType == kFileTypeNonexistent) ? "Creating" : "Opening", outputFile.string()); } status_t status; zip = new ZipFile; status = zip->open(outputFile.string(), ZipFile::kOpenReadWrite | ZipFile::kOpenCreate); if (status != NO_ERROR) { fprintf(stderr, "ERROR: unable to open '%s' as Zip file for writing\n", outputFile.string()); goto bail; } if (bundle->getVerbose()) { printf("Writing all files...\n"); } count = processAssets(bundle, zip, outputSet); if (count < 0) { fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n", outputFile.string()); result = count; goto bail; } if (bundle->getVerbose()) { printf("Generated %d file%s\n", count, (count==1) ? "" : "s"); } count = processJarFiles(bundle, zip); if (count < 0) { fprintf(stderr, "ERROR: unable to process jar files while packaging '%s'\n", outputFile.string()); result = count; goto bail; } if (bundle->getVerbose()) printf("Included %d file%s from jar/zip files.\n", count, (count==1) ? "" : "s"); result = NO_ERROR; /* * Check for cruft. We set the "marked" flag on all entries we created * or decided not to update. If the entry isn't already slated for * deletion, remove it now. */ { if (bundle->getVerbose()) printf("Checking for deleted files\n"); int i, removed = 0; for (i = 0; i < zip->getNumEntries(); i++) { ZipEntry* entry = zip->getEntryByIndex(i); if (!entry->getMarked() && entry->getDeleted()) { if (bundle->getVerbose()) { printf(" (removing crufty '%s')\n", entry->getFileName()); } zip->remove(entry); removed++; } } if (bundle->getVerbose() && removed > 0) printf("Removed %d file%s\n", removed, (removed==1) ? "" : "s"); } /* tell Zip lib to process deletions and other pending changes */ result = zip->flush(); if (result != NO_ERROR) { fprintf(stderr, "ERROR: Zip flush failed, archive may be hosed\n"); goto bail; } /* anything here? */ if (zip->getNumEntries() == 0) { if (bundle->getVerbose()) { printf("Archive is empty -- removing %s\n", outputFile.getPathLeaf().string()); } delete zip; // close the file so we can remove it in Win32 zip = NULL; if (unlink(outputFile.string()) != 0) { fprintf(stderr, "warning: could not unlink '%s'\n", outputFile.string()); } } // If we've been asked to generate a dependency file for the .ap_ package, // do so here if (bundle->getGenDependencies()) { // The dependency file gets output to the same directory // as the specified output file with an additional .d extension. // e.g. bin/resources.ap_.d String8 dependencyFile = outputFile; dependencyFile.append(".d"); FILE* fp = fopen(dependencyFile.string(), "a"); // Add this file to the dependency file fprintf(fp, "%s \\\n", outputFile.string()); fclose(fp); } assert(result == NO_ERROR); bail: delete zip; // must close before remove in Win32 if (result != NO_ERROR) { if (bundle->getVerbose()) { printf("Removing %s due to earlier failures\n", outputFile.string()); } if (unlink(outputFile.string()) != 0) { fprintf(stderr, "warning: could not unlink '%s'\n", outputFile.string()); } } if (result == NO_ERROR && bundle->getVerbose()) printf("Done!\n"); #if BENCHMARK fprintf(stdout, "BENCHMARK: End APK Bundling. Time Elapsed: %f ms \n",(clock() - startAPKTime)/1000.0); #endif /* BENCHMARK */ return result; }
ssize_t AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename) { int count = 0; SortedVector<AaptGroupEntry> entries; ZipFile* zip = new ZipFile; status_t err = zip->open(filename, ZipFile::kOpenReadOnly); if (err != NO_ERROR) { fprintf(stderr, "error opening zip file %s\n", filename); count = err; delete zip; return -1; } const int N = zip->getNumEntries(); for (int i=0; i<N; i++) { ZipEntry* entry = zip->getEntryByIndex(i); if (entry->getDeleted()) { continue; } String8 entryName(entry->getFileName()); String8 dirName = entryName.getPathDir(); sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName); String8 resType; AaptGroupEntry kind; String8 remain; if (entryName.walkPath(&remain) == kResourceDir) { // these are the resources, pull their type out of the directory name kind.initFromDirName(remain.walkPath().string(), &resType); } else { // these are untyped and don't have an AaptGroupEntry } if (entries.indexOf(kind) < 0) { entries.add(kind); mGroupEntries.add(kind); } // use the one from the zip file if they both exist. dir->removeFile(entryName.getPathLeaf()); sp<AaptFile> file = new AaptFile(entryName, kind, resType); status_t err = dir->addLeafFile(entryName.getPathLeaf(), file); if (err != NO_ERROR) { fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string()); count = err; goto bail; } file->setCompressionMethod(entry->getCompressionMethod()); #if 0 if (entryName == "AndroidManifest.xml") { printf("AndroidManifest.xml\n"); } printf("\n\nfile: %s\n", entryName.string()); #endif size_t len = entry->getUncompressedLen(); void* data = zip->uncompress(entry); void* buf = file->editData(len); memcpy(buf, data, len); #if 0 const int OFF = 0; const unsigned char* p = (unsigned char*)data; const unsigned char* end = p+len; p += OFF; for (int i=0; i<32 && p < end; i++) { printf("0x%03x ", i*0x10 + OFF); for (int j=0; j<0x10 && p < end; j++) { printf(" %02x", *p); p++; } printf("\n"); } #endif free(data); count++; } bail: delete zip; return count; }
/* * The directory hierarchy looks like this: * "outputDir" and "assetRoot" are existing directories. * * On success, "bundle->numPackages" will be the number of Zip packages * we created. */ status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets, const String8& outputFile) { status_t result = NO_ERROR; ZipFile* zip = NULL; int count; //bundle->setPackageCount(0); /* * Prep the Zip archive. * * If the file already exists, fail unless "update" or "force" is set. * If "update" is set, update the contents of the existing archive. * Else, if "force" is set, remove the existing archive. */ FileType fileType = getFileType(outputFile.string()); if (fileType == kFileTypeNonexistent) { // okay, create it below } else if (fileType == kFileTypeRegular) { if (bundle->getUpdate()) { // okay, open it below } else if (bundle->getForce()) { if (unlink(outputFile.string()) != 0) { fprintf(stderr, "ERROR: unable to remove '%s': %s\n", outputFile.string(), strerror(errno)); goto bail; } } else { fprintf(stderr, "ERROR: '%s' exists (use '-f' to force overwrite)\n", outputFile.string()); goto bail; } } else { fprintf(stderr, "ERROR: '%s' exists and is not a regular file\n", outputFile.string()); goto bail; } if (bundle->getVerbose()) { printf("%s '%s'\n", (fileType == kFileTypeNonexistent) ? "Creating" : "Opening", outputFile.string()); } status_t status; zip = new ZipFile; status = zip->open(outputFile.string(), ZipFile::kOpenReadWrite | ZipFile::kOpenCreate); if (status != NO_ERROR) { fprintf(stderr, "ERROR: unable to open '%s' as Zip file for writing\n", outputFile.string()); goto bail; } if (bundle->getVerbose()) { printf("Writing all files...\n"); } count = processAssets(bundle, zip, assets); if (count < 0) { fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n", outputFile.string()); result = count; goto bail; } if (bundle->getVerbose()) { printf("Generated %d file%s\n", count, (count==1) ? "" : "s"); } count = processJarFiles(bundle, zip); if (count < 0) { fprintf(stderr, "ERROR: unable to process jar files while packaging '%s'\n", outputFile.string()); result = count; goto bail; } if (bundle->getVerbose()) printf("Included %d file%s from jar/zip files.\n", count, (count==1) ? "" : "s"); result = NO_ERROR; /* * Check for cruft. We set the "marked" flag on all entries we created * or decided not to update. If the entry isn't already slated for * deletion, remove it now. */ { if (bundle->getVerbose()) printf("Checking for deleted files\n"); int i, removed = 0; for (i = 0; i < zip->getNumEntries(); i++) { ZipEntry* entry = zip->getEntryByIndex(i); if (!entry->getMarked() && entry->getDeleted()) { if (bundle->getVerbose()) { printf(" (removing crufty '%s')\n", entry->getFileName()); } zip->remove(entry); removed++; } } if (bundle->getVerbose() && removed > 0) printf("Removed %d file%s\n", removed, (removed==1) ? "" : "s"); } /* tell Zip lib to process deletions and other pending changes */ result = zip->flush(); if (result != NO_ERROR) { fprintf(stderr, "ERROR: Zip flush failed, archive may be hosed\n"); goto bail; } /* anything here? */ if (zip->getNumEntries() == 0) { if (bundle->getVerbose()) { printf("Archive is empty -- removing %s\n", outputFile.getPathLeaf().string()); } delete zip; // close the file so we can remove it in Win32 zip = NULL; if (unlink(outputFile.string()) != 0) { fprintf(stderr, "WARNING: could not unlink '%s'\n", outputFile.string()); } } assert(result == NO_ERROR); bail: delete zip; // must close before remove in Win32 if (result != NO_ERROR) { if (bundle->getVerbose()) { printf("Removing %s due to earlier failures\n", outputFile.string()); } if (unlink(outputFile.string()) != 0) { fprintf(stderr, "WARNING: could not unlink '%s'\n", outputFile.string()); } } if (result == NO_ERROR && bundle->getVerbose()) printf("Done!\n"); return result; }
/* * The directory hierarchy looks like this: * "outputDir" and "assetRoot" are existing directories. * * On success, "bundle->numPackages" will be the number of Zip packages * we created. */ status_t writeAPK(Bundle* bundle, ZipFile* zip, const char* outputFileName, const sp<OutputSet>& outputSet, bool isOverlay) { status_t result = NO_ERROR; int count; if (bundle->getVerbose()) { printf("Writing all files...\n"); } count = processAssets(bundle, zip, outputSet, isOverlay); if (count < 0) { fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n", outputFileName); result = count; goto bail; } if (bundle->getVerbose()) { printf("Generated %d file%s\n", count, (count==1) ? "" : "s"); } if (!isOverlay) { count = processJarFiles(bundle, zip); if (count < 0) { fprintf(stderr, "ERROR: unable to process jar files while packaging '%s'\n", outputFileName); result = count; goto bail; } if (bundle->getVerbose()) printf("Included %d file%s from jar/zip files.\n", count, (count==1) ? "" : "s"); } result = NO_ERROR; /* * Check for cruft. We set the "marked" flag on all entries we created * or decided not to update. If the entry isn't already slated for * deletion, remove it now. */ { if (bundle->getVerbose()) printf("Checking for deleted files\n"); int i, removed = 0; for (i = 0; i < zip->getNumEntries(); i++) { ZipEntry* entry = zip->getEntryByIndex(i); if (!entry->getMarked() && entry->getDeleted()) { if (bundle->getVerbose()) { printf(" (removing crufty '%s')\n", entry->getFileName()); } zip->remove(entry); removed++; } } if (bundle->getVerbose() && removed > 0) printf("Removed %d file%s\n", removed, (removed==1) ? "" : "s"); } /* tell Zip lib to process deletions and other pending changes */ result = zip->flush(); if (result != NO_ERROR) { fprintf(stderr, "ERROR: Zip flush failed, archive may be hosed\n"); goto bail; } bail: return result; }