diff options
Diffstat (limited to 'bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/StorageUtil.java')
-rw-r--r-- | bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/StorageUtil.java | 247 |
1 files changed, 104 insertions, 143 deletions
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/StorageUtil.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/StorageUtil.java index 033c5b427..2c9eddf8b 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/StorageUtil.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/StorageUtil.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2016 IBM Corporation and others. + * Copyright (c) 2005, 2021 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -10,46 +10,55 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Hannes Wellmann - Bug 577432 - Speed up and improve file processing in Storage *******************************************************************************/ package org.eclipse.osgi.storage; -import java.io.*; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Arrays; import java.util.Dictionary; +import java.util.HashSet; import java.util.Hashtable; -import java.util.concurrent.TimeUnit; +import java.util.Set; +import java.util.stream.Stream; import org.eclipse.osgi.internal.debug.Debug; -import org.osgi.framework.*; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; /** * A utility class with some generally useful static methods for adaptor hook implementations */ public class StorageUtil { - /** The NULL tag used in bundle storage */ - public static final byte NULL = 0; - /** The OBJECT tag used in bundle storage */ - public static final byte OBJECT = 1; /** - * Does a recursive copy of one directory to another. - * @param inDir input directory to copy. - * @param outDir output directory to copy to. + * Copies the content of the given path (file or directory) to the specified + * target. If the source is a directory all contained elements are copied + * recursively. + * @param inFile input directory to copy. + * @param outFile output directory to copy to. * @throws IOException if any error occurs during the copy. */ - public static void copyDir(File inDir, File outDir) throws IOException { - String[] files = inDir.list(); - if (files != null && files.length > 0) { - outDir.mkdir(); - for (int i = 0; i < files.length; i++) { - File inFile = new File(inDir, files[i]); - File outFile = new File(outDir, files[i]); - if (inFile.isDirectory()) { - copyDir(inFile, outFile); - } else { - InputStream in = new FileInputStream(inFile); - readFile(in, outFile); + public static void copy(File inFile, File outFile) throws IOException { + Path source = inFile.toPath(); + Path target = outFile.toPath(); + if (Files.exists(source)) { + Files.createDirectories(target.getParent()); + try (Stream<Path> walk = Files.walk(source)) { + for (Path s : (Iterable<Path>) walk::iterator) { + Path t = target.resolve(source.relativize(s)); + Files.copy(s, t, StandardCopyOption.REPLACE_EXISTING); } } } @@ -63,32 +72,7 @@ public class StorageUtil { * @exception IOException */ public static void readFile(InputStream in, File file) throws IOException { - FileOutputStream fos = null; - try { - fos = new FileOutputStream(file); - - byte buffer[] = new byte[1024]; - int count; - while ((count = in.read(buffer, 0, buffer.length)) > 0) { - fos.write(buffer, 0, count); - } - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ee) { - // nothing to do here - } - } - - if (fos != null) { - try { - fos.close(); - } catch (IOException ee) { - // nothing to do here - } - } - } + Files.copy(in, file.toPath(), StandardCopyOption.REPLACE_EXISTING); } /** @@ -98,51 +82,39 @@ public class StorageUtil { * @return false is the specified files still exists, true otherwise. */ public static boolean rm(File file, boolean DEBUG) { - if (file.exists()) { - if (file.isDirectory()) { - String list[] = file.list(); - if (list != null) { - int len = list.length; - for (int i = 0; i < len; i++) { - // we are doing a lot of garbage collecting here - rm(new File(file, list[i]), DEBUG); - } - } - } - if (DEBUG) { - if (file.isDirectory()) { - Debug.println("rmdir " + file.getPath()); //$NON-NLS-1$ - } else { - Debug.println("rm " + file.getPath()); //$NON-NLS-1$ + Path path = file.toPath(); + if (!Files.exists(path)) { + return true; + } + try { + Files.walkFileTree(path, new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult visitFile(Path f, BasicFileAttributes attrs) { + return delete(f, DEBUG); } - } - boolean success = file.delete(); - - if (DEBUG) { - if (!success) { - Debug.println(" rm failed!"); //$NON-NLS-1$ + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) { + return delete(dir, DEBUG); } - } - return (success); - } - return (true); - } - - public static String readString(DataInputStream in, boolean intern) throws IOException { - byte type = in.readByte(); - if (type == NULL) - return null; - return intern ? in.readUTF().intern() : in.readUTF(); - } - - public static void writeStringOrNull(DataOutputStream out, String string) throws IOException { - if (string == null) - out.writeByte(NULL); - else { - out.writeByte(OBJECT); - out.writeUTF(string); + private FileVisitResult delete(Path pathToDelete, boolean debug) { + try { + if (debug) { + Debug.println("rm " + pathToDelete); //$NON-NLS-1$ + } + Files.delete(pathToDelete); + } catch (IOException e) { + if (debug) { + Debug.println(" rm failed:" + e.getMessage()); //$NON-NLS-1$ + } + } + return FileVisitResult.CONTINUE; + } + }); + return !Files.exists(path); + } catch (IOException e) { + return false; } } @@ -154,28 +126,28 @@ public class StorageUtil { * @return the service registration object */ public static ServiceRegistration<?> register(String name, Object service, BundleContext context) { - Dictionary<String, Object> properties = new Hashtable<>(7); - Dictionary<String, String> headers = context.getBundle().getHeaders(); - properties.put(Constants.SERVICE_VENDOR, headers.get(Constants.BUNDLE_VENDOR)); + Dictionary<String, Object> properties = new Hashtable<>(); properties.put(Constants.SERVICE_RANKING, Integer.valueOf(Integer.MAX_VALUE)); properties.put(Constants.SERVICE_PID, context.getBundle().getBundleId() + "." + service.getClass().getName()); //$NON-NLS-1$ return context.registerService(name, service, properties); } public static boolean canWrite(File installDir) { - if (installDir.canWrite() == false) - return false; - if (!installDir.isDirectory()) return false; + if (Files.isWritable(installDir.toPath())) + return true; + File fileTest = null; try { // we use the .dll suffix to properly test on Vista virtual directories - // on Vista you are not allowed to write executable files on virtual directories like "Program Files" + // on Vista you are not allowed to write executable files on virtual directories + // like "Program Files" fileTest = File.createTempFile("writableArea", ".dll", installDir); //$NON-NLS-1$ //$NON-NLS-2$ } catch (IOException e) { - //If an exception occured while trying to create the file, it means that it is not writtable + // If an exception occured while trying to create the file, it means that it is + // not writtable return false; } finally { if (fileTest != null) @@ -230,58 +202,47 @@ public class StorageUtil { return classbytes; } - /** - * To remain Java 6 compatible work around the unreliable renameTo() via - * retries: http://bugs.java.com/view_bug.do?bug_id=6213298 - * - * @param from - * @param to - */ - public static boolean move(File from, File to, boolean DEBUG) { - // Try several attempts with incremental sleep - final int maxTries = 10; - final int sleepStep = 200; - for (int tryCount = 0, sleep = sleepStep;; sleep += sleepStep, tryCount++) { - if (from.renameTo(to)) { - return true; - } - + public static void move(File from, File to, boolean DEBUG) throws IOException { + try { + Files.move(from.toPath(), to.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); + } catch (IOException e) { if (DEBUG) { - Debug.println("move: failed to rename " + from + " to " + to + " (" + (maxTries - tryCount) + " attempts remaining)"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - } - - if (tryCount >= maxTries) { - break; - } - - try { - TimeUnit.MILLISECONDS.sleep(sleep); - } catch (InterruptedException e) { - // Ignore + Debug.println("Failed to move atomically: " + from + " to " + to); //$NON-NLS-1$ //$NON-NLS-2$ } + // remove in case it failed because the target to non-empty directory or + // the target type does not match the from + rm(to, DEBUG); + // also, try without atomic operation + Files.move(from.toPath(), to.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + if (DEBUG) { + Debug.println("Successfully moved file: " + from + " to " + to); //$NON-NLS-1$ //$NON-NLS-2$ } + } - // Try a copy - try { - if (from.isDirectory()) { - copyDir(from, to); - } else { - readFile(new FileInputStream(from), to); - } + private static final boolean IS_WINDOWS = File.separatorChar == '\\'; - if (!rm(from, DEBUG)) { - Debug.println("move: failed to delete " + from + " after copy to " + to + ". Scheduling for delete on JVM exit."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - from.deleteOnExit(); - } - return true; - } catch (IOException e) { - if (DEBUG) { - Debug.println("move: failed to copy " + from + " to " + to); //$NON-NLS-1$ //$NON-NLS-2$ - Debug.printStackTrace(e); - } + // reserved names according to + // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file + private static final Set<String> RESERVED_NAMES = new HashSet<>(Arrays.asList("aux", "com1", "com2", "com3", "com4", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + "com5", "com6", "com7", "com8", "com9", "con", "lpt1", "lpt2", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "nul", "prn")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ - // Give up + /** Tests whether the filename can escape path into special device **/ + public static boolean isReservedFileName(File file) { + // Directory names are not checked here because illegal directory names will be + // handled by OS. + if (!IS_WINDOWS) { // only windows has special file names which can escape any path return false; } + String fileName = file.getName(); + // Illegal characters are not checked here because they are check by both JDK + // and OS. This is only a check against technical allowed but unwanted device + // names. + int dot = fileName.indexOf('.'); + // on windows, filename suffixes are not relevant to name validity + String basename = dot == -1 ? fileName : fileName.substring(0, dot); + return RESERVED_NAMES.contains(basename.toLowerCase()); } + } |