-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# Conflicts: # pom.xml
- Loading branch information
Showing
23 changed files
with
1,017 additions
and
266 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
src/main/java/org/cryptomator/cryptofs/BiConsumerThrowingException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package org.cryptomator.cryptofs; | ||
|
||
interface BiConsumerThrowingException<A, B, E extends Exception> { | ||
|
||
void accept(A a, B b) throws E; | ||
|
||
} |
135 changes: 135 additions & 0 deletions
135
src/main/java/org/cryptomator/cryptofs/CopyAndMoveOperations.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package org.cryptomator.cryptofs; | ||
|
||
import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; | ||
import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; | ||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; | ||
import static java.nio.file.StandardOpenOption.CREATE_NEW; | ||
import static java.nio.file.StandardOpenOption.READ; | ||
import static java.nio.file.StandardOpenOption.WRITE; | ||
|
||
import java.io.IOException; | ||
import java.nio.channels.FileChannel; | ||
import java.nio.file.AtomicMoveNotSupportedException; | ||
import java.nio.file.CopyOption; | ||
import java.nio.file.DirectoryStream; | ||
import java.nio.file.FileAlreadyExistsException; | ||
import java.nio.file.NoSuchFileException; | ||
import java.nio.file.Path; | ||
import java.nio.file.attribute.BasicFileAttributeView; | ||
import java.nio.file.attribute.BasicFileAttributes; | ||
import java.nio.file.spi.FileSystemProvider; | ||
import java.util.EnumSet; | ||
import java.util.Optional; | ||
|
||
import javax.inject.Inject; | ||
|
||
import org.apache.commons.lang3.ArrayUtils; | ||
|
||
@PerProvider | ||
class CopyAndMoveOperations { | ||
|
||
@Inject | ||
public CopyAndMoveOperations() { | ||
} | ||
|
||
public void copy(CryptoPath source, CryptoPath target, CopyOption... options) throws IOException { | ||
if (source.equals(target)) { | ||
return; | ||
} | ||
if (pathsBelongToSameFileSystem(source, target)) { | ||
source.getFileSystem().copy(source, target, options); | ||
} else { | ||
Optional<BasicFileAttributes> sourceAttrs = attributes(source); | ||
Optional<BasicFileAttributes> targetAttrs = attributes(target); | ||
if (!sourceAttrs.isPresent()) { | ||
throw new NoSuchFileException(source.toUri().toString()); | ||
} | ||
if (targetAttrs.isPresent()) { | ||
if (ArrayUtils.contains(options, REPLACE_EXISTING)) { | ||
provider(target).delete(target); | ||
} else { | ||
throw new FileAlreadyExistsException(target.toUri().toString()); | ||
} | ||
} | ||
if (sourceAttrs.get().isDirectory()) { | ||
provider(target).createDirectory(target); | ||
} else { | ||
try (FileChannel sourceChannel = provider(source).newFileChannel(source, EnumSet.of(READ)); // | ||
FileChannel targetChannel = provider(target).newFileChannel(target, EnumSet.of(CREATE_NEW, WRITE))) { | ||
sourceChannel.transferTo(0, sourceChannel.size(), targetChannel); | ||
} | ||
} | ||
if (ArrayUtils.contains(options, COPY_ATTRIBUTES)) { | ||
BasicFileAttributeView targetAttrView = provider(target).getFileAttributeView(target, BasicFileAttributeView.class); | ||
targetAttrView.setTimes(sourceAttrs.get().lastModifiedTime(), sourceAttrs.get().lastAccessTime(), sourceAttrs.get().creationTime()); | ||
} | ||
} | ||
} | ||
|
||
private FileSystemProvider provider(CryptoPath path) { | ||
return path.getFileSystem().provider(); | ||
} | ||
|
||
public void move(CryptoPath source, CryptoPath target, CopyOption... options) throws IOException { | ||
if (source.equals(target)) { | ||
return; | ||
} | ||
if (pathsBelongToSameFileSystem(source, target)) { | ||
source.getFileSystem().move(source, target, options); | ||
} else { | ||
if (ArrayUtils.contains(options, ATOMIC_MOVE)) { | ||
throw new AtomicMoveNotSupportedException(source.toUri().toString(), target.toUri().toString(), "Move of encrypted file to different FileSystem"); | ||
} | ||
if (isNonEmptyDirectory(source)) { | ||
throw new IOException("Can not move non empty directory to different FileSystem"); | ||
} | ||
boolean success = false; | ||
try { | ||
copy(source, target, addCopyAttributesTo(options)); | ||
success = true; | ||
} finally { | ||
if (!success) { | ||
// do a best effort to clean a partially copied file | ||
try { | ||
provider(target).deleteIfExists(target); | ||
} catch (IOException e) { | ||
// ignore | ||
} | ||
} | ||
} | ||
provider(source).deleteIfExists(source); | ||
} | ||
} | ||
|
||
private boolean pathsBelongToSameFileSystem(CryptoPath source, CryptoPath target) { | ||
return source.getFileSystem() == target.getFileSystem(); | ||
} | ||
|
||
private boolean isNonEmptyDirectory(CryptoPath source) throws IOException { | ||
Optional<BasicFileAttributes> sourceAttrs = attributes(source); | ||
if (!sourceAttrs.map(BasicFileAttributes::isDirectory).orElse(false)) { | ||
return false; | ||
} | ||
try (DirectoryStream<Path> contents = provider(source).newDirectoryStream(source, ignored -> true)) { | ||
return contents.iterator().hasNext(); | ||
} | ||
} | ||
|
||
private Optional<BasicFileAttributes> attributes(CryptoPath path) { | ||
try { | ||
return Optional.of(provider(path).readAttributes(path, BasicFileAttributes.class)); | ||
} catch (IOException e) { | ||
return Optional.empty(); | ||
} | ||
} | ||
|
||
private CopyOption[] addCopyAttributesTo(CopyOption[] options) { | ||
CopyOption[] result = new CopyOption[options.length + 1]; | ||
for (int i = 0; i < options.length; i++) { | ||
result[i] = options[i]; | ||
} | ||
result[options.length] = COPY_ATTRIBUTES; | ||
return result; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.