This commit is contained in:
Sebastian Stenzel
2016-05-07 14:40:44 +02:00
parent 91646dd93d
commit 78645ecdf6
14 changed files with 541 additions and 4 deletions

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015 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
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>1.1.0-SNAPSHOT</version>
</parent>
<artifactId>filesystem-charsets</artifactId>
<name>Cryptomator filesystem: Filename charset compatibility layer</name>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-api</artifactId>
</dependency>
<!-- Tests -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>commons-test</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-inmemory</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* 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.filesystem.charsets;
import java.io.UncheckedIOException;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.delegating.DelegatingFile;
class NormalizedNameFile extends DelegatingFile<NormalizedNameFolder> {
private final Form displayForm;
public NormalizedNameFile(NormalizedNameFolder parent, File delegate, Form displayForm) {
super(parent, delegate);
this.displayForm = displayForm;
}
@Override
public String name() throws UncheckedIOException {
return Normalizer.normalize(super.name(), displayForm);
}
}

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* 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.filesystem.charsets;
import java.text.Normalizer.Form;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.delegating.DelegatingFileSystem;
public class NormalizedNameFileSystem extends NormalizedNameFolder implements DelegatingFileSystem {
public NormalizedNameFileSystem(Folder delegate, Form displayForm) {
super(null, delegate, displayForm);
}
@Override
public Folder getDelegate() {
return delegate;
}
}

View File

@@ -0,0 +1,76 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* 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.filesystem.charsets;
import java.io.UncheckedIOException;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.delegating.DelegatingFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class NormalizedNameFolder extends DelegatingFolder<NormalizedNameFolder, NormalizedNameFile> {
private static final Logger LOG = LoggerFactory.getLogger(NormalizedNameFolder.class);
private final Form displayForm;
public NormalizedNameFolder(NormalizedNameFolder parent, Folder delegate, Form displayForm) {
super(parent, delegate);
this.displayForm = displayForm;
}
@Override
public String name() throws UncheckedIOException {
return Normalizer.normalize(super.name(), displayForm);
}
@Override
public NormalizedNameFile file(String name) throws UncheckedIOException {
String nfcName = Normalizer.normalize(name, Form.NFC);
String nfdName = Normalizer.normalize(name, Form.NFD);
NormalizedNameFile nfcFile = super.file(nfcName);
NormalizedNameFile nfdFile = super.file(nfdName);
if (!nfcName.equals(nfdName) && nfcFile.exists() && nfdFile.exists()) {
LOG.warn("Ambiguous file names \"" + nfcName + "\" (NFC) vs. \"" + nfdName + "\" (NFD). Both files exist. Using \"" + nfcName + "\" (NFC).");
} else if (!nfcName.equals(nfdName) && !nfcFile.exists() && nfdFile.exists()) {
LOG.info("Moving file from \"" + nfcName + "\" (NFD) to \"" + nfdName + "\" (NFC).");
nfdFile.moveTo(nfcFile);
}
return nfcFile;
}
@Override
protected NormalizedNameFile newFile(File delegate) {
return new NormalizedNameFile(this, delegate, displayForm);
}
@Override
public NormalizedNameFolder folder(String name) throws UncheckedIOException {
String nfcName = Normalizer.normalize(name, Form.NFC);
String nfdName = Normalizer.normalize(name, Form.NFD);
NormalizedNameFolder nfcFolder = super.folder(nfcName);
NormalizedNameFolder nfdFolder = super.folder(nfdName);
if (!nfcName.equals(nfdName) && nfcFolder.exists() && nfdFolder.exists()) {
LOG.warn("Ambiguous folder names \"" + nfcName + "\" (NFC) vs. \"" + nfdName + "\" (NFD). Both files exist. Using \"" + nfcName + "\" (NFC).");
} else if (!nfcName.equals(nfdName) && !nfcFolder.exists() && nfdFolder.exists()) {
LOG.info("Moving folder from \"" + nfcName + "\" (NFD) to \"" + nfdName + "\" (NFC).");
nfdFolder.moveTo(nfcFolder);
}
return nfcFolder;
}
@Override
protected NormalizedNameFolder newFolder(Folder delegate) {
return new NormalizedNameFolder(this, delegate, displayForm);
}
}

View File

