3535import java .nio .file .DirectoryStream .Filter ;
3636import java .nio .file .FileAlreadyExistsException ;
3737import java .nio .file .FileStore ;
38+ import java .nio .file .FileSystemException ;
3839import java .nio .file .Files ;
3940import java .nio .file .LinkOption ;
4041import java .nio .file .NoSuchFileException ;
5960import java .util .Collections ;
6061import java .util .EnumSet ;
6162import java .util .Map ;
63+ import java .util .Optional ;
6264import java .util .Set ;
6365import java .util .stream .Collectors ;
6466
6870
6971@ CryptoFileSystemScoped
7072class CryptoFileSystemImpl extends CryptoFileSystem {
71-
73+
7274 private final CryptoFileSystemProvider provider ;
7375 private final CryptoFileSystems cryptoFileSystems ;
7476 private final Path pathToVault ;
@@ -80,6 +82,7 @@ class CryptoFileSystemImpl extends CryptoFileSystem {
8082 private final PathMatcherFactory pathMatcherFactory ;
8183 private final DirectoryStreamFactory directoryStreamFactory ;
8284 private final DirectoryIdProvider dirIdProvider ;
85+ private final DirectoryIdBackup dirIdBackup ;
8386 private final AttributeProvider fileAttributeProvider ;
8487 private final AttributeByNameProvider fileAttributeByNameProvider ;
8588 private final AttributeViewProvider fileAttributeViewProvider ;
@@ -98,7 +101,7 @@ class CryptoFileSystemImpl extends CryptoFileSystem {
98101 @ Inject
99102 public CryptoFileSystemImpl (CryptoFileSystemProvider provider , CryptoFileSystems cryptoFileSystems , @ PathToVault Path pathToVault , Cryptor cryptor ,
100103 CryptoFileStore fileStore , CryptoFileSystemStats stats , CryptoPathMapper cryptoPathMapper , CryptoPathFactory cryptoPathFactory ,
101- PathMatcherFactory pathMatcherFactory , DirectoryStreamFactory directoryStreamFactory , DirectoryIdProvider dirIdProvider ,
104+ PathMatcherFactory pathMatcherFactory , DirectoryStreamFactory directoryStreamFactory , DirectoryIdProvider dirIdProvider , DirectoryIdBackup dirIdBackup ,
102105 AttributeProvider fileAttributeProvider , AttributeByNameProvider fileAttributeByNameProvider , AttributeViewProvider fileAttributeViewProvider ,
103106 OpenCryptoFiles openCryptoFiles , Symlinks symlinks , FinallyUtil finallyUtil , CiphertextDirectoryDeleter ciphertextDirDeleter , ReadonlyFlag readonlyFlag ,
104107 CryptoFileSystemProperties fileSystemProperties ) {
@@ -113,6 +116,7 @@ public CryptoFileSystemImpl(CryptoFileSystemProvider provider, CryptoFileSystems
113116 this .pathMatcherFactory = pathMatcherFactory ;
114117 this .directoryStreamFactory = directoryStreamFactory ;
115118 this .dirIdProvider = dirIdProvider ;
119+ this .dirIdBackup = dirIdBackup ;
116120 this .fileAttributeProvider = fileAttributeProvider ;
117121 this .fileAttributeByNameProvider = fileAttributeByNameProvider ;
118122 this .fileAttributeViewProvider = fileAttributeViewProvider ;
@@ -235,13 +239,17 @@ <A extends BasicFileAttributes> A readAttributes(CryptoPath cleartextPath, Class
235239
236240 /**
237241 * @param cleartextPath the path to the file
238- * @param type the Class object corresponding to the file attribute view
239- * @param options future use
242+ * @param type the Class object corresponding to the file attribute view
243+ * @param options future use
240244 * @return a file attribute view of the specified type, or <code>null</code> if the attribute view type is not available
241245 * @see AttributeViewProvider#getAttributeView(CryptoPath, Class, LinkOption...)
242246 */
243247 <V extends FileAttributeView > V getFileAttributeView (CryptoPath cleartextPath , Class <V > type , LinkOption ... options ) {
244- return fileAttributeViewProvider .getAttributeView (cleartextPath , type , options );
248+ if (fileStore .supportsFileAttributeView (type )) {
249+ return fileAttributeViewProvider .getAttributeView (cleartextPath , type , options );
250+ } else {
251+ return null ;
252+ }
245253 }
246254
247255 void checkAccess (CryptoPath cleartextPath , AccessMode ... modes ) throws IOException {
@@ -282,6 +290,10 @@ boolean isHidden(CryptoPath cleartextPath) throws IOException {
282290 void createDirectory (CryptoPath cleartextDir , FileAttribute <?>... attrs ) throws IOException {
283291 readonlyFlag .assertWritable ();
284292 assertCleartextNameLengthAllowed (cleartextDir );
293+ if (rootPath .equals (cleartextDir )) {
294+ throw new FileAlreadyExistsException (rootPath .toString ());
295+ }
296+
285297 CryptoPath cleartextParentDir = cleartextDir .getParent ();
286298 if (cleartextParentDir == null ) {
287299 return ;
@@ -302,6 +314,7 @@ void createDirectory(CryptoPath cleartextDir, FileAttribute<?>... attrs) throws
302314 // create dir if and only if the dirFile has been created right now (not if it has been created before):
303315 try {
304316 Files .createDirectories (ciphertextDir .path );
317+ dirIdBackup .execute (ciphertextDir );
305318 ciphertextPath .persistLongFileName ();
306319 } catch (IOException e ) {
307320 // make sure there is no orphan dir file:
@@ -375,14 +388,17 @@ private FileChannel newFileChannelFromFile(CryptoPath cleartextFilePath, Effecti
375388 stats .incrementAccessesRead ();
376389 }
377390 return ch ;
378- } catch (Exception e ){
391+ } catch (Exception e ) {
379392 ch .close ();
380393 throw e ;
381394 }
382395 }
383396
384397 void delete (CryptoPath cleartextPath ) throws IOException {
385398 readonlyFlag .assertWritable ();
399+ if (rootPath .equals (cleartextPath )) {
400+ throw new FileSystemException ("The filesystem root cannot be deleted." );
401+ }
386402 CiphertextFileType ciphertextFileType = cryptoPathMapper .getCiphertextFileType (cleartextPath );
387403 CiphertextFilePath ciphertextPath = cryptoPathMapper .getCiphertextFilePath (cleartextPath );
388404 switch (ciphertextFileType ) {
@@ -414,6 +430,11 @@ void copy(CryptoPath cleartextSource, CryptoPath cleartextTarget, CopyOption...
414430 if (cleartextSource .equals (cleartextTarget )) {
415431 return ;
416432 }
433+
434+ if (rootPath .equals (cleartextTarget ) && ArrayUtils .contains (options , StandardCopyOption .REPLACE_EXISTING )) {
435+ throw new FileSystemException ("The filesystem root cannot be replaced." );
436+ }
437+
417438 CiphertextFileType ciphertextFileType = cryptoPathMapper .getCiphertextFileType (cleartextSource );
418439 if (!ArrayUtils .contains (options , StandardCopyOption .REPLACE_EXISTING )) {
419440 cryptoPathMapper .assertNonExisting (cleartextTarget );
@@ -508,6 +529,16 @@ private void copyAttributes(Path src, Path dst) throws IOException {
508529 void move (CryptoPath cleartextSource , CryptoPath cleartextTarget , CopyOption ... options ) throws IOException {
509530 readonlyFlag .assertWritable ();
510531 assertCleartextNameLengthAllowed (cleartextTarget );
532+
533+ if (rootPath .equals (cleartextSource )) {
534+ throw new FileSystemException ("Filesystem root cannot be moved." );
535+
536+ }
537+
538+ if (rootPath .equals (cleartextTarget )) {
539+ throw new FileAlreadyExistsException (rootPath .toString ());
540+ }
541+
511542 if (cleartextSource .equals (cleartextTarget )) {
512543 return ;
513544 }
@@ -582,7 +613,7 @@ private void moveDirectory(CryptoPath cleartextSource, CryptoPath cleartextTarge
582613 }
583614 Files .walkFileTree (ciphertextTarget .getRawPath (), DeletingFileVisitor .INSTANCE );
584615 }
585-
616+
586617 // no exceptions until this point, so MOVE:
587618 Files .move (ciphertextSource .getRawPath (), ciphertextTarget .getRawPath (), options );
588619 if (ciphertextTarget .isShortened ()) {
@@ -621,7 +652,7 @@ CryptoPath getEmptyPath() {
621652 }
622653
623654 void assertCleartextNameLengthAllowed (CryptoPath cleartextPath ) throws FileNameTooLongException {
624- String filename = cleartextPath .getFileName (). toString ();
655+ String filename = Optional . ofNullable ( cleartextPath .getFileName ()). map ( CryptoPath :: toString ). orElse ( "" ); //fs root has no explicit name
625656 if (filename .length () > fileSystemProperties .maxCleartextNameLength ()) {
626657 throw new FileNameTooLongException (cleartextPath .toString (), fileSystemProperties .maxCleartextNameLength ());
627658 }
0 commit comments