Skip to content

Commit d87e96a

Browse files
committed
Implemented truncate
1 parent c9f7e77 commit d87e96a

File tree

6 files changed

+137
-6
lines changed

6 files changed

+137
-6
lines changed

src/main/java/org/cryptomator/cryptofs/ChunkData.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ public boolean wasWritten() {
3838
return written;
3939
}
4040

41+
public void truncate(int length) {
42+
if (this.length > length) {
43+
this.length = length;
44+
this.written = true;
45+
}
46+
}
47+
4148
public CopyWithoutDirection copyData() {
4249
return copyDataStartingAt(0);
4350
}

src/main/java/org/cryptomator/cryptofs/OpenCryptoFile.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import static java.lang.Math.min;
1313
import static org.cryptomator.cryptofs.OpenCounter.OpenState.ALREADY_CLOSED;
1414
import static org.cryptomator.cryptofs.OpenCounter.OpenState.WAS_OPEN;
15+
import static org.cryptomator.cryptolib.Cryptors.ciphertextSize;
1516

1617
import java.io.IOException;
1718
import java.nio.ByteBuffer;
@@ -51,8 +52,6 @@ public OpenCryptoFile(EffectiveOpenOptions options, Cryptor cryptor, FileChannel
5152
this.header = header;
5253
this.size = size;
5354
this.stats = stats;
54-
55-
size.set(header.getFilesize());
5655
}
5756

