blob: b1c0261d73099deb117d27f0780ea2627b8d6e0f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 Cloudsmith Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Cloudsmith Inc. - initial API and implementation
* SAP AG - Ongoing development
*******************************************************************************/
package org.eclipse.equinox.internal.p2.touchpoint.natives;
import static java.nio.file.FileVisitResult.CONTINUE;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.util.stream.Collectors.joining;
import static org.eclipse.equinox.internal.p2.touchpoint.natives.Util.logError;
import static org.eclipse.equinox.internal.p2.touchpoint.natives.Util.logWarning;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import org.eclipse.osgi.util.NLS;
/**
* Stores files by moving them to a uniquely named temporary directory.
*
* TheBackupStore remembers filenames and can recreate them in their original
* location.
*
* <h3>Usage</h3> The user of this class should instantiate the BackupStore with
* some prefix that is meaningful to a human. Uniqueness is obtained without the
* prefix - the prefix is used to be able to differentiate between different
* backup directories by a human (in case of crashes etc).
*
* If instantiated with a directory this directory will be used to store the
* backup root directory. If this directory is null, the users home directory is
* used by default.
*
* Once instantiated, use the {@link #backup(File)} and
* {@link #backupDirectory(File)} methods to move files to backup instead of
* deleting them. A file that is backed up should not be deleted - it is simply
* moved out of the way. Use {@link #backupCopy(File)} to move the file out of
* harms way, but keep a copy of it in the original location. The methods
* {@link #backupAll(File)} and {@link #backupCopyAll(File)} backs up an entire
* structure.
*
* When backup is finished - the user should either call {@link #restore()} to
* put all of the files back, or call {@link #discard()} to remove all of the
* backed up "copies".
*
* If {@link #restore()} or {@link #discard()} is not called the backup files
* will never be deleted.
*
* The backup store does not synchronize directories - actions that write new
* files are responsible for removing them. Overwriting existing files should be
* done by first backing up the file, and then creating a new file. Modifying a
* file, should be done by using {@link #backupCopy(File)} or first making a
* copy, then backing up the original, and then renaming the copy.
*
* <h3>Read Only and Permissions</h3> Directories that are read only (to current
* user) can not be backed up. Backup is performed using
* {@link File#renameTo(File)} and handling of permissions is operating system
* dependent. It is expected that a Un*x type system retains the permissions as
* a file is moved to the backup store and later gets restored. Backup
* directories are created as they are needed and will (at least on Un*x)
* inherit the permissions from its parent directory.
*
* If a rename can not be performed, the backup store will make a copy and
* delete the original file. This makes it possible to backup and restore across
* volume boundaries.
*
* When restoring directories they will be created with permissions in a
* platform specific way (on UN*IX they will inherit the permissions of the
* parent directory).
*
* <h3>Checkpointing</h3> Checkpointing (i.e. to be able to rollback to a
* particular point) can be implemented by using multiple instances of
* BackupStore. The client code will need to remember the individual order among
* the backup stores.
*
* <h3>Restartability</h3> Not implemented - it is possible to obtain the name
* of the backup directories, so manual restore is possible after a crash. An
* idea is to add persistence to a file, and be able to read it back in again.
*
* <h3>A note about exceptions</h3> In general {@link IllegalArgumentException}
* is thrown when attempting an operation that is considered "wrong use", and an
* {@link IllegalStateException} or subclass thereof is thrown on an overall
* wrong use of BackupStore (i.e. attempt to backup when store has been
* restored). Some cases of "wrong use" can not be differentiated from I/O
* errors (like a "file not found" as this could be caused by an entire disk
* disappearing - in these case an {@link IOException} is thrown.
*
* <h3>Implementation Note</h3> The backup root directory will contain folders
* that reflects file system roots. These are encoded using "_" for the UNI*X
* root directory, "__" for a Windows network mounted directory, and single
* "drive letter" folders corresponding to Windows drive letters. Typically, on
* UN*X there will only be a "_" directory in the backup root, and on windows
* there will typically be a single directory called "C".
*/
public class SimpleBackupStore implements IBackupStore {
public static final String BACKUP_FILE_EXTENSION = "p2bu"; //$NON-NLS-1$
public static final String DIR_PLACEHOLDER = "emptydir"; //$NON-NLS-1$
/**
* The name to use for a directory that represents leading separator (i.e. "/"
* or "\").
*/
private static final String ROOTCHAR = "_"; //$NON-NLS-1$
/**
* Map of directory File to backup root (File) - the backup root has a directory
* named {@link #buStoreName} where the backup is found.
*/
private final Path buStoreRoot;
private String buInPlaceSuffix;
/**
* Backup files that sit next to the original rather than in the backup store.
*/
private List<Path> buInPlace;
/**
* Counter of how many files where backed up. Used as a simple check mechanism
* if everything was restored (a guard against manual/external tampering with
* the backup directories).
*/
private long backupCounter;
/**
* Counter of how many files where restored. See {@link #backupCounter}.
*/
private long restoreCounter;
/**
* Flag indicating if this BackupStore has been restored or canceled.
*/
private boolean closed;
/**
* Generates a BackupStore with a default prefix of ".p2bu" for backup directory
* and probe file.
*/
public SimpleBackupStore() {
this(null, "." + BACKUP_FILE_EXTENSION); //$NON-NLS-1$
}
/**
* Generates a BackupStore with a specified prefix for backup directories and
* probe file.
*
* @param buStoreParent Parent under which the backup store will be created. If
* null, java.io.tmpdir is used
* @param prefix Prefix used for human identification of backup stores.
*/
public SimpleBackupStore(File buStoreParent, String prefix) {
String unique = UUID.randomUUID().toString();
String buStoreName = prefix + "_" + unique; //$NON-NLS-1$
this.buStoreRoot = (buStoreParent != null) ? buStoreParent.toPath().resolve(buStoreName)
: Paths.get(System.getProperty("java.io.tmpdir")).resolve(buStoreName); //$NON-NLS-1$
this.buInPlaceSuffix = String.format("-%s.%s", unique, BACKUP_FILE_EXTENSION); //$NON-NLS-1$
this.buInPlace = new ArrayList<>();
}
/**
* Returns the unique backup name (this is the name of generated backup
* directories).
*
* @return the backup name.
*/
@Override
public String getBackupName() {
return buStoreRoot.getFileName().toString();
}
/**
* @return the parent dire under which backups are created
*/
public File getBackupRoot() {
return buStoreRoot.toFile();
}
/**
* Backup the file by moving it to the backup store (for later (optional)
* restore). Calling this method with a file that represents a directory is
* equivalent to calling {@link #backupDirectory(File)}.
*
* A file (path) can only be backed up once per BackupStore instance. When the
* file is backed up, it is moved to a directory under this BackupStore
* instance's directory with a relative path corresponding to the original
* relative path from the backup root e.g. the file /A/B/C/foo.txt could be
* moved to /A/.p2bu_ffffff_ffffff/B/C/foo.txt when /A is the backup root.
*
* If a directory is first backed up, and later replaced by a regular file, and
* this file is backed up (or vice versa) - an {@link IllegalArgumentException}
* is thrown
*
* A backup can not be performed on a closed BackupStore.
*
* @param file - the file (or directory) to backup
*
* @return true if the file was backed up, false if this file (path) has already
* been backed up (the file is not moved to the store).
*
* @throws IOException - if the backup operation fails, or the
* file does not exist
* @throws ClosedBackupStoreException - if the BackupStore has been closed
* @throws IllegalArgumentException - on type mismatch (file vs. directory) of
* earlier backup, or if file does not exist
*/
@Override
public boolean backup(File file) throws IOException {
assertOpen();
Path path = file.toPath();
if (Files.isDirectory(path)) {
return backupDirectory(path.toFile());
}
if (!Files.exists(path)) {
throw new IOException(NLS.bind(Messages.BackupStore_file_not_found, path.toAbsolutePath()));
}
Path buPath = toBackupPath(path);
// Already backed up, but was a directory - wrong usage
if (Files.isDirectory(buPath)) {
throw new IllegalArgumentException(
NLS.bind(Messages.BackupStore_directory_file_mismatch, buPath.toAbsolutePath()));
}
return moveToBackup(path, buPath);
}
/**
* Performs backup of an empty directory.
*
* The directory must be empty before it can be backed up (i.e. similar to a
* delete of a directory). The called must backup the files of the directory
* first. A call to backup a directory is really only needed for empty
* directories as a restore of a file will also restore all of its parent
* directories.
*
* @param file - the (empty) directory to back up
*
* @return true if the directory was moved to backup. false if the directory was
* already backed up.
*
* @throws IllegalArgumentException if file is not a directory, or is not empty.
* @throws IOException if directory can not be moved to the backup
* store, or if the directory is not writeable
*/
@Override
public boolean backupDirectory(File file) throws IOException {
assertOpen();
Path path = file.toPath();
if (!Files.isDirectory(path)) {
throw new IllegalArgumentException(NLS.bind(Messages.BackupStore_not_a_directory, file.getAbsolutePath()));
}
if (Files.list(path).count() > 0) {
throw new IllegalArgumentException(
NLS.bind(Messages.BackupStore_directory_not_empty, file.getAbsolutePath()));
}
return moveDirToBackup(path);
}
/**
* Backs up a file, or everything under a directory.
*
* @param file - file to backup or directory
*
* @throws IOException if backup operation failed
*/
@Override
public void backupAll(File file) throws IOException {
assertOpen();
Path path = file.toPath().normalize();
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path f, BasicFileAttributes attrs) throws IOException {
backup(f.toFile());
return CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
if (exc != null) {
throw exc;
}
moveDirToBackup(dir);
return CONTINUE;
}
});
}
/**
* Backup the file by leaving a copy of the contents in the original location.
*
* Calling this method with a file that represents a directory throws an
* {@link IllegalArgumentException}.
*
* A file (path) can only be backed up once per BackupStore instance. When the
* file is backed up, it is moved to a directory under this BackupStore
* instance's directory with a relative path corresponding to the original
* relative path from the backup root e.g. the file /A/B/C/foo.txt could be
* moved to /A/.p2bu_ffffff_ffffff/B/C/foo.txt when /A is the backup root.
*
* If a directory is first backed up, and later replaced by a regular file, and
* this file is backed up (or vice versa) - an {@link IllegalArgumentException}
* is thrown
*
* A backup can not be performed on a closed BackupStore.
*
* @param file - the file (or directory) to backup
*
* @return true if the file was backed up, false if this file (path) has already
* been backed up (the file is not moved to the store).
*
* @throws IOException if the backup operation fails, or the file
* does not exist
* @throws ClosedBackupStoreException if the BackupStore has been closed
* @throws IllegalArgumentException on type mismatch (file vs. directory) of
* earlier backup, or if file is a Directory
*/
@Override
public boolean backupCopy(File file) throws IOException {
assertOpen();
Path path = file.toPath();
if (!Files.exists(path)) {
throw new IOException(NLS.bind(Messages.BackupStore_file_not_found, file.getAbsolutePath()));
}
if (Files.isDirectory(path)) {
throw new IllegalArgumentException(
NLS.bind(Messages.BackupStore_can_not_copy_directory, file.getAbsolutePath()));
}
Path buPath = toBackupPath(path);
// Already backed up, but was a directory = wrong usage
if (Files.isDirectory(buPath)) {
throw new IllegalArgumentException(
NLS.bind(Messages.BackupStore_directory_file_mismatch, buPath.toAbsolutePath()));
}
// Already backed up, can only be done once with one BackupStore
if (Files.exists(buPath)) {
return false;
}
Files.createDirectories(buPath.getParent());
Files.copy(path, buPath, REPLACE_EXISTING);
backupCounter++;
return true;
}
/**
* Backs up a file, or everything under a directory.
*
* A copy of the backup is left in the original place.
*
* @param file
*
* @throws IOException
*/
@Override
public void backupCopyAll(File file) throws IOException {
assertOpen();
Path path = file.toPath();
if (!Files.exists(path)) {
return;
}
path = path.normalize();
if (Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS)) {
backupCopy(file);
} else if (Files.isDirectory(path)) {
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path f, BasicFileAttributes attrs) throws IOException {
backupCopy(f.toFile());
return CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
if (exc != null) {
throw exc;
}
copyDirToBackup(dir);
return CONTINUE;
}
});
}
}
/**
* Restores all backup files from backup store. Note that restore of a (non
* directory) file deletes an existing file or directory found in the restore
* location. When the backup has been restored this BackupStore instance is
* closed and can not be used for further backup or restore.
*
* If there are unrestorable items (non writable directories, or general IO
* exceptions) these items are written to the log, and the backup copies remain
* in the file system and can be manually restored (using a simple zip of the
* backup directory, and an unzip to the buRoot once the problem has been
* corrected).
*
* @throws IOException if the backup was not fully restored -
* unrestored items have been logged.
* @throws ClosedBackupStoreException if the backup is already closed.
*/
@Override
public void restore() throws IOException {
assertOpen();
closed = true;
// Put back all files.
// Collect things that could not be restored
Map<Path, Throwable> unrestorable = new HashMap<>();
restoreBackups(unrestorable);
restoreInPlaceBackups(unrestorable);
boolean restored = true;
// Checked failed attempts to restore
if (!unrestorable.isEmpty()) {
restored = false;
unrestorable.forEach((p, err) -> {
logError(NLS.bind(Messages.BackupStore_manual_restore_needed, err, p.toAbsolutePath()));
});
}
// Check external tampering with backup store
if (backupCounter != restoreCounter) {
restored = false;
if (!unrestorable.isEmpty()) {
logError(NLS.bind(Messages.BackupStore_0_of_1_items_restored, restoreCounter, backupCounter));
} else {
logError(NLS.bind(Messages.BackupStore_externally_modified_0_of_1_restored, restoreCounter,
backupCounter));
}
}
if (!restored) {
throw new IOException(Messages.BackupStore_errors_while_restoring_see_log);
}
}
/**
* Discards and closes this BackupStore. Does nothing if this store is already
* restored or discarded.
*/
@Override
public void discard() {
if (closed) {
return;
}
closed = true;
try {
deleteAll(buStoreRoot);
} catch (IOException e) {
logWarning(NLS.bind(Messages.BackupStore_can_not_remove_bu_directory, buStoreRoot.toAbsolutePath()));
}
for (Path buFile : buInPlace) {
try {
deleteAll(buFile);
} catch (IOException e) {
logWarning(NLS.bind(Messages.BackupStore_can_not_remove_bu_file, buFile.toAbsolutePath()));
}
}
}
private void assertOpen() {
if (closed) {
throw new ClosedBackupStoreException(Messages.BackupStore_closed_store);
}
}
/**
* Makes sure a directory exists in the backup store without touching the original directory content
*
* @param path
*
* @return false if the directory is already created in the backup store, false if a placeholder had
* to be created and backed up.
*
* @throws IOException
*/
private boolean copyDirToBackup(Path path) throws IOException {
Path buPath = toBackupPath(path);
if (Files.exists(buPath)) {
return false;
}
Path placeholderPath = path.resolve(DIR_PLACEHOLDER);
try {
Files.createFile(placeholderPath);
} catch (IOException e) {
throw new IOException(
NLS.bind(Messages.BackupStore_can_not_create_placeholder, placeholderPath.toAbsolutePath()), e);
}
Path buPlaceholderPath = buPath.resolve(DIR_PLACEHOLDER);
moveToBackup(placeholderPath, buPlaceholderPath);
return true;
}
private boolean moveDirToBackup(Path dir) throws IOException {
boolean copied = copyDirToBackup(dir);
try {
Files.delete(dir);
} catch (IOException e) {
throw new IOException(NLS.bind(Messages.BackupStore_can_not_remove, dir.toAbsolutePath()));
}
return copied;
}
/**
* Move/rename file to a backup file.
*
* Exposed for testing purposes.
*
* Callers of the method must have ensured that the source file exists and the
* backup file does not exist.
*
* @param file source file to move; should already exist and must not be
* directory
* @param buFile destination backup file to move to; should not exist and must
* be a directory
*
* @throws IOException if the backup operation fails
*/
private boolean moveToBackup(Path path, Path buPath) throws IOException {
// Already backed up. Can only be done once with one BackupStore.
if (Files.exists(buPath)) {
/*
* Although backed up, the file can be still on the file system. For example,
* two IUs may be unzipping their contents to the same location and share a few
* common files, which have to be removed twice.
*/
try {
Files.delete(path);
} catch (IOException e) {
throw new IOException(NLS.bind(Messages.BackupStore_can_not_remove, path.toAbsolutePath()), e);
}
return false;
}
// make sure all of the directories exist / gets created
Path buPathDir = buPath.getParent();
try {
Files.createDirectories(buPathDir);
} catch (IOException e) {
throw new IllegalArgumentException(
NLS.bind(Messages.BackupStore_file_directory_mismatch, buPathDir.toAbsolutePath()), e);
}
move(path, buPath);
if (isEclipseExe(path) && Files.isRegularFile(path)) {
// The original is the launcher executable and it still exists at the original
// location although the move succeeded.
// This happens when it is the Windows executable that is locked because it's
// running and we are attempting to move it to a different drive.
// In this case the target will exist as a copy, so we should delete it.
// Then backup in place which will necessarily be on the same drive.
Files.delete(buPath);
Path inPlaceBuPath = toInPlaceBackupPath(path);
move(path, inPlaceBuPath);
buInPlace.add(inPlaceBuPath);
}
backupCounter++;
return true;
}
/**
* Restores everything stored in the backup root
*
* Responsible for converting the root prefix of the path from backup format
* back to the original real OS names. I.e. "_/" to "//", "__/" to "///", "C/"
* to "C:", etc.
*
* @param unrestorable accumulate unrestorable paths (including the entire
* backup store).
*
* @throws IOException
*/
private void restoreBackups(Map<Path, Throwable> unrestorable) throws IOException {
if (!Files.exists(buStoreRoot)) {
unrestorable.put(buStoreRoot, new IOException(
NLS.bind(Messages.BackupStore_missing_backup_directory, buStoreRoot.toAbsolutePath())));
return;
}
Files.walkFileTree(buStoreRoot, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path buDir, BasicFileAttributes attrs) {
try {
if (Files.isSameFile(buStoreRoot, buDir)) {
return CONTINUE;
}
Path dir = toSourcePath(buDir);
// There is a file where we the original directory used to be - delete it
if (Files.isRegularFile(dir)) {
Files.delete(dir);
}
// Make the original directory if needed
Files.createDirectories(dir);
} catch (IOException e) {
unrestorable.put(buDir, e);
}
return CONTINUE;
}
@Override
public FileVisitResult visitFile(Path buFile, BasicFileAttributes attrs) {
Path file = toSourcePath(buFile);
try {
// The first level children of buStoreRoot are always directories since they
// model file system roots
if (Files.isSameFile(buFile.getParent(), buStoreRoot)) {
unrestorable.put(buFile, new IOException("Not a directory")); //$NON-NLS-1$
} else {
/*
* Do not restore the place-holders as they are used to trigger creation of
* empty directories and are not wanted in the restored location.
*
* They are counted as restored non the less.
*/
if (!DIR_PLACEHOLDER.equals(buFile.getFileName().toString())) {
// Clean up the site where the original used to be.
// It may be that a file or a directory now occupies it.
deleteAll(file);
// Move the backup to the original location
move(buFile, file);
} else {
Files.delete(buFile);
}
restoreCounter++;
}
} catch (IOException e) {
unrestorable.put(buFile, e);
}
return CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
unrestorable.put(file, exc);
throw exc;
}
@Override
public FileVisitResult postVisitDirectory(Path buDir, IOException exc) throws IOException {
if (exc != null) {
unrestorable.put(buDir, exc);
throw exc;
}
try {
Files.delete(buDir);
} catch (DirectoryNotEmptyException e) {
String children = Files.list(buDir)
.map(p -> p.relativize(buDir))
.map(Path::toString)
.collect(joining(",")); //$NON-NLS-1$
unrestorable.put(buDir, new IOException(String.format(
"Directory %s not empty: %s", buDir, children, e))); //$NON-NLS-1$
} catch (IOException e) {
unrestorable.put(buDir, e);
}
return CONTINUE;
}
});
}
private void restoreInPlaceBackups(Map<Path, Throwable> unrestorable) {
for (Path buPath : buInPlace) {
Path path = toInPlaceSourcePath(buPath);
try {
move(buPath, path);
restoreCounter++;
} catch (IOException e) {
unrestorable.put(buPath, e);
}
}
}
/**
* Converts a source path to a backup path.
*
* Exposed for testing purposes.
*
* A leading "root" is transformed to the ROOTCHAR character. On Windows,
* network mapped drives starts with two separators - and are encoded as two
* ROOTCHAR.
*
* E.g. \\Host\C$\file becomes __\Host\C$\file /users/test/file becomes
* _/users/test/file C:/file becomes C/file
*
* @param file a source file that needs to be backed up
*
* @return a file to which the original content can be backed up
*
* @throws IOException
*/
protected Path toBackupPath(Path path) throws IOException {
Path pathNormal = path.normalize();
String buPath = pathNormal.toAbsolutePath().toString();
String buPrefix = ""; //$NON-NLS-1$
while (buPath.startsWith(File.separator)) {
buPrefix += ROOTCHAR;
buPath = buPath.substring(1);
}
// Linux or Windows net mount
if (!buPrefix.isEmpty()) {
buPath = Paths.get(buPrefix, buPath).toString();
}
// Windows
else {
// It is a windows drive letter first.
// Transform C:/foo to C/foo
int idx = buPath.indexOf(":"); //$NON-NLS-1$
if (idx < 1) {
throw new IllegalArgumentException("File is neither absolute nor has a drive name: " + buPath); //$NON-NLS-1$
}
buPath = buPath.substring(0, idx) + buPath.substring(idx + 1);
}
Path buFile = buStoreRoot.resolve(buPath);
return buFile;
}
/**
* Converts a backup file to the original source file.
*
* ///x/y/z -> ___x/y/z \\x\y\z c:\x\y\z -> c\x\y\z
*
* @param buPath an absolute file under {@link #buStoreRoot} to which some
* content is backed up.
*
* @return the original source file to which the content can be restored.
*/
protected Path toSourcePath(Path buPath) {
Path buPathRel = buStoreRoot.relativize(buPath);
String pathName = buPathRel.toString();
String prefix = ""; //$NON-NLS-1$
while (pathName.startsWith(ROOTCHAR)) {
prefix += File.separator;
pathName = pathName.substring(1);
}
if (prefix.isEmpty()) {
// The first char is a windows drive name
pathName = pathName.charAt(0) + ":" + pathName.substring(1); //$NON-NLS-1$
} else {
pathName = prefix + pathName;
}
return Paths.get(pathName);
}
/**
* Converts a path to an in-place backup path.
*
* Exposed for testing purposes.
*
* @param path
*
* @return a path next to the original where the original will be moved, rather
* than will be moved
*/
protected Path toInPlaceBackupPath(Path path) {
String buPathName = path.getFileName() + buInPlaceSuffix;
Path buPath = path.toAbsolutePath().resolveSibling(buPathName);
return buPath;
}
/**
* Converts a in-place backup path to the original source path.
*
* Exposed for testing purposes.
*
* @param path
*
* @return a source path
*/
protected Path toInPlaceSourcePath(Path buPath) {
String buPathName = buPath.getFileName().toString();
int suffixIdx = buPathName.indexOf(buInPlaceSuffix);
if (suffixIdx <= 0) {
throw new IllegalArgumentException();
}
String pathName = buPathName.substring(0, suffixIdx);
Path path = buPath.resolveSibling(pathName);
return path;
}
/**
* A generic file operation that attempts to move a file.
*
* Exposed in a separate method for testing purposes.
*/
protected void move(Path source, Path target) throws IOException {
Files.move(source, target, REPLACE_EXISTING);
}
private static boolean isEclipseExe(Path file) {
String name = file.getFileName().toString();
String launcher = System.getProperty("eclipse.launcher"); //$NON-NLS-1$
if (launcher != null) {
String launcherName = Paths.get(launcher).getFileName().toString();
if (name.equalsIgnoreCase(launcherName)) {
return true;
}
}
return name.equalsIgnoreCase("eclipse.exe") || name.equalsIgnoreCase("eclipsec.exe"); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Deletes a file, or a directory with all of it's children.
*
* @param path
*
* @throws IOException
*/
private static void deleteAll(Path path) throws IOException {
if (!Files.exists(path)) {
return;
}
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
if (exc != null) {
throw exc;
}
Files.delete(dir);
return CONTINUE;
}
});
}
}