Jelajahi Sumber

directory moving

Sebastian Stenzel 10 tahun lalu
induk
melakukan
4cf872f916

+ 60 - 6
main/core/src/main/java/org/cryptomator/webdav/jackrabbit/CryptoLocator.java

@@ -1,10 +1,21 @@
 package org.cryptomator.webdav.jackrabbit;
 
+import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.FileSystems;
+import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
 
 import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.jackrabbit.webdav.DavResourceLocator;
@@ -69,7 +80,7 @@ class CryptoLocator implements DavResourceLocator {
 	@Override
 	public String getRepositoryPath() {
 		if (isRootLocation()) {
-			return getDirectoryPath();
+			return getEncryptedRootDirectoryPath();
 		}
 		try {
 			final String plaintextPath = getResourcePath();
@@ -88,18 +99,61 @@ class CryptoLocator implements DavResourceLocator {
 	 * Returns the encrypted, absolute path on the local filesystem to the directory represented by this locator.
 	 * 
 	 * @return Absolute, encrypted path as string (use {@link #getEncryptedDirectoryPath()} for {@link Path}s).
+	 * @throws IOException
 	 */
-	public String getDirectoryPath() {
-		final String ciphertextPath = cryptor.encryptDirectoryPath(getResourcePath(), FileSystems.getDefault().getSeparator());
-		return rootPath.resolve(ciphertextPath).toString();
+	public String getDirectoryPath(boolean create) throws IOException {
+		if (isRootLocation()) {
+			return getEncryptedRootDirectoryPath();
+		} else {
+			final List<String> cleartextPathComponents = Arrays.asList(StringUtils.split(getResourcePath(), "/"));
+			return getEncryptedDirectoryPath(rootPath, cleartextPathComponents, false).toString();
+		}
+	}
+
+	private Path getEncryptedDirectoryPath(Path encryptedParentDirectoryPath, List<String> cleartextSubPathComponents, boolean create) throws IOException {
+		if (cleartextSubPathComponents.size() == 0) {
+			return encryptedParentDirectoryPath;
+		} else {
+			final String nextPathComponent = cleartextSubPathComponents.get(0);
+			final List<String> remainingSubPathComponents = cleartextSubPathComponents.subList(1, cleartextSubPathComponents.size());
+			final String fullEncryptedSubdirectoryPath = getEncryptedDirectoryPath(encryptedParentDirectoryPath, nextPathComponent, create);
+			return getEncryptedDirectoryPath(rootPath.resolve(fullEncryptedSubdirectoryPath), remainingSubPathComponents, create);
+		}
+	}
+
+	private String getEncryptedDirectoryPath(Path encryptedParentDirectoryPath, String cleartextDirectoryName, boolean create) throws IOException {
+		final String encryptedDirectoryName = this.cryptor.encryptFilename(cleartextDirectoryName, this.factory);
+		// TODO file extensions...
+		final Path directoryFile = encryptedParentDirectoryPath.resolve(encryptedDirectoryName + ".dir");
+		if (Files.exists(directoryFile)) {
+			try (final FileChannel c = FileChannel.open(directoryFile, StandardOpenOption.READ, StandardOpenOption.DSYNC); final FileLock lock = c.lock(0L, Long.MAX_VALUE, true)) {
+				final ByteBuffer buffer = ByteBuffer.allocate((int) c.size());
+				c.read(buffer);
+				final String directoryUuid = buffer.asCharBuffer().toString();
+				return this.cryptor.encryptDirectoryPath(directoryUuid, FileSystems.getDefault().getSeparator());
+			}
+		} else if (create) {
+			try (final FileChannel c = FileChannel.open(directoryFile, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.DSYNC); final FileLock lock = c.lock()) {
+				final String directoryUuid = UUID.randomUUID().toString();
+				final ByteBuffer buf = ByteBuffer.wrap(directoryUuid.getBytes(StandardCharsets.UTF_8));
+				c.write(buf);
+				return this.cryptor.encryptDirectoryPath(directoryUuid, FileSystems.getDefault().getSeparator());
+			}
+		} else {
+			throw new FileNotFoundException(directoryFile.toString());
+		}
+	}
+
+	private String getEncryptedRootDirectoryPath() {
+		return this.cryptor.encryptDirectoryPath("", FileSystems.getDefault().getSeparator());
 	}
 
 	public Path getEncryptedFilePath() {
 		return FileSystems.getDefault().getPath(getRepositoryPath());
 	}
 
-	public Path getEncryptedDirectoryPath() {
-		return FileSystems.getDefault().getPath(getDirectoryPath());
+	public Path getEncryptedDirectoryPath(boolean create) throws IOException {
+		return FileSystems.getDefault().getPath(getDirectoryPath(create));
 	}
 
 	/* other stuff */

+ 20 - 3
main/core/src/main/java/org/cryptomator/webdav/jackrabbit/CryptoResourceFactory.java

@@ -1,5 +1,7 @@
 package org.cryptomator.webdav.jackrabbit;
 
+import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -17,6 +19,7 @@ import org.apache.jackrabbit.webdav.DavSession;
 import org.apache.jackrabbit.webdav.lock.LockManager;
 import org.apache.jackrabbit.webdav.lock.SimpleLockManager;
 import org.cryptomator.crypto.Cryptor;
+import org.cryptomator.webdav.exceptions.IORuntimeException;
 import org.eclipse.jetty.http.HttpHeader;
 
 public class CryptoResourceFactory implements DavResourceFactory {
@@ -52,10 +55,17 @@ public class CryptoResourceFactory implements DavResourceFactory {
 
 	private DavResource createResource(CryptoLocator locator, DavServletRequest request, DavServletResponse response) throws DavException {
 		final Path filepath = FileSystems.getDefault().getPath(locator.getRepositoryPath());
-		final Path dirpath = FileSystems.getDefault().getPath(locator.getDirectoryPath());
+		Path dirpath = null;
+		try {
+			dirpath = FileSystems.getDefault().getPath(locator.getDirectoryPath(DavMethods.METHOD_MKCOL.equals(request.getMethod())));
+		} catch (FileNotFoundException e) {
+			// no-op
+		} catch (IOException e) {
+			throw new IORuntimeException(e);
+		}
 		final String rangeHeader = request.getHeader(HttpHeader.RANGE.asString());
 
-		if (Files.isDirectory(dirpath) || DavMethods.METHOD_MKCOL.equals(request.getMethod())) {
+		if (Files.isDirectory(dirpath)) {
 			return createDirectory(locator, request.getDavSession());
 		} else if (Files.isRegularFile(filepath) && DavMethods.METHOD_GET.equals(request.getMethod()) && rangeHeader != null) {
 			response.setStatus(HttpStatus.SC_PARTIAL_CONTENT);
@@ -69,7 +79,14 @@ public class CryptoResourceFactory implements DavResourceFactory {
 
 	private DavResource createResource(CryptoLocator locator, DavSession session) throws DavException {
 		final Path filepath = FileSystems.getDefault().getPath(locator.getRepositoryPath());
-		final Path dirpath = FileSystems.getDefault().getPath(locator.getDirectoryPath());
+		Path dirpath = null;
+		try {
+			dirpath = FileSystems.getDefault().getPath(locator.getDirectoryPath(false));
+		} catch (FileNotFoundException e) {
+			// no-op
+		} catch (IOException e) {
+			throw new IORuntimeException(e);
+		}
 
 		if (Files.isDirectory(dirpath)) {
 			return createDirectory(locator, session);

+ 28 - 13
main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedDir.java

@@ -8,6 +8,7 @@
  ******************************************************************************/
 package org.cryptomator.webdav.jackrabbit;
 
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.nio.channels.SeekableByteChannel;
 import java.nio.file.AtomicMoveNotSupportedException;
@@ -53,7 +54,11 @@ class EncryptedDir extends AbstractEncryptedNode {
 
 	@Override
 	protected Path getPhysicalPath() {
-		return locator.getEncryptedDirectoryPath();
+		try {
+			return locator.getEncryptedDirectoryPath(false);
+		} catch (IOException e) {
+			throw new IORuntimeException(e);
+		}
 	}
 
 	@Override
@@ -63,13 +68,17 @@ class EncryptedDir extends AbstractEncryptedNode {
 
 	@Override
 	public boolean exists() {
-		return Files.isDirectory(locator.getEncryptedDirectoryPath());
+		try {
+			return Files.isDirectory(locator.getEncryptedDirectoryPath(false));
+		} catch (IOException e) {
+			return false;
+		}
 	}
 
 	@Override
 	public long getModificationTime() {
 		try {
-			return Files.getLastModifiedTime(locator.getEncryptedDirectoryPath()).toMillis();
+			return Files.getLastModifiedTime(locator.getEncryptedDirectoryPath(false)).toMillis();
 		} catch (IOException e) {
 			return -1;
 		}
@@ -94,8 +103,7 @@ class EncryptedDir extends AbstractEncryptedNode {
 
 	private void addMemberDir(CryptoLocator childLocator, InputContext inputContext) throws DavException {
 		try {
-			Files.createDirectories(childLocator.getEncryptedFilePath());
-			Files.createDirectories(childLocator.getEncryptedDirectoryPath());
+			Files.createDirectories(childLocator.getEncryptedDirectoryPath(true));
 		} catch (SecurityException e) {
 			throw new DavException(DavServletResponse.SC_FORBIDDEN, e);
 		} catch (IOException e) {
@@ -126,7 +134,7 @@ class EncryptedDir extends AbstractEncryptedNode {
 	@Override
 	public DavResourceIterator getMembers() {
 		try {
-			final DirectoryStream<Path> directoryStream = Files.newDirectoryStream(locator.getEncryptedDirectoryPath(), cryptor.getPayloadFilesFilter());
+			final DirectoryStream<Path> directoryStream = Files.newDirectoryStream(locator.getEncryptedDirectoryPath(false), cryptor.getPayloadFilesFilter());
 			final List<DavResource> result = new ArrayList<>();
 
 			for (final Path childPath : directoryStream) {
@@ -162,11 +170,13 @@ class EncryptedDir extends AbstractEncryptedNode {
 
 	private void removeMember(AbstractEncryptedNode member) {
 		try {
+			Files.deleteIfExists(member.getLocator().getEncryptedFilePath());
 			if (member.isCollection()) {
 				member.getMembers().forEachRemaining(m -> securelyRemoveMemberOfCollection(member, m));
-				Files.deleteIfExists(member.getLocator().getEncryptedDirectoryPath());
+				Files.deleteIfExists(member.getLocator().getEncryptedDirectoryPath(false));
 			}
-			Files.deleteIfExists(member.getLocator().getEncryptedFilePath());
+		} catch (FileNotFoundException e) {
+			// no-op
 		} catch (IOException e) {
 			throw new IORuntimeException(e);
 		}
@@ -182,8 +192,8 @@ class EncryptedDir extends AbstractEncryptedNode {
 
 	@Override
 	public void move(AbstractEncryptedNode dest) throws DavException, IOException {
-		final Path srcDir = this.locator.getEncryptedDirectoryPath();
-		final Path dstDir = dest.locator.getEncryptedDirectoryPath();
+		final Path srcDir = this.locator.getEncryptedDirectoryPath(false);
+		final Path dstDir = dest.locator.getEncryptedDirectoryPath(true);
 		final Path srcFile = this.locator.getEncryptedFilePath();
 		final Path dstFile = dest.locator.getEncryptedFilePath();
 
@@ -205,8 +215,8 @@ class EncryptedDir extends AbstractEncryptedNode {
 
 	@Override
 	public void copy(AbstractEncryptedNode dest, boolean shallow) throws DavException, IOException {
-		final Path srcDir = this.locator.getEncryptedDirectoryPath();
-		final Path dstDir = dest.locator.getEncryptedDirectoryPath();
+		final Path srcDir = this.locator.getEncryptedDirectoryPath(false);
+		final Path dstDir = dest.locator.getEncryptedDirectoryPath(true);
 		final Path srcFile = this.locator.getEncryptedFilePath();
 		final Path dstFile = dest.locator.getEncryptedFilePath();
 
@@ -233,7 +243,12 @@ class EncryptedDir extends AbstractEncryptedNode {
 
 	@Override
 	protected void determineProperties() {
-		final Path path = locator.getEncryptedDirectoryPath();
+		Path path;
+		try {
+			path = locator.getEncryptedDirectoryPath(false);
+		} catch (IOException e) {
+			throw new IORuntimeException(e);
+		}
 		properties.add(new ResourceType(ResourceType.COLLECTION));
 		properties.add(new DefaultDavProperty<Integer>(DavPropertyName.ISCOLLECTION, 1));
 		if (Files.exists(path)) {

+ 0 - 28
main/core/src/main/java/org/cryptomator/webdav/jackrabbit/ResourcePathUtils.java

@@ -1,28 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Sebastian Stenzel
- * This file is licensed under the terms of the MIT license.
- * See the LICENSE.txt file for more info.
- * 
- * Contributors:
- *     Sebastian Stenzel - initial API and implementation
- ******************************************************************************/
-package org.cryptomator.webdav.jackrabbit;
-
-import java.nio.file.FileSystems;
-import java.nio.file.Path;
-
-final class ResourcePathUtils {
-
-	private ResourcePathUtils() {
-		throw new IllegalStateException("not instantiable");
-	}
-
-	public static Path getPhysicalFilePath(CryptoLocator locator) {
-		return FileSystems.getDefault().getPath(locator.getRepositoryPath());
-	}
-
-	public static Path getPhysicalDirectoryPath(CryptoLocator locator) {
-		return FileSystems.getDefault().getPath(locator.getDirectoryPath());
-	}
-
-}