feat: properly implement filesystems

This commit is contained in:
TopchetoEU 2023-11-25 19:36:18 +02:00
parent 567eaa8514
commit f52f47cdb4
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
9 changed files with 81 additions and 64 deletions

View File

@ -18,8 +18,7 @@ import me.topchetoeu.jscript.exceptions.InterruptException;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.filesystem.MemoryFilesystem;
import me.topchetoeu.jscript.filesystem.Mode;
import me.topchetoeu.jscript.filesystem.RootFilesystem;
import me.topchetoeu.jscript.lib.FilesystemLib;
import me.topchetoeu.jscript.filesystem.PhysicalFilesystem;
import me.topchetoeu.jscript.lib.Internals;
public class Main {
@ -117,10 +116,8 @@ public class Main {
}
});
var fs = new RootFilesystem(null);
fs.protocols.put("file", new MemoryFilesystem(Mode.READ_WRITE));
environment.global.define((Context)null, "fs", false, new FilesystemLib(fs));
environment.filesystem.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE));
environment.filesystem.protocols.put("file", new PhysicalFilesystem(Path.of(".").toAbsolutePath()));
}
private static void initEngine() {
debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine));

View File

@ -8,13 +8,15 @@ import me.topchetoeu.jscript.engine.values.NativeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Symbol;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.filesystem.RootFilesystem;
import me.topchetoeu.jscript.interop.Native;
import me.topchetoeu.jscript.interop.NativeGetter;
import me.topchetoeu.jscript.interop.NativeSetter;
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
import me.topchetoeu.jscript.permissions.PermissionsManager;
import me.topchetoeu.jscript.permissions.Permission;
import me.topchetoeu.jscript.permissions.PermissionsProvider;
public class Environment {
public class Environment implements PermissionsProvider {
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
public final Data data = new Data();
@ -22,7 +24,8 @@ public class Environment {
public GlobalScope global;
public WrappersProvider wrappers;
public PermissionsManager permissions;
public PermissionsProvider permissions = null;
public final RootFilesystem filesystem = new RootFilesystem(this);
private static int nextId = 0;
@ -74,6 +77,13 @@ public class Environment {
return res;
}
@Override public boolean hasPermission(Permission perm, char delim) {
return permissions == null || permissions.hasPermission(perm, delim);
}
@Override public boolean hasPermission(Permission perm) {
return permissions == null || permissions.hasPermission(perm);
}
public Context context(Engine engine) {
return new Context(engine).pushEnv(this);
}

View File

@ -60,7 +60,7 @@ public class PhysicalFilesystem implements Filesystem {
var _path = getPath(path);
var f = _path.toFile();
if (f.exists()) throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST);
if (!f.exists()) throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST);
checkMode(_path, Mode.READ);
return new FileStat(

View File

@ -5,26 +5,25 @@ import java.util.Map;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
import me.topchetoeu.jscript.permissions.PermissionsManager;
import me.topchetoeu.jscript.permissions.PermissionsProvider;
public class RootFilesystem implements Filesystem {
public final Map<String, Filesystem> protocols = new HashMap<>();
public final PermissionsManager perms;
public final PermissionsProvider perms;
private boolean canRead(PermissionsManager perms, String _path) {
return perms.has("jscript.file.read:" + _path, '/');
private boolean canRead(String _path) {
return perms.hasPermission("jscript.file.read:" + _path, '/');
}
private boolean canWrite(PermissionsManager perms, String _path) {
return perms.has("jscript.file.write:" + _path, '/');
private boolean canWrite(String _path) {
return perms.hasPermission("jscript.file.write:" + _path, '/');
}
private void modeAllowed(String _path, Mode mode) throws FilesystemException {
if (mode.readable && perms != null && !canRead(perms, _path)) throw new FilesystemException(_path, FSCode.NO_PERMISSIONS_R);
if (mode.writable && perms != null && !canWrite(perms, _path)) throw new FilesystemException(_path, FSCode.NO_PERMISSIONS_RW);
if (mode.readable && perms != null && !canRead(_path)) throw new FilesystemException(_path, FSCode.NO_PERMISSIONS_R);
if (mode.writable && perms != null && !canWrite(_path)) throw new FilesystemException(_path, FSCode.NO_PERMISSIONS_RW);
}
@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 protocol = protocols.get(filename.protocol);
if (protocol == null) throw new FilesystemException(filename.toString(), FSCode.DOESNT_EXIST);
@ -33,8 +32,7 @@ public class RootFilesystem implements Filesystem {
try { return protocol.open(filename.path, perms); }
catch (FilesystemException e) { throw new FilesystemException(filename.toString(), e.code); }
}
@Override
public void create(String path, EntryType type) throws FilesystemException {
@Override public void create(String path, EntryType type) throws FilesystemException {
var filename = Filename.parse(path);
var protocol = protocols.get(filename.protocol);
if (protocol == null) throw new FilesystemException(filename.toString(), FSCode.DOESNT_EXIST);
@ -43,8 +41,7 @@ public class RootFilesystem implements Filesystem {
try { protocol.create(filename.path, type); }
catch (FilesystemException e) { throw new FilesystemException(filename.toString(), e.code); }
}
@Override
public FileStat stat(String path) throws FilesystemException {
@Override public FileStat stat(String path) throws FilesystemException {
var filename = Filename.parse(path);
var protocol = protocols.get(filename.protocol);
if (protocol == null) throw new FilesystemException(filename.toString(), FSCode.DOESNT_EXIST);
@ -54,7 +51,7 @@ public class RootFilesystem implements Filesystem {
catch (FilesystemException e) { throw new FilesystemException(filename.toString(), e.code); }
}
public RootFilesystem(PermissionsManager perms) {
public RootFilesystem(PermissionsProvider perms) {
this.perms = perms;
}
}

View File

@ -538,7 +538,7 @@ declare var Symbol: SymbolConstructor;
declare var Promise: PromiseConstructor;
declare var Math: MathObject;
declare var Encoding: Encoding;
declare var fs: Filesystem;
declare var Filesystem: Filesystem;
declare var Error: ErrorConstructor;
declare var RangeError: RangeErrorConstructor;

View File

@ -8,36 +8,44 @@ import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.filesystem.EntryType;
import me.topchetoeu.jscript.filesystem.File;
import me.topchetoeu.jscript.filesystem.FileStat;
import me.topchetoeu.jscript.filesystem.Filesystem;
import me.topchetoeu.jscript.filesystem.FilesystemException;
import me.topchetoeu.jscript.filesystem.Mode;
import me.topchetoeu.jscript.filesystem.RootFilesystem;
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
import me.topchetoeu.jscript.interop.Native;
@Native("Filesystem")
public class FilesystemLib {
public final RootFilesystem fs;
private static Filesystem fs(Context ctx) {
var env = ctx.environment();
if (env != null) {
var fs = ctx.environment().filesystem;
if (fs != null) return fs;
}
throw EngineException.ofError("Current environment doesn't have a file system.");
}
@Native public PromiseLib open(Context ctx, String _path, String mode) {
@Native public static PromiseLib open(Context ctx, String _path, String mode) {
var filename = Filename.parse(_path);
var _mode = Mode.parse(mode);
return PromiseLib.await(ctx, () -> {
try {
if (fs.stat(filename.path).type != EntryType.FILE) {
if (fs(ctx).stat(filename.path).type != EntryType.FILE) {
throw new FilesystemException(filename.toString(), FSCode.NOT_FILE);
}
var file = fs.open(filename.path, _mode);
var file = fs(ctx).open(filename.path, _mode);
return new FileLib(file);
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Native public ObjectValue ls(Context ctx, String _path) throws IOException {
@Native public static ObjectValue ls(Context ctx, String _path) throws IOException {
var filename = Filename.parse(_path);
return Values.toJSAsyncIterator(ctx, new Iterator<>() {
@ -49,11 +57,11 @@ public class FilesystemLib {
if (done) return;
if (!failed) {
if (file == null) {
if (fs.stat(filename.path).type != EntryType.FOLDER) {
if (fs(ctx).stat(filename.path).type != EntryType.FOLDER) {
throw new FilesystemException(filename.toString(), FSCode.NOT_FOLDER);
}
file = fs.open(filename.path, Mode.READ);
file = fs(ctx).open(filename.path, Mode.READ);
}
if (nextLine == null) {
@ -90,29 +98,29 @@ public class FilesystemLib {
}
});
}
@Native public PromiseLib mkdir(Context ctx, String _path) throws IOException {
@Native public static PromiseLib mkdir(Context ctx, String _path) throws IOException {
return PromiseLib.await(ctx, () -> {
try {
fs.create(Filename.parse(_path).toString(), EntryType.FOLDER);
fs(ctx).create(Filename.parse(_path).toString(), EntryType.FOLDER);
return null;
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Native public PromiseLib mkfile(Context ctx, String _path) throws IOException {
@Native public static PromiseLib mkfile(Context ctx, String _path) throws IOException {
return PromiseLib.await(ctx, () -> {
try {
fs.create(Filename.parse(_path).toString(), EntryType.FILE);
fs(ctx).create(Filename.parse(_path).toString(), EntryType.FILE);
return null;
}
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Native public PromiseLib rm(Context ctx, String _path, boolean recursive) throws IOException {
@Native public static PromiseLib rm(Context ctx, String _path, boolean recursive) throws IOException {
return PromiseLib.await(ctx, () -> {
try {
if (!recursive) fs.create(Filename.parse(_path).toString(), EntryType.NONE);
if (!recursive) fs(ctx).create(Filename.parse(_path).toString(), EntryType.NONE);
else {
var stack = new Stack<String>();
stack.push(_path);
@ -121,13 +129,13 @@ public class FilesystemLib {
var path = Filename.parse(stack.pop()).toString();
FileStat stat;
try { stat = fs.stat(path); }
try { stat = fs(ctx).stat(path); }
catch (FilesystemException e) { continue; }
if (stat.type == EntryType.FOLDER) {
for (var el : fs.open(path, Mode.READ).readToString().split("\n")) stack.push(el);
for (var el : fs(ctx).open(path, Mode.READ).readToString().split("\n")) stack.push(el);
}
else fs.create(path, EntryType.NONE);
else fs(ctx).create(path, EntryType.NONE);
}
}
return null;
@ -135,10 +143,10 @@ public class FilesystemLib {
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Native public PromiseLib stat(Context ctx, String _path) throws IOException {
@Native public static PromiseLib stat(Context ctx, String _path) throws IOException {
return PromiseLib.await(ctx, () -> {
try {
var stat = fs.stat(_path);
var stat = fs(ctx).stat(_path);
var res = new ObjectValue();
res.defineProperty(ctx, "type", stat.type.name);
@ -148,14 +156,10 @@ public class FilesystemLib {
catch (FilesystemException e) { throw e.toEngineException(); }
});
}
@Native public PromiseLib exists(Context ctx, String _path) throws IOException {
@Native public static PromiseLib exists(Context ctx, String _path) throws IOException {
return PromiseLib.await(ctx, () -> {
try { fs.stat(_path); return true; }
try { fs(ctx).stat(_path); return true; }
catch (FilesystemException e) { return false; }
});
}
public FilesystemLib(RootFilesystem fs) {
this.fs = fs;
}
}

View File

@ -120,6 +120,7 @@ public class Internals {
glob.define(null, "Math", false, wp.getNamespace(MathLib.class));
glob.define(null, "JSON", false, wp.getNamespace(JSONLib.class));
glob.define(null, "Encoding", false, wp.getNamespace(EncodingLib.class));
glob.define(null, "Filesystem", false, wp.getNamespace(FilesystemLib.class));
glob.define(null, "Date", false, wp.getConstr(DateLib.class));
glob.define(null, "Object", false, wp.getConstr(ObjectLib.class));

View File

@ -2,38 +2,33 @@ package me.topchetoeu.jscript.permissions;
import java.util.ArrayList;
public class PermissionsManager {
public static final PermissionsManager ALL_PERMS = new PermissionsManager().add(new Permission("**"));
public class PermissionsManager implements PermissionsProvider {
public static final PermissionsProvider ALL_PERMS = new PermissionsManager().add(new Permission("**"));
public final ArrayList<Permission> allowed = new ArrayList<>();
public final ArrayList<Permission> denied = new ArrayList<>();
public PermissionsManager add(Permission perm) {
public PermissionsProvider add(Permission perm) {
allowed.add(perm);
return this;
}
public PermissionsManager add(String perm) {
public PermissionsProvider add(String perm) {
allowed.add(new Permission(perm));
return this;
}
public boolean has(Permission perm, char delim) {
@Override
public boolean hasPermission(Permission perm, char delim) {
for (var el : denied) if (el.match(perm, delim)) return false;
for (var el : allowed) if (el.match(perm, delim)) return true;
return false;
}
public boolean has(Permission perm) {
@Override
public boolean hasPermission(Permission perm) {
for (var el : denied) if (el.match(perm)) return false;
for (var el : allowed) if (el.match(perm)) return true;
return false;
}
public boolean has(String perm, char delim) {
return has(new Permission(perm), delim);
}
public boolean has(String perm) {
return has(new Permission(perm));
}
}

View File

@ -0,0 +1,13 @@
package me.topchetoeu.jscript.permissions;
public interface PermissionsProvider {
boolean hasPermission(Permission perm, char delim);
boolean hasPermission(Permission perm);
default boolean hasPermission(String perm, char delim) {
return hasPermission(new Permission(perm), delim);
}
default boolean hasPermission(String perm) {
return hasPermission(new Permission(perm));
}
}