@@ -0,0 +1,16 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* 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
*******************************************************************************/
/**
* Makes sure, the filesystems wrapped by this filesystem work only on UTF-8 encoded file paths using Normalization Form C.
* Filesystems wrapping this file system, on the other hand, will get filenames reported in a specified Normalization Form.
* It is recommended to use NFD for OS X and NFC for other operating systems.
* When looking for a file or folder with a name given in either form, both possibilities are considered
* and files/folders stored in NFD are automatically migrated to NFC.
*/
package org.cryptomator.filesystem.charsets;

View File

@@ -0,0 +1,90 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* 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.filesystem.charsets;
import java.nio.ByteBuffer;
import java.text.Normalizer.Form;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
import org.junit.Assert;
import org.junit.Test;
public class NormalizedNameFileSystemTest {
@Test
public void testFileMigration() {
FileSystem inMemoryFs = new InMemoryFileSystem();
try (WritableFile writable = inMemoryFs.file("\u006F\u0302").openWritable()) {
writable.write(ByteBuffer.allocate(0));
}
FileSystem normalizationFs = new NormalizedNameFileSystem(inMemoryFs, Form.NFC);
Assert.assertTrue(normalizationFs.file("\u00F4").exists());
Assert.assertTrue(normalizationFs.file("\u006F\u0302").exists());
Assert.assertFalse(inMemoryFs.file("\u006F\u0302").exists());
Assert.assertTrue(inMemoryFs.file("\u00F4").exists());
}
@Test
public void testNoFileMigration() {
FileSystem inMemoryFs = new InMemoryFileSystem();
try (WritableFile writable = inMemoryFs.file("\u00F4").openWritable()) {
writable.write(ByteBuffer.allocate(0));
}
FileSystem normalizationFs = new NormalizedNameFileSystem(inMemoryFs, Form.NFC);
Assert.assertTrue(normalizationFs.file("\u00F4").exists());
Assert.assertTrue(normalizationFs.file("\u006F\u0302").exists());
Assert.assertFalse(inMemoryFs.file("\u006F\u0302").exists());
Assert.assertTrue(inMemoryFs.file("\u00F4").exists());
}
@Test
public void testFolderMigration() {
FileSystem inMemoryFs = new InMemoryFileSystem();
inMemoryFs.folder("\u006F\u0302").create();
FileSystem normalizationFs = new NormalizedNameFileSystem(inMemoryFs, Form.NFC);
Assert.assertTrue(normalizationFs.folder("\u00F4").exists());
Assert.assertTrue(normalizationFs.folder("\u006F\u0302").exists());
Assert.assertFalse(inMemoryFs.folder("\u006F\u0302").exists());
Assert.assertTrue(inMemoryFs.folder("\u00F4").exists());
}
@Test
public void testNoFolderMigration() {
FileSystem inMemoryFs = new InMemoryFileSystem();
inMemoryFs.folder("\u00F4").create();
FileSystem normalizationFs = new NormalizedNameFileSystem(inMemoryFs, Form.NFC);
Assert.assertTrue(normalizationFs.folder("\u00F4").exists());
Assert.assertTrue(normalizationFs.folder("\u006F\u0302").exists());
Assert.assertFalse(inMemoryFs.folder("\u006F\u0302").exists());
Assert.assertTrue(inMemoryFs.folder("\u00F4").exists());
}
@Test
public void testNfcDisplayNames() {
FileSystem inMemoryFs = new InMemoryFileSystem();
inMemoryFs.folder("a\u00F4").create();
inMemoryFs.folder("b\u006F\u0302").create();
FileSystem normalizationFs = new NormalizedNameFileSystem(inMemoryFs, Form.NFC);
Assert.assertEquals("a\u00F4", normalizationFs.folder("a\u00F4").name());
Assert.assertEquals("b\u00F4", normalizationFs.folder("b\u006F\u0302").name());
}
@Test
public void testNfdDisplayNames() {
FileSystem inMemoryFs = new InMemoryFileSystem();
inMemoryFs.folder("a\u00F4").create();
inMemoryFs.folder("b\u006F\u0302").create();
FileSystem normalizationFs = new NormalizedNameFileSystem(inMemoryFs, Form.NFD);
Assert.assertEquals("a\u006F\u0302", normalizationFs.folder("a\u00F4").name());
Assert.assertEquals("b\u006F\u0302", normalizationFs.folder("b\u006F\u0302").name());
}
}

