Skip to content

Commit 16d292f

Browse files
chenkinsdkocher
authored andcommitted
Read file in subdir with current revision.
1 parent c021651 commit 16d292f

File tree

10 files changed

+111
-7
lines changed

10 files changed

+111
-7
lines changed

cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,69 @@ public Path decrypt(final Session<?> session, final Path file) throws Background
176176
}
177177
}
178178

179+
public Path encrypt(Session<?> session, Path file, byte[] directoryId, boolean metadata) throws BackgroundException {
180+
final Path encrypted;
181+
if(file.isFile() || metadata) {
182+
if(file.getType().contains(Path.Type.vault)) {
183+
log.warn("Skip file {} because it is marked as an internal vault path", file);
184+
return file;
185+
}
186+
if(new SimplePathPredicate(file).test(this.getHome())) {
187+
log.warn("Skip vault home {} because the root has no metadata file", file);
188+
return file;
189+
}
190+
final Path parent;
191+
final String filename;
192+
if(file.getType().contains(Path.Type.encrypted)) {
193+
final Path decrypted = file.attributes().getDecrypted();
194+
parent = this.getDirectoryProvider().toEncrypted(session, decrypted.getParent().attributes().getDirectoryId(), decrypted.getParent());
195+
filename = this.getDirectoryProvider().toEncrypted(session, parent.attributes().getDirectoryId(), decrypted.getName(), decrypted.getType());
196+
}
197+
else {
198+
parent = this.getDirectoryProvider().toEncrypted(session, file.getParent().attributes().getDirectoryId(), file.getParent());
199+
// / diff to AbstractVault.encrypt
200+
String filenameO = this.getDirectoryProvider().toEncrypted(session, parent.attributes().getDirectoryId(), file.getName(), file.getType());
201+
filename = ((CryptoDirectoryUVFProvider) this.getDirectoryProvider()).toEncrypted(session, file.getParent(), file.getName());
202+
// \ diff to AbstractVault.decrypt
203+
}
204+
final PathAttributes attributes = new PathAttributes(file.attributes());
205+
attributes.setDirectoryId(null);
206+
if(!file.isFile() && !metadata) {
207+
// The directory is different from the metadata file used to resolve the actual folder
208+
attributes.setVersionId(null);
209+
attributes.setFileId(null);
210+
}
211+
// Translate file size
212+
attributes.setSize(this.toCiphertextSize(0L, file.attributes().getSize()));
213+
final EnumSet<Path.Type> type = EnumSet.copyOf(file.getType());
214+
if(metadata && this.getVersion() == VAULT_VERSION_DEPRECATED) {
215+
type.remove(Path.Type.directory);
216+
type.add(Path.Type.file);
217+
}
218+
type.remove(Path.Type.decrypted);
219+
type.add(Path.Type.encrypted);
220+
encrypted = new Path(parent, filename, type, attributes);
221+
}
222+
else {
223+
if(file.getType().contains(Path.Type.encrypted)) {
224+
log.warn("Skip file {} because it is already marked as an encrypted path", file);
225+
return file;
226+
}
227+
if(file.getType().contains(Path.Type.vault)) {
228+
return this.getDirectoryProvider().toEncrypted(session, this.getHome().attributes().getDirectoryId(), this.getHome());
229+
}
230+
encrypted = this.getDirectoryProvider().toEncrypted(session, directoryId, file);
231+
}
232+
// Add reference to decrypted file
233+
if(!file.getType().contains(Path.Type.encrypted)) {
234+
encrypted.attributes().setDecrypted(file);
235+
}
236+
// Add reference for vault
237+
file.attributes().setVault(this.getHome());
238+
encrypted.attributes().setVault(this.getHome());
239+
return encrypted;
240+
}
241+
179242
private int loadRevision(final Session<?> session, final Path directory) throws BackgroundException {
180243
// Read directory id from file
181244
log.debug("Read directory ID from {}", directory);

cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryUVFProvider.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import java.nio.charset.StandardCharsets;
3838
import java.util.EnumSet;
3939

40+
import com.google.common.io.BaseEncoding;
41+
4042
public class CryptoDirectoryUVFProvider extends CryptoDirectoryV7Provider {
4143
private static final Logger log = LogManager.getLogger(CryptoDirectoryUVFProvider.class);
4244

@@ -47,10 +49,12 @@ public class CryptoDirectoryUVFProvider extends CryptoDirectoryV7Provider {
4749
= new UUIDRandomStringService();
4850
private final Path dataRoot;
4951
private final CryptorCache filenameCryptor;
52+
private final CryptoFilename filenameProvider;
5053

5154
public CryptoDirectoryUVFProvider(final AbstractVault vault, final CryptoFilename filenameProvider, final CryptorCache filenameCryptor) {
5255
super(vault, filenameProvider, filenameCryptor);
5356
this.filenameCryptor = filenameCryptor;
57+
this.filenameProvider = filenameProvider;
5458
this.home = vault.getHome();
5559
this.vault = vault;
5660
this.dataRoot = new Path(home, "d", home.getType());
@@ -64,6 +68,20 @@ protected byte[] toDirectoryId(final Session<?> session, final Path directory, f
6468
return super.toDirectoryId(session, directory, directoryId);
6569
}
6670

71+
// interface mismatch: we need parent path to get dirId and revision from dir.uvf
72+
public String toEncrypted(final Session<?> session, final Path parent, final String filename) throws BackgroundException {
73+
if(new SimplePathPredicate(home).test(parent)) {
74+
final String ciphertextName = filenameCryptor.encryptFilename(BaseEncoding.base64Url(), filename, vault.getRootDirId()) + vault.getRegularFileExtension();
75+
log.debug("Encrypted filename {} to {}", filename, ciphertextName);
76+
return filenameProvider.deflate(session, ciphertextName);
77+
78+
}
79+
final byte[] directoryId = load(session, parent);
80+
final String ciphertextName = vault.getCryptor().fileNameCryptor(loadRevision(session, parent)).encryptFilename(BaseEncoding.base64Url(), filename, directoryId) + vault.getRegularFileExtension();
81+
log.debug("Encrypted filename {} to {}", filename, ciphertextName);
82+
return filenameProvider.deflate(session, ciphertextName);
83+
}
84+
6785
@Override
6886
public Path toEncrypted(final Session<?> session, final byte[] directoryId, final Path directory) throws BackgroundException {
6987
if(!directory.isDirectory()) {
@@ -99,6 +117,9 @@ public Path toEncrypted(final Session<?> session, final byte[] directoryId, fina
99117
}
100118

101119
protected byte[] load(final Session<?> session, final Path directory) throws BackgroundException {
120+
if(new SimplePathPredicate(home).test(directory)) {
121+
return vault.getRootDirId();
122+
}
102123
final Path parent = this.toEncrypted(session, directory.getParent().attributes().getDirectoryId(), directory.getParent());
103124
final String cleartextName = directory.getName();
104125
final String ciphertextName = this.toEncrypted(session, parent.attributes().getDirectoryId(), cleartextName, EnumSet.of(Path.Type.directory));

s3/src/test/java/ch/cyberduck/core/cryptomator/UVFIntegrationTest.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import ch.cyberduck.core.features.AttributesFinder;
2121
import ch.cyberduck.core.features.Bulk;
2222
import ch.cyberduck.core.features.Delete;
23+
import ch.cyberduck.core.features.Read;
2324
import ch.cyberduck.core.features.Write;
2425
import ch.cyberduck.core.io.StatusOutputStream;
2526
import ch.cyberduck.core.proxy.ProxyFactory;
@@ -49,6 +50,7 @@
4950
import java.io.ByteArrayInputStream;
5051
import java.io.File;
5152
import java.io.IOException;
53+
import java.io.InputStream;
5254
import java.util.AbstractMap;
5355
import java.util.Arrays;
5456
import java.util.Collections;
@@ -99,11 +101,11 @@ public void listMinio() throws BackgroundException, IOException {
99101
new S3BucketCreateService(storage).create(bucket, "us-east-1");
100102

101103
final List<String> files = Arrays.asList(
102-
"/d/RZ/K7ZH7KBXULNEKBMGX3CU42PGUIAIX4/rExOms183v5evFwgIKiW0qvbsor1Hg==.uvf/dir.uvf",
103-
"/d/RZ/K7ZH7KBXULNEKBMGX3CU42PGUIAIX4/dir.uvf",
104-
"/d/RZ/K7ZH7KBXULNEKBMGX3CU42PGUIAIX4/GsMMTRvsuuP_6NjgRwopmWcuof-PyRQ=.uvf",
105-
"/d/TU/EVUUXMHY2HHNQ4BLKNE3GBLEFD4YW6/4RuVMuXcOTOfhSQZAwEV1E4XiNrMVOY=.uvf",
106-
"/d/TU/EVUUXMHY2HHNQ4BLKNE3GBLEFD4YW6/dir.uvf"
104+
"/d/RZ/K7ZH7KBXULNEKBMGX3CU42PGUIAIX4/rExOms183v5evFwgIKiW0qvbsor1Hg==.uvf/dir.uvf", // -> /subir
105+
"/d/RZ/K7ZH7KBXULNEKBMGX3CU42PGUIAIX4/dir.uvf", // -> /
106+
"/d/RZ/K7ZH7KBXULNEKBMGX3CU42PGUIAIX4/GsMMTRvsuuP_6NjgRwopmWcuof-PyRQ=.uvf", // -> /foo.txt
107+
"/d/6L/HPWBEU3OJP2EZUCP4CV3HHL47BXVEX/5qTOPMA1BouBRhz_G7qfmKety92geI4=.uvf", // -> /subdir/bar.txt
108+
"/d/6L/HPWBEU3OJP2EZUCP4CV3HHL47BXVEX/dir.uvf" // /subdir
107109
);
108110
final String jwe = "{\n" +
109111
" \"fileFormat\": \"AES-256-GCM-32k\",\n" +
@@ -152,14 +154,32 @@ public Credentials prompt(final Host bookmark, final String title, final String
152154
{
153155
final AttributedList<Path> list = storage.getFeature(ListService.class).list(home, new DisabledListProgressListener());
154156
assertEquals(2, list.size());
155-
assertTrue(Arrays.toString(list.toArray()), list.contains(new Path("/cyberduckbucket/foo.txt", EnumSet.of(AbstractPath.Type.file, AbstractPath.Type.decrypted))));
157+
final Path foo = new Path("/cyberduckbucket/foo.txt", EnumSet.of(AbstractPath.Type.file, AbstractPath.Type.decrypted));
158+
assertTrue(Arrays.toString(list.toArray()), list.contains(foo));
156159
assertTrue(Arrays.toString(list.toArray()), list.contains(new Path("/cyberduckbucket/subdir", EnumSet.of(AbstractPath.Type.directory, AbstractPath.Type.placeholder, AbstractPath.Type.decrypted))));
160+
161+
final byte[] buf = new byte[300];
162+
final TransferStatus status = new TransferStatus();
163+
try(final InputStream inputStream = storage.getFeature(Read.class).read(foo, status, new DisabledConnectionCallback())) {
164+
int l = inputStream.read(buf);
165+
assertEquals(9, l);
166+
assertEquals("Hello Foo", new String(Arrays.copyOfRange(buf, 0, l)));
167+
}
157168
}
158169
{
159170
final PathAttributes subdir = storage.getFeature(AttributesFinder.class).find(new Path("/cyberduckbucket/subdir", EnumSet.of(AbstractPath.Type.directory, AbstractPath.Type.placeholder, AbstractPath.Type.decrypted)));
160171
final AttributedList<Path> list = storage.getFeature(ListService.class).list(new Path("/cyberduckbucket/subdir", EnumSet.of(AbstractPath.Type.directory, AbstractPath.Type.placeholder, AbstractPath.Type.decrypted)).withAttributes(subdir), new DisabledListProgressListener());
161172
assertEquals(1, list.size());
162-
assertTrue(Arrays.toString(list.toArray()), list.contains(new Path("/cyberduckbucket/subdir/bar.txt", EnumSet.of(AbstractPath.Type.file, AbstractPath.Type.decrypted))));
173+
final Path bar = new Path("/cyberduckbucket/subdir/bar.txt", EnumSet.of(AbstractPath.Type.file, AbstractPath.Type.decrypted));
174+
assertTrue(Arrays.toString(list.toArray()), list.contains(bar));
175+
176+
final byte[] buf = new byte[300];
177+
final TransferStatus status = new TransferStatus();
178+
try(final InputStream inputStream = storage.getFeature(Read.class).read(bar, status, new DisabledConnectionCallback())) {
179+
int l = inputStream.read(buf);
180+
assertEquals(9, l);
181+
assertEquals("Hello Bar", new String(Arrays.copyOfRange(buf, 0, l)));
182+
}
163183
}
164184
}
165185
finally {
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)