Skip to content

Commit 81f3581

Browse files
committed
normalize incoming paths in VFS to resolve scenarios when path starts in
VFS but points to rel FS or vice vers
1 parent b21a770 commit 81f3581

File tree

2 files changed

+73
-24
lines changed

2 files changed

+73
-24
lines changed

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

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import java.nio.file.attribute.FileAttribute;
6666
import java.nio.file.attribute.FileTime;
6767
import java.util.ArrayList;
68+
import java.util.Collections;
6869
import java.util.HashSet;
6970
import java.util.Iterator;
7071
import java.util.List;
@@ -142,7 +143,8 @@ public void toRealPath() throws Exception {
142143
}
143144

144145
// from real FS
145-
final Path realFSPath = Files.createTempDirectory("graalpy.vfs.test").resolve("extractme");
146+
final Path realFSDir = Files.createTempDirectory("graalpy.vfs.test");
147+
final Path realFSPath = realFSDir.resolve("extractme");
146148
realFSPath.toFile().createNewFile();
147149
for (FileSystem fs : new FileSystem[]{rwHostIOVFS, rHostIOVFS}) {
148150
assertTrue(Files.isSameFile(realFSPath, fs.toRealPath(Path.of(realFSPath.getParent().toString() + "/../" + realFSPath.getParent().getFileName().toString() + "/extractme"))));
@@ -151,6 +153,7 @@ public void toRealPath() throws Exception {
151153
withCWD(fs, VFS_ROOT_PATH, (fst) -> assertTrue(Files.isSameFile(realFSPath, fst.toRealPath(Path.of("../" + realFSPath.toString())))));
152154
}
153155
checkException(SecurityException.class, () -> noHostIOVFS.toRealPath(realFSPath), "expected error for no host io fs");
156+
assertEquals(VFS_ROOT_PATH.resolve("dir1"), rwHostIOVFS.toRealPath(Path.of(realFSDir.toString() + "/..".repeat(realFSDir.getNameCount()) + VFS_ROOT).resolve("dir1")));
154157
}
155158

