From 55e3d46bc2181be957fc50b0f641d8de66098a0c Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 25 Nov 2023 14:18:04 +0200 Subject: [PATCH] feat: implement memory and root fs --- .../topchetoeu/jscript/filesystem/Buffer.java | 41 +++++++++ .../jscript/filesystem/MemoryFile.java | 61 ++++++++----- .../jscript/filesystem/MemoryFilesystem.java | 89 +++++++++++++++++++ .../jscript/filesystem/RootFilesystem.java | 60 +++++++++++++ 4 files changed, 227 insertions(+), 24 deletions(-) create mode 100644 src/me/topchetoeu/jscript/filesystem/Buffer.java create mode 100644 src/me/topchetoeu/jscript/filesystem/MemoryFilesystem.java create mode 100644 src/me/topchetoeu/jscript/filesystem/RootFilesystem.java diff --git a/src/me/topchetoeu/jscript/filesystem/Buffer.java b/src/me/topchetoeu/jscript/filesystem/Buffer.java new file mode 100644 index 0000000..3a77157 --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/Buffer.java @@ -0,0 +1,41 @@ +package me.topchetoeu.jscript.filesystem; + +public class Buffer { + private byte[] data; + private int length; + + public void write(int i, byte[] val) { + if (i + val.length > data.length) { + var newCap = i + val.length + 1; + if (newCap < data.length * 2) newCap = data.length * 2; + + var tmp = new byte[newCap]; + System.arraycopy(this.data, 0, tmp, 0, length); + this.data = tmp; + } + + System.arraycopy(val, 0, data, i, val.length); + if (i + val.length > length) length = i + val.length; + } + public int read(int i, byte[] buff) { + int n = buff.length; + if (i + n > length) n = length - i; + System.arraycopy(data, i, buff, 0, n); + return n; + } + + public byte[] data() { + var res = new byte[length]; + System.arraycopy(this.data, 0, res, 0, length); + return res; + } + public int length() { + return length; + } + + public Buffer(byte[] data) { + this.data = new byte[data.length]; + this.length = data.length; + System.arraycopy(data, 0, this.data, 0, data.length); + } +} diff --git a/src/me/topchetoeu/jscript/filesystem/MemoryFile.java b/src/me/topchetoeu/jscript/filesystem/MemoryFile.java index c414a7e..0ca8425 100644 --- a/src/me/topchetoeu/jscript/filesystem/MemoryFile.java +++ b/src/me/topchetoeu/jscript/filesystem/MemoryFile.java @@ -1,53 +1,66 @@ package me.topchetoeu.jscript.filesystem; -import java.io.IOException; +import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode; public class MemoryFile implements File { private int ptr; - private Permissions mode; - public final byte[] data; + private Mode mode; + private Buffer data; + private String filename; + + public Buffer data() { return data; } @Override - public int read() throws IOException, InterruptedException { - if (data == null || !mode.readable || ptr >= data.length) return -1; - return data[ptr++]; + public int read(byte[] buff) { + if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); + var res = data.read(ptr, buff); + ptr += res; + return res; + } + @Override + public void write(byte[] buff) { + if (data == null || !mode.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW); + + data.write(ptr, buff); + ptr += buff.length; } @Override - public boolean write(byte val) throws IOException, InterruptedException { - if (data == null || !mode.writable || ptr >= data.length) return false; - data[ptr++] = val; - return true; - } - - @Override - public long tell() throws IOException, InterruptedException { + public long getPtr() { + if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); return ptr; } - @Override - public void seek(long offset, int pos) throws IOException, InterruptedException { - if (data == null) return; + public void setPtr(long offset, int pos) { + if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); if (pos == 0) ptr = (int)offset; else if (pos == 1) ptr += (int)offset; - else if (pos == 2) ptr = data.length - (int)offset; + else if (pos == 2) ptr = data.length() - (int)offset; } @Override - public void close() throws IOException, InterruptedException { - mode = null; + public void close() { + mode = Mode.NONE; ptr = 0; } - @Override - public Permissions perms() { - if (data == null) return Permissions.NONE; + public Mode mode() { + if (data == null) return Mode.NONE; return mode; } - public MemoryFile(byte[] buff, Permissions mode) { + public MemoryFile(String filename, Buffer buff, Mode mode) { + this.filename = filename; this.data = buff; this.mode = mode; } + + public static MemoryFile fromFileList(String filename, java.io.File[] list) { + var res = new StringBuilder(); + + for (var el : list) res.append(el.getName()).append('\n'); + + return new MemoryFile(filename, new Buffer(res.toString().getBytes()), Mode.READ); + } } diff --git a/src/me/topchetoeu/jscript/filesystem/MemoryFilesystem.java b/src/me/topchetoeu/jscript/filesystem/MemoryFilesystem.java new file mode 100644 index 0000000..7db85db --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/MemoryFilesystem.java @@ -0,0 +1,89 @@ +package me.topchetoeu.jscript.filesystem; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.HashSet; + +import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode; + +public class MemoryFilesystem implements Filesystem { + public final Mode mode; + private HashMap files = new HashMap<>(); + private HashSet folders = new HashSet<>(); + + private Path getPath(String name) { + return Path.of("/" + name.replace("\\", "/")).normalize(); + } + + @Override + public void create(String path, EntryType type) { + var _path = getPath(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(new byte[0])); + 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); + break; + default: + case NONE: + if (!folders.remove(_path) && files.remove(_path) == null) throw new FilesystemException(path, FSCode.DOESNT_EXIST); + } + } + + @Override + public File open(String path, Mode perms) { + var _path = getPath(path); + var pcount = _path.getNameCount(); + + if (files.containsKey(_path)) return new MemoryFile(path, 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; + res.append(folder.toFile().getName()).append('\n'); + } + for (var file : files.keySet()) { + if (pcount + 1 != file.getNameCount()) 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)); + } + else throw new FilesystemException(path, FSCode.DOESNT_EXIST); + } + + @Override + public FileStat stat(String path) { + var _path = getPath(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); + } + + public MemoryFilesystem put(String path, byte[] data) { + var _path = getPath(path); + var _curr = "/"; + + for (var seg : _path) { + create(_curr, EntryType.FOLDER); + _curr += seg + "/"; + } + + files.put(_path, new Buffer(data)); + return this; + } + + public MemoryFilesystem(Mode mode) { + this.mode = mode; + folders.add(Path.of("/")); + } +} diff --git a/src/me/topchetoeu/jscript/filesystem/RootFilesystem.java b/src/me/topchetoeu/jscript/filesystem/RootFilesystem.java new file mode 100644 index 0000000..ba9296a --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/RootFilesystem.java @@ -0,0 +1,60 @@ +package me.topchetoeu.jscript.filesystem; + +import java.util.HashMap; +import java.util.Map; + +import me.topchetoeu.jscript.Filename; +import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode; +import me.topchetoeu.jscript.permissions.PermissionsManager; + +public class RootFilesystem implements Filesystem { + public final Map protocols = new HashMap<>(); + public final PermissionsManager perms; + + private boolean canRead(PermissionsManager perms, String _path) { + return perms.has("jscript.file.read:" + _path, '/'); + } + private boolean canWrite(PermissionsManager perms, String _path) { + return perms.has("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); + } + + @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); + modeAllowed(filename.toString(), perms); + + 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 { + var filename = Filename.parse(path); + var protocol = protocols.get(filename.protocol); + if (protocol == null) throw new FilesystemException(filename.toString(), FSCode.DOESNT_EXIST); + modeAllowed(filename.toString(), Mode.READ_WRITE); + + try { protocol.create(filename.path, type); } + catch (FilesystemException e) { throw new FilesystemException(filename.toString(), e.code); } + } + @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); + modeAllowed(filename.toString(), Mode.READ); + + try { return protocol.stat(path); } + catch (FilesystemException e) { throw new FilesystemException(filename.toString(), e.code); } + } + + public RootFilesystem(PermissionsManager perms) { + this.perms = perms; + } +}