X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fnestedvm%2FUnixRuntime.java;h=4c9ed5ab5a12ae6a9bbcf43ad8fd7434b7fe45b6;hp=051a50be063926a90b68c1f811487254a09ae4d2;hb=37a9506df9dab17552c6c34b62dc4295504b6c8c;hpb=c59b7cfc7a6b67574d38c5c8eb7732bad37236b0 diff --git a/src/org/ibex/nestedvm/UnixRuntime.java b/src/org/ibex/nestedvm/UnixRuntime.java index 051a50b..4c9ed5a 100644 --- a/src/org/ibex/nestedvm/UnixRuntime.java +++ b/src/org/ibex/nestedvm/UnixRuntime.java @@ -3,20 +3,22 @@ package org.ibex.nestedvm; import org.ibex.nestedvm.util.*; import java.io.*; import java.util.*; +import java.net.*; -// FEATURE: Remove System.{out,err}.printlns and throw Errors where applicable - -// 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: vfork public abstract class UnixRuntime extends Runtime implements Cloneable { /** The pid of this "process" */ private int pid; private UnixRuntime parent; - protected int getPid() { return pid; } + public final int getPid() { return pid; } - private static final GlobalState defaultGD = new GlobalState(); - private GlobalState gd = defaultGD; + 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 "" = root, "bin" = /bin "usr/bin" = /usr/bin */ @@ -29,12 +31,14 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { private Vector activeChildren; private Vector exitedChildren; - public UnixRuntime(int pageSize, int totalPages) { + protected UnixRuntime(int pageSize, int totalPages) { super(pageSize,totalPages); // FEATURE: Do the proper mangling for non-unix hosts String userdir = getSystemProperty("user.dir"); - cwd = userdir != null && userdir.startsWith("/") && File.separatorChar == '/' ? userdir.substring(1) : ""; + cwd = + userdir != null && userdir.startsWith("/") && File.separatorChar == '/' && getSystemProperty("nestedvm.root") == null + ? userdir.substring(1) : ""; } // NOTE: getDisplayName() is a Java2 function @@ -59,17 +63,18 @@ 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= gd.tasks.length)) return -ECHILD; + if(pid !=-1 && (pid <= 0 || pid >= gs.tasks.length)) return -ECHILD; if(children == null) return blocking ? -ECHILD : 0; UnixRuntime done = null; @@ -166,7 +196,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { if(pid == -1) { if(exitedChildren.size() > 0) done = (UnixRuntime)exitedChildren.remove(exitedChildren.size() - 1); } else if(pid > 0) { - UnixRuntime t = gd.tasks[pid]; + 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"); @@ -174,14 +204,14 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } } else { // process group stuff, EINVAL returned above - throw new Error("should never happen"); + 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()); + //System.err.println("waitpid woke up: " + exitedChildren.size()); } else { - gd.tasks[done.pid] = null; + gs.tasks[done.pid] = null; break; } } @@ -191,11 +221,11 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } - protected void _exited() { + void _exited() { if(children != null) synchronized(children) { for(Enumeration e = exitedChildren.elements(); e.hasMoreElements(); ) { UnixRuntime child = (UnixRuntime) e.nextElement(); - gd.tasks[child.pid] = null; + gs.tasks[child.pid] = null; } exitedChildren.clear(); for(Enumeration e = activeChildren.elements(); e.hasMoreElements(); ) { @@ -207,13 +237,13 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { UnixRuntime _parent = parent; if(_parent == null) { - gd.tasks[pid] = null; + gs.tasks[pid] = null; } else { synchronized(_parent.children) { if(parent == null) { - gd.tasks[pid] = null; + gs.tasks[pid] = null; } else { - parent.activeChildren.remove(this); + if(!parent.activeChildren.remove(this)) throw new Error("should never happen _exited: pid: " + pid); parent.exitedChildren.add(this); parent.children.notify(); } @@ -222,7 +252,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } protected Object clone() throws CloneNotSupportedException { - UnixRuntime r = (UnixRuntime) super.clone(); + UnixRuntime r = (UnixRuntime) super.clone(); r.pid = 0; r.parent = null; r.children = null; @@ -248,10 +278,10 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { try { r._started(); } catch(ProcessTableFullExn e) { - return -ENOMEM; + return -ENOMEM; } - System.err.println("fork " + pid + " -> " + r.pid + " tasks[" + r.pid + "] = " + gd.tasks[r.pid]); + //System.err.println("fork " + pid + " -> " + r.pid + " tasks[" + r.pid + "] = " + gd.tasks[r.pid]); if(children == null) { children = new Object(); activeChildren = new Vector(); @@ -279,10 +309,10 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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; @@ -290,7 +320,7 @@ 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= 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 = gd.stat(this,normalizePath(cstring(cstring))); + 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 = gd.lstat(this,normalizePath(cstring(cstring))); + 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 { - gd.mkdir(this,normalizePath(cstring(cstring)),mode); + 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) throws FaultException, ErrnoException { byte[] b = getBytes(cwd); @@ -417,25 +514,458 @@ 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(gd.stat(this,path).type() != FStat.S_IFDIR) return -ENOTDIR; + //System.err.println("Chdir: " + cstring(addr) + " -> " + path + " pwd: " + cwd); + if(gs.stat(this,path).type() != FStat.S_IFDIR) return -ENOTDIR; cwd = path; - System.err.println("Now: [" + cwd + "]"); + //System.err.println("Now: [" + cwd + "]"); + 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; + } + + 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()) { + ((Socket)o).setKeepAlive((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 { + DatagramSocket ds = (DatagramSocket) o; + dp.setData(a,off,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 { + DatagramSocket ds = (DatagramSocket) o; + dp.setData(a,off,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 { + inetAddr = InetAddress.getByAddress(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: { + DatagramSocket s = (DatagramSocket) fd.o; + if(s == null) s = new DatagramSocket(); + s.connect(inetAddr,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 = InetAddress.getByAddress(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_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 { + fd.o = 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 = (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 { + 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.o == null) return -ENOTCONN; + + Socket s = (Socket) fd.o; + + try { + if(how == SHUT_RD || how == SHUT_RDWR) s.shutdownInput(); + if(how == SHUT_WR || how == SHUT_RDWR) s.shutdownOutput(); + } 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 LSTAT = 3; protected static final int MKDIR = 4; + protected static final int UNLINK = 5; - // Make this protected (need to fix _exit) - private final UnixRuntime[] tasks; - private int nextPID = 1; + final UnixRuntime[] tasks; + int nextPID = 1; - private final MP[][] mps = new MP[128][]; + private MP[] mps = new MP[0]; private FS root; public GlobalState() { this(255); } @@ -443,12 +973,12 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { public GlobalState(int maxProcs, boolean defaultMounts) { tasks = new UnixRuntime[maxProcs+1]; if(defaultMounts) { - root = new HostFS(); + addMount("/",new HostFS()); addMount("/dev",new DevFS()); } } - private static class MP { + private static class MP implements Comparable { public MP(String path, FS fs) { this.path = path; this.fs = fs; } public String path; public FS fs; @@ -462,64 +992,79 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /"); if(path.equals("/")) return root; path = path.substring(1); - 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; + 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++; @@ -703,29 +1234,45 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { //System.err.println("normalize: " + path + " -> " + new String(out,0,outp) + " (cwd: " + cwd + ")"); return new String(out,0,outp); } - - // FEATURE: override Runtime.hostFStat to add executable type checking - // hostFStat - /* try { - FileInputStream fis = new FileInputStream(f); - switch(fis.read()) { - case '\177': _executable = fis.read() == 'E' && fis.read() == 'L' && fis.read() == 'F'; break; - case '#': _executable = fis.read() == '!'; - } - fis.close(); - } catch(IOException e) { } - */ + + 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; } + }; + } - // FEATURE: inode stuff - FD hostFSDirFD(File f) { return FS.directoryFD(f.list(),f.hashCode()); } + 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; } 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; @@ -736,7 +1283,7 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { 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 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(); } + }; + } } public static class DevFS extends FS { - private static class DevFStat extends FStat { - public int dev() { return 1; } + 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 static FD devZeroFD = new FD() { - public boolean readable() { return true; } - public boolean writable() { return true; } + + 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); 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(); } + public FStat _fstat() { return new DevFStat(){ public int inode() { return ZERO_INODE; } }; } }; - private static FD devNullFD = new FD() { - public boolean readable() { return true; } - public boolean writable() { return true; } + 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 FStat _fstat() { return new DevFStat(){ public int inode() { return NULL_INODE; } }; } }; public FD open(UnixRuntime r, String path, int mode, int flags) throws ErrnoException { @@ -813,15 +1451,42 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { } if(path.equals("fd")) { int count=0; - for(int i=0;i