From c80f3e562ee143b3282a9afe95c98cd40d0f4665 Mon Sep 17 00:00:00 2001 From: "Vladimir.Orlov" Date: Mon, 20 Oct 2014 15:58:12 +0400 Subject: [PATCH] sync updater with changes from android studio updater. --- .../intellij/updater/BaseDeleteAction.java | 59 +++++++++ .../com/intellij/updater/DeleteZipAction.java | 20 ++++ .../intellij/updater/NativeFileManager.java | 113 ++++++++++++++++++ .../intellij/updater/Win32RestartManager.java | 66 ++++++++++ 4 files changed, 258 insertions(+) create mode 100644 updater/src/com/intellij/updater/BaseDeleteAction.java create mode 100644 updater/src/com/intellij/updater/DeleteZipAction.java create mode 100644 updater/src/com/intellij/updater/NativeFileManager.java create mode 100644 updater/src/com/intellij/updater/Win32RestartManager.java diff --git a/updater/src/com/intellij/updater/BaseDeleteAction.java b/updater/src/com/intellij/updater/BaseDeleteAction.java new file mode 100644 index 000000000000..510ec97c892b --- /dev/null +++ b/updater/src/com/intellij/updater/BaseDeleteAction.java @@ -0,0 +1,59 @@ +package com.intellij.updater; + +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +public abstract class BaseDeleteAction extends PatchAction { + public BaseDeleteAction(String path, long checksum) { + super(path, checksum); + } + + public BaseDeleteAction(DataInputStream in) throws IOException { + super(in); + } + + @Override + public void doBuildPatchFile(File olderFile, File newerFile, ZipOutputStream patchOutput) throws IOException { + // do nothing + } + + @Override + protected ValidationResult doValidate(File toFile) throws IOException { + ValidationResult result = doValidateAccess(toFile, ValidationResult.Action.DELETE); + if (result != null) return result; + + if (toFile.exists() && isModified(toFile)) { + return new ValidationResult(ValidationResult.Kind.CONFLICT, + myPath, + ValidationResult.Action.DELETE, + "Modified", + ValidationResult.Option.DELETE, + ValidationResult.Option.KEEP); + } + return null; + } + + @Override + protected boolean shouldApplyOn(File toFile) { + return toFile.exists(); + } + + @Override + protected void doApply(ZipFile patchFile, File toFile) throws IOException { + Utils.delete(toFile); + } + + protected void doBackup(File toFile, File backupFile) throws IOException { + Utils.copy(toFile, backupFile); + } + + protected void doRevert(File toFile, File backupFile) throws IOException { + if (!toFile.exists() || toFile.isDirectory() || isModified(toFile)) { + Utils.delete(toFile); // make sure there is no directory remained on this path (may remain from previous 'create' actions + Utils.copy(backupFile, toFile); + } + } +} diff --git a/updater/src/com/intellij/updater/DeleteZipAction.java b/updater/src/com/intellij/updater/DeleteZipAction.java new file mode 100644 index 000000000000..0cd46d9fa9e5 --- /dev/null +++ b/updater/src/com/intellij/updater/DeleteZipAction.java @@ -0,0 +1,20 @@ +package com.intellij.updater; + +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; + +public class DeleteZipAction extends BaseDeleteAction { + public DeleteZipAction(String path, long checksum) { + super(path, checksum); + } + + public DeleteZipAction(DataInputStream in) throws IOException { + super(in); + } + + @Override + protected boolean isModified(File toFile) throws IOException { + return myChecksum != Digester.digestFile(toFile); + } +} diff --git a/updater/src/com/intellij/updater/NativeFileManager.java b/updater/src/com/intellij/updater/NativeFileManager.java new file mode 100644 index 000000000000..b61c1a7c35a1 --- /dev/null +++ b/updater/src/com/intellij/updater/NativeFileManager.java @@ -0,0 +1,113 @@ +/* + * 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. + */ +package com.intellij.updater; + +import com.sun.jna.Pointer; +import com.sun.jna.StringArray; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinBase; +import com.sun.jna.platform.win32.WinNT; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; + +import java.io.File; +import java.util.LinkedList; +import java.util.List; + +/** + * A utility class to find processes that hold a lock to a file. This relies on a Windows API called + * RestartManager {@see http://msdn.microsoft.com/en-us/library/windows/desktop/cc948910(v=vs.85).aspx} + * + * This class uses the RestartManager and the Kernel32 APIs, and it tries to initialize them the first + * time it is run. If the RestartManager DLL is not found, it being because we are running on XP or + * because we are not running on Windows, then the class is flagged as failed and no further attempts + * will be made to load the DLL. + */ +public class NativeFileManager { + + private static final int MAX_PROCESSES = 10; + + private static boolean ourFailed = false; + + public static class Process { + public final int pid; + public final String name; + + public Process(int pid, String name) { + this.pid = pid; + this.name = name; + } + + public boolean terminate() { + Kernel32.HANDLE process = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_TERMINATE | WinNT.SYNCHRONIZE, false, pid); + if (process.getPointer() == null) { + Runner.logger.warn("Unable to find process " + name + "(" + pid + ")"); + return false; + } else { + Kernel32.INSTANCE.TerminateProcess(process, 1); + int wait = Kernel32.INSTANCE.WaitForSingleObject(process, 1000); + if (wait != WinBase.WAIT_OBJECT_0) { + Runner.logger.warn("Timed out while waiting for process " + name + "(" + pid + ") to end"); + return false; + } + Kernel32.INSTANCE.CloseHandle(process); + return true; + } + } + } + + public static List getProcessesUsing(File file) { + List processes = new LinkedList(); + // If the DLL was not present (XP or other OS), do not try to find it again. + if (ourFailed) { + return processes; + } + try { + IntByReference session = new IntByReference(); + char[] sessionKey = new char[Win32RestartManager.CCH_RM_SESSION_KEY + 1]; + int error = Win32RestartManager.INSTANCE.RmStartSession(session, 0, sessionKey); + if (error != 0) { + Runner.logger.warn("Unable to start restart manager session"); + return processes; + } + StringArray resources = new StringArray(new WString[]{new WString(file.toString())}); + error = Win32RestartManager.INSTANCE.RmRegisterResources(session.getValue(), 1, resources, 0, Pointer.NULL, 0, null); + if (error != 0) { + Runner.logger.warn("Unable to register restart manager resource " + file.getAbsolutePath()); + return processes; + } + + IntByReference procInfoNeeded = new IntByReference(); + Win32RestartManager.RmProcessInfo info = new Win32RestartManager.RmProcessInfo(); + Win32RestartManager.RmProcessInfo[] infos = (Win32RestartManager.RmProcessInfo[])info.toArray(MAX_PROCESSES); + IntByReference procInfo = new IntByReference(infos.length); + error = Win32RestartManager.INSTANCE.RmGetList(session.getValue(), procInfoNeeded, procInfo, info, new LongByReference()); + if (error != 0) { + Runner.logger.warn("Unable to get the list of processes using " + file.getAbsolutePath()); + return processes; + } + for (int i = 0; i < procInfo.getValue(); i++) { + processes.add(new Process(infos[i].Process.dwProcessId, new String(infos[i].strAppName).trim())); + } + Win32RestartManager.INSTANCE.RmEndSession(session.getValue()); + } catch (Throwable t) { + // Best effort approach, if no DLL is found ignore. + ourFailed = true; + } + return processes; + } +} diff --git a/updater/src/com/intellij/updater/Win32RestartManager.java b/updater/src/com/intellij/updater/Win32RestartManager.java new file mode 100644 index 000000000000..a502a57c0931 --- /dev/null +++ b/updater/src/com/intellij/updater/Win32RestartManager.java @@ -0,0 +1,66 @@ +/* + * 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. + */ +package com.intellij.updater; + +import com.sun.jna.*; +import com.sun.jna.platform.win32.WinBase; +import com.sun.jna.platform.win32.WinDef; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; + +public interface Win32RestartManager extends Library { + + Win32RestartManager INSTANCE = (Win32RestartManager) Native.loadLibrary("Rstrtmgr", Win32RestartManager.class); + + int CCH_RM_SESSION_KEY = 32; + int CCH_RM_MAX_APP_NAME = 255; + int CCH_RM_MAX_SVC_NAME = 63; + + class RmUniqueProcess extends Structure { + public int dwProcessId; + public WinBase.FILETIME ProcessStartTime; + } + + class RmProcessInfo extends Structure { + public RmUniqueProcess Process; + public char[] strAppName = new char[CCH_RM_MAX_APP_NAME + 1]; + public char[] strServiceShortName = new char[CCH_RM_MAX_SVC_NAME + 1]; + public int ApplicationType; + public WinDef.LONG AppStatus; + public int TSSessionId; + public boolean bRestartable; + } + + int RmGetList(int dwSessionHandle, + IntByReference pnProcInfoNeeded, + IntByReference pnProcInfo, + RmProcessInfo rgAffectedApps, + LongByReference lpdwRebootReasons); + + int RmStartSession(IntByReference pSessionHandle, + int dwSessionFlags, + char[] strSessionKey); + + int RmRegisterResources(int dwSessionHandle, + int nFiles, + StringArray rgsFilenames, + int nApplications, + Pointer rgApplications, + int nServices, + StringArray rgsServiceNames); + + int RmEndSession(int dwSessionHandle); +} \ No newline at end of file