diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Folder.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Folder.java index af9718ce6..72bcde132 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Folder.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Folder.java @@ -7,7 +7,6 @@ package org.cryptomator.filesystem; import java.io.IOException; import java.io.UncheckedIOException; -import java.time.Instant; import java.util.stream.Stream; /** @@ -151,17 +150,4 @@ public interface Folder extends Node { } } - /** - *

- * Sets the creation time of the folder. - *

- * Setting the creation time may not be supported by all {@link FileSystem FileSystems}. If the {@code FileSystem} this {@code Folder} belongs to does not support the - * setting the creation time the behavior of this method is unspecified. - * - * @param instant the time to set as creation time - */ - default void setCreationTime(Instant instant) throws UncheckedIOException { - throw new UncheckedIOException(new IOException("CreationTime not supported")); - } - } diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Node.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Node.java index 5d5ee3890..a5fab7d2c 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Node.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Node.java @@ -5,6 +5,7 @@ ******************************************************************************/ package org.cryptomator.filesystem; +import java.io.IOException; import java.io.UncheckedIOException; import java.time.Instant; import java.util.Optional; @@ -45,6 +46,19 @@ public interface Node { return Optional.empty(); } + /** + *

+ * Sets the creation time of this node. + *

+ * Setting the creation time may not be supported by all {@link FileSystem FileSystems}. If the {@code FileSystem} this {@code Node} belongs to does not support the + * setting the creation time the behavior of this method is unspecified. + * + * @param instant the time to set as creation time + */ + default void setCreationTime(Instant instant) throws UncheckedIOException { + throw new UncheckedIOException(new IOException("CreationTime not supported")); + } + /** * @return the {@link FileSystem} this Node belongs to */ diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFile.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFile.java index 89ac586be..4d8db50ef 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFile.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFile.java @@ -9,6 +9,7 @@ package org.cryptomator.filesystem.delegating; import java.io.UncheckedIOException; +import java.time.Instant; import java.util.Optional; import org.cryptomator.filesystem.File; @@ -69,4 +70,14 @@ public abstract class DelegatingFile> extends D } } + @Override + public Optional creationTime() throws UncheckedIOException { + return delegate.creationTime(); + } + + @Override + public void setCreationTime(Instant instant) throws UncheckedIOException { + delegate.setCreationTime(instant); + } + } diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFolder.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFolder.java index afb6823da..d62b9740c 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFolder.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFolder.java @@ -97,6 +97,11 @@ public abstract class DelegatingFolder, F exten } } + @Override + public Optional creationTime() throws UncheckedIOException { + return delegate.creationTime(); + } + @Override public void setCreationTime(Instant instant) throws UncheckedIOException { delegate.setCreationTime(instant); diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java index 4bb406b79..a79285f8c 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java @@ -143,6 +143,11 @@ class BlockAlignedWritableFile implements WritableFile { delegate.get().setLastModified(instant); } + @Override + public void setCreationTime(Instant instant) throws UncheckedIOException { + delegate.get().setCreationTime(instant); + } + @Override public void delete() throws UncheckedIOException { delegate.get().delete(); diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFile.java index e8c4aa9b6..24e33c3b3 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFile.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFile.java @@ -12,7 +12,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import java.io.UncheckedIOException; import java.time.Instant; -import java.util.Optional; import org.cryptomator.crypto.engine.Cryptor; import org.cryptomator.filesystem.File; @@ -57,9 +56,4 @@ public class CryptoFile extends CryptoNode implements File { return toString().compareTo(o.toString()); } - @Override - public Optional creationTime() throws UncheckedIOException { - return physicalFile().creationTime(); - } - } diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFolder.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFolder.java index d67a2ff3a..f1457d84a 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFolder.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFolder.java @@ -13,10 +13,9 @@ import static java.nio.charset.StandardCharsets.UTF_8; import java.io.IOException; import java.io.Reader; import java.io.UncheckedIOException; -import java.nio.ByteBuffer; +import java.io.Writer; import java.nio.channels.Channels; import java.time.Instant; -import java.util.Optional; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; @@ -30,7 +29,6 @@ import org.cryptomator.filesystem.Deleter; import org.cryptomator.filesystem.File; import org.cryptomator.filesystem.Folder; import org.cryptomator.filesystem.Node; -import org.cryptomator.filesystem.WritableFile; class CryptoFolder extends CryptoNode implements Folder { @@ -131,14 +129,16 @@ class CryptoFolder extends CryptoNode implements Folder { @Override public void create() { final File dirFile = physicalFile(); - if (dirFile.exists()) { + final Folder dir = physicalFolder(); + if (dirFile.exists() && dir.exists()) { return; } parent.create(); final String directoryId = getDirectoryId(); - try (WritableFile writable = dirFile.openWritable()) { - final ByteBuffer buf = ByteBuffer.wrap(directoryId.getBytes()); - writable.write(buf); + try (Writer writer = Channels.newWriter(dirFile.openWritable(), UTF_8.newEncoder(), -1)) { + writer.write(directoryId); + } catch (IOException e) { + throw new UncheckedIOException(e); } physicalFolder().create(); } @@ -157,13 +157,15 @@ class CryptoFolder extends CryptoNode implements Folder { throw new IllegalArgumentException("Can not move directories containing one another (src: " + this + ", dst: " + target + ")"); } + // directoryId will be used by target, from now on we must no longer use the same id + // a new one will be generated on-demand. + final String oldDirectoryId = getDirectoryId(); + directoryId.set(null); + target.physicalFile().parent().get().create(); this.physicalFile().moveTo(target.physicalFile()); - // directoryId is now used by target, we must no longer use the same id - // (we'll generate a new one when needed) - target.directoryId.set(getDirectoryId()); - directoryId.set(null); + target.directoryId.set(oldDirectoryId); } @Override @@ -172,13 +174,9 @@ class CryptoFolder extends CryptoNode implements Folder { return; } Deleter.deleteContent(this); - physicalFile().delete(); physicalFolder().delete(); - } - - @Override - public Optional creationTime() throws UncheckedIOException { - return physicalFile().creationTime(); + physicalFile().delete(); + directoryId.set(null); } @Override diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoNode.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoNode.java index 384ddab7e..cfa2f0d79 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoNode.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoNode.java @@ -8,6 +8,8 @@ *******************************************************************************/ package org.cryptomator.filesystem.crypto; +import java.io.UncheckedIOException; +import java.time.Instant; import java.util.Optional; import org.cryptomator.crypto.engine.Cryptor; @@ -55,7 +57,16 @@ abstract class CryptoNode implements Node { @Override public boolean exists() { return physicalFile().exists(); - // return parent.children().anyMatch(node -> node.equals(this)); + } + + @Override + public Optional creationTime() throws UncheckedIOException { + return physicalFile().creationTime(); + } + + @Override + public void setCreationTime(Instant instant) throws UncheckedIOException { + physicalFile().setCreationTime(instant); } @Override diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java index 681687d98..7eb2a8b9d 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java @@ -86,6 +86,11 @@ class CryptoWritableFile implements WritableFile { file.setLastModified(instant); } + @Override + public void setCreationTime(Instant instant) throws UncheckedIOException { + file.setCreationTime(instant); + } + @Override public void delete() { writeTask.cancel(true); @@ -99,11 +104,6 @@ class CryptoWritableFile implements WritableFile { initialize(0); } - @Override - public void setCreationTime(Instant instant) throws UncheckedIOException { - file.setCreationTime(instant); - } - @Override public boolean isOpen() { return file.isOpen(); diff --git a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java index 31fe9a64e..ea143eb49 100644 --- a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java +++ b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java @@ -71,10 +71,6 @@ class InMemoryFile extends InMemoryNode implements File { this.lastModified = lastModified; } - private void setCreationTime(Instant creationTime) { - this.creationTime = creationTime; - } - private ByteBuffer getContent() { return content; } diff --git a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFolder.java b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFolder.java index d6bfb5d1d..6d76b8f6b 100644 --- a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFolder.java +++ b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFolder.java @@ -10,15 +10,15 @@ package org.cryptomator.filesystem.inmem; import static java.lang.String.format; -import java.io.IOException; +import java.io.FileNotFoundException; import java.io.UncheckedIOException; +import java.nio.file.FileAlreadyExistsException; import java.time.Instant; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; -import org.apache.commons.io.FileExistsException; import org.cryptomator.common.WeakValuedCache; import org.cryptomator.filesystem.Folder; @@ -38,7 +38,7 @@ class InMemoryFolder extends InMemoryNode implements Folder { if (exists()) { return existingChildren.values().stream(); } else { - throw new UncheckedIOException(new IOException(format("Folder %s does not exist", this))); + throw new UncheckedIOException(new FileNotFoundException(format("Folder %s does not exist", this))); } } @@ -69,7 +69,7 @@ class InMemoryFolder extends InMemoryNode implements Folder { parent.existingChildren.compute(name, (k, v) -> { if (v != null) { // other file or folder with same name already exists. - throw new UncheckedIOException(new FileExistsException(k)); + throw new UncheckedIOException(new FileAlreadyExistsException(k)); } else { this.lastModified = Instant.now(); return this; @@ -113,11 +113,6 @@ class InMemoryFolder extends InMemoryNode implements Folder { assert!this.exists(); } - @Override - public void setCreationTime(Instant instant) throws UncheckedIOException { - creationTime = instant; - } - @Override public String toString() { return parent.toString() + name + "/"; diff --git a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryNode.java b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryNode.java index f20d28d88..55dd2d012 100644 --- a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryNode.java +++ b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryNode.java @@ -83,4 +83,9 @@ class InMemoryNode implements Node { } } + @Override + public void setCreationTime(Instant creationTime) throws UncheckedIOException { + this.creationTime = creationTime; + } + } diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioNode.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioNode.java index db3d32494..f7ed1ac8c 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioNode.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioNode.java @@ -3,6 +3,7 @@ package org.cryptomator.filesystem.nio; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Path; +import java.nio.file.attribute.FileTime; import java.time.Instant; import java.util.Optional; @@ -59,4 +60,13 @@ abstract class NioNode implements Node { } } + @Override + public void setCreationTime(Instant creationTime) throws UncheckedIOException { + try { + nioAccess.setCreationTime(path, FileTime.from(creationTime)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFolder.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFolder.java index f22ca07f5..4992967cd 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFolder.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFolder.java @@ -147,15 +147,20 @@ class DavFolder extends DavNode { @Override public void copy(DavResource destination, boolean shallow) throws DavException { - if (destination instanceof DavFolder) { - DavFolder dst = (DavFolder) destination; - if (dst.node.exists()) { - dst.node.delete(); - } else if (!dst.node.parent().get().exists()) { + if (!node.exists()) { + throw new DavException(DavServletResponse.SC_NOT_FOUND); + } + if (destination instanceof DavNode) { + DavNode dst = (DavNode) destination; + if (!dst.node.parent().get().exists()) { throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist."); } - dst.node.create(); + } + if (destination instanceof DavFolder) { + DavFolder dst = (DavFolder) destination; if (shallow) { + // create destination, if it doesn't exist yet: + dst.node.create(); // http://www.webdav.org/specs/rfc2518.html#copy.for.collections node.creationTime().ifPresent(dst::setCreationTime); dst.setModificationTime(node.lastModified()); @@ -168,8 +173,6 @@ class DavFolder extends DavNode { Folder newDst = parent.folder(dst.node.name()); if (dst.node.exists()) { dst.node.delete(); - } else if (!parent.exists()) { - throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist."); } node.copyTo(newDst); } else {