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;
public interface Filesystem {
String cwd(String cwd, String path);
String normalize(String path);
File open(String path, Mode mode) throws FilesystemException;
void create(String path, EntryType type) throws FilesystemException;
FileStat stat(String path) throws FilesystemException;

View File

@ -5,6 +5,7 @@ import java.util.HashMap;
import java.util.HashSet;
import me.topchetoeu.jscript.Buffer;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
public class MemoryFilesystem implements Filesystem {
@ -12,66 +13,76 @@ public class MemoryFilesystem implements Filesystem {
private HashMap<Path, Buffer> files = new HashMap<>();
private HashSet<Path> folders = new HashSet<>();
private Path getPath(String name) {
return Path.of("/" + name.replace("\\", "/")).normalize();
private Path realPath(String path) {
return Filename.normalize(path);
}
@Override
public void create(String path, EntryType type) {
var _path = getPath(path);
public String normalize(String 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) {
case FILE:
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)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
files.put(_path, new Buffer());
if (!folders.contains(path.getParent())) throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST);
if (folders.contains(path) || files.containsKey(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
if (folders.contains(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
files.put(path, new Buffer());
break;
case FOLDER:
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);
folders.add(_path);
if (!folders.contains(path.getParent())) throw new FilesystemException(_path, FSCode.DOESNT_EXIST);
if (folders.contains(path) || files.containsKey(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
folders.add(path);
break;
default:
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
public File open(String path, Mode perms) {
var _path = getPath(path);
var pcount = _path.getNameCount();
public File open(String _path, Mode perms) {
var path = realPath(_path);
var pcount = path.getNameCount();
if (files.containsKey(_path)) return new MemoryFile(path, files.get(_path), perms);
else if (folders.contains(_path)) {
if (files.containsKey(path)) return new MemoryFile(path.toString(), files.get(path), perms);
else if (folders.contains(path)) {
var res = new StringBuilder();
for (var folder : folders) {
if (pcount + 1 != folder.getNameCount()) continue;
if (!folder.startsWith(_path)) continue;
if (!folder.startsWith(path)) continue;
res.append(folder.toFile().getName()).append('\n');
}
for (var file : files.keySet()) {
if (pcount + 1 != file.getNameCount()) continue;
if (!file.startsWith(_path)) continue;
if (!file.startsWith(path)) continue;
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
public FileStat stat(String path) {
var _path = getPath(path);
public FileStat stat(String _path) {
var path = realPath(_path);
if (files.containsKey(_path)) return new FileStat(mode, EntryType.FILE);
else if (folders.contains(_path)) return new FileStat(mode, EntryType.FOLDER);
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
if (files.containsKey(path)) return new FileStat(mode, EntryType.FILE);
else if (folders.contains(path)) return new FileStat(mode, EntryType.FOLDER);
else return new FileStat(Mode.NONE, EntryType.NONE);
}
public MemoryFilesystem put(String path, byte[] data) {
var _path = getPath(path);
var _path = realPath(path);
var _curr = "/";
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;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
public class PhysicalFilesystem implements Filesystem {
public final Path root;
private Path getPath(String name) {
var absolutized = Path.of("/" + name.replace("\\", "/")).normalize().toString();
var res = Path.of(root.toString() + absolutized).normalize();
return res;
}
public final String root;
private void checkMode(Path path, Mode mode) {
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);
if (mode.writable && !path.toFile().canWrite()) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW);
var stat = stat(path.toString());
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
public File open(String path, Mode perms) {
var _path = getPath(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); }
public String normalize(String path) {
return Paths.normalize(path);
}
@Override
public void create(String path, EntryType type) {
var _path = getPath(path);
var f = _path.toFile();
public String cwd(String cwd, String path) {
return Paths.cwd(cwd, path);
}
@Override
public File open(String _path, Mode perms) {
var path = realPath(_path);
checkMode(path, perms);
try {
if (Files.isDirectory(path)) return new ListFile(path);
else return new PhysicalFile(path.toString(), perms);
}
catch (IOException e) { throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST); }
}
@Override
public void create(String _path, EntryType type) {
var path = realPath(_path);
if (type == EntryType.NONE != Files.exists(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
try {
switch (type) {
case FILE:
try {
if (!f.createNewFile()) throw new FilesystemException(_path.toString(), FSCode.ALREADY_EXISTS);
else break;
}
catch (IOException e) { throw new FilesystemException(_path.toString(), FSCode.NO_PERMISSIONS_RW); }
Files.createFile(path);
break;
case FOLDER:
if (!f.mkdir()) throw new FilesystemException(_path.toString(), FSCode.ALREADY_EXISTS);
else break;
Files.createDirectories(path);
break;
case NONE:
default:
if (!f.delete()) throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST);
else break;
Files.delete(path);
}
}
catch (IOException e) { throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW); }
}
@Override
public FileStat stat(String path) {
var _path = getPath(path);
var f = _path.toFile();
public FileStat stat(String _path) {
var path = realPath(_path);
if (!f.exists()) throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST);
checkMode(_path, Mode.READ);
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(
f.canWrite() ? Mode.READ_WRITE : Mode.READ,
f.isFile() ? EntryType.FILE : EntryType.FOLDER
perms,
Files.isDirectory(path) ? EntryType.FOLDER : EntryType.FILE
);
}
public PhysicalFilesystem(Path root) {
this.root = root.toAbsolutePath().normalize();
public PhysicalFilesystem(String root) {
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);
}
@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 {
var filename = Filename.parse(path);
var protocol = protocols.get(filename.protocol);