156159
private void toRealPathVFS(FileSystem fs, String pathPrefix) throws IOException {
@@ -165,7 +168,7 @@ private void toRealPathVFS(FileSystem fs, String pathPrefix) throws IOException
165168

166169
@Test
167170
public void toAbsolutePath() throws Exception {
168-
// from VFS
171+
// VFS
169172
for (FileSystem fs : new FileSystem[]{rwHostIOVFS, rHostIOVFS, noHostIOVFS}) {
170173
checkException(NullPointerException.class, () -> fs.toAbsolutePath(null));
171174

@@ -193,15 +196,59 @@ public void toAbsolutePath() throws Exception {
193196
});
194197
}
195198

196-
// from real FS
197-
Path realFSPath = Files.createTempDirectory("graalpy.vfs.test").resolve("extractme");
199+
// real FS
200+
Path realFSDir = Files.createTempDirectory("graalpy.vfs.test");
201+
Path realFSDir2 = realFSDir.resolve("dir");
202+
Files.createDirectories(realFSDir2);
203+
Path realFSPath = realFSDir.resolve("extractme");
198204
realFSPath.toFile().createNewFile();
199205
for (FileSystem fs : new FileSystem[]{rwHostIOVFS, rHostIOVFS}) {
200206
assertEquals(Path.of("extractme").toAbsolutePath(), fs.toAbsolutePath(Path.of("extractme")));
207+
208+
// absolute path starting with VFS, pointing to real FS
209+
// /VFS_ROOT/../real/fs/path/
210+
assertEquals(realFSPath, fs.toAbsolutePath(Path.of(VFS_ROOT + ".." + realFSPath.toString())));
211+
// absolute path starting with real FS, pointing to VFS
212+
// /real/fs/path/../../../VFS_ROOT
213+
assertEquals(VFS_ROOT_PATH, fs.toAbsolutePath(Path.of(realFSDir.toString() + "/..".repeat(realFSDir.getNameCount()) + VFS_ROOT)));
214+
215+
// no CWD set, so relative path starting in real FS, pointing to VFS
216+
// ../../../VFS_ROOT
217+
Path defaultCWD = Path.of(".").toAbsolutePath();
218+
assertEquals(VFS_ROOT_PATH, fs.toAbsolutePath(Path.of("../".repeat(defaultCWD.getNameCount()) + VFS_ROOT)));
219+
// ../../../VFS_ROOT/../real/fs/path
220+
assertEquals(realFSPath, fs.toAbsolutePath(Path.of("../".repeat(defaultCWD.getNameCount()) + VFS_ROOT + ".." + realFSPath.toString())));
221+
222+
// CWD is VFS_ROOT, relative path pointing to real FS
223+
// ../real/fs/path
224+
withCWD(fs, VFS_ROOT_PATH, (fst) -> assertEquals(realFSPath, fst.toAbsolutePath(Path.of("../" + realFSPath.toString()))));
225+
// CWD is VFS_ROOT, relative path pointing through real FS back to VFS
226+
// ../real/fs/path/../../../VFS_ROOT_PATH
227+
withCWD(fs, VFS_ROOT_PATH, (fst) -> assertEquals(VFS_ROOT_PATH, fst.toAbsolutePath(Path.of("../some/path/../../" + VFS_ROOT))));
228+
// CWD is real FS, relative path pointing to VFS
229+
// real/fs/path/../../
230+
withCWD(fs, realFSPath.getParent(), (fst) -> assertEquals(VFS_ROOT_PATH, fst.toAbsolutePath(Path.of("dir/" + "../".repeat(realFSDir2.getNameCount()) + VFS_ROOT))));
231+
// CWD is real FS, relative path pointing through VFS to real FS
232+
// real/fs/path/../../../VFS
233+
withCWD(fs, realFSPath.getParent(),
234+
(fst) -> assertEquals(realFSPath, fst.toAbsolutePath(Path.of("dir/" + "../".repeat(realFSDir2.getNameCount()) + VFS_ROOT + "../" + realFSPath.toString()))));
235+
assertEquals(Path.of("extractme").toAbsolutePath(), fs.toAbsolutePath(Path.of("extractme")));
236+
201237
withCWD(fs, realFSPath.getParent(), (fst) -> assertEquals(realFSPath, fst.toAbsolutePath(realFSPath.getFileName())));
238+
202239
}
240+
203241
checkException(SecurityException.class, () -> noHostIOVFS.toAbsolutePath(realFSPath));
204242
assertEquals(Path.of(VFS_SRC + "extractme"), noHostIOVFS.toAbsolutePath(realFSPath.getFileName()));
243+
244+
// absolute path starting with real FS, pointing to VFS
245+
// /real/fs/path/../../../VFS_ROOT
246+
assertEquals(VFS_ROOT_PATH, noHostIOVFS.toAbsolutePath(Path.of(realFSDir.toString() + "/..".repeat(realFSDir.getNameCount()) + VFS_ROOT)));
247+
248+
// no CWD set, relative path starting in real FS, pointing to VFS
249+
// ../../../VFS_ROOT
250+
Path defaultCWD = Path.of(".").toAbsolutePath();
251+
assertEquals(VFS_ROOT_PATH, noHostIOVFS.toAbsolutePath(Path.of("../".repeat(defaultCWD.getNameCount()) + VFS_ROOT)));
205252
}
206253

207254
@Test
@@ -608,10 +655,7 @@ private static void checkException(Class<?> exType, ExceptionCall c, String msg)
608655
}
609656

