fix: improve path resolutions and FS API

This commit is contained in:
TopchetoEU 2023-12-26 11:27:40 +02:00
parent 1c64912786
commit 8e01db637b
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
5 changed files with 165 additions and 73 deletions

View File

@ -1,6 +1,8 @@
package me.topchetoeu.jscript.filesystem; package me.topchetoeu.jscript.filesystem;
public interface Filesystem { public interface Filesystem {
String cwd(String cwd, String path);
String normalize(String path);
File open(String path, Mode mode) throws FilesystemException; File open(String path, Mode mode) throws FilesystemException;
void create(String path, EntryType type) throws FilesystemException; void create(String path, EntryType type) throws FilesystemException;
FileStat stat(String path) throws FilesystemException; FileStat stat(String path) throws FilesystemException;

View File

@ -5,6 +5,7 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import me.topchetoeu.jscript.Buffer; import me.topchetoeu.jscript.Buffer;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode; import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
public class MemoryFilesystem implements Filesystem { public class MemoryFilesystem implements Filesystem {
@ -12,66 +13,76 @@ public class MemoryFilesystem implements Filesystem {
private HashMap<Path, Buffer> files = new HashMap<>(); private HashMap<Path, Buffer> files = new HashMap<>();
private HashSet<Path> folders = new HashSet<>(); private HashSet<Path> folders = new HashSet<>();
private Path getPath(String name) { private Path realPath(String path) {
return Path.of("/" + name.replace("\\", "/")).normalize(); return Filename.normalize(path);
} }
@Override @Override
public void create(String path, EntryType type) { public String normalize(String path) {
var _path = getPath(path); return Paths.normalize(path);
}
@Override
public String cwd(String cwd, String path) {
return Paths.cwd(cwd, path);
}
@Override
public void create(String _path, EntryType type) {
var path = realPath(_path);
switch (type) { switch (type) {
case FILE: case FILE:
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST); if (!folders.contains(path.getParent())) throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST);
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS); if (folders.contains(path) || files.containsKey(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
if (folders.contains(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS); if (folders.contains(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
files.put(_path, new Buffer()); files.put(path, new Buffer());
break; break;
case FOLDER: case FOLDER:
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST); if (!folders.contains(path.getParent())) throw new FilesystemException(_path, FSCode.DOESNT_EXIST);
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS); if (folders.contains(path) || files.containsKey(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
folders.add(_path); folders.add(path);
break; break;
default: default:
case NONE: case NONE:
if (!folders.remove(_path) && files.remove(_path) == null) throw new FilesystemException(path, FSCode.DOESNT_EXIST); if (!folders.remove(path) && files.remove(path) == null) throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST);
} }
} }
@Override @Override
public File open(String path, Mode perms) { public File open(String _path, Mode perms) {
var _path = getPath(path); var path = realPath(_path);
var pcount = _path.getNameCount(); var pcount = path.getNameCount();
if (files.containsKey(_path)) return new MemoryFile(path, files.get(_path), perms); if (files.containsKey(path)) return new MemoryFile(path.toString(), files.get(path), perms);
else if (folders.contains(_path)) { else if (folders.contains(path)) {
var res = new StringBuilder(); var res = new StringBuilder();
for (var folder : folders) { for (var folder : folders) {
if (pcount + 1 != folder.getNameCount()) continue; if (pcount + 1 != folder.getNameCount()) continue;
if (!folder.startsWith(_path)) continue; if (!folder.startsWith(path)) continue;
res.append(folder.toFile().getName()).append('\n'); res.append(folder.toFile().getName()).append('\n');
} }
for (var file : files.keySet()) { for (var file : files.keySet()) {
if (pcount + 1 != file.getNameCount()) continue; if (pcount + 1 != file.getNameCount()) continue;
if (!file.startsWith(_path)) continue; if (!file.startsWith(path)) continue;
res.append(file.toFile().getName()).append('\n'); res.append(file.toFile().getName()).append('\n');
} }
return new MemoryFile(path, new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ)); return new MemoryFile(path.toString(), new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ));
} }
else throw new FilesystemException(path, FSCode.DOESNT_EXIST); else throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST);
} }
@Override @Override
public FileStat stat(String path) { public FileStat stat(String _path) {
var _path = getPath(path); var path = realPath(_path);
if (files.containsKey(_path)) return new FileStat(mode, EntryType.FILE); if (files.containsKey(path)) return new FileStat(mode, EntryType.FILE);
else if (folders.contains(_path)) return new FileStat(mode, EntryType.FOLDER); else if (folders.contains(path)) return new FileStat(mode, EntryType.FOLDER);
else throw new FilesystemException(path, FSCode.DOESNT_EXIST); else return new FileStat(Mode.NONE, EntryType.NONE);
} }
public MemoryFilesystem put(String path, byte[] data) { public MemoryFilesystem put(String path, byte[] data) {
var _path = getPath(path); var _path = realPath(path);
var _curr = "/"; var _curr = "/";
for (var seg : _path) { for (var seg : _path) {

View File

@ -0,0 +1,51 @@
package me.topchetoeu.jscript.filesystem;
import java.util.ArrayList;
public class Paths {
public static String normalize(String path) {
var parts = path.split("[\\\\/]");
var res = new ArrayList<String>();
for (var part : parts) {
if (part.equals("...")) res.clear();
else if (part.equals("..")) {
if (res.size() > 0) res.remove(res.size() - 1);
}
else if (!part.equals(".")) res.add(part);
}
var sb = new StringBuilder();
for (var el : res) sb.append("/").append(el);
return sb.toString();
}
public static String chroot(String root, String path) {
return normalize(root) + normalize(path);
}
public static String cwd(String cwd, String path) {
return normalize(cwd + "/" + path);
}
public static String filename(String path) {
var i = path.lastIndexOf('/');
if (i < 0) i = path.lastIndexOf('\\');
if (i < 0) return path;
else return path.substring(i + 1);
}
public static String extension(String path) {
var i = path.lastIndexOf('.');
if (i < 0) return "";
else return path.substring(i + 1);
}
public static String dir(String path) {
return normalize(path + "/..");
}
}

View File

@ -1,76 +1,92 @@
package me.topchetoeu.jscript.filesystem; package me.topchetoeu.jscript.filesystem;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode; import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
public class PhysicalFilesystem implements Filesystem { public class PhysicalFilesystem implements Filesystem {
public final Path root; public final String root;
private Path getPath(String name) {
var absolutized = Path.of("/" + name.replace("\\", "/")).normalize().toString();
var res = Path.of(root.toString() + absolutized).normalize();
return res;
}
private void checkMode(Path path, Mode mode) { private void checkMode(Path path, Mode mode) {
if (!path.startsWith(root)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R); if (!path.startsWith(root)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R);
if (mode.readable && !path.toFile().canRead()) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R); var stat = stat(path.toString());
if (mode.writable && !path.toFile().canWrite()) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW); if (mode.readable && !stat.mode.readable) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R);
if (mode.writable && !stat.mode.writable) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW);
}
private Path realPath(String path) {
return Path.of(Paths.chroot(root, path));
} }
@Override @Override
public File open(String path, Mode perms) { public String normalize(String path) {
var _path = getPath(path); return Paths.normalize(path);
var f = _path.toFile();
checkMode(_path, perms);
if (f.isDirectory()) return MemoryFile.fromFileList(path, f.listFiles());
else try { return new PhysicalFile(path, perms); }
catch (FileNotFoundException e) { throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST); }
} }
@Override @Override
public void create(String path, EntryType type) { public String cwd(String cwd, String path) {
var _path = getPath(path); return Paths.cwd(cwd, path);
var f = _path.toFile(); }
switch (type) { @Override
case FILE: public File open(String _path, Mode perms) {
try { var path = realPath(_path);
if (!f.createNewFile()) throw new FilesystemException(_path.toString(), FSCode.ALREADY_EXISTS);
else break; checkMode(path, perms);
}
catch (IOException e) { throw new FilesystemException(_path.toString(), FSCode.NO_PERMISSIONS_RW); } try {
case FOLDER: if (Files.isDirectory(path)) return new ListFile(path);
if (!f.mkdir()) throw new FilesystemException(_path.toString(), FSCode.ALREADY_EXISTS); else return new PhysicalFile(path.toString(), perms);
else break;
case NONE:
default:
if (!f.delete()) throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST);
else break;
} }
catch (IOException e) { throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST); }
} }
@Override @Override
public FileStat stat(String path) { public void create(String _path, EntryType type) {
var _path = getPath(path); var path = realPath(_path);
var f = _path.toFile();
if (!f.exists()) throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST); if (type == EntryType.NONE != Files.exists(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
checkMode(_path, Mode.READ);
try {
switch (type) {
case FILE:
Files.createFile(path);
break;
case FOLDER:
Files.createDirectories(path);
break;
case NONE:
default:
Files.delete(path);
}
}
catch (IOException e) { throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW); }
}
@Override
public FileStat stat(String _path) {
var path = realPath(_path);
if (!Files.exists(path)) return new FileStat(Mode.NONE, EntryType.NONE);
var perms = Mode.NONE;
if (Files.isReadable(path)) {
if (Files.isWritable(path)) perms = Mode.READ_WRITE;
else perms = Mode.READ;
}
if (perms == Mode.NONE) return new FileStat(Mode.NONE, EntryType.NONE);
return new FileStat( return new FileStat(
f.canWrite() ? Mode.READ_WRITE : Mode.READ, perms,
f.isFile() ? EntryType.FILE : EntryType.FOLDER Files.isDirectory(path) ? EntryType.FOLDER : EntryType.FILE
); );
} }
public PhysicalFilesystem(Path root) { public PhysicalFilesystem(String root) {
this.root = root.toAbsolutePath().normalize(); this.root = Paths.normalize(Path.of(root).toAbsolutePath().toString());
} }
} }

View File

@ -23,6 +23,18 @@ public class RootFilesystem implements Filesystem {
if (mode.writable && perms != null && !canWrite(_path)) throw new FilesystemException(_path, FSCode.NO_PERMISSIONS_RW); if (mode.writable && perms != null && !canWrite(_path)) throw new FilesystemException(_path, FSCode.NO_PERMISSIONS_RW);
} }
@Override public String normalize(String path) {
var filename = Filename.parse(path);
var protocol = protocols.get(filename.protocol);
if (protocol == null) return filename.toString();
else return new Filename(filename.protocol, protocol.normalize(filename.path)).toString();
}
@Override public String cwd(String cwd, String path) {
var filename = Filename.parse(cwd);
var protocol = protocols.get(filename.protocol);
if (protocol == null) return filename.toString();
else return new Filename(filename.protocol, protocol.cwd(filename.path, path)).toString();
}
@Override public File open(String path, Mode perms) throws FilesystemException { @Override public File open(String path, Mode perms) throws FilesystemException {
var filename = Filename.parse(path); var filename = Filename.parse(path);
var protocol = protocols.get(filename.protocol); var protocol = protocols.get(filename.protocol);