@@ -178,7 +178,7 @@ private static String absoluteResourcePath(String... components) {
178178 private static final char RESOURCE_SEPARATOR_CHAR = '/' ;
179179 private static final String RESOURCE_SEPARATOR = String .valueOf (RESOURCE_SEPARATOR_CHAR );
180180
181- private abstract class BaseEntry {
181+ private abstract sealed class BaseEntry permits FileEntry , DirEntry {
182182 final String platformPath ;
183183
184184 private BaseEntry (String platformPath ) {
@@ -192,6 +192,10 @@ String getPlatformPath() {
192192 String getResourcePath () {
193193 return platformPathToResourcePath (platformPath );
194194 }
195+
196+ static AssertionError throwUnexpectedSubclass () {
197+ throw new AssertionError ("Unexpected subclass of sealed DirEntry" );
198+ }
195199 }
196200
197201 private final class FileEntry extends BaseEntry {
@@ -735,6 +739,15 @@ private BaseEntry getEntry(Path inputPath) {
735739 return vfsEntries .get (toCaseComparable (path .toString ()));
736740 }
737741
742+ private BaseEntry getEntrySafe (String callerId , Path path ) throws NoSuchFileException {
743+ BaseEntry entry = getEntry (path );
744+ if (entry == null ) {
745+ finer ("%s: no such file or directory: '%s'" , callerId , path );
746+ throw new NoSuchFileException (path .toString ());
747+ }
748+ return entry ;
749+ }
750+
738751 /**
739752 * Determines if the given path belongs to the VFS. The path should be already normalized
740753 */
@@ -947,11 +960,7 @@ public void checkAccess(Path p, Set<? extends AccessMode> modes, LinkOption... l
947960 throw securityException ("VFS.checkAccess" , String .format ("execute access not supported for '%s'" , p ));
948961 }
949962
950- if (getEntry (path ) == null ) {
951- String msg = String .format ("no such file or directory: '%s'" , path );
952- finer ("VFS.checkAccess %s" , msg );
953- throw new NoSuchFileException (msg );
954- }
963+ getEntrySafe ("VFS.checkAccess" , path );
955964 finer ("VFS.checkAccess %s OK" , path );
956965 }
957966
@@ -1100,7 +1109,7 @@ public DirectoryStream<Path> newDirectoryStream(Path d, DirectoryStream.Filter<?
11001109 Objects .requireNonNull (d );
11011110 Path dir = toAbsoluteNormalizedPath (d );
11021111 Objects .requireNonNull (filter );
1103- BaseEntry entry = getEntry ( dir );
1112+ BaseEntry entry = getEntrySafe ( "VFS.newDirectoryStream" , dir );
11041113 if (entry instanceof FileEntry ) {
11051114 finer ("VFS.newDirectoryStream not a directory %s" , dir );
11061115 throw new NotDirectoryException (dir .toString ());
@@ -1127,9 +1136,8 @@ public Iterator<Path> iterator() {
11271136 }).map (e -> Path .of (e .getPlatformPath ())).iterator ();
11281137 }
11291138 };
1130- } else {
1131- throw new NoSuchFileException (dir .toString ());
11321139 }
1140+ throw BaseEntry .throwUnexpectedSubclass ();
11331141 }
11341142
11351143 private static Path toAbsoluteNormalizedPath (Path path ) {
@@ -1195,12 +1203,7 @@ public Map<String, Object> readAttributes(Path p, String attributes, LinkOption.
11951203 }
11961204 }
11971205
1198- BaseEntry entry = getEntry (path );
1199- if (entry == null ) {
1200- String msg = String .format ("no such file or directory: '%s'" , path );
1201- finer ("VFS.readAttributes %s" , msg );
1202- throw new NoSuchFileException (msg );
1203- }
1206+ BaseEntry entry = getEntrySafe ("VFS.readAttributes" , path );
12041207 HashMap <String , Object > attrs = new HashMap <>();
12051208 if (attributes .startsWith ("unix:" ) || attributes .startsWith ("posix:" )) {
12061209 finer ("VFS.readAttributes unsupported attributes '%s' %s" , path , attributes );
@@ -1282,7 +1285,7 @@ public Path readSymbolicLink(Path link) throws IOException {
12821285 }
12831286 if (getEntry (path ) == null ) {
12841287 finer ("VFS.readSymbolicLink no entry for path '%s'" , link );
1285- throw new NoSuchFileException (String . format ( "no such file or directory: '%s'" , path ));
1288+ throw new NoSuchFileException (path . toString ( ));
12861289 }
12871290 throw new NotLinkException (link .toString ());
12881291 }
@@ -1304,6 +1307,63 @@ public Path getTempDirectory() {
13041307 throw new RuntimeException ("should not reach here" );
13051308 }
13061309
1310+ @ Override
1311+ public boolean isFileStoreReadOnly (Path path ) throws NoSuchFileException {
1312+ Objects .requireNonNull (path );
1313+ getEntrySafe ("VFS.isFileStoreReadOnly" , path );
1314+ return true ;
1315+ }
1316+
1317+ @ Override
1318+ public long getFileStoreTotalSpace (Path path ) throws IOException {
1319+ Objects .requireNonNull (path );
1320+ getEntrySafe ("VFS.getFileStoreTotalSpace" , path );
1321+ return getEntryTotalSpace (getEntrySafe ("VFS.getFileStoreTotalSpace:VFS-root:" , mountPoint ));
1322+ }
1323+
1324+ private long getEntryTotalSpace (BaseEntry entry ) throws IOException {
1325+ // This is a bit arbitrary best effort approximation.
1326+ // For each file we count its data size and for each entry we take its full path size (+1
1327+ // for newline) as this is what we store in the filelist.txt, so the total should
1328+ // approximate the size the VFS takes up in the resources.
1329+ try {
1330+ long size = entry .getResourcePath ().length () + 1 ;
1331+ if (entry instanceof FileEntry fe ) {
1332+ size = Math .addExact (size , fe .getData ().length );
1333+ } else if (entry instanceof DirEntry de ) {
1334+ for (BaseEntry e : de .entries ) {
1335+ size = Math .addExact (size , getEntryTotalSpace (e ));
1336+ }
1337+ } else {
1338+ throw BaseEntry .throwUnexpectedSubclass ();
1339+ }
1340+ return size ;
1341+ } catch (ArithmeticException ex ) {
1342+ return Long .MAX_VALUE ;
1343+ }
1344+ }
1345+
1346+ @ Override
1347+ public long getFileStoreUsableSpace (Path path ) throws NoSuchFileException {
1348+ Objects .requireNonNull (path );
1349+ getEntrySafe ("VFS.getFileStoreUsableSpace" , path );
1350+ return 0 ;
1351+ }
1352+
1353+ @ Override
1354+ public long getFileStoreUnallocatedSpace (Path path ) throws NoSuchFileException {
1355+ Objects .requireNonNull (path );
1356+ getEntrySafe ("VFS.getFileStoreUnallocatedSpace" , path );
1357+ return 0 ;
1358+ }
1359+
1360+ @ Override
1361+ public long getFileStoreBlockSize (Path path ) throws NoSuchFileException {
1362+ Objects .requireNonNull (path );
1363+ getEntrySafe ("VFS.getFileStoreBlockSize" , path );
1364+ return 4096 ;
1365+ }
1366+
13071367 private static void warn (String msgFormat , Object ... args ) {
13081368 if (LOGGER .isLoggable (Level .WARNING )) {
13091369 LOGGER .log (Level .WARNING , String .format (msgFormat , args ));
0 commit comments