X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fnestedvm%2FUnixRuntime.java;h=0b50ee30b65315cd3500d455d6b3bf71d33bf1e3;hp=a6847ee7d918a88f02c183bd9cc074a2c072b054;hb=18f9d34628d03afdf74b95ccd96e6e2e8441fb54;hpb=47e2513b584db639600b57c79be03093f99b0073 diff --git a/src/org/ibex/nestedvm/UnixRuntime.java b/src/org/ibex/nestedvm/UnixRuntime.java index a6847ee..0b50ee3 100644 --- a/src/org/ibex/nestedvm/UnixRuntime.java +++ b/src/org/ibex/nestedvm/UnixRuntime.java @@ -1,8 +1,13 @@ package org.ibex.nestedvm; import org.ibex.nestedvm.util.*; +// HACK: This is ugly, this stuff needs to be in org.ibex.util or something +import org.ibex.classgen.util.Sort; import java.io.*; import java.util.*; +import java.net.*; + +// FEATURE: vfork public abstract class UnixRuntime extends Runtime implements Cloneable { /** The pid of this "process" */ @@ -11,7 +16,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { public final int getPid() { return pid; } private static final GlobalState defaultGS = new GlobalState(); - private GlobalState gs = defaultGS; + private GlobalState gs; public void setGlobalState(GlobalState gs) { if(state != STOPPED) throw new IllegalStateException("can't change GlobalState when running"); this.gs = gs; @@ -28,27 +33,50 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { private Vector activeChildren; private Vector exitedChildren; - protected UnixRuntime(int pageSize, int totalPages) { - super(pageSize,totalPages); + protected UnixRuntime(int pageSize, int totalPages) { this(pageSize,totalPages,false); } + protected UnixRuntime(int pageSize, int totalPages, boolean exec) { + super(pageSize,totalPages,exec); - // FEATURE: Do the proper mangling for non-unix hosts - String userdir = getSystemProperty("user.dir"); - cwd = userdir != null && userdir.startsWith("/") && File.separatorChar == '/' ? userdir.substring(1) : ""; + if(!exec) { + gs = defaultGS; + String userdir = Platform.getProperty("user.dir"); + String nvroot = Platform.getProperty("nestedvm.root"); + cwd = ""; + if(userdir != null && nvroot == null) { + if(userdir.startsWith("/") && File.separatorChar == '/') { + cwd = userdir.substring(1); + } else { + Vector vec = new Vector(); + File root = HostFS.hostRootDir(); + String s = new File(userdir).getAbsolutePath(); + File d = new File(s); + System.err.println(s); + System.err.println(d); + while(!d.equals(root)) { + System.err.println("Got " + d.getName()); + vec.addElement(d.getName()); + if((s = d.getParent()) == null) break; + d = new File(s); + } + if(s != null) + for(int i=vec.size()-1;i>=0;i--) cwd += (String) vec.elementAt(i) + (i==0?"":"/"); + } + } + } } - // NOTE: getDisplayName() is a Java2 function private static String posixTZ() { StringBuffer sb = new StringBuffer(); TimeZone zone = TimeZone.getDefault(); int off = zone.getRawOffset() / 1000; - sb.append(zone.getDisplayName(false,TimeZone.SHORT)); + sb.append(Platform.timeZoneGetDisplayName(zone,false,false)); if(off > 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(); } @@ -59,16 +87,17 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } String[] createEnv(String[] extra) { - String[] defaults = new String[5]; + 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.home") != null) - defaults[n++] = "HOME=" + getSystemProperty("user.home"); + if(!envHas("USER",extra) && Platform.getProperty("user.name") != null) + defaults[n++] = "USER=" + Platform.getProperty("user.name"); + if(!envHas("HOME",extra) && Platform.getProperty("user.home") != null) + defaults[n++] = "HOME=" + Platform.getProperty("user.home"); if(!envHas("SHELL",extra)) defaults[n++] = "SHELL=/bin/sh"; - if(!envHas("TERM",extra)) defaults[n++] = "TERM=vt100"; + if(!envHas("TERM",extra) && !win32Hacks) 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 0) done = (UnixRuntime)exitedChildren.remove(exitedChildren.size() - 1); + if(exitedChildren.size() > 0) { + done = (UnixRuntime)exitedChildren.elementAt(exitedChildren.size() - 1); + exitedChildren.removeElementAt(exitedChildren.size() - 1); + } } else if(pid > 0) { + if(pid >= gs.tasks.length) return -ECHILD; UnixRuntime t = gs.tasks[pid]; if(t.parent != this) return -ECHILD; if(t.state == EXITED) { - if(!exitedChildren.remove(t)) throw new Error("should never happen"); + if(!exitedChildren.removeElement(t)) throw new Error("should never happen"); done = t; } } else { @@ -197,12 +258,12 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { UnixRuntime child = (UnixRuntime) e.nextElement(); gs.tasks[child.pid] = null; } - exitedChildren.clear(); + exitedChildren.removeAllElements(); for(Enumeration e = activeChildren.elements(); e.hasMoreElements(); ) { UnixRuntime child = (UnixRuntime) e.nextElement(); child.parent = null; } - activeChildren.clear(); + activeChildren.removeAllElements(); } UnixRuntime _parent = parent; @@ -213,8 +274,8 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { if(parent == null) { gs.tasks[pid] = null; } else { - parent.activeChildren.remove(this); - parent.exitedChildren.add(this); + if(!parent.activeChildren.removeElement(this)) throw new Error("should never happen _exited: pid: " + pid); + parent.exitedChildren.addElement(this); parent.children.notify(); } } @@ -231,9 +292,6 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } private int sys_fork() { - CPUState state = new CPUState(); - getCPUState(state); - int sp = state.r[SP]; final UnixRuntime r; try { @@ -257,8 +315,10 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { activeChildren = new Vector(); exitedChildren = new Vector(); } - activeChildren.add(r); + activeChildren.addElement(r); + CPUState state = new CPUState(); + getCPUState(state); state.r[V0] = 0; // return 0 to child state.pc += 4; // skip over syscall instruction r.setCPUState(state); @@ -304,15 +364,22 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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); }*/ + Object o = gs.exec(this,normalizedPath); if(o == null) return -ENOENT; if(o instanceof Class) { Class c = (Class) o; try { - return exec((UnixRuntime) c.newInstance(),argv,envp); + UnixRuntime r = (UnixRuntime) c.getDeclaredConstructor(new Class[]{Boolean.TYPE}).newInstance(new Object[]{Boolean.TRUE}); + return exec(r,argv,envp); } catch(Exception e) { - e.printStackTrace(); + e.printStackTrace(); return -ENOEXEC; } } else { @@ -351,10 +418,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { return 0; } - // FEATURE: Make sure fstat info is correct - // FEATURE: This could be faster if we did direct copies from each process' memory - // FEATURE: Check this logic one more time - public static class Pipe { + static class Pipe { private final byte[] pipebuf = new byte[PIPE_BUF*4]; private int readPos; private int writePos; @@ -363,7 +427,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { public final FD writer = new Writer(); public class Reader extends FD { - protected FStat _fstat() { return new FStat(); } + protected FStat _fstat() { return new SocketFStat(); } public int read(byte[] buf, int off, int len) throws ErrnoException { if(len == 0) return 0; synchronized(Pipe.this) { @@ -378,11 +442,12 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { return len; } } + public int flags() { return O_RDONLY; } public void _close() { synchronized(Pipe.this) { readPos = -1; Pipe.this.notify(); } } } public class Writer extends FD { - protected FStat _fstat() { return new FStat(); } + protected FStat _fstat() { return new SocketFStat(); } public int write(byte[] buf, int off, int len) throws ErrnoException { if(len == 0) return 0; synchronized(Pipe.this) { @@ -402,6 +467,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { return len; } } + public int flags() { return O_WRONLY; } public void _close() { synchronized(Pipe.this) { writePos = -1; Pipe.this.notify(); } } } } @@ -461,6 +527,11 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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) throws FaultException, ErrnoException { byte[] b = getBytes(cwd); if(size == 0) return -EINVAL; @@ -473,10 +544,10 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { private int sys_chdir(int addr) throws ErrnoException, FaultException { String path = normalizePath(cstring(addr)); - //System.err.println("Chdir: " + cstring(addr) + " -> " + path + " pwd: " + cwd); - if(gs.stat(this,path).type() != FStat.S_IFDIR) return -ENOTDIR; + FStat st = gs.stat(this,path); + if(st == null) return -ENOENT; + if(st.type() != FStat.S_IFDIR) return -ENOTDIR; cwd = path; - //System.err.println("Now: [" + cwd + "]"); return 0; } @@ -490,13 +561,459 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { return n; } - // 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 LSTAT = 3; - protected static final int MKDIR = 4; + 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; + + Socket s; + ServerSocket ss; + DatagramSocket ds; + + InetAddress bindAddr; + int bindPort = -1; + InetAddress connectAddr; + int connectPort = -1; + + DatagramPacket dp; + InputStream is; + OutputStream os; + private static final byte[] EMPTY = new byte[0]; + public SocketFD(int type) { + flags = type; + if(type == TYPE_DGRAM) + dp = new DatagramPacket(EMPTY,0); + } + + public void setOptions() { + try { + if(s != null && type() == TYPE_STREAM && !listen()) { + Platform.socketSetKeepAlive(s,(options & SO_KEEPALIVE) != 0); + } + } catch(SocketException e) { + if(STDERR_DIAG) e.printStackTrace(); + } + } + + public void _close() { + try { + if(s != null) s.close(); + if(ss != null) ss.close(); + if(ds != null) ds.close(); + } catch(IOException e) { + /* ignore */ + } + } + + public int read(byte[] a, int off, int length) throws ErrnoException { + if(type() == TYPE_DGRAM) return recvfrom(a,off,length,null,null); + 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); + } + } + + public int recvfrom(byte[] a, int off, int length, InetAddress[] sockAddr, int[] port) throws ErrnoException { + if(type() == TYPE_STREAM) return read(a,off,length); + + if(off != 0) throw new IllegalArgumentException("off must be 0"); + dp.setData(a); + dp.setLength(length); + try { + if(ds == null) ds = new DatagramSocket(); + ds.receive(dp); + } catch(IOException e) { + if(STDERR_DIAG) e.printStackTrace(); + throw new ErrnoException(EIO); + } + if(sockAddr != null) { + sockAddr[0] = dp.getAddress(); + port[0] = dp.getPort(); + } + return dp.getLength(); + } + + public int write(byte[] a, int off, int length) throws ErrnoException { + if(type() == TYPE_DGRAM) return sendto(a,off,length,null,-1); + + if(os == null) throw new ErrnoException(EPIPE); + try { + os.write(a,off,length); + return length; + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } + + public int sendto(byte[] a, int off, int length, InetAddress destAddr, int destPort) throws ErrnoException { + if(off != 0) throw new IllegalArgumentException("off must be 0"); + if(type() == TYPE_STREAM) return write(a,off,length); + + if(destAddr == null) { + destAddr = connectAddr; + destPort = connectPort; + + if(destAddr == null) throw new ErrnoException(ENOTCONN); + } + + dp.setAddress(destAddr); + dp.setPort(destPort); + dp.setData(a); + dp.setLength(length); + + try { + if(ds == null) ds = new DatagramSocket(); + ds.send(dp); + } catch(IOException e) { + if(STDERR_DIAG) e.printStackTrace(); + if("Network is unreachable".equals(e.getMessage())) throw new ErrnoException(EHOSTUNREACH); + throw new ErrnoException(EIO); + } + return dp.getLength(); + } + + public int flags() { return O_RDWR; } + public FStat _fstat() { return new SocketFStat(); } + } + + 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.s != null || fd.ss != 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 { + inetAddr = Platform.inetAddressFromBytes(ip); + } catch(UnknownHostException e) { + return -EADDRNOTAVAIL; + } + + fd.connectAddr = inetAddr; + fd.connectPort = port; + + try { + switch(fd.type()) { + case SocketFD.TYPE_STREAM: { + Socket s = new Socket(inetAddr,port); + fd.s = s; + fd.setOptions(); + fd.is = s.getInputStream(); + fd.os = s.getOutputStream(); + break; + } + case SocketFD.TYPE_DGRAM: + 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: { + if(fd.ds != null) fd.ds.close(); + try { + fd.ds = 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_listen(int fdn, int backlog) throws ErrnoException { + SocketFD fd = getSocketFD(fdn); + if(fd.type() != SocketFD.TYPE_STREAM) return -EOPNOTSUPP; + if(fd.ss != null || fd.s != null) return -EISCONN; + if(fd.bindPort < 0) return -EOPNOTSUPP; + + try { + fd.ss = new ServerSocket(fd.bindPort,backlog,fd.bindAddr); + fd.flags |= SocketFD.LISTEN; + return 0; + } catch(IOException e) { + return -EADDRINUSE; + } + + } + + 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; + + int size = memRead(lenaddr); + + ServerSocket s = fd.ss; + 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.s = client; + try { + clientFD.is = client.getInputStream(); + clientFD.os = client.getOutputStream(); + } catch(IOException e) { + return -EIO; + } + 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.s == null) return -ENOTCONN; + + Socket s = fd.s; + + 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 int sys_sendto(int fdn, int addr, int count, int flags, int destAddr, int socklen) throws ErrnoException,ReadFaultException { + SocketFD fd = getSocketFD(fdn); + if(flags != 0) throw new ErrnoException(EINVAL); + + int word1 = memRead(destAddr); + if( ((word1 >>> 16)&0xff) != AF_INET) return -EAFNOSUPPORT; + int port = word1 & 0xffff; + InetAddress inetAddr; + byte[] ip = new byte[4]; + copyin(destAddr+4,ip,4); + try { + inetAddr = Platform.inetAddressFromBytes(ip); + } catch(UnknownHostException e) { + return -EADDRNOTAVAIL; + } + + count = Math.min(count,MAX_CHUNK); + byte[] buf = byteBuf(count); + copyin(addr,buf,count); + try { + return fd.sendto(buf,0,count,inetAddr,port); + } catch(ErrnoException e) { + if(e.errno == EPIPE) exit(128+13,true); + throw e; + } + } + + private int sys_recvfrom(int fdn, int addr, int count, int flags, int sourceAddr, int socklenAddr) throws ErrnoException, FaultException { + SocketFD fd = getSocketFD(fdn); + if(flags != 0) throw new ErrnoException(EINVAL); + + InetAddress[] inetAddr = sourceAddr == 0 ? null : new InetAddress[1]; + int[] port = sourceAddr == 0 ? null : new int[1]; + + count = Math.min(count,MAX_CHUNK); + byte[] buf = byteBuf(count); + int n = fd.recvfrom(buf,0,count,inetAddr,port); + copyout(buf,addr,n); + + if(sourceAddr != 0) { + memWrite(sourceAddr,(AF_INET << 16) | port[0]); + byte[] ip = inetAddr[0].getAddress(); + copyout(ip,sourceAddr+4,4); + } + + return n; + } + + private int sys_select(int n, int readFDs, int writeFDs, int exceptFDs, int timevalAddr) throws ReadFaultException, ErrnoException { + return -ENOSYS; + } + + 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 static class GlobalState { final UnixRuntime[] tasks; int nextPID = 1; @@ -513,7 +1030,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } } - private static class MP { + private static class MP implements Sort.Comparable { public MP(String path, FS fs) { this.path = path; this.fs = fs; } public String path; public FS fs; @@ -545,7 +1062,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { MP[] newMPS = new MP[oldLength + 1]; if(oldLength != 0) System.arraycopy(mps,0,newMPS,0,oldLength); newMPS[oldLength] = new MP(path,fs); - Arrays.sort(newMPS); + Sort.sort(newMPS); mps = newMPS; int highdevno = 0; for(int i=0;i 0) outp--; while(outp > 0 && out[outp] != '/') outp--; //System.err.println("After ..: " + new String(out,0,outp)); continue; } + // Just read a /.[^.] or /..[^/$] inp++; out[outp++] = '/'; out[outp++] = '.'; @@ -793,11 +1329,19 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { protected File root; public File getRoot() { return root; } - private static File hostRootDir() { - String cwd = getSystemProperty("user.dir"); + static File hostRootDir() { + if(Platform.getProperty("nestedvm.root") != null) { + File f = new File(Platform.getProperty("nestedvm.root")); + if(f.isDirectory()) return f; + // fall through to case below + } + String cwd = Platform.getProperty("user.dir"); File f = new File(cwd != null ? cwd : "."); + if(!f.exists()) throw new Error("Couldn't get File for cwd"); f = new File(f.getAbsolutePath()); while(f.getParent() != null) f = new File(f.getParent()); + // This works around a bug in some versions of ClassPath + if(f.getPath().length() == 0) f = new File("/"); return f; } @@ -825,6 +1369,13 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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(UnixRuntime r, String path) throws ErrnoException { File f = hostFile(path); if(r.sm != null && !r.sm.allowStat(f)) throw new ErrnoException(EACCES); @@ -837,21 +1388,32 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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; children = f.listFiles(); } + public HostDirFD(File f) { + this.f = f; + String[] l = f.list(); + children = new File[l.length]; + for(int i=0;i