X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fnestedvm%2FUnixRuntime.java;h=a4b1991dde398d7182d6c2b05b1d0ce1a9992c3d;hp=5b47219b2f784b4b40299b7c21c23b687725bdde;hb=615390526d6598c4124370fdd77b55894413cd1d;hpb=ef1c27c7eff6c432f3ad7a21671947b87539acb2 diff --git a/src/org/ibex/nestedvm/UnixRuntime.java b/src/org/ibex/nestedvm/UnixRuntime.java index 5b47219..a4b1991 100644 --- a/src/org/ibex/nestedvm/UnixRuntime.java +++ b/src/org/ibex/nestedvm/UnixRuntime.java @@ -3,25 +3,21 @@ package org.ibex.nestedvm; import org.ibex.nestedvm.util.*; import java.io.*; import java.util.*; +import java.net.*; -// FEATURE: BusyBox's ASH doesn't like \r\n at the end of lines -// is ash just broken or are many apps like this? if so workaround in nestedvm - -// FEATURE: Throw ErrnoException and catch in syscall whereever possible -// (only in cases where we've already paid the price for a throw) +// FEATURE: vfork public abstract class UnixRuntime extends Runtime implements Cloneable { /** The pid of this "process" */ private int pid; - private int ppid; - protected int getPid() { return pid; } + private UnixRuntime parent; + public final int getPid() { return pid; } - /** processes filesystem */ - private FS fs; - public FS getFS() { return fs; } - public void setFS(FS fs) { - if(state != STOPPED) throw new IllegalStateException("Can't change fs while process is running"); - this.fs = fs; + private static final GlobalState defaultGS = new GlobalState(); + private GlobalState gs = defaultGS; + public void setGlobalState(GlobalState gs) { + if(state != STOPPED) throw new IllegalStateException("can't change GlobalState when running"); + this.gs = gs; } /** proceses' current working directory - absolute path WITHOUT leading slash @@ -30,58 +26,33 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { /** The runtime that should be run next when in state == EXECED */ private UnixRuntime execedRuntime; + + private Object children; // used only for synchronizatin + private Vector activeChildren; + private Vector exitedChildren; - /* Static stuff */ - // FEATURE: Most of this is O(n) or worse - fix it - private final Object waitNotification = new Object(); - private final static int MAX_TASKS = 256; - private final static UnixRuntime[] tasks = new UnixRuntime[MAX_TASKS]; - private static int addTask(UnixRuntime rt) { - synchronized(tasks) { - for(int i=1;i 0) sb.append("-"); else off = -off; sb.append(off/3600); off = off%3600; if(off > 0) sb.append(":").append(off/60); off=off%60; if(off > 0) sb.append(":").append(off); if(zone.useDaylightTime()) - sb.append(zone.getDisplayName(true,TimeZone.SHORT)); + sb.append(Platform.timeZoneGetDisplayName(zone,true,false)); return sb.toString(); } @@ -91,65 +62,91 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { return false; } - protected String[] createEnv(String[] extra) { - String[] defaults = new String[5]; + String[] createEnv(String[] extra) { + String[] defaults = new String[6]; int n=0; if(extra == null) extra = new String[0]; if(!envHas("USER",extra) && getSystemProperty("user.name") != null) defaults[n++] = "USER=" + getSystemProperty("user.name"); - if(!envHas("HOME",extra) && getSystemProperty("user.name") != null) + if(!envHas("HOME",extra) && getSystemProperty("user.home") != null) defaults[n++] = "HOME=" + getSystemProperty("user.home"); if(!envHas("SHELL",extra)) defaults[n++] = "SHELL=/bin/sh"; if(!envHas("TERM",extra)) defaults[n++] = "TERM=vt100"; if(!envHas("TZ",extra)) defaults[n++] = "TZ=" + posixTZ(); + if(!envHas("PATH",extra)) defaults[n++] = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin"; String[] env = new String[extra.length+n]; for(int i=0;i= MAX_TASKS)) return -ECHILD; - for(;;) { - synchronized(tasks) { - UnixRuntime task = null; + if(pid == 0 || pid < -1) { + if(STDERR_DIAG) System.err.println("WARNING: waitpid called with a pid of " + pid); + return -ECHILD; + } + boolean blocking = (options&WNOHANG)==0; + + if(pid !=-1 && (pid <= 0 || pid >= gs.tasks.length)) return -ECHILD; + if(children == null) return blocking ? -ECHILD : 0; + + UnixRuntime done = null; + + synchronized(children) { + for(;;) { if(pid == -1) { - for(int i=0;i 0) { + done = (UnixRuntime)exitedChildren.elementAt(exitedChildren.size() - 1); + exitedChildren.removeElementAt(exitedChildren.size() - 1); } - } else if(tasks[pid] != null && tasks[pid].ppid == this.pid && tasks[pid].state == EXITED) { - task = tasks[pid]; - } - - if(task != null) { - removeTask(task); - try { - if(statusAddr!=0) memWrite(statusAddr,task.exitStatus()<<8); - } catch(FaultException e) { - return -EFAULT; + } else if(pid > 0) { + UnixRuntime t = gs.tasks[pid]; + if(t.parent != this) return -ECHILD; + if(t.state == EXITED) { + if(!exitedChildren.removeElement(t)) throw new Error("should never happen"); + done = t; } - - return task.pid; + } else { + // process group stuff, EINVAL returned above + throw new Error("should never happen"); + } + if(done == null) { + if(!blocking) return 0; + try { children.wait(); } catch(InterruptedException e) {} + //System.err.println("waitpid woke up: " + exitedChildren.size()); + } else { + gs.tasks[done.pid] = null; + break; } } - if((options&WNOHANG)!=0) return 0; - synchronized(waitNotification) { - try { waitNotification.wait(); } catch(InterruptedException e) { /* ignore */ } + } + if(statusAddr!=0) memWrite(statusAddr,done.exitStatus()<<8); + return done.pid; + } + + + void _exited() { + if(children != null) synchronized(children) { + for(Enumeration e = exitedChildren.elements(); e.hasMoreElements(); ) { + UnixRuntime child = (UnixRuntime) e.nextElement(); + gs.tasks[child.pid] = null; + } + exitedChildren.removeAllElements(); + for(Enumeration e = activeChildren.elements(); e.hasMoreElements(); ) { + UnixRuntime child = (UnixRuntime) e.nextElement(); + child.parent = null; + } + activeChildren.removeAllElements(); + } + + UnixRuntime _parent = parent; + if(_parent == null) { + gs.tasks[pid] = null; + } else { + synchronized(_parent.children) { + if(parent == null) { + gs.tasks[pid] = null; + } else { + if(!parent.activeChildren.removeElement(this)) throw new Error("should never happen _exited: pid: " + pid); + parent.exitedChildren.addElement(this); + parent.children.notify(); + } } } } protected Object clone() throws CloneNotSupportedException { - UnixRuntime r = (UnixRuntime) super.clone(); - r.pid = r.ppid = 0; + UnixRuntime r = (UnixRuntime) super.clone(); + r.pid = 0; + r.parent = null; + r.children = null; + r.activeChildren = r.exitedChildren = null; return r; } @@ -232,39 +274,47 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { e.printStackTrace(); return -ENOMEM; } - - int childPID = addTask(r); - if(childPID < 0) return -ENOMEM; - - r.ppid = pid; + + r.parent = this; + + try { + r._started(); + } catch(ProcessTableFullExn e) { + return -ENOMEM; + } + + //System.err.println("fork " + pid + " -> " + r.pid + " tasks[" + r.pid + "] = " + gd.tasks[r.pid]); + if(children == null) { + children = new Object(); + activeChildren = new Vector(); + exitedChildren = new Vector(); + } + activeChildren.addElement(r); state.r[V0] = 0; // return 0 to child state.pc += 4; // skip over syscall instruction r.setCPUState(state); r.state = PAUSED; - new Thread() { - public void run() { - try { - while(!r.execute()); - } catch(Exception e) { - System.err.println("Forked process threw exception: "); - e.printStackTrace(); - } - } - }.start(); + new ForkedProcess(r); - return childPID; + return r.pid; + } + + public static final class ForkedProcess extends Thread { + private final UnixRuntime initial; + public ForkedProcess(UnixRuntime initial) { this.initial = initial; start(); } + public void run() { UnixRuntime.executeAndExec(initial); } } public static int runAndExec(UnixRuntime r, String argv0, String[] rest) { return runAndExec(r,concatArgv(argv0,rest)); } public static int runAndExec(UnixRuntime r, String[] argv) { r.start(argv); return executeAndExec(r); } public static int executeAndExec(UnixRuntime r) { - for(;;) { + for(;;) { for(;;) { if(r.execute()) break; - System.err.println("WARNING: Pause requested while executing runAndExec()"); + if(STDERR_DIAG) System.err.println("WARNING: Pause requested while executing runAndExec()"); } if(r.state != EXECED) return r.exitStatus(); r = r.execedRuntime; @@ -272,80 +322,53 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } private String[] readStringArray(int addr) throws ReadFaultException { - int count = 0; + int count = 0; for(int p=addr;memRead(p) != 0;p+=4) count++; String[] a = new String[count]; for(int i=0,p=addr;i= 0) return -EACCES; - return -ENOENT; - } - catch(IOException e) { return -EIO; } + private int exec(String normalizedPath, String[] argv, String[] envp) throws ErrnoException { + if(argv.length == 0) argv = new String[]{""}; + + // NOTE: For this little hack to work nestedvm.root MUST be "." + /*try { + System.err.println("Execing normalized path: " + normalizedPath); + if(true) return exec(new Interpreter(normalizedPath),argv,envp); + } catch(IOException e) { throw new Error(e); }*/ - try { - int p = 0; - byte[] buf = new byte[4096]; - OUTER: for(;;) { - int n = s.read(buf,p,buf.length-p); - if(n == -1) break; - for(;n > 0; n--) if(buf[p++] == '\n' || p == 4096) break OUTER; - } - if(buf[0] == '!' && buf[1] == '#') { - String cmd = new String(buf,2,p-2); - String argv1 = null; - if((p = cmd.indexOf(' ')) != -1) { - do { p++; } while(cmd.charAt(p)==' '); - argv1 = cmd.substring(p); - cmd = cmd.substring(0,p-1); - } - String[] newArgv = new String[argv.length + argv1 != null ? 2 : 1]; - p = 0; - newArgv[p++] = argv[0]; - if(argv1 != null) newArgv[p++] = argv1; - newArgv[p++] = path; - for(int i=1;i= 0) return -EACCES; - return -ENOENT; - } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + private int sys_dup(int oldd) { + if(oldd < 0 || oldd >= OPEN_MAX) return -EBADFD; + if(fds[oldd] == null) return -EBADFD; + FD fd = fds[oldd].dup(); + int newd = addFD(fd); + if(newd < 0) { fd.close(); return -ENFILE; } + return newd; } + private int sys_stat(int cstring, int addr) throws FaultException, ErrnoException { + FStat s = gs.stat(this,normalizePath(cstring(cstring))); + if(s == null) return -ENOENT; + return stat(s,addr); + } - private int sys_mkdir(int cstring, int mode) { - try { - fs.mkdir(normalizePath(cstring(cstring))); - return 0; - } - catch(ErrnoException e) { return -e.errno; } - catch(FileNotFoundException e) { return -ENOENT; } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + private int sys_lstat(int cstring, int addr) throws FaultException, ErrnoException { + FStat s = gs.lstat(this,normalizePath(cstring(cstring))); + if(s == null) return -ENOENT; + return stat(s,addr); + } + + private int sys_mkdir(int cstring, int mode) throws FaultException, ErrnoException { + gs.mkdir(this,normalizePath(cstring(cstring)),mode); + return 0; } + private int sys_unlink(int cstring) throws FaultException, ErrnoException { + gs.unlink(this,normalizePath(cstring(cstring))); + return 0; + } - private int sys_getcwd(int addr, int size) { + private int sys_getcwd(int addr, int size) throws FaultException, ErrnoException { byte[] b = getBytes(cwd); if(size == 0) return -EINVAL; if(size < b.length+2) return -ERANGE; + memset(addr,'/',1); + copyout(b,addr+1,b.length); + memset(addr+b.length+1,0,1); + return addr; + } + + private int sys_chdir(int addr) throws ErrnoException, FaultException { + String path = normalizePath(cstring(addr)); + FStat st = gs.stat(this,path); + if(st == null) return -ENOENT; + if(st.type() != FStat.S_IFDIR) return -ENOTDIR; + cwd = path; + return 0; + } + + private int sys_getdents(int fdn, int addr, int count, int seekptr) throws FaultException, ErrnoException { + count = Math.min(count,MAX_CHUNK); + if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; + if(fds[fdn] == null) return -EBADFD; + byte[] buf = byteBuf(count); + int n = fds[fdn].getdents(buf,0,count); + copyout(buf,addr,n); + return n; + } + + // FIXME: UDP is totally broken + + static class SocketFD extends FD { + public static final int TYPE_STREAM = 0; + public static final int TYPE_DGRAM = 1; + public static final int LISTEN = 2; + public int type() { return flags & 1; } + public boolean listen() { return (flags & 2) != 0; } + + int flags; + int options; + Object o; + InetAddress bindAddr; + int bindPort = -1; + DatagramPacket dp; + InputStream is; + OutputStream os; + + public SocketFD(int type) { flags = type; } + + public void setOptions() { + try { + if(o != null && type() == TYPE_STREAM && !listen()) { + Platform.socketSetKeepAlive((Socket)o,(options & SO_KEEPALIVE) != 0); + } + } catch(SocketException e) { + if(STDERR_DIAG) e.printStackTrace(); + } + } + + public void _close() { + if(o != null) { + try { + if(type() == TYPE_STREAM) { + if(listen()) ((ServerSocket)o).close(); + else ((Socket)o).close(); + } else { + ((DatagramSocket)o).close(); + } + } catch(IOException e) { + /* ignore */ + } + } + } + + public int read(byte[] a, int off, int length) throws ErrnoException { + if(type() == TYPE_STREAM) { + if(is == null) throw new ErrnoException(EPIPE); + try { + int n = is.read(a,off,length); + return n < 0 ? 0 : n; + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } else { + if(off != 0) throw new IllegalArgumentException("off must be 0"); + DatagramSocket ds = (DatagramSocket) o; + dp.setData(a); + dp.setLength(length); + try { + ds.receive(dp); + } catch(IOException e) { + throw new ErrnoException(EIO); + } + return dp.getLength(); + } + } + + public int write(byte[] a, int off, int length) throws ErrnoException { + if(type() == TYPE_STREAM) { + if(os == null) throw new ErrnoException(EPIPE); + try { + os.write(a,off,length); + return length; + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } else { + if(off != 0) throw new IllegalArgumentException("off must be 0"); + DatagramSocket ds = (DatagramSocket) o; + dp.setData(a); + dp.setLength(length); + try { + ds.send(dp); + } catch(IOException e) { + throw new ErrnoException(EIO); + } + return dp.getLength(); + } + } + + // FEATURE: Check that these are correct + public int flags() { + if(is != null && os != null) return O_RDWR; + if(is != null) return O_RDONLY; + if(os != null) return O_WRONLY; + return 0; + } + + // FEATURE: Populate this properly + public FStat _fstat() { return new FStat(); } + } + + private int sys_socket(int domain, int type, int proto) { + if(domain != AF_INET || (type != SOCK_STREAM && type != SOCK_DGRAM)) return -EPROTONOSUPPORT; + return addFD(new SocketFD(type == SOCK_STREAM ? SocketFD.TYPE_STREAM : SocketFD.TYPE_DGRAM)); + } + + private SocketFD getSocketFD(int fdn) throws ErrnoException { + if(fdn < 0 || fdn >= OPEN_MAX) throw new ErrnoException(EBADFD); + if(fds[fdn] == null) throw new ErrnoException(EBADFD); + if(!(fds[fdn] instanceof SocketFD)) throw new ErrnoException(ENOTSOCK); + + return (SocketFD) fds[fdn]; + } + + private int sys_connect(int fdn, int addr, int namelen) throws ErrnoException, FaultException { + SocketFD fd = getSocketFD(fdn); + + if(fd.type() == SocketFD.TYPE_STREAM && fd.o != null) return -EISCONN; + int word1 = memRead(addr); + if( ((word1 >>> 16)&0xff) != AF_INET) return -EAFNOSUPPORT; + int port = word1 & 0xffff; + byte[] ip = new byte[4]; + copyin(addr+4,ip,4); + + InetAddress inetAddr; try { - memset(addr,'/',1); - copyout(b,addr+1,b.length); - memset(addr+b.length+1,0,1); - return addr; - } catch(FaultException e) { - return -EFAULT; + inetAddr = Platform.inetAddressFromBytes(ip); + } catch(UnknownHostException e) { + return -EADDRNOTAVAIL; + } + + try { + switch(fd.type()) { + case SocketFD.TYPE_STREAM: { + Socket s = new Socket(inetAddr,port); + fd.o = s; + fd.setOptions(); + fd.is = s.getInputStream(); + fd.os = s.getOutputStream(); + break; + } + case SocketFD.TYPE_DGRAM: { + if(fd.dp == null) fd.dp = new DatagramPacket(null,0); + fd.dp.setAddress(inetAddr); + fd.dp.setPort(port); + break; + } + default: + throw new Error("should never happen"); + } + } catch(IOException e) { + return -ECONNREFUSED; + } + + return 0; + } + + private int sys_resolve_hostname(int chostname, int addr, int sizeAddr) throws FaultException { + String hostname = cstring(chostname); + int size = memRead(sizeAddr); + InetAddress[] inetAddrs; + try { + inetAddrs = InetAddress.getAllByName(hostname); + } catch(UnknownHostException e) { + return HOST_NOT_FOUND; + } + int count = min(size/4,inetAddrs.length); + for(int i=0;i>> 16)&0xff) != AF_INET) return -EAFNOSUPPORT; + int port = word1 & 0xffff; + InetAddress inetAddr = null; + if(memRead(addr+4) != 0) { + byte[] ip = new byte[4]; + copyin(addr+4,ip,4); + + try { + inetAddr = Platform.inetAddressFromBytes(ip); + } catch(UnknownHostException e) { + return -EADDRNOTAVAIL; + } + } + + switch(fd.type()) { + case SocketFD.TYPE_STREAM: { + fd.bindAddr = inetAddr; + fd.bindPort = port; + return 0; + } + case SocketFD.TYPE_DGRAM: { + DatagramSocket s = (DatagramSocket) fd.o; + if(s != null) s.close(); + try { + fd.o = inetAddr != null ? new DatagramSocket(port,inetAddr) : new DatagramSocket(port); + } catch(IOException e) { + return -EADDRINUSE; + } + return 0; + } + default: + throw new Error("should never happen"); } } - private int sys_chdir(int addr) { + private int sys_listen(int fdn, int backlog) throws ErrnoException { + SocketFD fd = getSocketFD(fdn); + if(fd.type() != SocketFD.TYPE_STREAM) return -EOPNOTSUPP; + if(fd.o != null) return -EISCONN; + if(fd.bindPort < 0) return -EOPNOTSUPP; + try { - String path = normalizePath(cstring(addr)); - System.err.println("Chdir: " + cstring(addr) + " -> " + path + " pwd: " + cwd); - if(fs.stat(path).type() != FStat.S_IFDIR) return -ENOTDIR; - cwd = path; - System.err.println("Now: [" + cwd + "]"); + fd.o = new ServerSocket(fd.bindPort,backlog,fd.bindAddr); + fd.flags |= SocketFD.LISTEN; return 0; + } catch(IOException e) { + return -EADDRINUSE; } - catch(ErrnoException e) { return -e.errno; } - catch(FileNotFoundException e) { return -ENOENT; } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + } + + private int sys_accept(int fdn, int addr, int lenaddr) throws ErrnoException, FaultException { + SocketFD fd = getSocketFD(fdn); + if(fd.type() != SocketFD.TYPE_STREAM) return -EOPNOTSUPP; + if(!fd.listen()) return -EOPNOTSUPP; - public void chdir(String dir) throws FileNotFoundException { - if(state != STOPPED) throw new IllegalStateException("Can't chdir while process is running"); + int size = memRead(lenaddr); + + ServerSocket s = (ServerSocket) fd.o; + Socket client; + try { + client = s.accept(); + } catch(IOException e) { + return -EIO; + } + + if(size >= 8) { + memWrite(addr,(6 << 24) | (AF_INET << 16) | client.getPort()); + byte[] b = client.getInetAddress().getAddress(); + copyout(b,addr+4,4); + memWrite(lenaddr,8); + } + + SocketFD clientFD = new SocketFD(SocketFD.TYPE_STREAM); + clientFD.o = client; try { - dir = normalizePath(dir); - if(fs.stat(dir).type() != FStat.S_IFDIR) throw new FileNotFoundException(); + clientFD.is = client.getInputStream(); + clientFD.os = client.getOutputStream(); } catch(IOException e) { - throw new FileNotFoundException(); + return -EIO; } - cwd = dir; + int n = addFD(clientFD); + if(n == -1) { clientFD.close(); return -ENFILE; } + return n; } + + private int sys_shutdown(int fdn, int how) throws ErrnoException { + SocketFD fd = getSocketFD(fdn); + if(fd.type() != SocketFD.TYPE_STREAM || fd.listen()) return -EOPNOTSUPP; + if(fd.o == null) return -ENOTCONN; - public abstract static class FS { - protected FD _open(String path, int flags, int mode) throws IOException { return null; } - protected FStat _stat(String path) throws IOException { return null; } - protected void _mkdir(String path) throws IOException { throw new ErrnoException(EROFS); } + Socket s = (Socket) fd.o; + + try { + if(how == SHUT_RD || how == SHUT_RDWR) Platform.socketHalfClose(s,false); + if(how == SHUT_WR || how == SHUT_RDWR) Platform.socketHalfClose(s,true); + } catch(IOException e) { + return -EIO; + } + + return 0; + } + + private static String hostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch(UnknownHostException e) { + return "darkstar"; + } + } + + private int sys_sysctl(int nameaddr, int namelen, int oldp, int oldlenaddr, int newp, int newlen) throws FaultException { + if(newp != 0) return -EPERM; + if(namelen == 0) return -ENOENT; + if(oldp == 0) return 0; + + Object o = null; + switch(memRead(nameaddr)) { + case CTL_KERN: + if(namelen != 2) break; + switch(memRead(nameaddr+4)) { + case KERN_OSTYPE: o = "NestedVM"; break; + case KERN_HOSTNAME: o = hostName(); break; + case KERN_OSRELEASE: o = VERSION; break; + case KERN_VERSION: o = "NestedVM Kernel Version " + VERSION; break; + } + break; + case CTL_HW: + if(namelen != 2) break; + switch(memRead(nameaddr+4)) { + case HW_MACHINE: o = "NestedVM Virtual Machine"; break; + } + break; + } + if(o == null) return -ENOENT; + int len = memRead(oldlenaddr); + if(o instanceof String) { + byte[] b = getNullTerminatedBytes((String)o); + if(len < b.length) return -ENOMEM; + len = b.length; + copyout(b,oldp,len); + memWrite(oldlenaddr,len); + } else if(o instanceof Integer) { + if(len < 4) return -ENOMEM; + memWrite(oldp,((Integer)o).intValue()); + } else { + throw new Error("should never happen"); + } + return 0; + } + + /*public int sys_opensocket(int cstring, int port) throws FaultException, ErrnoException { + String hostname = cstring(cstring); + try { + FD fd = new SocketFD(new Socket(hostname,port)); + int n = addFD(fd); + if(n == -1) fd.close(); + return n; + } catch(IOException e) { + return -EIO; + } + } + + private static class ListenSocketFD extends FD { + ServerSocket s; + public ListenSocketFD(ServerSocket s) { this.s = s; } + public int flags() { return 0; } + // FEATURE: What should these be? + public FStat _fstat() { return new FStat(); } + public void _close() { try { s.close(); } catch(IOException e) { } } + } + + public int sys_listensocket(int port) { + try { + ListenSocketFD fd = new ListenSocketFD(new ServerSocket(port)); + int n = addFD(fd); + if(n == -1) fd.close(); + return n; + } catch(IOException e) { + return -EIO; + } + } + + public int sys_accept(int fdn) { + if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; + if(fds[fdn] == null) return -EBADFD; + if(!(fds[fdn] instanceof ListenSocketFD)) return -EBADFD; + try { + ServerSocket s = ((ListenSocketFD)fds[fdn]).s; + SocketFD fd = new SocketFD(s.accept()); + int n = addFD(fd); + if(n == -1) fd.close(); + return n; + } catch(IOException e) { + return -EIO; + } + }*/ + + // FEATURE: Run through the fork/wait stuff one more time + public static class GlobalState { protected static final int OPEN = 1; protected static final int STAT = 2; - protected static final int MKDIR = 3; - - protected Object op(int op, String path, int arg1, int arg2) throws IOException { - switch(op) { - case OPEN: return _open(path,arg1,arg2); - case STAT: return _stat(path); - case MKDIR: _mkdir(path); return null; - default: throw new IllegalArgumentException("Unknown FS OP"); - } - } - - public final FD open(String path, int flags, int mode) throws IOException { return (FD) op(OPEN,path,flags,mode); } - public final FStat stat(String path) throws IOException { return (FStat) op(STAT,path,0,0); } - public final void mkdir(String path) throws IOException { op(MKDIR,path,0,0); } - - - // FEATURE: inode stuff - // FEATURE: Implement whatever is needed to get newlib's opendir and friends to work - that patch is a pain - protected static FD directoryFD(String[] files, int hashCode) throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(bos); - for(int i=0;i 0) outp--; while(outp > 0 && out[outp] != '/') outp--; - System.err.println("After ..: " + new String(out,0,outp)); + //System.err.println("After ..: " + new String(out,0,outp)); continue; } inp++; @@ -556,93 +1243,55 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { return new String(out,0,outp); } - public static class MountPointFS extends FS { - private static class MP { - public MP(String path, FS fs) { this.path = path; this.fs = fs; } - public String path; - public FS fs; - public int compareTo(Object o) { - if(!(o instanceof MP)) return 1; - return -path.compareTo(((MP)o).path); - } - } - private final MP[][] mps = new MP[128][]; - private final FS root; - public MountPointFS(FS root) { this.root = root; } - - private static String fixup(String path) { - if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /"); - path = path.substring(1); - if(path.length() == 0) throw new IllegalArgumentException("Zero length mount point path"); - return path; - } - public synchronized FS get(String path) { - path = fixup(path); - int f = path.charAt(0) & 0x7f; - for(int i=0;mps[f] != null && i < mps[f].length;i++) - if(mps[f][i].path.equals(path)) return mps[f][i].fs; - return null; - } - - public synchronized void add(String path, FS fs) { - if(get(path) != null) throw new IllegalArgumentException("mount point already exists"); - path = fixup(path); - int f = path.charAt(0) & 0x7f; - int oldLength = mps[f] == null ? 0 : mps[f].length; - MP[] newList = new MP[oldLength + 1]; - if(oldLength != 0) System.arraycopy(mps[f],0,newList,0,oldLength); - newList[oldLength] = new MP(path,fs); - Arrays.sort(newList); - mps[f] = newList; - } - - public synchronized void remove(String path) { - path = fixup(path); - if(get(path) == null) throw new IllegalArgumentException("mount point doesn't exist"); - int f = path.charAt(0) & 0x7f; - MP[] oldList = mps[f]; - MP[] newList = new MP[oldList.length - 1]; - int p = 0; - for(p=0;p " + f + " " + f.exists()); - if(f.isDirectory()) { - if((flags&3)!=RD_ONLY) throw new ErrnoException(EACCES); - return directoryFD(f.list(),path.hashCode()); - } - if((flags & (O_EXCL|O_CREAT)) == (O_EXCL|O_CREAT)) - if(!f.createNewFile()) throw new ErrnoException(EEXIST); - if((flags&O_CREAT) == 0 && !f.exists()) - return null; - final Seekable.File sf = new Seekable.File(f,(flags&3)!=RD_ONLY); - if((flags&O_TRUNC)!=0) sf.setLength(0); - return new SeekableFD(sf,mode) { - protected FStat _fstat() { return new HostFStat(f) { - public int size() { - try { return sf.length(); } catch(IOException e) { return 0; } - } - };} - }; + return r.hostFSOpen(f,flags,mode,this); + } + + public void unlink(UnixRuntime r, String path) throws ErrnoException { + File f = hostFile(path); + if(r.sm != null && !r.sm.allowUnlink(f)) throw new ErrnoException(EPERM); + if(!f.exists()) throw new ErrnoException(ENOENT); + if(!f.delete()) throw new ErrnoException(EPERM); } - public FStat _stat(String path) throws FileNotFoundException { + public FStat stat(UnixRuntime r, String path) throws ErrnoException { File f = hostFile(path); - if(!f.exists()) throw new FileNotFoundException(); - return new HostFStat(f); + if(r.sm != null && !r.sm.allowStat(f)) throw new ErrnoException(EACCES); + if(!f.exists()) return null; + return r.hostFStat(f,this); } - public void _mkdir(String path) throws IOException { + public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { File f = hostFile(path); + if(r.sm != null && !r.sm.allowWrite(f)) throw new ErrnoException(EACCES); if(f.exists() && f.isDirectory()) throw new ErrnoException(EEXIST); if(f.exists()) throw new ErrnoException(ENOTDIR); - File parent = f.getParentFile(); + File parent = getParentFile(f); if(parent!=null && (!parent.exists() || !parent.isDirectory())) throw new ErrnoException(ENOTDIR); if(!f.mkdir()) throw new ErrnoException(EIO); } + + private static File getParentFile(File f) { + String p = f.getParent(); + return p == null ? null : new File(f,p); + } + + public class HostDirFD extends DirFD { + private final File f; + private final File[] children; + public HostDirFD(File f) { + this.f = f; + String[] l = f.list(); + children = new File[l.length]; + for(int i=0;i>>24)&0xff); + buf[off+1] = (byte)((n>>>16)&0xff); + buf[off+2] = (byte)((n>>> 8)&0xff); + buf[off+3] = (byte)((n>>> 0)&0xff); + } + + public static abstract class DirFD extends FD { + private int pos = -2; + + protected abstract int size(); + protected abstract String name(int n); + protected abstract int inode(int n); + protected abstract int myDev(); + protected int parentInode() { return -1; } + protected int myInode() { return -1; } + + public int getdents(byte[] buf, int off, int len) { + int ooff = off; + int ino; + int reclen; + OUTER: for(;len > 0 && pos < size();pos++){ + switch(pos) { + case -2: + case -1: + ino = pos == -1 ? parentInode() : myInode(); + if(ino == -1) continue; + reclen = 9 + (pos == -1 ? 2 : 1); + if(reclen > len) break OUTER; + buf[off+8] = '.'; + if(pos == -1) buf[off+9] = '.'; + break; + default: { + String f = name(pos); + byte[] fb = getBytes(f); + reclen = fb.length + 9; + if(reclen > len) break OUTER; + ino = inode(pos); + System.arraycopy(fb,0,buf,off+8,fb.length); + } + } + buf[off+reclen-1] = 0; // null terminate + reclen = (reclen + 3) & ~3; // add padding + putInt(buf,off,reclen); + putInt(buf,off+4,ino); + off += reclen; + len -= reclen; + } + return off-ooff; + } + + protected FStat _fstat() { + return new FStat() { + public int type() { return S_IFDIR; } + public int inode() { return myInode(); } + public int dev() { return myDev(); } + }; + } } - private static class DevFStat extends FStat { - public int dev() { return 1; } - public int mode() { return 0666; } - public int type() { return S_IFCHR; } - public int nlink() { return 1; } - } - private static FD devZeroFD = new FD() { - public boolean readable() { return true; } - public boolean writable() { return true; } - public int read(byte[] a, int off, int length) { Arrays.fill(a,off,off+length,(byte)0); return length; } - public int write(byte[] a, int off, int length) { return length; } - public int seek(int n, int whence) { return 0; } - public FStat _fstat() { return new DevFStat(); } - }; - private static FD devNullFD = new FD() { - public boolean readable() { return true; } - public boolean writable() { return true; } - public int read(byte[] a, int off, int length) { return 0; } - public int write(byte[] a, int off, int length) { return length; } - public int seek(int n, int whence) { return 0; } - public FStat _fstat() { return new DevFStat(); } - }; - - // FIXME: Support /dev/fd (need to have syscalls pass along Runtime instance) public static class DevFS extends FS { - public FD _open(String path, int mode, int flags) throws IOException { + private static final int ROOT_INODE = 1; + private static final int NULL_INODE = 2; + private static final int ZERO_INODE = 3; + private static final int FD_INODE = 4; + private static final int FD_INODES = 32; + + private class DevFStat extends FStat { + public int dev() { return devno; } + public int mode() { return 0666; } + public int type() { return S_IFCHR; } + public int nlink() { return 1; } + } + + private abstract class DevDirFD extends DirFD { + public int myDev() { return devno; } + } + + private FD devZeroFD = new FD() { + public int read(byte[] a, int off, int length) { + /*Arrays.fill(a,off,off+length,(byte)0);*/ + for(int i=off;i= OPEN_MAX) throw new FileNotFoundException(); - if(fds[n] == null) throw new FileNotFoundException(); - return fds[n].dup(); + if(n < 0 || n >= OPEN_MAX) return null; + if(r.fds[n] == null) return null; + return r.fds[n].dup(); } if(path.equals("fd")) { int count=0; - for(int i=0;i= OPEN_MAX) throw new FileNotFoundException(); - if(fds[n] == null) throw new FileNotFoundException(); - return fds[n].fstat(); + if(n < 0 || n >= OPEN_MAX) return null; + if(r.fds[n] == null) return null; + return r.fds[n].fstat(); } + // FEATURE: inode stuff if(path.equals("fd")) return new FStat() { public int type() { return S_IFDIR; } public int mode() { return 0444; }}; - */ if(path.equals("")) return new FStat() { public int type() { return S_IFDIR; } public int mode() { return 0444; }}; - throw new FileNotFoundException(); + return null; } - public void _mkdir(String path) throws IOException { throw new ErrnoException(EACCES); } - } + public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { throw new ErrnoException(EROFS); } + public void unlink(UnixRuntime r, String path) throws ErrnoException { throw new ErrnoException(EROFS); } + } }