5857
public FileChannel newFileChannel(EffectiveOpenOptions options) throws IOException {
@@ -135,13 +134,22 @@ public long size() {
135134
}
136135

137136
public synchronized void truncate(long size) throws IOException {
138-
// TODO
137+
long originalSize = this.size.getAndUpdate(current -> min(size, current));
138+
if (originalSize > size) {
139+
int cleartextChunkSize = cryptor.fileContentCryptor().cleartextChunkSize();
140+
long indexOfLastChunk = (size + cleartextChunkSize - 1) / cleartextChunkSize - 1;
141+
int sizeOfIncompleteChunk = (int) (size % cleartextChunkSize);
142+
if (sizeOfIncompleteChunk > 0) {
143+
chunkCache.get(indexOfLastChunk).truncate(sizeOfIncompleteChunk);
144+
}
145+
long ciphertextFileSize = cryptor.fileHeaderCryptor().headerSize() + ciphertextSize(size, cryptor);
146+
channel.truncate(ciphertextFileSize);
147+
}
139148
}
140149

141150
public synchronized void force(boolean metaData, EffectiveOpenOptions options) throws IOException {
142151
chunkCache.invalidateAll(); // TODO increase performance by writing chunks but keeping them cached
143152
if (options.writable()) {
144-
header.setFilesize(size.get());
145153
channel.write(cryptor.fileHeaderCryptor().encryptHeader(header), 0);
146154
}
147155
channel.force(metaData);

src/main/java/org/cryptomator/cryptofs/OpenCryptoFileFactoryModule.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.cryptomator.cryptofs;
22

33
import static org.cryptomator.cryptofs.UncheckedThrows.rethrowUnchecked;
4+
import static org.cryptomator.cryptolib.Cryptors.cleartextSize;
45

56
import java.io.IOException;
67
import java.nio.ByteBuffer;
@@ -26,8 +27,16 @@ public FileChannel provideFileChannel(@OpenFilePath Path path, EffectiveOpenOpti
2627
@Provides
2728
@PerOpenFile
2829
@OpenFileSize
29-
public AtomicLong provideFileSize() {
30-
return new AtomicLong();
30+
public AtomicLong provideFileSize(FileChannel channel, Cryptor cryptor) {
31+
return rethrowUnchecked(IOException.class).from(() -> {
32+
long size = channel.size();
33+
if (size == 0) {
34+
return new AtomicLong();
35+
} else {
36+
int headerSize = cryptor.fileHeaderCryptor().headerSize();
37+
return new AtomicLong(cleartextSize(size - headerSize, cryptor));
38+
}
39+
});
3140
}
3241

3342
@Provides

src/main/java/org/cryptomator/cryptofs/PatternPathMatcher.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ public PatternPathMatcher(Pattern pattern) {
1212
this.pattern = pattern;
1313
}
1414

15+
/**
16+
* @deprecated for testing
17+
*/
18+
@Deprecated
19+
Pattern getPattern() {
20+
return pattern;
21+
}
22+
1523
@Override
1624
public boolean matches(Path path) {
1725
return pattern.matcher(path.toString()).matches();

src/test/java/org/cryptomator/cryptofs/CryptoFileChannelWriteReadIntegrationTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*******************************************************************************/
99
package org.cryptomator.cryptofs;
1010

11+
import static java.lang.Math.min;
1112
import static java.lang.String.format;
1213
import static java.nio.file.StandardOpenOption.APPEND;
1314
import static java.nio.file.StandardOpenOption.CREATE;
@@ -88,6 +89,38 @@ public void testWriteAndReadNothing() throws IOException {
8889
}
8990
}
9091

92+
@Theory
93+
public void testWriteDataAndTruncateToOffset(@FromDataPoints("dataSizes") int cleartextSize, @FromDataPoints("writeOffsets") int truncateToSize) throws IOException {
94+
long fileId = nextFileId();
95+
96+
int targetSize = min(truncateToSize, cleartextSize);
97+
98+
try (FileChannel channel = writableChannel(fileId)) {
99+
assertEquals(0, channel.size());
100+
channel.write(repeat(1).times(cleartextSize).asByteBuffer());
101+
assertEquals(cleartextSize, channel.size());
102+
}
103+
104+
try (FileChannel channel = writableChannel(fileId)) {
105+
assertEquals(cleartextSize, channel.size());
106+
channel.truncate(truncateToSize);
107+
assertEquals(targetSize, channel.size());
108+
}
109+
110+
try (FileChannel channel = readableChannel(fileId)) {
111+
if (targetSize > 0) {
112+
ByteBuffer buffer = ByteBuffer.allocate(targetSize);
113+
int result = channel.read(buffer);
114+
assertEquals(targetSize, result);
115+
buffer.flip();
116+
for (int i = 0; i < targetSize; i++) {
117+
assertEquals(format("byte(%d) = 1", i), 1, buffer.get(i));
118+
}
119+
}
120+
assertEquals(EOF, channel.read(ByteBuffer.allocate(0)));
121+
}
122+
}
123+
91124
@Theory
92125
public void testWithWritingOffset(@FromDataPoints("dataSizes") int dataSize, @FromDataPoints("writeOffsets") int writeOffset) throws IOException {
93126
assumeTrue(dataSize != 0 || writeOffset != 0);
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package org.cryptomator.cryptofs;
2+
3+
import static org.hamcrest.CoreMatchers.instanceOf;
4+
import static org.hamcrest.Matchers.is;
5+
import static org.junit.Assert.assertThat;
6+
import static org.mockito.Matchers.anyString;
7+
import static org.mockito.Mockito.mock;
8+
import static org.mockito.Mockito.when;
9+
10+
import java.nio.file.PathMatcher;
11+
12+
import org.junit.Rule;
13+
import org.junit.Test;
14+
import org.junit.rules.ExpectedException;
15+
import org.mockito.junit.MockitoJUnit;
16+
import org.mockito.junit.MockitoRule;
17+
18+
public class PathMatcherFactoryTest {
19+
20+
@Rule
21+
public MockitoRule mockitoRule = MockitoJUnit.rule();
22+
23+
@Rule
24+
public ExpectedException thrown = ExpectedException.none();
25+
26+
private GlobToRegexConverter globToRegexConverter = mock(GlobToRegexConverter.class);
27+
28+
private PathMatcherFactory inTest = new PathMatcherFactory(globToRegexConverter);
29+
30+
@Test
31+
public void testSyntaxAndPatternNotStartingWithGlobOrRegexThrowsUnsupportedOperationException() {
32+
thrown.expect(UnsupportedOperationException.class);
33+
34+
inTest.pathMatcherFrom("fail");
35+
}
36+
37+
@Test
38+
@SuppressWarnings("deprecation")
39+
public void testSyntaxAndPatternStartingWithRegexCreatesPatternPathMatcherWithCorrectPattern() {
40+
PathMatcher pathMatcher = inTest.pathMatcherFrom("regex:test[02]");
41+
42+
assertThat(pathMatcher, is(instanceOf(PatternPathMatcher.class)));
43+
assertThat(((PatternPathMatcher) pathMatcher).getPattern().pattern(), is("test[02]"));
44+
}
45+
46+
@Test
47+
@SuppressWarnings("deprecation")
48+
public void testSyntaxAndPatternStartingWithGlobCreatesPatternPathMatcherWithCorrectPattern() {
49+
String regexp = "test[abcd]*";
50+
when(globToRegexConverter.convert("abcd")).thenReturn(regexp);
51+
52+
PathMatcher pathMatcher = inTest.pathMatcherFrom("glob:abcd");
53+
54+
assertThat(pathMatcher, is(instanceOf(PatternPathMatcher.class)));
55+
assertThat(((PatternPathMatcher) pathMatcher).getPattern().pattern(), is(regexp));
56+
}
57+
58+
@Test
59+
public void testSyntaxAndPatternIgnoresCase() {
60+
when(globToRegexConverter.convert(anyString())).thenReturn("a");
61+
62+
inTest.pathMatcherFrom("reGEx:a");
63+
inTest.pathMatcherFrom("gLOb:a");
64+
}
65+
66+
}

0 commit comments

Comments
 (0)