610657
@Test
611-
public void setCurrentWorkingDirectory() throws Exception {
612-
613-
// XXX test with not normalized paths
614-
658+
public void currentWorkingDirectory() throws Exception {
615659
Path realFSDir = Files.createTempDirectory("graalpy.vfs.test");
616660
Path realFSFile = realFSDir.resolve("extractme");
617661
Files.createFile(realFSFile);
@@ -648,6 +692,12 @@ public void setCurrentWorkingDirectory() throws Exception {
648692

649693
fs.setCurrentWorkingDirectory(realFSDir);
650694
assertEquals(realFSDir, fs.toAbsolutePath(Path.of("dir")).getParent());
695+
696+
fs.setCurrentWorkingDirectory(Path.of(VFS_ROOT + "../" + realFSDir.toString()));
697+
assertEquals(realFSDir, fs.toAbsolutePath(Path.of("dir")).getParent());
698+
699+
fs.setCurrentWorkingDirectory(Path.of(realFSDir.toString() + "/..".repeat(realFSDir.getNameCount()) + VFS_ROOT));
700+
assertEquals(VFS_ROOT_PATH.resolve("dir1"), fs.toAbsolutePath(Path.of("dir1")));
651701
} finally {
652702
resetCWD(fs);
653703
}
@@ -721,7 +771,7 @@ public void createLink() throws Exception {
721771
for (FileSystem fs : new FileSystem[]{rwHostIOVFS, rHostIOVFS, noHostIOVFS}) {
722772
checkException(NullPointerException.class, () -> fs.createLink(null, null));
723773
checkException(NullPointerException.class, () -> fs.createLink(VFS_ROOT_PATH, null));
724-
checkException(SecurityException.class, () -> fs.createLink(VFS_ROOT_PATH.resolve("/link1"), realFSPath));
774+
checkException(SecurityException.class, () -> fs.createLink(VFS_ROOT_PATH.resolve("link1"), realFSPath));
725775
checkException(SecurityException.class, () -> fs.createLink(realFSPath.getParent().resolve("link2"), VFS_ROOT_PATH));
726776
checkException(SecurityException.class, () -> fs.createLink(VFS_ROOT_PATH, VFS_ROOT_PATH.resolve("link")));
727777
}
@@ -746,8 +796,8 @@ public void createAndReadSymbolicLink() throws Exception {
746796
checkException(NullPointerException.class, () -> fs.createSymbolicLink(null, null));
747797
checkException(NullPointerException.class, () -> fs.readSymbolicLink(null));
748798

749-
checkException(SecurityException.class, () -> fs.createSymbolicLink(VFS_ROOT_PATH.resolve("/link1"), realFSPath));
750-
checkException(SecurityException.class, () -> fs.readSymbolicLink(VFS_ROOT_PATH.resolve("/link1")));
799+
checkException(SecurityException.class, () -> fs.createSymbolicLink(VFS_ROOT_PATH.resolve("link1"), realFSPath));
800+
checkException(SecurityException.class, () -> fs.readSymbolicLink(VFS_ROOT_PATH.resolve("link1")));
751801
checkException(SecurityException.class, () -> fs.createSymbolicLink(realFSPath.getParent().resolve("link2"), VFS_ROOT_PATH));
752802
checkException(SecurityException.class, () -> fs.createSymbolicLink(VFS_ROOT_PATH, VFS_ROOT_PATH.resolve("link")));
753803
}

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

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -851,16 +851,12 @@ public Iterator<Path> iterator() {
851851

852852
private Path resolveVFSRelative(Path path) {
853853
if (path.isAbsolute()) {
854-
return path;
854+
return path.normalize();
855855
}
856856
if (cwd == null) {
857-
return path.toAbsolutePath();
857+
return path.toAbsolutePath().normalize();
858858
} else {
859-
if (pathIsInVfs(cwd)) {
860-
return cwd.resolve(path).normalize();
861-
} else {
862-
return cwd.resolve(path);
863-
}
859+
return cwd.resolve(path).normalize();
864860
}
865861
}
866862

@@ -966,11 +962,15 @@ public Map<String, Object> readAttributes(Path p, String attributes, LinkOption.
966962
}
967963

968964
@Override
969-
public void setCurrentWorkingDirectory(Path dir) {
970-
Objects.requireNonNull(dir, "Current working directory must be non null.");
971-
if (!dir.isAbsolute()) {
965+
public void setCurrentWorkingDirectory(Path d) {
966+
Objects.requireNonNull(d, "Current working directory must be non null.");
967+
if (!d.isAbsolute()) {
972968
throw new IllegalArgumentException("Current working directory must be absolute.");
973969
}
970+
// need resolve paths starting in VFS but pointing to real FS or vice versa
971+
// /vfs_mount_point/../real/fs/path
972+
// /real/fs/path/../../vfs_mount_point/...
973+
Path dir = d.normalize();
974974
if (pathIsInVfs(dir)) {
975975
try {
976976
BaseEntry entry = getEntry(dir);
@@ -984,9 +984,8 @@ public void setCurrentWorkingDirectory(Path dir) {
984984
if (delegate != null) {
985985
delegate.setCurrentWorkingDirectory(dir);
986986
} else {
987-
String msg = String.format("filesystem without host IO: '%s'", dir);
988-
finest("VFS.setCurrentWorkingDirectory %s", msg);
989-
throw new SecurityException(msg);
987+
// allow so that we can resolve relative paths pointing from real FS to VFS
988+
// {cwd}/real/fs/../ ... /../vfs_root
990989
}
991990
}
992991
cwd = dir;

0 commit comments

Comments
 (0)