Skip to content

Commit 8bd61d4

Browse files
committed
VirtualFileSystem: implement new methods from polyglot FileSystem
1 parent 0ff3a32 commit 8bd61d4

File tree

5 files changed

+117
-37
lines changed

5 files changed

+117
-37
lines changed

graalpython/com.oracle.graal.python.test.integration/src/GRAALPY-VFS/com.oracle.graal.python.test/integration/fileslist.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
/GRAALPY-VFS/com.oracle.graal.python.test/integration/emptydir/
66
/GRAALPY-VFS/com.oracle.graal.python.test/integration/file1
77
/GRAALPY-VFS/com.oracle.graal.python.test/integration/extractme
8-
/GRAALPY-VFS/com.oracle.graal.python.test/integration/fileslist.txt
98
/GRAALPY-VFS/com.oracle.graal.python.test/integration/SomeFile
109
/GRAALPY-VFS/com.oracle.graal.python.test/integration/site-packages/
1110
/GRAALPY-VFS/com.oracle.graal.python.test/integration/site-packages/testpkg/

graalpython/com.oracle.graal.python.test.integration/src/org.graalvm.python.vfs/fileslist.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
/org.graalvm.python.vfs/emptydir/
66
/org.graalvm.python.vfs/file1
77
/org.graalvm.python.vfs/extractme
8-
/org.graalvm.python.vfs/fileslist.txt
98
/org.graalvm.python.vfs/SomeFile
109
/org.graalvm.python.vfs/site-packages/
1110
/org.graalvm.python.vfs/site-packages/testpkg/