View File

@@ -0,0 +1,48 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* 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.filesystem.charsets;
import java.text.Normalizer.Form;
import org.cryptomator.filesystem.File;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class NormalizedNameFileTest {
private File delegateNfc;
private File delegateNfd;
@Before
public void setup() {
delegateNfc = Mockito.mock(File.class);
delegateNfd = Mockito.mock(File.class);
Mockito.when(delegateNfc.name()).thenReturn("\u00C5");
Mockito.when(delegateNfd.name()).thenReturn("\u0041\u030A");
}
@Test
public void testDisplayNameInNfc() {
File file1 = new NormalizedNameFile(null, delegateNfc, Form.NFC);
File file2 = new NormalizedNameFile(null, delegateNfd, Form.NFC);
Assert.assertEquals("\u00C5", file1.name());
Assert.assertEquals("\u00C5", file2.name());
}
@Test
public void testDisplayNameInNfd() {
File file1 = new NormalizedNameFile(null, delegateNfc, Form.NFD);
File file2 = new NormalizedNameFile(null, delegateNfd, Form.NFD);
Assert.assertEquals("\u0041\u030A", file1.name());
Assert.assertEquals("\u0041\u030A", file2.name());
}
}

View File

@@ -0,0 +1,149 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* 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.filesystem.charsets;
import java.text.Normalizer.Form;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class NormalizedNameFolderTest {
private Folder delegate;
private File delegateSubFileNfc;
private File delegateSubFileNfd;
private Folder delegateSubFolderNfc;
private Folder delegateSubFolderNfd;
@Before
public void setup() {
delegate = Mockito.mock(Folder.class);
delegateSubFileNfc = Mockito.mock(File.class);
delegateSubFileNfd = Mockito.mock(File.class);
Mockito.when(delegate.file("\u00C5")).thenReturn(delegateSubFileNfc);
Mockito.when(delegateSubFileNfc.name()).thenReturn("\u00C5");
Mockito.when(delegate.file("\u0041\u030A")).thenReturn(delegateSubFileNfd);
Mockito.when(delegateSubFileNfd.name()).thenReturn("\u0041\u030A");
delegateSubFolderNfc = Mockito.mock(Folder.class);
delegateSubFolderNfd = Mockito.mock(Folder.class);
Mockito.when(delegate.folder("\u00F4")).thenReturn(delegateSubFolderNfc);
Mockito.when(delegateSubFolderNfc.name()).thenReturn("\u00F4");
Mockito.when(delegate.folder("\u006F\u0302")).thenReturn(delegateSubFolderNfd);
Mockito.when(delegateSubFolderNfd.name()).thenReturn("\u006F\u0302");
}
@Test
public void testDisplayNameInNfc() {
Folder folder1 = new NormalizedNameFolder(null, delegateSubFolderNfc, Form.NFC);
Folder folder2 = new NormalizedNameFolder(null, delegateSubFolderNfd, Form.NFC);
Assert.assertEquals("\u00F4", folder1.name());
Assert.assertEquals("\u00F4", folder2.name());
}
@Test
public void testDisplayNameInNfd() {
Folder folder1 = new NormalizedNameFolder(null, delegateSubFolderNfc, Form.NFD);
Folder folder2 = new NormalizedNameFolder(null, delegateSubFolderNfd, Form.NFD);
Assert.assertEquals("\u006F\u0302", folder1.name());
Assert.assertEquals("\u006F\u0302", folder2.name());
}
@Test
public void testNoFolderMigration1() {
Mockito.when(delegateSubFolderNfc.exists()).thenReturn(true);
Mockito.when(delegateSubFolderNfd.exists()).thenReturn(false);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
Folder sub1 = folder.folder("\u00F4");
Folder sub2 = folder.folder("\u006F\u0302");
Mockito.verify(delegateSubFolderNfd, Mockito.never()).moveTo(Mockito.any());
Assert.assertSame(sub1, sub2);
}
@Test
public void testNoFolderMigration2() {
Mockito.when(delegateSubFolderNfc.exists()).thenReturn(true);
Mockito.when(delegateSubFolderNfd.exists()).thenReturn(true);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
Folder sub1 = folder.folder("\u00F4");
Folder sub2 = folder.folder("\u006F\u0302");
Mockito.verify(delegateSubFolderNfd, Mockito.never()).moveTo(Mockito.any());
Assert.assertSame(sub1, sub2);
}
@Test
public void testNoFolderMigration3() {
Mockito.when(delegateSubFolderNfc.exists()).thenReturn(false);
Mockito.when(delegateSubFolderNfd.exists()).thenReturn(false);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
Folder sub1 = folder.folder("\u00F4");
Folder sub2 = folder.folder("\u006F\u0302");
Mockito.verify(delegateSubFolderNfd, Mockito.never()).moveTo(Mockito.any());
Assert.assertSame(sub1, sub2);
}
@Test
public void testFolderMigration() {
Mockito.when(delegateSubFolderNfc.exists()).thenReturn(false);
Mockito.when(delegateSubFolderNfd.exists()).thenReturn(true);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
Folder sub1 = folder.folder("\u00F4");
Mockito.verify(delegateSubFolderNfd).moveTo(delegateSubFolderNfc);
Folder sub2 = folder.folder("\u006F\u0302");
Assert.assertSame(sub1, sub2);
}
@Test
public void testNoFileMigration1() {
Mockito.when(delegateSubFileNfc.exists()).thenReturn(true);
Mockito.when(delegateSubFileNfd.exists()).thenReturn(false);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
File sub1 = folder.file("\u00C5");
File sub2 = folder.file("\u0041\u030A");
Mockito.verify(delegateSubFileNfd, Mockito.never()).moveTo(Mockito.any());
Assert.assertSame(sub1, sub2);
}
@Test
public void testNoFileMigration2() {
Mockito.when(delegateSubFileNfc.exists()).thenReturn(true);
Mockito.when(delegateSubFileNfd.exists()).thenReturn(true);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
File sub1 = folder.file("\u00C5");
File sub2 = folder.file("\u0041\u030A");
Mockito.verify(delegateSubFileNfd, Mockito.never()).moveTo(Mockito.any());
Assert.assertSame(sub1, sub2);
}
@Test
public void testNoFileMigration3() {
Mockito.when(delegateSubFileNfc.exists()).thenReturn(false);
Mockito.when(delegateSubFileNfd.exists()).thenReturn(false);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
File sub1 = folder.file("\u00C5");
File sub2 = folder.file("\u0041\u030A");
Mockito.verify(delegateSubFileNfd, Mockito.never()).moveTo(Mockito.any());
Assert.assertSame(sub1, sub2);
}
@Test
public void testFileMigration() {
Mockito.when(delegateSubFileNfc.exists()).thenReturn(false);
Mockito.when(delegateSubFileNfd.exists()).thenReturn(true);
Folder folder = new NormalizedNameFolder(null, delegate, Form.NFC);
File sub1 = folder.file("\u00C5");
Mockito.verify(delegateSubFileNfd).moveTo(delegateSubFileNfc);
File sub2 = folder.file("\u0041\u030A");
Assert.assertSame(sub1, sub2);
}
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%16d %-5p [%c{1}:%L] %m%n" />
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="ACCEPT" />
</Console>
<Console name="StdErr" target="SYSTEM_ERR">
<PatternLayout pattern="%16d %-5p [%c{1}:%L] %m%n" />
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY" />
</Console>
</Appenders>
<Loggers>
<Root level="DEBUG">
<AppenderRef ref="Console" />
<AppenderRef ref="StdErr" />
</Root>
</Loggers>
</Configuration>

View File

@@ -20,6 +20,10 @@
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-api</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-charsets</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-crypto</artifactId>

View File

@@ -4,11 +4,13 @@ import static org.cryptomator.common.test.TempFilesRemovedOnShutdown.createTempD
import java.io.IOException;
import java.io.UncheckedIOException;
import java.text.Normalizer.Form;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.charsets.NormalizedNameFileSystem;
import org.cryptomator.filesystem.crypto.CryptoEngineTestModule;
import org.cryptomator.filesystem.crypto.CryptoFileSystemDelegate;
import org.cryptomator.filesystem.crypto.CryptoFileSystemTestComponent;
@@ -35,8 +37,10 @@ class FileSystemFactories implements Iterable<FileSystemFactory> {
add("ShorteningFileSystem > InMemoryFileSystem", this::createShorteningFileSystemInMemory);
add("StatsFileSystem > NioFileSystem", this::createStatsFileSystemNio);
add("StatsFileSystem > InMemoryFileSystem", this::createStatsFileSystemInMemory);
add("StatsFileSystem > CryptoFileSystem > ShorteningFileSystem > InMemoryFileSystem", this::createCompoundFileSystemInMemory);
add("StatsFileSystem > CryptoFileSystem > ShorteningFileSystem > NioFileSystem", this::createCompoundFileSystemNio);
add("NormalizingFileSystem > NioFileSystem", this::createNormalizingFileSystemNio);
add("NormalizingFileSystem > InMemoryFileSystem", this::createNormalizingFileSystemInMemory);
add("StatsFileSystem > NormalizingFileSystem > CryptoFileSystem > ShorteningFileSystem > InMemoryFileSystem", this::createCompoundFileSystemInMemory);
add("StatsFileSystem > NormalizingFileSystem > CryptoFileSystem > ShorteningFileSystem > NioFileSystem", this::createCompoundFileSystemNio);
}
private FileSystem createCryptoFileSystemInMemory() {
@@ -63,6 +67,14 @@ class FileSystemFactories implements Iterable<FileSystemFactory> {
return createStatsFileSystem(createInMemoryFileSystem());
}
private FileSystem createNormalizingFileSystemNio() {
return createNormalizingFileSystem(createInMemoryFileSystem());
}
private FileSystem createNormalizingFileSystemInMemory() {
return createNormalizingFileSystem(createInMemoryFileSystem());
}
private FileSystem createCompoundFileSystemNio() {
return createCompoundFileSystem(createNioFileSystem());
}
@@ -84,13 +96,17 @@ class FileSystemFactories implements Iterable<FileSystemFactory> {
}
private FileSystem createCompoundFileSystem(FileSystem delegate) {
return createStatsFileSystem(createCryptoFileSystem(createShorteningFileSystem(delegate)));
return createStatsFileSystem(createNormalizingFileSystem(createCryptoFileSystem(createShorteningFileSystem(delegate))));
}
private FileSystem createStatsFileSystem(FileSystem delegate) {
return new StatsFileSystem(delegate);
}
private FileSystem createNormalizingFileSystem(FileSystem delegate) {
return new NormalizedNameFileSystem(delegate, Form.NFC);
}
private FileSystem createCryptoFileSystem(FileSystem delegate) {
CRYPTO_FS_COMP.cryptoFileSystemFactory().initializeNew(delegate, "aPassphrase");
return CRYPTO_FS_COMP.cryptoFileSystemFactory().unlockExisting(delegate, "aPassphrase", Mockito.mock(CryptoFileSystemDelegate.class));

View File

@@ -80,6 +80,11 @@
<artifactId>filesystem-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-charsets</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-nio</artifactId>
@@ -286,6 +291,7 @@
<module>frontend-api</module>
<module>frontend-webdav</module>
<module>ui</module>
<module>filesystem-charsets</module>
</modules>
<profiles>

View File

@@ -38,6 +38,10 @@
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-crypto</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-charsets</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-stats</artifactId>

View File

@@ -30,6 +30,7 @@ import org.cryptomator.common.LazyInitializer;
import org.cryptomator.common.Optionals;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.charsets.NormalizedNameFileSystem;
import org.cryptomator.filesystem.crypto.CryptoFileSystemDelegate;
import org.cryptomator.filesystem.crypto.CryptoFileSystemFactory;
import org.cryptomator.filesystem.nio.NioFileSystem;
@@ -126,7 +127,8 @@ public class Vault implements CryptoFileSystemDelegate {
FileSystem fs = getNioFileSystem();
FileSystem shorteningFs = shorteningFileSystemFactory.get(fs);
FileSystem cryptoFs = cryptoFileSystemFactory.unlockExisting(shorteningFs, passphrase, this);
StatsFileSystem statsFs = new StatsFileSystem(cryptoFs);
FileSystem normalizingFs = new NormalizedNameFileSystem(cryptoFs, SystemUtils.IS_OS_MAC_OSX ? Form.NFD : Form.NFC);
StatsFileSystem statsFs = new StatsFileSystem(normalizingFs);
statsFileSystem = Optional.of(statsFs);
String contextPath = StringUtils.prependIfMissing(mountName, "/");
Frontend frontend = frontendFactory.create(statsFs, contextPath);