X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fnestedvm%2FUnixRuntime.java;h=adddc51ff21b05617746a2c803ff38d89779be49;hp=a4b1991dde398d7182d6c2b05b1d0ce1a9992c3d;hb=b8372da83bb6fe6064c1f6387411a5b2af9f4e7b;hpb=615390526d6598c4124370fdd77b55894413cd1d diff --git a/src/org/ibex/nestedvm/UnixRuntime.java b/src/org/ibex/nestedvm/UnixRuntime.java index a4b1991..adddc51 100644 --- a/src/org/ibex/nestedvm/UnixRuntime.java +++ b/src/org/ibex/nestedvm/UnixRuntime.java @@ -1,9 +1,14 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache 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.lang.reflect.*; // For lazily linked RuntimeCompiler // FEATURE: vfork @@ -14,9 +19,10 @@ 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"); + if(gs == null) throw new NullPointerException("gs is null"); this.gs = gs; } @@ -31,14 +37,17 @@ 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 == '/' && getSystemProperty("nestedvm.root") == null - ? userdir.substring(1) : ""; + 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() { @@ -63,15 +72,18 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } String[] createEnv(String[] extra) { - String[] defaults = new String[6]; + String[] defaults = new String[7]; 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"); + String tmp; + if(!envHas("USER",extra) && Platform.getProperty("user.name") != null) + defaults[n++] = "USER=" + Platform.getProperty("user.name"); + if(!envHas("HOME",extra) && (tmp=Platform.getProperty("user.home")) != null && (tmp=gs.mapHostPath(tmp)) != null) + defaults[n++] = "HOME=" + tmp; + if(!envHas("TMPDIR",extra) && (tmp=Platform.getProperty("java.io.tmpdir")) != null && (tmp=gs.mapHostPath(tmp)) != null) + defaults[n++] = "TMPDIR=" + tmp; 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]; @@ -134,19 +146,70 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { case SYS_accept: return sys_accept(a,b,c); case SYS_shutdown: return sys_shutdown(a,b); case SYS_sysctl: return sys_sysctl(a,b,c,d,e,f); - + case SYS_sendto: return sys_sendto(a,b,c,d,e,f); + case SYS_recvfrom: return sys_recvfrom(a,b,c,d,e,f); + case SYS_select: return sys_select(a,b,c,d,e); + case SYS_access: return sys_access(a,b); + case SYS_realpath: return sys_realpath(a,b); + case SYS_chown: return sys_chown(a,b,c); + case SYS_lchown: return sys_chown(a,b,c); + case SYS_fchown: return sys_fchown(a,b,c); + case SYS_chmod: return sys_chmod(a,b,c); + case SYS_fchmod: return sys_fchmod(a,b,c); + case SYS_fcntl: return sys_fcntl_lock(a,b,c); + case SYS_umask: return sys_umask(a); + default: return super._syscall(syscall,a,b,c,d,e,f); } } FD _open(String path, int flags, int mode) throws ErrnoException { - return gs.open(this,normalizePath(path),flags,mode); + path = normalizePath(path); + FD fd = gs.open(this,path,flags,mode); + if (fd != null && path != null) fd.setNormalizedPath(path); + return fd; } private int sys_getppid() { return parent == null ? 1 : parent.pid; } + + private int sys_chown(int fileAddr, int uid, int gid) { + return 0; + } + private int sys_lchown(int fileAddr, int uid, int gid) { + return 0; + } + private int sys_fchown(int fd, int uid, int gid) { + return 0; + } + private int sys_chmod(int fileAddr, int uid, int gid) { + return 0; + } + private int sys_fchmod(int fd, int uid, int gid) { + return 0; + } + private int sys_umask(int mask) { + return 0; + } + + private int sys_access(int cstring, int mode) throws ErrnoException, ReadFaultException { + // FEATURE: sys_access + return gs.stat(this,normalizePath(cstring(cstring))) == null ? -ENOENT : 0; + } + + private int sys_realpath(int inAddr, int outAddr) throws FaultException { + String s = normalizePath(cstring(inAddr)); + byte[] b = getNullTerminatedBytes(s); + if(b.length > PATH_MAX) return -ERANGE; + copyout(b,outAddr,b.length); + return 0; + } + // FEATURE: Signal handling + // check flag only on backwards jumps to basic blocks without compulsatory checks + // (see A Portable Research Framework for the Execution of Java Bytecode - Etienne Gagnon, Chapter 2) + /** The kill syscall. SIGSTOP, SIGTSTO, SIGTTIN, and SIGTTOUT pause the process. SIGCONT, SIGCHLD, SIGIO, and SIGWINCH are ignored. @@ -162,16 +225,13 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { case 18: // SIGTSTP case 21: // SIGTTIN case 22: // SIGTTOU - state = PAUSED; - break; case 19: // SIGCONT case 20: // SIGCHLD case 23: // SIGIO case 28: // SIGWINCH break; default: - // FEATURE: This is ugly, make a clean interface to sys_exit - return syscall(SYS_exit,128+signal,0,0,0,0,0); + exit(128+signal, true); } return 0; } @@ -198,6 +258,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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) { @@ -263,9 +324,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 { @@ -291,6 +349,8 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } 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); @@ -332,42 +392,156 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { private int sys_exec(int cpath, int cargv, int cenvp) throws ErrnoException, FaultException { return exec(normalizePath(cstring(cpath)),readStringArray(cargv),readStringArray(cenvp)); } + + private final static Method runtimeCompilerCompile; + static { + Method m; + try { + m = Class.forName("org.ibex.nestedvm.RuntimeCompiler").getMethod("compile",new Class[]{Seekable.class,String.class,String.class}); + } catch(NoSuchMethodException e) { + m = null; + } catch(ClassNotFoundException e) { + m = null; + } + runtimeCompilerCompile = m; + } + + public Class runtimeCompile(Seekable s, String sourceName) throws IOException { + if(runtimeCompilerCompile == null) { + if(STDERR_DIAG) System.err.println("WARNING: Exec attempted but RuntimeCompiler not found!"); + return null; + } - private int exec(String normalizedPath, String[] argv, String[] envp) throws ErrnoException { + try { + return (Class) runtimeCompilerCompile.invoke(null,new Object[]{s,"unixruntime,maxinsnpermethod=256,lessconstants",sourceName}); + } catch(IllegalAccessException e) { + e.printStackTrace(); + return null; + } catch(InvocationTargetException e) { + Throwable t = e.getTargetException(); + if(t instanceof IOException) throw (IOException) t; + if(t instanceof RuntimeException) throw (RuntimeException) t; + if(t instanceof Error) throw (Error) t; + if(STDERR_DIAG) t.printStackTrace(); + return null; + } + } + + private int exec(String path, String[] argv, String[] envp) throws ErrnoException { if(argv.length == 0) argv = new String[]{""}; - + // HACK: Hideous hack to make a standalone busybox possible + if(path.equals("bin/busybox") && getClass().getName().endsWith("BusyBox")) + return execClass(getClass(),argv,envp); + // 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); - } catch(Exception e) { - e.printStackTrace(); - return -ENOEXEC; + FStat fstat = gs.stat(this,path); + if(fstat == null) return -ENOENT; + GlobalState.CacheEnt ent = (GlobalState.CacheEnt) gs.execCache.get(path); + long mtime = fstat.mtime(); + long size = fstat.size(); + if(ent != null) { + //System.err.println("Found cached entry for " + path); + if(ent.time ==mtime && ent.size == size) { + if(ent.o instanceof Class) + return execClass((Class) ent.o,argv,envp); + if(ent.o instanceof String[]) + return execScript(path,(String[]) ent.o,argv,envp); + throw new Error("should never happen"); } - } else { - String[] command = (String[]) o; - String[] newArgv = new String[argv.length + command[1] != null ? 2 : 1]; - int p = command[0].lastIndexOf('/'); - newArgv[0] = p == -1 ? command[0] : command[0].substring(p+1); - p = 1; - if(command[1] != null) newArgv[p++] = command[1]; - newArgv[p++] = "/" + normalizedPath; - for(int i=1;i= 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; @@ -544,19 +878,31 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { int flags; int options; - Object o; + + Socket s; + ServerSocket ss; + DatagramSocket ds; + InetAddress bindAddr; int bindPort = -1; + InetAddress connectAddr; + int connectPort = -1; + DatagramPacket dp; InputStream is; OutputStream os; - public SocketFD(int type) { flags = type; } + 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(o != null && type() == TYPE_STREAM && !listen()) { - Platform.socketSetKeepAlive((Socket)o,(options & SO_KEEPALIVE) != 0); + if(s != null && type() == TYPE_STREAM && !listen()) { + Platform.socketSetKeepAlive(s,(options & SO_KEEPALIVE) != 0); } } catch(SocketException e) { if(STDERR_DIAG) e.printStackTrace(); @@ -564,76 +910,87 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } 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 */ - } + 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_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(); + 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 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(); - } + 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); - // 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; + if(os == null) throw new ErrnoException(EPIPE); + try { + os.write(a,off,length); + return length; + } catch(IOException e) { + throw new ErrnoException(EIO); + } } - // FEATURE: Populate this properly - public FStat _fstat() { return new FStat(); } + 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) { @@ -652,7 +1009,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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; + 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; @@ -666,22 +1023,21 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { return -EADDRNOTAVAIL; } + fd.connectAddr = inetAddr; + fd.connectPort = port; + try { switch(fd.type()) { case SocketFD.TYPE_STREAM: { Socket s = new Socket(inetAddr,port); - fd.o = s; + fd.s = 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); + case SocketFD.TYPE_DGRAM: break; - } default: throw new Error("should never happen"); } @@ -761,7 +1117,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { private int sys_bind(int fdn, int addr, int namelen) throws FaultException, ErrnoException { SocketFD fd = getSocketFD(fdn); - if(fd.type() == SocketFD.TYPE_STREAM && fd.o != null) return -EISCONN; + 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; @@ -784,10 +1140,9 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { return 0; } case SocketFD.TYPE_DGRAM: { - DatagramSocket s = (DatagramSocket) fd.o; - if(s != null) s.close(); + if(fd.ds != null) fd.ds.close(); try { - fd.o = inetAddr != null ? new DatagramSocket(port,inetAddr) : new DatagramSocket(port); + fd.ds = inetAddr != null ? new DatagramSocket(port,inetAddr) : new DatagramSocket(port); } catch(IOException e) { return -EADDRINUSE; } @@ -801,11 +1156,11 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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.ss != null || fd.s != null) return -EISCONN; if(fd.bindPort < 0) return -EOPNOTSUPP; try { - fd.o = new ServerSocket(fd.bindPort,backlog,fd.bindAddr); + fd.ss = new ServerSocket(fd.bindPort,backlog,fd.bindAddr); fd.flags |= SocketFD.LISTEN; return 0; } catch(IOException e) { @@ -821,7 +1176,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { int size = memRead(lenaddr); - ServerSocket s = (ServerSocket) fd.o; + ServerSocket s = fd.ss; Socket client; try { client = s.accept(); @@ -837,7 +1192,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } SocketFD clientFD = new SocketFD(SocketFD.TYPE_STREAM); - clientFD.o = client; + clientFD.s = client; try { clientFD.is = client.getInputStream(); clientFD.os = client.getOutputStream(); @@ -852,9 +1207,9 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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; + if(fd.s == null) return -ENOTCONN; - Socket s = (Socket) fd.o; + Socket s = fd.s; try { if(how == SHUT_RD || how == SHUT_RDWR) Platform.socketHalfClose(s,false); @@ -866,6 +1221,58 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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(); @@ -913,66 +1320,16 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } 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 LSTAT = 3; - protected static final int MKDIR = 4; - protected static final int UNLINK = 5; + 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; @@ -981,12 +1338,65 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { public GlobalState(int maxProcs, boolean defaultMounts) { tasks = new UnixRuntime[maxProcs+1]; if(defaultMounts) { - addMount("/",new HostFS()); + 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 + ")"); - return new String(out,0,outp); + int outStart = out[0] == '/' ? 1 : 0; + return new String(out,outStart,outp - outStart); } FStat hostFStat(final File f, Object data) { @@ -1272,21 +1602,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { protected File root; public File getRoot() { return root; } - private static File hostRootDir() { - if(getSystemProperty("nestedvm.root") != null) { - File f = new File(getSystemProperty("nestedvm.root")); - if(f.isDirectory()) return f; - // fall through to case below - } - String cwd = getSystemProperty("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()); - return f; - } - - private File hostFile(String path) { + protected File hostFile(String path) { char sep = File.separatorChar; if(sep != '/') { char buf[] = path.toCharArray(); @@ -1300,11 +1616,9 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { return new File(root,path); } - public HostFS() { this(hostRootDir()); } public HostFS(String root) { this(new File(root)); } public HostFS(File root) { this.root = root; } - public FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException { final File f = hostFile(path); return r.hostFSOpen(f,flags,mode,this); @@ -1314,7 +1628,21 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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); + if(!f.delete()) { + // Can't delete file immediately, so mark for + // delete on close all matching FDs + boolean marked = false; + for(int i=0;i 'z' || path.charAt(1) != '/') + return null; + + path = drive + ":" + path.substring(1).replace('/', '\\'); + return new File(path); + } + + public CygdriveFS() { super("/"); } + } private static void putInt(byte[] buf, int off, int n) { buf[off+0] = (byte)((n>>>24)&0xff); @@ -1374,9 +1720,10 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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; } - + 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; @@ -1427,11 +1774,12 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { private static final int FD_INODE = 4; private static final int FD_INODES = 32; - private class DevFStat extends FStat { + 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 { @@ -1447,12 +1795,14 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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(){ public int inode() { return ZERO_INODE; } }; } + public int flags() { return O_RDWR; } }; private FD devNullFD = new FD() { 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(){ public int inode() { return NULL_INODE; } }; } + public int flags() { return O_RDWR; } }; public FD open(UnixRuntime r, String path, int mode, int flags) throws ErrnoException { @@ -1461,7 +1811,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { if(path.startsWith("fd/")) { int n; try { - n = Integer.parseInt(path.substring(4)); + n = Integer.parseInt(path.substring(3)); } catch(NumberFormatException e) { return null; } @@ -1486,8 +1836,8 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { if(path.equals("")) { return new DevDirFD() { public int myInode() { return ROOT_INODE; } - // FEATURE: Get the real parent inode somehow - public int parentInode() { return -1; } + // HACK: We don't have any clean way to get the parent inode + public int parentInode() { return ROOT_INODE; } public int inode(int n) { switch(n) { case 0: return NULL_INODE; @@ -1517,7 +1867,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { if(path.startsWith("fd/")) { int n; try { - n = Integer.parseInt(path.substring(4)); + n = Integer.parseInt(path.substring(3)); } catch(NumberFormatException e) { return null; } @@ -1525,13 +1875,62 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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; }}; + 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 unlink(UnixRuntime r, String path) 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); } + + 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); } + } + } }