Pārlūkot izejas kodu

small I/O tweaks

Sebastian Stenzel 9 gadi atpakaļ
vecāks
revīzija
6b073c1499

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

@@ -19,6 +19,8 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.FileTime;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -155,8 +157,10 @@ class EncryptedDir extends AbstractEncryptedNode implements FileConstants {
 			final String cleartextFilename = FilenameUtils.getName(childLocator.getResourcePath());
 			final String ciphertextFilename = filenameTranslator.getEncryptedFilename(cleartextFilename);
 			final Path filePath = dirPath.resolve(ciphertextFilename);
-			try (final FileChannel c = FileChannel.open(filePath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
-					final SilentlyFailingFileLock lock = new SilentlyFailingFileLock(c, 0L, FILE_HEADER_LENGTH, false)) {
+			final Path tmpFilePath = Files.createTempFile(dirPath, null, null);
+			// encrypt to tmp file:
+			try (final FileChannel c = FileChannel.open(tmpFilePath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+					final SilentlyFailingFileLock lock = new SilentlyFailingFileLock(c, false)) {
 				cryptor.encryptFile(inputContext.getInputStream(), c);
 			} catch (SecurityException e) {
 				throw new DavException(DavServletResponse.SC_FORBIDDEN, e);
@@ -169,6 +173,13 @@ class EncryptedDir extends AbstractEncryptedNode implements FileConstants {
 			} finally {
 				IOUtils.closeQuietly(inputContext.getInputStream());
 			}
+			// mv tmp to target file:
+			try {
+				Files.move(tmpFilePath, filePath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
+			} catch (AtomicMoveNotSupportedException e) {
+				Files.move(tmpFilePath, filePath, StandardCopyOption.REPLACE_EXISTING);
+			}
+			Files.setLastModifiedTime(filePath, FileTime.from(Instant.now()));
 		} catch (IOException e) {
 			LOG.error("Failed to create file.", e);
 			throw new IORuntimeException(e);

+ 1 - 0
main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedFile.java

@@ -109,6 +109,7 @@ class EncryptedFile extends AbstractEncryptedNode implements FileConstants {
 					final boolean authenticate = !cryptoWarningHandler.ignoreMac(getLocator().getResourcePath());
 					cryptor.decryptFile(c, outputContext.getOutputStream(), authenticate);
 				}
+				outputContext.getOutputStream().flush();
 			} catch (EOFException e) {
 				LOG.warn("Unexpected end of stream (possibly client hung up).");
 			}

+ 1 - 0
main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedFilePart.java

@@ -71,6 +71,7 @@ class EncryptedFilePart extends EncryptedFile {
 				final boolean authenticate = !cryptoWarningHandler.ignoreMac(getLocator().getResourcePath());
 				cryptor.decryptRange(c, outputContext.getOutputStream(), range.getLeft(), rangeLength, authenticate);
 			}
+			outputContext.getOutputStream().flush();
 		} catch (EOFException e) {
 			if (LOG.isDebugEnabled()) {
 				LOG.trace("Unexpected end of stream during delivery of partial content (client hung up).");

+ 30 - 5
main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java

@@ -311,7 +311,7 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration {
 		// read header:
 		encryptedFile.position(0);
 		final ByteBuffer headerBuf = ByteBuffer.allocate(104);
-		final int headerBytesRead = encryptedFile.read(headerBuf);
+		final int headerBytesRead = readFromChannel(encryptedFile, headerBuf);
 		if (headerBytesRead != headerBuf.capacity()) {
 			return null;
 		}
@@ -373,7 +373,7 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration {
 		// read header:
 		encryptedFile.position(0l);
 		final ByteBuffer headerBuf = ByteBuffer.allocate(104);
-		final int headerBytesRead = encryptedFile.read(headerBuf);
+		final int headerBytesRead = readFromChannel(encryptedFile, headerBuf);
 		if (headerBytesRead != headerBuf.capacity()) {
 			throw new IOException("Failed to read file header.");
 		}
@@ -474,7 +474,7 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration {
 			}
 			final int inBufSize = numBlocks * (CONTENT_MAC_BLOCK + 32);
 			final ByteBuffer buf = ByteBuffer.allocate(inBufSize);
-			bytesRead = encryptedFile.read(buf);
+			bytesRead = readFromChannel(encryptedFile, buf);
 			buf.flip();
 			final int blocksRead = (int) Math.ceil(bytesRead / (double) (CONTENT_MAC_BLOCK + 32));
 			final boolean consumedInTime = executor.offer(new BlocksData(buf.asReadOnlyBuffer(), blockNumber, blocksRead), 1, TimeUnit.SECONDS);
@@ -508,7 +508,7 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration {
 		// read header:
 		encryptedFile.position(0l);
 		final ByteBuffer headerBuf = ByteBuffer.allocate(104);
-		final int headerBytesRead = encryptedFile.read(headerBuf);
+		final int headerBytesRead = readFromChannel(encryptedFile, headerBuf);
 		if (headerBytesRead != headerBuf.capacity()) {
 			throw new IOException("Failed to read file header.");
 		}
@@ -685,7 +685,7 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration {
 			}
 			final int inBufSize = numBlocks * CONTENT_MAC_BLOCK;
 			final ByteBuffer inBuf = ByteBuffer.allocate(inBufSize);
-			bytesRead = channel.read(inBuf);
+			bytesRead = readFromChannel(channel, inBuf);
 			inBuf.flip();
 			final int blocksRead = (int) Math.ceil(bytesRead / (double) CONTENT_MAC_BLOCK);
 			final boolean consumedInTime = executor.offer(new BlocksData(inBuf.asReadOnlyBuffer(), blockNumber, blocksRead), 1, TimeUnit.SECONDS);
@@ -736,4 +736,29 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration {
 		return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(lng).array();
 	}
 
+	/**
+	 * Reads bytes from a ReadableByteChannel.
+	 * <p>
+	 * This implementation guarantees that it will read as many bytes
+	 * as possible before giving up; this may not always be the case for
+	 * subclasses of {@link ReadableByteChannel}.
+	 *
+	 * @param input the byte channel to read
+	 * @param buffer byte buffer destination
+	 * @return the actual length read; may be less than requested if EOF was reached
+	 * @throws IOException if a read error occurs
+	 * @see
+	 * 		<a href="http://commons.apache.org/proper/commons-io/apidocs/src-html/org/apache/commons/io/IOUtils.html">Apache Commons IOUtils 2.5</a>
+	 */
+	public static int readFromChannel(final ReadableByteChannel input, final ByteBuffer buffer) throws IOException {
+		final int length = buffer.remaining();
+		while (buffer.remaining() > 0) {
+			final int count = input.read(buffer);
+			if (count == -1) { // EOF
+				break;
+			}
+		}
+		return length - buffer.remaining();
+	}
+
 }