graalpython/com.oracle.graal.python.test.integration/src/org/graalvm/python/embedding/test/integration/VirtualFileSystemIntegrationTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -441,12 +441,11 @@ public void fsOperations(Context ctx, String pathPrefix) {
441441
assert len(f) == 0, 'expected no files'
442442
443443
f = listdir('/test_mount_point/')
444-
assert len(f) == 7, 'expected 7 files, got ' + str(len(f))
444+
assert len(f) == 6, 'expected 6 files, got ' + repr(list(f))
445445
446446
assert 'dir1' in f, 'does not contain "dir1"'
447447
assert 'emptydir' in f, 'does not contain "emptydir"'
448448
assert 'file1' in f, 'does not contain "file1"'
449-
assert 'fileslist.txt' in f, 'does not contain "fileslist.txt"'
450449
451450
f = listdir('{pathPrefix}dir1')
452451
if len(f) != 2:
@@ -485,7 +484,7 @@ assert len(f) == 0, 'expected no files in emptydir'
485484
dirs.add(r + "/" + dd)
486485
487486
assert len(roots) == 9, 'expected 10 roots, got ' + str(len(roots))
488-
assert len(files) == 15, 'expected 15 files, got ' + str(len(files))
487+
assert len(files) == 14, 'expected 14 files, got ' + str(len(files))
489488
assert len(dirs) == 8, 'expected 8 dirs, got ' + str(len(dirs))
490489
""", pathPrefix);
491490

graalpython/com.oracle.graal.python.test/src/org/graalvm/python/embedding/test/VirtualFileSystemTest.java

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,15 @@
4141

4242
package org.graalvm.python.embedding.test;
4343

44-
import org.graalvm.polyglot.io.FileSystem;
45-
import org.graalvm.python.embedding.VirtualFileSystem;
46-
import org.junit.After;
47-
import org.junit.Assert;
48-
import org.junit.Before;
49-
import org.junit.Test;
44+
import static com.oracle.graal.python.test.integration.Utils.IS_WINDOWS;
45+
import static org.graalvm.python.embedding.VirtualFileSystem.HostIO.NONE;
46+
import static org.graalvm.python.embedding.VirtualFileSystem.HostIO.READ;
47+
import static org.graalvm.python.embedding.VirtualFileSystem.HostIO.READ_WRITE;
48+
import static org.junit.Assert.assertEquals;
49+
import static org.junit.Assert.assertFalse;
50+
import static org.junit.Assert.assertNotEquals;
51+
import static org.junit.Assert.assertTrue;
52+
import static org.junit.Assert.fail;
5053

5154
import java.io.File;
5255
import java.io.IOException;
@@ -82,15 +85,12 @@
8285
import java.util.logging.Logger;
8386
import java.util.stream.Stream;
8487

85-
import static com.oracle.graal.python.test.integration.Utils.IS_WINDOWS;
86-
import static org.graalvm.python.embedding.VirtualFileSystem.HostIO.NONE;
87-
import static org.graalvm.python.embedding.VirtualFileSystem.HostIO.READ;
88-
import static org.graalvm.python.embedding.VirtualFileSystem.HostIO.READ_WRITE;
89-
import static org.junit.Assert.assertEquals;
90-
import static org.junit.Assert.assertFalse;
91-
import static org.junit.Assert.assertNotEquals;
92-
import static org.junit.Assert.assertTrue;
93-
import static org.junit.Assert.fail;
88+
import org.graalvm.polyglot.io.FileSystem;
89+
import org.graalvm.python.embedding.VirtualFileSystem;
90+
import org.junit.After;
91+
import org.junit.Assert;
92+
import org.junit.Before;
93+
import org.junit.Test;
9494

9595
public class VirtualFileSystemTest {
9696

@@ -740,7 +740,7 @@ public void noExtractFilter() throws Exception {
740740
extractFilter(null).//
741741
resourceLoadingClass(VirtualFileSystemTest.class).build()) {
742742
FileSystem fs = getDelegatingFS(vfs);
743-
assertEquals(23, checkNotExtracted(fs, VFS_ROOT_PATH));
743+
assertEquals(22, checkNotExtracted(fs, VFS_ROOT_PATH));
744744
}
745745
}
746746

@@ -1130,6 +1130,29 @@ public void copy() throws Exception {
11301130

11311131
}
11321132

1133+
@Test
1134+
public void testIsReadOnly() throws Exception {
1135+
for (FileSystem fs : new FileSystem[]{rwHostIOVFS, rHostIOVFS, noHostIOVFS}) {
1136+
assertTrue(fs.isFileStoreReadOnly(VFS_ROOT_PATH.resolve("file1")));
1137+
Assert.assertThrows(NoSuchFileException.class, () -> fs.isFileStoreReadOnly(VFS_ROOT_PATH.resolve("bogus")));
1138+
}
1139+
}
1140+
1141+
@Test
1142+
public void testGetFileStoreSpace() throws Exception {
1143+
for (FileSystem fs : new FileSystem[]{rwHostIOVFS, rHostIOVFS, noHostIOVFS}) {
1144+
long expectedSize = IS_WINDOWS ? 1162 : 1158; // this is because of different newlines
1145+
assertEquals(expectedSize, fs.getFileStoreTotalSpace(VFS_ROOT_PATH.resolve("file1")));
1146+
assertEquals(0, fs.getFileStoreUnallocatedSpace(VFS_ROOT_PATH.resolve("file1")));
1147+
assertEquals(0, fs.getFileStoreUsableSpace(VFS_ROOT_PATH.resolve("file1")));
1148+
assertEquals(4096, fs.getFileStoreBlockSize(VFS_ROOT_PATH.resolve("file1")));
1149+
Assert.assertThrows(NoSuchFileException.class, () -> fs.isFileStoreReadOnly(VFS_ROOT_PATH.resolve("bogus")));
1150+
Assert.assertThrows(NoSuchFileException.class, () -> fs.getFileStoreUnallocatedSpace(VFS_ROOT_PATH.resolve("bogus")));
1151+
Assert.assertThrows(NoSuchFileException.class, () -> fs.getFileStoreUsableSpace(VFS_ROOT_PATH.resolve("bogus")));
1152+
Assert.assertThrows(NoSuchFileException.class, () -> fs.getFileStoreBlockSize(VFS_ROOT_PATH.resolve("bogus")));
1153+
}
1154+
}
1155+
11331156
@Test
11341157
public void testImpl() throws Exception {
11351158
Set<String> ignored = Set.of(

graalpython/org.graalvm.python.embedding/src/org/graalvm/python/embedding/VirtualFileSystemImpl.java

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)