package kpc.common.computer.fs; import kpc.api.fs.FileSystem; import kpc.api.fs.Mount; import kpc.api.fs.MountRegistry; import kpc.api.fs.WritableMount; import kpc.api.fs.io.InputStream; import kpc.api.fs.io.OutputStream; import java.io.IOException; import java.nio.file.Path; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; public final class Ext9001FileSystem implements FileSystem { @Override public OutputStream openOutputStream(String path) throws IOException { try{ path = this.sanitize(path, false); Mount m = this.getMount(path); return m instanceof WritableMount ? ((WritableMount) m).openOutputStream(path) : null; } catch(Exception e){ throw new RuntimeException(e); } } @Override public InputStream openInputStream(String path) throws IOException { try{ path = this.sanitize(path, false); Mount m = this.getMount(path); return m != null ? m.openInputStream(path) : null; } catch(Exception e){ throw new RuntimeException(e); } } @Override public boolean exists(String path) { try{ path = this.sanitize(path, false); Mount m = this.getMount(path); return m != null && m.exists(path); } catch(Exception e){ throw new RuntimeException(e); } } @Override public boolean isDirectory(String path) { try{ path = this.sanitize(path, false); Mount m = this.getMount(path); return m != null && m.isDirectory(path); } catch(Exception e){ throw new RuntimeException(e); } } @Override public boolean mv(String path, String to) { try{ path = this.sanitize(path, false); to = this.sanitize(to, false); Mount pathMount = this.getMount(path); Mount toMount = this.getMount(to); if(!(toMount instanceof WritableMount)){ return false; } try(java.io.InputStream in = pathMount.openInputStream(path).toInputStream(); java.io.OutputStream out = ((WritableMount) toMount).openOutputStream(to).toOutputStream()){ byte[] buffer = new byte[8192]; int len; while((len = in.read(buffer, 0, 8192)) != -1){ out.write(buffer, 0, len); } return this.rm(path); } } catch(Exception e){ throw new RuntimeException(e); } } @Override public boolean cp(String path, String to) { try{ path = this.sanitize(path, false); to = this.sanitize(to, false); Mount pathMount = this.getMount(path); Mount toMount = this.getMount(to); if(!(toMount instanceof WritableMount)){ return false; } try(java.io.InputStream in = pathMount.openInputStream(path).toInputStream(); java.io.OutputStream out = ((WritableMount) toMount).openOutputStream(to).toOutputStream()){ byte[] buffer = new byte[8192]; int len; while((len = in.read(buffer, 0, 8192)) != -1){ out.write(buffer, 0, len); } return true; } } catch(Exception e){ throw new RuntimeException(e); } } @Override public boolean rm(String path) { try{ path = this.sanitize(path, false); Mount m = this.getMount(path); return m instanceof WritableMount && ((WritableMount) m).rm(path); } catch(Exception e){ throw new RuntimeException(e); } } @Override public void list(String path, final List<String> list) throws IOException{ try{ path = this.sanitize(path, false); Mount m = this.getMount(path); m.list(path, list); for (Map.Entry<String, Mount> entry : MountRegistry.mounts()) { if (this.getDirectory(entry.getKey()) .equals(path)) { list.add(this.getName(entry.getKey())); } } } catch(Exception e){ throw new RuntimeException(e); } } private String getName(String path){ path = this.sanitize(path, true); if(path.isEmpty()){ return "/"; } int lastSlash = path.lastIndexOf('/'); if(lastSlash >= 0){ return path.substring(lastSlash + 1); } return path; } private String getDirectory(String path){ path = this.sanitize(path, true); if(path.isEmpty()){ return ".."; } int lastSlash = path.lastIndexOf('/'); if(lastSlash >= 0){ return path.substring(0, lastSlash); } return ""; } @Override public Path resolve(String path) { path = this.sanitize(path, false); Mount m = this.getMount(path); return m.resolve(path); } @Override public boolean mkdir(String path) { try{ path = this.sanitize(path, false); Mount m = this.getMount(path); return m instanceof WritableMount && ((WritableMount) m).mkdir(path); } catch(Exception e){ throw new RuntimeException(e); } } @Override public boolean touch(String path) { try { path = this.sanitize(path, false); Mount m = this.getMount(path); return m instanceof WritableMount && ((WritableMount) m).touch(path); } catch(Exception e){ throw new RuntimeException(e); } } private String sanitize(String path, boolean wildcards){ path = path.replace("\\", "/"); char[] specChars = { '"', ':', '<', '>', '?', '|'}; StringBuilder clean = new StringBuilder(); for(int i = 0; i < path.length(); i++){ char c = path.charAt(i); if((c >= ' ') && Arrays.binarySearch(specChars, c) < 0 && (wildcards || (c != '*'))){ clean.append(c); } } path = clean.toString(); String[] parts = path.split("/"); Stack<String> output = new Stack<>(); for (String part : parts) { if ((part.length() != 0) && (!part.equals("."))) { if (part.equals("..")) { if (!output.empty()) { String top = output.peek(); if (!top.equals("..")) { output.pop(); } else { output.push(".."); } } else { output.push(".."); } } else if (part.length() >= 255) { output.push(part.substring(0, 255)); } else { output.push(part); } } } StringBuilder res = new StringBuilder(""); Iterator<String> it = output.iterator(); while(it.hasNext()){ String part = it.next(); res.append(part); if(it.hasNext()){ res.append("/"); } } return res.toString(); } private Mount getMount(String path){ Iterator<Map.Entry<String, Mount>> it = MountRegistry.mounts().iterator(); int len = 0; Mount match = null; while(it.hasNext()){ Map.Entry<String, Mount> next = it.next(); if(contains(next.getKey(), path)){ int l = toLocal(path, next.getKey()).length(); if((match == null) || (l < len)){ match = next.getValue(); len = l; } } } if(match == null){ throw new IllegalArgumentException("Invalid path"); } return match; } private String toLocal(String path, String loc){ path = this.sanitize(path, false); loc = this.sanitize(loc, false); String local = path.substring(loc.length()); if(local.startsWith("/")){ return local.substring(1); } return local; } private boolean contains(String a, String b) { a = this.sanitize(a, false); b = this.sanitize(b, false); return !(b.equals("..") || b.startsWith("../")) && (b.equals(a) || a.isEmpty() || b.startsWith(a + "/")); } }