// Copyright 2000-2005 the Contributors, as shown in the revision logs. // Licensed under the Apache Public Source License 2.0 ("the License"). // You may not use this file except in compliance with the License. package org.ibex.nestedvm; import org.ibex.nestedvm.util.*; import java.io.*; import java.util.*; import java.net.*; import java.nio.file.*; import java.lang.reflect.*; // For lazily linked RuntimeCompiler // FEATURE: vfork public abstract class UnixRuntime extends Runtime implements Cloneable { /** The pid of this "process" */ private int pid; private UnixRuntime parent; public final int getPid() { return pid; } private static final GlobalState defaultGS = new GlobalState(); private GlobalState gs; public void setGlobalState(GlobalState gs) { if(state != STOPPED) throw new IllegalStateException("can't change GlobalState when running"); if(gs == null) throw new NullPointerException("gs is null"); this.gs = gs; } /** proceses' current working directory - absolute path WITHOUT leading slash "" = root, "bin" = /bin "usr/bin" = /usr/bin */ private String cwd; /** 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; protected UnixRuntime(int pageSize, int totalPages) { this(pageSize,totalPages,false); } protected UnixRuntime(int pageSize, int totalPages, boolean exec) { super(pageSize,totalPages,exec); if(!exec) { gs = defaultGS; String userdir = Platform.getProperty("user.dir"); cwd = userdir == null ? null : gs.mapHostPath(userdir); if(cwd == null) cwd = "/"; cwd = cwd.substring(1); } } private static String posixTZ() { StringBuffer sb = new StringBuffer(); TimeZone zone = TimeZone.getDefault(); int off = zone.getRawOffset() / 1000; 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(Platform.timeZoneGetDisplayName(zone,true,false)); return sb.toString(); } private static boolean envHas(String key,String[] environ) { for(int i=0; i= gs.tasks.length)) return -ECHILD; if(children == null) return blocking ? -ECHILD : 0; UnixRuntime done = null; synchronized(children) { for(;;) { if(pid == -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.removeElement(t)) throw new Error("should never happen"); done = t; } } 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(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 = 0; r.parent = null; r.children = null; r.activeChildren = r.exitedChildren = null; return r; } private int sys_fork() { final UnixRuntime r; try { r = (UnixRuntime) clone(); } catch(Exception e) { e.printStackTrace(); return -ENOMEM; } 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); CPUState state = new CPUState(); getCPUState(state); state.r[V0] = 0; // return 0 to child state.pc += 4; // skip over syscall instruction r.setCPUState(state); r.state = PAUSED; new ForkedProcess(r); 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(;;) { if(r.execute()) break; if(STDERR_DIAG) System.err.println("WARNING: Pause requested while executing runAndExec()"); } if(r.state != EXECED) return r.exitStatus(); r = r.execedRuntime; } } private String[] readStringArray(int addr) throws ReadFaultException { 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 c,String[] argv, String[] envp) { try { UnixRuntime r = (UnixRuntime) c.getDeclaredConstructor(new Class[] {Boolean.TYPE}).newInstance(new Object[] {Boolean.TRUE}); return exec(r,argv,envp); } catch(Exception e) { e.printStackTrace(); return -ENOEXEC; } } private int exec(UnixRuntime r, String[] argv, String[] envp) { //System.err.println("Execing " + r); for(int i=0; i= OPEN_MAX) return -EBADFD; if(newd < 0 || newd >= OPEN_MAX) return -EBADFD; if(fds[oldd] == null) return -EBADFD; if(fds[newd] != null) fds[newd].close(); fds[newd] = fds[oldd].dup(); return 0; } 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_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_link(int cstring1, int cstring2) throws FaultException, ErrnoException { gs.link(this,normalizePath(cstring(cstring1)), normalizePath(cstring(cstring2))); return 0; } private int sys_rmdir(int ptrPathName) throws ErrnoException, ReadFaultException { gs.rmdir(this, normalizePath(cstring(ptrPathName))); return 0; } 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; } void _preCloseFD(FD fd) { // release all fcntl locks on this file Seekable s = fd.seekable(); if (s == null) return; try { for (int i=0; i < gs.locks.length; i++) { Seekable.Lock l = gs.locks[i]; if (l == null) continue; if (s.equals(l.seekable()) && l.getOwner() == this) { l.release(); gs.locks[i] = null; } } } catch (IOException e) { throw new RuntimeException(e); } } void _postCloseFD(FD fd) { if (fd.isMarkedForDeleteOnClose()) { try { gs.unlink(this, fd.getNormalizedPath()); } catch (Throwable t) {} } } /** Implements the F_GETLK and F_SETLK cases of fcntl syscall. * If l_start = 0 and l_len = 0 the lock refers to the entire file. * Uses GlobalState to ensure locking across processes in the same JVM. struct flock { short l_type; // lock type: F_UNLCK, F_RDLCK, F_WRLCK short l_whence; // type of l_start: SEEK_SET, SEEK_CUR, SEEK_END long l_start; // starting offset, bytes long l_len; // len = 0 means until EOF short l_pid; // lock owner short l_xxx; // padding }; */ private int sys_fcntl_lock(int fdn, int cmd, int arg) throws FaultException { if (cmd != F_GETLK && cmd != F_SETLK) return sys_fcntl(fdn, cmd, arg); if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; if(fds[fdn] == null) return -EBADFD; FD fd = fds[fdn]; if (arg == 0) return -EINVAL; int word = memRead(arg); int l_start = memRead(arg+4); int l_len = memRead(arg+8); int l_type = word>>16; int l_whence = word&0x00ff; Seekable.Lock[] locks = gs.locks; Seekable s = fd.seekable(); if (s == null) return -EINVAL; try { switch (l_whence) { case SEEK_SET: break; case SEEK_CUR: l_start += s.pos(); break; case SEEK_END: l_start += s.length(); break; default: return -1; } if (cmd == F_GETLK) { // The simple Java file locking below will happily return // a lock that overlaps one already held by the JVM. Thus // we must check over all the locks held by other Runtimes for (int i=0; i < locks.length; i++) { if (locks[i] == null || !s.equals(locks[i].seekable())) continue; if (!locks[i].overlaps(l_start, l_len)) continue; if (locks[i].getOwner() == this) continue; if (locks[i].isShared() && l_type == F_RDLCK) continue; // overlapping lock held by another process return 0; } // check if an area is lockable by attempting to obtain a lock Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK); if (lock != null) { // no lock exists memWrite(arg, SEEK_SET|(F_UNLCK<<16)); lock.release(); } return 0; } // now processing F_SETLK if (cmd != F_SETLK) return -EINVAL; if (l_type == F_UNLCK) { // release all locks that fall within the boundaries given for (int i=0; i < locks.length; i++) { if (locks[i] == null || !s.equals(locks[i].seekable())) continue; if (locks[i].getOwner() != this) continue; int pos = (int)locks[i].position(); if (pos < l_start) continue; if (l_start != 0 && l_len != 0) // start/len 0 means unlock all if (pos + locks[i].size() > l_start + l_len) continue; locks[i].release(); locks[i] = null; } return 0; } else if (l_type == F_RDLCK || l_type == F_WRLCK) { // first see if a lock already exists for (int i=0; i < locks.length; i++) { if (locks[i] == null || !s.equals(locks[i].seekable())) continue; if (locks[i].getOwner() == this) { // if this Runtime owns an overlapping lock work with it if (locks[i].contained(l_start, l_len)) { locks[i].release(); locks[i] = null; } else if (locks[i].contains(l_start, l_len)) { if (locks[i].isShared() == (l_type == F_RDLCK)) { // return this more general lock memWrite(arg+4, (int)locks[i].position()); memWrite(arg+8, (int)locks[i].size()); return 0; } else { locks[i].release(); locks[i] = null; } } } else { // if another Runtime has an lock and it is exclusive or // we want an exclusive lock then fail if (locks[i].overlaps(l_start, l_len) && (!locks[i].isShared() || l_type == F_WRLCK)) return -EAGAIN; } } // create the lock Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK); if (lock == null) return -EAGAIN; lock.setOwner(this); int i; for (i=0; i < locks.length; i++) if (locks[i] == null) break; if (i == locks.length) return -ENOLCK; locks[i] = lock; return 0; } else { return -EINVAL; } } catch (IOException e) { throw new RuntimeException(e); } } 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 SocketAddress getLocalSocketAddress() { int type = type(); if(type == SocketFD.TYPE_STREAM) { if(listen()) { return ss.getLocalSocketAddress(); } else { return s.getLocalSocketAddress(); } } else { return ds.getLocalSocketAddress(); } } 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 final class GlobalState { Hashtable execCache = new Hashtable(); final UnixRuntime[] tasks; int nextPID = 1; /** Table of all current file locks held by this process. */ Seekable.Lock[] locks = new Seekable.Lock[16]; private MP[] mps = new MP[0]; private FS root; public GlobalState() { this(255); } public GlobalState(int maxProcs) { this(maxProcs,true); } public GlobalState(int maxProcs, boolean defaultMounts) { tasks = new UnixRuntime[maxProcs+1]; if(defaultMounts) { File root = null; if(Platform.getProperty("nestedvm.root") != null) { root = new File(Platform.getProperty("nestedvm.root")); if(!root.isDirectory()) throw new IllegalArgumentException("nestedvm.root is not a directory"); } else { String cwd = Platform.getProperty("user.dir"); root = Platform.getRoot(new File(cwd != null ? cwd : ".")); } addMount("/",new HostFS(root)); if(Platform.getProperty("nestedvm.root") == null) { File[] roots = Platform.listRoots(); for(int i=0; i=0; i--) { FS fs = i == mps.length ? root : mps[i].fs; String path = i == mps.length ? "" : mps[i].path; if(!(fs instanceof HostFS)) continue; File fsroot = ((HostFS)fs).getRoot(); if(!fsroot.isAbsolute()) fsroot = new File(fsroot.getAbsolutePath()); if(f.getPath().startsWith(fsroot.getPath())) { char sep = File.separatorChar; String child = f.getPath().substring(fsroot.getPath().length()); if(sep != '/') { char[] child_ = child.toCharArray(); for(int j=0; j 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++] = '.'; } if(outp > 0 && out[outp-1] == '/') outp--; //System.err.println("normalize: " + path + " -> " + new String(out,0,outp) + " (cwd: " + cwd + ")"); int outStart = out[0] == '/' ? 1 : 0; return new String(out,outStart,outp - outStart); } FStat hostFStat(final File f, Object data) { boolean e = false; try { FileInputStream fis = new FileInputStream(f); switch(fis.read()) { case '\177': e = fis.read() == 'E' && fis.read() == 'L' && fis.read() == 'F'; break; case '#': e = fis.read() == '!'; } fis.close(); } catch(IOException e2) { } HostFS fs = (HostFS) data; final int inode = fs.inodes.get(f.getAbsolutePath()); final int devno = fs.devno; return new HostFStat(f,e) { public int inode() { return inode; } public int dev() { return devno; } }; } FD hostFSDirFD(File f, Object _fs) { HostFS fs = (HostFS) _fs; return fs.new HostDirFD(f); } public static class HostFS extends FS { InodeCache inodes = new InodeCache(4000); protected File root; public File getRoot() { return root; } protected File hostFile(String path) { char sep = File.separatorChar; if(sep != '/') { char buf[] = path.toCharArray(); 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 abstract int parentInode(); protected abstract int myInode(); public int flags() { return O_RDONLY; } 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(); } }; } } public static class DevFS extends FS { 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 abstract 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; } public abstract int inode(); } 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) 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) return null; if(r.fds[n] == null) return null; return r.fds[n].fstat(); } if(path.equals("fd")) return new FStat() { public int inode() { return FD_INODE; } public int dev() { return devno; } public int type() { return S_IFDIR; } public int mode() { return 0444; } }; if(path.equals("")) return new FStat() { public int inode() { return ROOT_INODE; } public int dev() { return devno; } public int type() { return S_IFDIR; } public int mode() { return 0444; } }; return null; } public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { throw new ErrnoException(EROFS); } public void rmdir(UnixRuntime r, String path) throws ErrnoException { throw new ErrnoException(EROFS); } public void unlink(UnixRuntime r, String path) throws ErrnoException { throw new ErrnoException(EROFS); } public void link(UnixRuntime r, String oldpath, String newpath) throws ErrnoException { throw new ErrnoException(EROFS); } } public static class ResourceFS extends FS { final InodeCache inodes = new InodeCache(500); public FStat lstat(UnixRuntime r, String path) throws ErrnoException { return stat(r,path); } 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); } public void rmdir(UnixRuntime r, String path) throws ErrnoException { throw new ErrnoException(EROFS); } public void link(UnixRuntime r, String oldpath, String newpath) throws ErrnoException { throw new ErrnoException(EROFS); } FStat connFStat(final URLConnection conn) { return new FStat() { public int type() { return S_IFREG; } public int nlink() { return 1; } public int mode() { return 0444; } public int size() { return conn.getContentLength(); } public int mtime() { return (int)(conn.getDate() / 1000); } public int inode() { return inodes.get(conn.getURL().toString()); } public int dev() { return devno; } }; } public FStat stat(UnixRuntime r, String path) throws ErrnoException { URL url = r.getClass().getResource("/" + path); if(url == null) return null; try { return connFStat(url.openConnection()); } catch(IOException e) { throw new ErrnoException(EIO); } } public FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException { if((flags & ~3) != 0) { if(STDERR_DIAG) System.err.println("WARNING: Unsupported flags passed to ResourceFS.open(\"" + path + "\"): " + toHex(flags & ~3)); throw new ErrnoException(ENOTSUP); } if((flags&3) != RD_ONLY) throw new ErrnoException(EROFS); URL url = r.getClass().getResource("/" + path); if(url == null) return null; try { final URLConnection conn = url.openConnection(); Seekable.InputStream si = new Seekable.InputStream(conn.getInputStream()); return new SeekableFD(si,flags) { protected FStat _fstat() { return connFStat(conn); } }; } catch(FileNotFoundException e) { if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) throw new ErrnoException(EACCES); return null; } catch(IOException e) { throw new ErrnoException(EIO); } } } }