/** * Internally undo recorded changes we did so far. * * @return whether the state required undo actions */ boolean undoChanges() { final State state = stateUpdater.getAndSet(this, State.ROLLBACK_ONLY); if (state == State.COMPLETED || state == State.ROLLBACK_ONLY) { // Was actually completed already return false; } PatchingTaskContext.Mode currentMode = this.mode; mode = PatchingTaskContext.Mode.UNDO; final PatchContentLoader loader = PatchContentLoader.create(miscBackup, null, null); // Undo changes for the identity undoChanges(identityEntry, loader); // TODO maybe check if we need to do something for the layers too !? if (state == State.INVALIDATE || currentMode == PatchingTaskContext.Mode.ROLLBACK) { // For apply the state needs to be invalidate // For rollback the files are invalidated as part of the tasks final PatchingTaskContext.Mode mode = currentMode == PatchingTaskContext.Mode.APPLY ? PatchingTaskContext.Mode.ROLLBACK : PatchingTaskContext.Mode.APPLY; for (final File file : moduleInvalidations) { try { PatchModuleInvalidationUtils.processFile(file, mode); } catch (Exception e) { PatchLogger.ROOT_LOGGER.debugf(e, "failed to restore state for %s", file); } } } return true; }
/** * Complete the current operation and persist the current state to the disk. This will also * trigger the invalidation of outdated modules. * * @param modification the current modification * @param callback the completion callback */ private void complete( final InstallationManager.InstallationModification modification, final FinalizeCallback callback) { final List<File> processed = new ArrayList<File>(); try { try { // Update the state to invalidate and process module resources if (stateUpdater.compareAndSet(this, State.PREPARED, State.INVALIDATE) && mode == PatchingTaskContext.Mode.APPLY) { // Only invalidate modules when applying patches; on rollback files are immediately // restored for (final File invalidation : moduleInvalidations) { processed.add(invalidation); PatchModuleInvalidationUtils.processFile(invalidation, mode); } } modification.complete(); callback.completed(this); state = State.COMPLETED; } catch (Exception e) { this.moduleInvalidations.clear(); this.moduleInvalidations.addAll(processed); throw new RuntimeException(e); } } finally { if (state != State.COMPLETED) { try { modification.cancel(); } finally { try { undoChanges(); } finally { callback.operationCancelled(this); } } } else { try { if (checkForGarbageOnRestart) { final File cleanupMarker = new File(installedImage.getInstallationMetadata(), "cleanup-patching-dirs"); cleanupMarker.createNewFile(); } } catch (IOException e) { PatchLogger.ROOT_LOGGER.infof(e, "failed to create cleanup marker"); } } } }
/** * Undo changes for a single patch entry. * * @param entry the patch entry * @param loader the content loader */ static void undoChanges(final PatchEntry entry, final PatchContentLoader loader) { final List<ContentModification> modifications = new ArrayList<ContentModification>(entry.rollbackActions); for (final ContentModification modification : modifications) { final ContentItem item = modification.getItem(); if (item.getContentType() != ContentType.MISC) { // Skip modules and bundles they should be removed as part of the {@link FinalizeCallback} continue; } final PatchingTaskDescription description = new PatchingTaskDescription(entry.applyPatchId, modification, loader, false, false); try { final PatchingTask task = PatchingTask.Factory.create(description, entry); task.execute(entry); } catch (Exception e) { PatchLogger.ROOT_LOGGER.warnf(e, "failed to undo change (%s)", modification); } } }
/** * Update the central directory signature of a .jar. * * @param file the file to process * @param searchPattern the search patter to use * @param badSkipBytes the bad bytes skip table * @param newSig the new signature * @param endSig the expected signature * @throws IOException */ private static void updateJar( final File file, final byte[] searchPattern, final int[] badSkipBytes, final int newSig, final int endSig) throws IOException { final RandomAccessFile raf = new RandomAccessFile(file, "rw"); try { final FileChannel channel = raf.getChannel(); try { long pos = channel.size() - ENDLEN; if (!validateEndRecord(file, channel, pos, endSig)) { pos = scanForEndSig(file, channel, searchPattern, badSkipBytes, endSig); } if (pos == -1) { // Don't fail patching if we cannot validate a valid zip PatchLogger.ROOT_LOGGER.cannotInvalidateZip(file.getAbsolutePath()); return; } // Update the central directory record channel.position(pos); final ByteBuffer buffer = ByteBuffer.allocate(4); buffer.order(ByteOrder.LITTLE_ENDIAN); buffer.putInt(newSig); buffer.flip(); while (buffer.hasRemaining()) { channel.write(buffer); } } finally { safeClose(channel); } } finally { safeClose(raf); } }
/** * In case we cannot delete a directory create a marker to recheck whether we can garbage collect * some not referenced directories and files. * * @param file the directory */ protected void failedToCleanupDir(final File file) { checkForGarbageOnRestart = true; PatchLogger.ROOT_LOGGER.cannotDeleteFile(file.getAbsolutePath()); }