licensing update to APSL 2.0
[nestedvm.git] / src / org / ibex / nestedvm / UnixRuntime.java
1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
4
5 package org.ibex.nestedvm;
6
7 import org.ibex.nestedvm.util.*;
8 // HACK: This is ugly, this stuff needs to be in org.ibex.util or something
9 import org.ibex.classgen.util.Sort;
10 import java.io.*;
11 import java.util.*;
12 import java.net.*;
13 import java.lang.reflect.*; // For lazily linked RuntimeCompiler
14
15 // FEATURE: vfork
16
17 public abstract class UnixRuntime extends Runtime implements Cloneable {
18     /** The pid of this "process" */
19     private int pid;
20     private UnixRuntime parent;
21     public final int getPid() { return pid; }
22     
23     private static final GlobalState defaultGS = new GlobalState();
24     private GlobalState gs;
25     public void setGlobalState(GlobalState gs) {
26         if(state != STOPPED) throw new IllegalStateException("can't change GlobalState when running");
27         if(gs == null) throw new NullPointerException("gs is null");
28         this.gs = gs;
29     }
30     
31     /** proceses' current working directory - absolute path WITHOUT leading slash
32         "" = root, "bin" = /bin "usr/bin" = /usr/bin */
33     private String cwd;
34     
35     /** The runtime that should be run next when in state == EXECED */
36     private UnixRuntime execedRuntime;
37
38     private Object children; // used only for synchronizatin
39     private Vector activeChildren;
40     private Vector exitedChildren;
41     
42     protected UnixRuntime(int pageSize, int totalPages) { this(pageSize,totalPages,false); }
43     protected UnixRuntime(int pageSize, int totalPages, boolean exec) {
44         super(pageSize,totalPages,exec);
45                 
46         if(!exec) {
47             gs = defaultGS;
48             String userdir = Platform.getProperty("user.dir");
49             String nvroot = Platform.getProperty("nestedvm.root");
50             cwd = "";
51             if(userdir != null && nvroot == null) {
52                 if(userdir.startsWith("/") && File.separatorChar == '/') {
53                     cwd = userdir.substring(1);
54                 } else {
55                     Vector vec = new Vector();
56                     File root = HostFS.hostRootDir();
57                     String s = new File(userdir).getAbsolutePath();
58                     File d = new File(s);
59                     System.err.println(s);
60                     System.err.println(d);
61                     while(!d.equals(root)) {
62                         System.err.println("Got " + d.getName());
63                         vec.addElement(d.getName());
64                         if((s = d.getParent()) == null) break;
65                         d = new File(s);
66                     }
67                     if(s != null)
68                         for(int i=vec.size()-1;i>=0;i--) cwd += (String) vec.elementAt(i) + (i==0?"":"/");
69                 }
70             }
71         }
72     }
73     
74     private static String posixTZ() {
75         StringBuffer sb = new StringBuffer();
76         TimeZone zone = TimeZone.getDefault();
77         int off = zone.getRawOffset() / 1000;
78         sb.append(Platform.timeZoneGetDisplayName(zone,false,false));
79         if(off > 0) sb.append("-");
80         else off = -off;
81         sb.append(off/3600); off = off%3600;
82         if(off > 0) sb.append(":").append(off/60); off=off%60;
83         if(off > 0) sb.append(":").append(off);
84         if(zone.useDaylightTime())
85             sb.append(Platform.timeZoneGetDisplayName(zone,true,false));
86         return sb.toString();
87     }
88     
89     private static boolean envHas(String key,String[] environ) {
90         for(int i=0;i<environ.length;i++)
91             if(environ[i]!=null && environ[i].startsWith(key + "=")) return true;
92         return false;
93     }
94     
95     String[] createEnv(String[] extra) {
96         String[] defaults = new String[6];
97         int n=0;
98         if(extra == null) extra = new String[0];
99         if(!envHas("USER",extra) && Platform.getProperty("user.name") != null)
100             defaults[n++] = "USER=" + Platform.getProperty("user.name");
101         if(!envHas("HOME",extra) && Platform.getProperty("user.home") != null)
102             defaults[n++] = "HOME=" + Platform.getProperty("user.home");
103         if(!envHas("SHELL",extra)) defaults[n++] = "SHELL=/bin/sh";
104         if(!envHas("TERM",extra) && !win32Hacks)  defaults[n++] = "TERM=vt100";
105         if(!envHas("TZ",extra))    defaults[n++] = "TZ=" + posixTZ();
106         if(!envHas("PATH",extra))  defaults[n++] = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin";
107         String[] env = new String[extra.length+n];
108         for(int i=0;i<n;i++) env[i] = defaults[i];
109         for(int i=0;i<extra.length;i++) env[n++] = extra[i];
110         return env;
111     }
112     
113     private static class ProcessTableFullExn extends RuntimeException { }
114     
115     void _started() {
116         UnixRuntime[] tasks = gs.tasks;
117         synchronized(gs) {
118             if(pid != 0) {
119                 UnixRuntime prev = tasks[pid];
120                 if(prev == null || prev == this || prev.pid != pid || prev.parent != parent)
121                     throw new Error("should never happen");
122                 synchronized(parent.children) {
123                     int i = parent.activeChildren.indexOf(prev);
124                     if(i == -1) throw new Error("should never happen");
125                     parent.activeChildren.setElementAt(this,i);
126                 }
127             } else {
128                 int newpid = -1;
129                 int nextPID = gs.nextPID;
130                 for(int i=nextPID;i<tasks.length;i++) if(tasks[i] == null) { newpid = i; break; }
131                 if(newpid == -1) for(int i=1;i<nextPID;i++) if(tasks[i] == null) { newpid = i; break; }
132                 if(newpid == -1) throw new ProcessTableFullExn();
133                 pid = newpid;
134                 gs.nextPID = newpid + 1;
135             }
136             tasks[pid] = this;
137         }
138     }
139     
140     int _syscall(int syscall, int a, int b, int c, int d, int e, int f) throws ErrnoException, FaultException {
141         switch(syscall) {
142             case SYS_kill: return sys_kill(a,b);
143             case SYS_fork: return sys_fork();
144             case SYS_pipe: return sys_pipe(a);
145             case SYS_dup2: return sys_dup2(a,b);
146             case SYS_dup: return sys_dup(a);
147             case SYS_waitpid: return sys_waitpid(a,b,c);
148             case SYS_stat: return sys_stat(a,b);
149             case SYS_lstat: return sys_lstat(a,b);
150             case SYS_mkdir: return sys_mkdir(a,b);
151             case SYS_getcwd: return sys_getcwd(a,b);
152             case SYS_chdir: return sys_chdir(a);
153             case SYS_exec: return sys_exec(a,b,c);
154             case SYS_getdents: return sys_getdents(a,b,c,d);
155             case SYS_unlink: return sys_unlink(a);
156             case SYS_getppid: return sys_getppid();
157             case SYS_socket: return sys_socket(a,b,c);
158             case SYS_connect: return sys_connect(a,b,c);
159             case SYS_resolve_hostname: return sys_resolve_hostname(a,b,c);
160             case SYS_setsockopt: return sys_setsockopt(a,b,c,d,e);
161             case SYS_getsockopt: return sys_getsockopt(a,b,c,d,e);
162             case SYS_bind: return sys_bind(a,b,c);
163             case SYS_listen: return sys_listen(a,b);
164             case SYS_accept: return sys_accept(a,b,c);
165             case SYS_shutdown: return sys_shutdown(a,b);
166             case SYS_sysctl: return sys_sysctl(a,b,c,d,e,f);
167             case SYS_sendto: return sys_sendto(a,b,c,d,e,f);
168             case SYS_recvfrom: return sys_recvfrom(a,b,c,d,e,f);
169             case SYS_select: return sys_select(a,b,c,d,e);
170
171             default: return super._syscall(syscall,a,b,c,d,e,f);
172         }
173     }
174     
175     FD _open(String path, int flags, int mode) throws ErrnoException {
176         return gs.open(this,normalizePath(path),flags,mode);
177     }
178     
179     private int sys_getppid() {
180         return parent == null ? 1 : parent.pid;
181     }
182
183     // FEATURE: Signal handling
184     // check flag only on backwards jumps to basic blocks without compulsatory checks 
185     // (see A Portable Research Framework for the Execution of Java Bytecode - Etienne Gagnon, Chapter 2)
186     
187     /** The kill syscall.
188        SIGSTOP, SIGTSTO, SIGTTIN, and SIGTTOUT pause the process.
189        SIGCONT, SIGCHLD, SIGIO, and SIGWINCH are ignored.
190        Anything else terminates the process. */
191     private int sys_kill(int pid, int signal) {
192         // This will only be called by raise() in newlib to invoke the default handler
193         // We don't have to worry about actually delivering the signal
194         if(pid != pid) return -ESRCH;
195         if(signal < 0 || signal >= 32) return -EINVAL;
196         switch(signal) {
197             case 0: return 0;
198             case 17: // SIGSTOP
199             case 18: // SIGTSTP
200             case 21: // SIGTTIN
201             case 22: // SIGTTOU
202             case 19: // SIGCONT
203             case 20: // SIGCHLD
204             case 23: // SIGIO
205             case 28: // SIGWINCH
206                 break;
207             default:
208                 exit(128+signal, true);
209         }
210         return 0;
211     }
212
213     private int sys_waitpid(int pid, int statusAddr, int options) throws FaultException, ErrnoException {
214         final int WNOHANG = 1;
215         if((options & ~(WNOHANG)) != 0) return -EINVAL;
216         if(pid == 0 || pid < -1) {
217             if(STDERR_DIAG) System.err.println("WARNING: waitpid called with a pid of " + pid);
218             return -ECHILD;
219         }
220         boolean blocking = (options&WNOHANG)==0;
221         
222         if(pid !=-1 && (pid <= 0 || pid >= gs.tasks.length)) return -ECHILD;
223         if(children == null) return blocking ? -ECHILD : 0;
224         
225         UnixRuntime done = null;
226         
227         synchronized(children) {
228             for(;;) {
229                 if(pid == -1) {
230                     if(exitedChildren.size() > 0) {
231                         done = (UnixRuntime)exitedChildren.elementAt(exitedChildren.size() - 1);
232                         exitedChildren.removeElementAt(exitedChildren.size() - 1);
233                     }
234                 } else if(pid > 0) {
235                     if(pid >= gs.tasks.length) return -ECHILD;
236                     UnixRuntime t = gs.tasks[pid];
237                     if(t.parent != this) return -ECHILD;
238                     if(t.state == EXITED) {
239                         if(!exitedChildren.removeElement(t)) throw new Error("should never happen");
240                         done = t;
241                     }
242                 } else {
243                     // process group stuff, EINVAL returned above
244                         throw new Error("should never happen");
245                 }
246                 if(done == null) {
247                     if(!blocking) return 0;
248                     try { children.wait(); } catch(InterruptedException e) {}
249                     //System.err.println("waitpid woke up: " + exitedChildren.size());
250                 } else {
251                     gs.tasks[done.pid] = null;
252                     break;
253                 }
254             }
255         }
256         if(statusAddr!=0) memWrite(statusAddr,done.exitStatus()<<8);
257         return done.pid;
258     }
259     
260     
261     void _exited() {
262         if(children != null) synchronized(children) {
263             for(Enumeration e = exitedChildren.elements(); e.hasMoreElements(); ) {
264                 UnixRuntime child = (UnixRuntime) e.nextElement();
265                 gs.tasks[child.pid] = null;
266             }
267             exitedChildren.removeAllElements();
268             for(Enumeration e = activeChildren.elements(); e.hasMoreElements(); ) {
269                 UnixRuntime child = (UnixRuntime) e.nextElement();
270                 child.parent = null;
271             }
272             activeChildren.removeAllElements();
273         }
274         
275         UnixRuntime _parent = parent;
276         if(_parent == null) {
277             gs.tasks[pid] = null;
278         } else {
279             synchronized(_parent.children) {
280                 if(parent == null) {
281                     gs.tasks[pid] = null;
282                 } else {
283                     if(!parent.activeChildren.removeElement(this)) throw new Error("should never happen _exited: pid: " + pid);
284                     parent.exitedChildren.addElement(this);
285                     parent.children.notify();
286                 }
287             }
288         }
289     }
290     
291     protected Object clone() throws CloneNotSupportedException {
292         UnixRuntime r = (UnixRuntime) super.clone();
293         r.pid = 0;
294         r.parent = null;
295         r.children = null;
296         r.activeChildren = r.exitedChildren = null;
297         return r;
298     }
299
300     private int sys_fork() {
301         final UnixRuntime r;
302         
303         try {
304             r = (UnixRuntime) clone();
305         } catch(Exception e) {
306             e.printStackTrace();
307             return -ENOMEM;
308         }
309
310         r.parent = this;
311
312         try {
313             r._started();
314         } catch(ProcessTableFullExn e) {
315             return -ENOMEM;
316         }
317
318         //System.err.println("fork " + pid + " -> " + r.pid + " tasks[" + r.pid + "] = " + gd.tasks[r.pid]);
319         if(children == null) {
320             children = new Object();
321             activeChildren = new Vector();
322             exitedChildren = new Vector();
323         }
324         activeChildren.addElement(r);
325         
326         CPUState state = new CPUState();
327         getCPUState(state);
328         state.r[V0] = 0; // return 0 to child
329         state.pc += 4; // skip over syscall instruction
330         r.setCPUState(state);
331         r.state = PAUSED;
332         
333         new ForkedProcess(r);
334         
335         return r.pid;
336     }
337     
338     public static final class ForkedProcess extends Thread {
339         private final UnixRuntime initial;
340         public ForkedProcess(UnixRuntime initial) { this.initial = initial; start(); }
341         public void run() { UnixRuntime.executeAndExec(initial); }
342     }
343     
344     public static int runAndExec(UnixRuntime r, String argv0, String[] rest) { return runAndExec(r,concatArgv(argv0,rest)); }
345     public static int runAndExec(UnixRuntime r, String[] argv) { r.start(argv); return executeAndExec(r); }
346     
347     public static int executeAndExec(UnixRuntime r) {
348         for(;;) {
349             for(;;) {
350                 if(r.execute()) break;
351                 if(STDERR_DIAG) System.err.println("WARNING: Pause requested while executing runAndExec()");
352             }
353             if(r.state != EXECED) return r.exitStatus();
354             r = r.execedRuntime;
355         }
356     }
357      
358     private String[] readStringArray(int addr) throws ReadFaultException {
359         int count = 0;
360         for(int p=addr;memRead(p) != 0;p+=4) count++;
361         String[] a = new String[count];
362         for(int i=0,p=addr;i<count;i++,p+=4) a[i] = cstring(memRead(p));
363         return a;
364     }
365     
366     private int sys_exec(int cpath, int cargv, int cenvp) throws ErrnoException, FaultException {
367         return exec(normalizePath(cstring(cpath)),readStringArray(cargv),readStringArray(cenvp));
368     }
369     
370     private final static Method runtimeCompilerCompile;
371     static {
372         Method m;
373         try {
374             m = Class.forName("org.ibex.nestedvm.RuntimeCompiler").getMethod("compile",new Class[]{Seekable.class,String.class});
375         } catch(NoSuchMethodException e) {
376             m = null;
377         } catch(ClassNotFoundException e) {
378             m = null;
379         }
380         runtimeCompilerCompile = m;
381     }
382             
383     public Class runtimeCompile(Seekable s) throws IOException {
384         if(runtimeCompilerCompile == null) {
385             if(STDERR_DIAG) System.err.println("WARNING: Exec attempted but RuntimeCompiler not found!");
386             return null;
387         }
388         
389         try {
390             return (Class) runtimeCompilerCompile.invoke(null,new Object[]{s,"unixruntime"});
391         } catch(IllegalAccessException e) {
392             e.printStackTrace();
393             return null;
394         } catch(InvocationTargetException e) {
395             Throwable t = e.getTargetException();
396             if(t instanceof IOException) throw (IOException) t;
397             if(t instanceof RuntimeException) throw (RuntimeException) t;
398             if(t instanceof Error) throw (Error) t;
399             if(STDERR_DIAG) t.printStackTrace();
400             return null;
401         }
402     }
403         
404     private int exec(String path, String[] argv, String[] envp) throws ErrnoException {
405         if(argv.length == 0) argv = new String[]{""};
406         // HACK: Hideous hack to make a standalone busybox possible
407         if(path.equals("bin/busybox") && getClass().getName().endsWith("BusyBox"))
408             return execClass(getClass(),argv,envp);
409         
410         // NOTE: For this little hack to work nestedvm.root MUST be "."
411         /*try {
412             System.err.println("Execing normalized path: " + normalizedPath);
413             if(true) return exec(new Interpreter(normalizedPath),argv,envp);
414         } catch(IOException e) { throw new Error(e); }*/
415         
416         FStat fstat = gs.stat(this,path);
417         if(fstat == null) return -ENOENT;
418         GlobalState.CacheEnt ent = (GlobalState.CacheEnt) gs.execCache.get(path);
419         long mtime = fstat.mtime();
420         long size = fstat.size();
421         if(ent != null) {
422             //System.err.println("Found cached entry for " + path);
423             if(ent.time ==mtime && ent.size == size) {
424                 if(ent.o instanceof Class)
425                     return execClass((Class) ent.o,argv,envp);
426                 if(ent.o instanceof String[]) 
427                     return execScript(path,(String[]) ent.o,argv,envp);
428                 throw new Error("should never happen");
429             }
430             //System.err.println("Cache was out of date");
431             gs.execCache.remove(path);
432         }
433         
434         FD fd = gs.open(this,path,RD_ONLY,0);
435         if(fd == null) throw new ErrnoException(ENOENT);
436         Seekable s = fd.seekable();        
437         if(s == null) throw new ErrnoException(EACCES);
438         
439         byte[] buf = new byte[4096];
440         
441         try {
442             int n = s.read(buf,0,buf.length);
443             if(n == -1) throw new ErrnoException(ENOEXEC);
444             
445             switch(buf[0]) {
446                 case '\177': // possible ELF
447                     if(n < 4) s.tryReadFully(buf,n,4-n);
448                     if(buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F') return -ENOEXEC;
449                     s.seek(0);
450                     Class c = runtimeCompile(s);
451                     if(c == null) throw new ErrnoException(ENOEXEC);
452                     gs.execCache.put(path,new GlobalState.CacheEnt(mtime,size,c));
453                     return execClass(c,argv,envp);
454                 case '#':
455                     if(n == 1) {
456                         int n2 = s.read(buf,1,buf.length-1);
457                         if(n2 == -1) return -ENOEXEC;
458                         n += n2;
459                     }
460                     if(buf[1] != '!') return -ENOEXEC;
461                     int p = 2;
462                     n -= 2;
463                     OUTER: for(;;) {
464                         for(int i=p;i<p+n;i++) if(buf[i] == '\n') { p = i; break OUTER; }
465                             p += n;
466                         if(p == buf.length) break OUTER;
467                         n = s.read(buf,p,buf.length-p);
468                     }
469                     int arg;
470                     for(arg=2;arg<p;arg++) if(buf[arg] == ' ') break;
471                     int cmdEnd = arg;
472                     while(arg < p && buf[arg] == ' ') arg++;
473                     String[] command = new String[] {
474                         new String(buf,2,cmdEnd),
475                         arg < p ? new String(buf,arg,p-arg) : null
476                     };
477                     gs.execCache.put(path,new GlobalState.CacheEnt(mtime,size,command));
478                     return execScript(path,command,argv,envp);
479                 default:
480                     return -ENOEXEC;
481             }
482         } catch(IOException e) {
483             return -EIO;
484         } finally {
485             fd.close();
486         }        
487     }
488     
489     public int execScript(String path, String[] command, String[] argv, String[] envp) throws ErrnoException {
490         String[] newArgv = new String[argv.length + command[1] != null ? 2 : 1];
491         int p = command[0].lastIndexOf('/');
492         newArgv[0] = p == -1 ? command[0] : command[0].substring(p+1);
493         p = 1;
494         if(command[1] != null) newArgv[p++] = command[1];
495         newArgv[p++] = "/" + path;
496         for(int i=1;i<argv.length;i++) newArgv[p++] = argv[i];
497         return exec(command[0],newArgv,envp);
498     }
499     
500     public int execClass(Class c,String[] argv, String[] envp) {
501         try {
502             UnixRuntime r = (UnixRuntime) c.getDeclaredConstructor(new Class[]{Boolean.TYPE}).newInstance(new Object[]{Boolean.TRUE});
503             return exec(r,argv,envp);
504         } catch(Exception e) {
505             e.printStackTrace();
506             return -ENOEXEC;
507         }
508     }
509     
510     private int exec(UnixRuntime r, String[] argv, String[] envp) {     
511         //System.err.println("Execing " + r);
512         for(int i=0;i<OPEN_MAX;i++) if(closeOnExec[i]) closeFD(i);
513         r.fds = fds;
514         r.closeOnExec = closeOnExec;
515         // make sure this doesn't get messed with these since we didn't copy them
516         fds = null;
517         closeOnExec = null;
518         
519         r.gs = gs;
520         r.sm = sm;
521         r.cwd = cwd;
522         r.pid = pid;
523         r.parent = parent;
524         r.start(argv,envp);
525                 
526         state = EXECED;
527         execedRuntime = r;
528         
529         return 0;   
530     }
531     
532     static class Pipe {
533         private final byte[] pipebuf = new byte[PIPE_BUF*4];
534         private int readPos;
535         private int writePos;
536         
537         public final FD reader = new Reader();
538         public final FD writer = new Writer();
539         
540         public class Reader extends FD {
541             protected FStat _fstat() { return new SocketFStat(); }
542             public int read(byte[] buf, int off, int len) throws ErrnoException {
543                 if(len == 0) return 0;
544                 synchronized(Pipe.this) {
545                     while(writePos != -1 && readPos == writePos) {
546                         try { Pipe.this.wait(); } catch(InterruptedException e) { /* ignore */ }
547                     }
548                     if(writePos == -1) return 0; // eof
549                     len = Math.min(len,writePos-readPos);
550                     System.arraycopy(pipebuf,readPos,buf,off,len);
551                     readPos += len;
552                     if(readPos == writePos) Pipe.this.notify();
553                     return len;
554                 }
555             }
556             public int flags() { return O_RDONLY; }
557             public void _close() { synchronized(Pipe.this) { readPos = -1; Pipe.this.notify(); } }
558         }
559         
560         public class Writer extends FD {   
561             protected FStat _fstat() { return new SocketFStat(); }
562             public int write(byte[] buf, int off, int len) throws ErrnoException {
563                 if(len == 0) return 0;
564                 synchronized(Pipe.this) {
565                     if(readPos == -1) throw new ErrnoException(EPIPE);
566                     if(pipebuf.length - writePos < Math.min(len,PIPE_BUF)) {
567                         // not enough space to atomicly write the data
568                         while(readPos != -1 && readPos != writePos) {
569                             try { Pipe.this.wait(); } catch(InterruptedException e) { /* ignore */ }
570                         }
571                         if(readPos == -1) throw new ErrnoException(EPIPE);
572                         readPos = writePos = 0;
573                     }
574                     len = Math.min(len,pipebuf.length - writePos);
575                     System.arraycopy(buf,off,pipebuf,writePos,len);
576                     if(readPos == writePos) Pipe.this.notify();
577                     writePos += len;
578                     return len;
579                 }
580             }
581             public int flags() { return O_WRONLY; }
582             public void _close() { synchronized(Pipe.this) { writePos = -1; Pipe.this.notify(); } }
583         }
584     }
585     
586     private int sys_pipe(int addr) {
587         Pipe pipe = new Pipe();
588         
589         int fd1 = addFD(pipe.reader);
590         if(fd1 < 0) return -ENFILE;
591         int fd2 = addFD(pipe.writer);
592         if(fd2 < 0) { closeFD(fd1); return -ENFILE; }
593         
594         try {
595             memWrite(addr,fd1);
596             memWrite(addr+4,fd2);
597         } catch(FaultException e) {
598             closeFD(fd1);
599             closeFD(fd2);
600             return -EFAULT;
601         }
602         return 0;
603     }
604     
605     private int sys_dup2(int oldd, int newd) {
606         if(oldd == newd) return 0;
607         if(oldd < 0 || oldd >= OPEN_MAX) return -EBADFD;
608         if(newd < 0 || newd >= OPEN_MAX) return -EBADFD;
609         if(fds[oldd] == null) return -EBADFD;
610         if(fds[newd] != null) fds[newd].close();
611         fds[newd] = fds[oldd].dup();
612         return 0;
613     }
614     
615     private int sys_dup(int oldd) {
616         if(oldd < 0 || oldd >= OPEN_MAX) return -EBADFD;
617         if(fds[oldd] == null) return -EBADFD;
618         FD fd = fds[oldd].dup();
619         int newd = addFD(fd);
620         if(newd < 0) { fd.close(); return -ENFILE; }
621         return newd;
622     }
623     
624     private int sys_stat(int cstring, int addr) throws FaultException, ErrnoException {
625         FStat s = gs.stat(this,normalizePath(cstring(cstring)));
626         if(s == null) return -ENOENT;
627         return stat(s,addr);
628     }
629     
630     private int sys_lstat(int cstring, int addr) throws FaultException, ErrnoException {
631         FStat s = gs.lstat(this,normalizePath(cstring(cstring)));
632         if(s == null) return -ENOENT;
633         return stat(s,addr);
634     }
635     
636     private int sys_mkdir(int cstring, int mode) throws FaultException, ErrnoException {
637         gs.mkdir(this,normalizePath(cstring(cstring)),mode);
638         return 0;
639     }
640    
641     private int sys_unlink(int cstring) throws FaultException, ErrnoException {
642         gs.unlink(this,normalizePath(cstring(cstring)));
643         return 0;
644     }
645     
646     private int sys_getcwd(int addr, int size) throws FaultException, ErrnoException {
647         byte[] b = getBytes(cwd);
648         if(size == 0) return -EINVAL;
649         if(size < b.length+2) return -ERANGE;
650         memset(addr,'/',1);
651         copyout(b,addr+1,b.length);
652         memset(addr+b.length+1,0,1);
653         return addr;
654     }
655     
656     private int sys_chdir(int addr) throws ErrnoException, FaultException {
657         String path = normalizePath(cstring(addr));
658         FStat st = gs.stat(this,path);
659         if(st == null) return -ENOENT;
660         if(st.type() != FStat.S_IFDIR) return -ENOTDIR;
661         cwd = path;
662         return 0;
663     }
664     
665     private int sys_getdents(int fdn, int addr, int count, int seekptr) throws FaultException, ErrnoException {
666         count = Math.min(count,MAX_CHUNK);
667         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
668         if(fds[fdn] == null) return -EBADFD;
669         byte[] buf = byteBuf(count);
670         int n = fds[fdn].getdents(buf,0,count);
671         copyout(buf,addr,n);
672         return n;
673     }
674     
675     static class SocketFD extends FD {
676         public static final int TYPE_STREAM = 0;
677         public static final int TYPE_DGRAM = 1;
678         public static final int LISTEN = 2;
679         public int type() { return flags & 1; }
680         public boolean listen() { return (flags & 2) != 0; }
681         
682         int flags;
683         int options;
684         
685         Socket s;
686         ServerSocket ss;
687         DatagramSocket ds;
688         
689         InetAddress bindAddr;
690         int bindPort = -1;
691         InetAddress connectAddr;
692         int connectPort = -1;
693         
694         DatagramPacket dp;
695         InputStream is;
696         OutputStream os; 
697         
698         private static final byte[] EMPTY = new byte[0];
699         public SocketFD(int type) {
700                 flags = type;
701                 if(type == TYPE_DGRAM)
702                         dp = new DatagramPacket(EMPTY,0);
703         }
704         
705         public void setOptions() {
706             try {
707                 if(s != null && type() == TYPE_STREAM && !listen()) {
708                     Platform.socketSetKeepAlive(s,(options & SO_KEEPALIVE) != 0);
709                 }
710             } catch(SocketException e) {
711                 if(STDERR_DIAG) e.printStackTrace();
712             }
713         }
714         
715         public void _close() {
716             try {
717                if(s != null) s.close();
718                if(ss != null) ss.close();
719                if(ds != null) ds.close();
720             } catch(IOException e) {
721                 /* ignore */
722             }
723         }
724         
725         public int read(byte[] a, int off, int length) throws ErrnoException {
726             if(type() == TYPE_DGRAM) return recvfrom(a,off,length,null,null);
727             if(is == null) throw new ErrnoException(EPIPE);
728             try {
729                 int n = is.read(a,off,length);
730                 return n < 0 ? 0 : n;
731             } catch(IOException e) {
732                 throw new ErrnoException(EIO);
733             }
734         }    
735         
736         public int recvfrom(byte[] a, int off, int length, InetAddress[] sockAddr, int[] port) throws ErrnoException {
737                 if(type() == TYPE_STREAM) return read(a,off,length);
738                 
739                 if(off != 0) throw new IllegalArgumentException("off must be 0");
740                 dp.setData(a);
741                 dp.setLength(length);
742                 try {
743                         if(ds == null) ds = new DatagramSocket();
744                         ds.receive(dp);
745                 } catch(IOException e) {
746                         if(STDERR_DIAG) e.printStackTrace();
747                         throw new ErrnoException(EIO);
748                 }
749                 if(sockAddr != null) {
750                         sockAddr[0] = dp.getAddress();
751                         port[0] = dp.getPort();
752                 }
753                 return dp.getLength();
754         }
755         
756         public int write(byte[] a, int off, int length) throws ErrnoException {
757             if(type() == TYPE_DGRAM) return  sendto(a,off,length,null,-1);
758
759             if(os == null) throw new ErrnoException(EPIPE);
760             try {
761                 os.write(a,off,length);
762                 return length;
763             } catch(IOException e) {
764                 throw new ErrnoException(EIO);
765             }
766         }
767         
768         public int sendto(byte[] a, int off, int length, InetAddress destAddr, int destPort) throws ErrnoException {
769                 if(off != 0) throw new IllegalArgumentException("off must be 0");
770                 if(type() == TYPE_STREAM) return write(a,off,length);
771                 
772                 if(destAddr == null) {
773                         destAddr = connectAddr;
774                         destPort = connectPort;
775                         
776                         if(destAddr == null) throw new ErrnoException(ENOTCONN);
777                 }
778                 
779                 dp.setAddress(destAddr);
780                 dp.setPort(destPort);
781                 dp.setData(a);
782                 dp.setLength(length);
783                 
784                 try {
785                         if(ds == null) ds = new DatagramSocket();
786                         ds.send(dp);
787                 } catch(IOException e) {
788                         if(STDERR_DIAG) e.printStackTrace();
789                         if("Network is unreachable".equals(e.getMessage())) throw new ErrnoException(EHOSTUNREACH);
790                         throw new ErrnoException(EIO);
791                 }
792                 return dp.getLength();
793         }
794
795         public int flags() { return O_RDWR; }
796         public FStat _fstat() { return new SocketFStat(); }
797     }
798     
799     private int sys_socket(int domain, int type, int proto) {
800         if(domain != AF_INET || (type != SOCK_STREAM && type != SOCK_DGRAM)) return -EPROTONOSUPPORT;
801         return addFD(new SocketFD(type == SOCK_STREAM ? SocketFD.TYPE_STREAM : SocketFD.TYPE_DGRAM));
802     }
803     
804     private SocketFD getSocketFD(int fdn) throws ErrnoException {
805         if(fdn < 0 || fdn >= OPEN_MAX) throw new ErrnoException(EBADFD);
806         if(fds[fdn] == null) throw new ErrnoException(EBADFD);
807         if(!(fds[fdn] instanceof SocketFD)) throw new ErrnoException(ENOTSOCK);
808         
809         return (SocketFD) fds[fdn];
810     }
811     
812     private int sys_connect(int fdn, int addr, int namelen) throws ErrnoException, FaultException {
813         SocketFD fd = getSocketFD(fdn);
814         
815         if(fd.type() == SocketFD.TYPE_STREAM && (fd.s != null || fd.ss != null)) return -EISCONN;
816         int word1 = memRead(addr);
817         if( ((word1 >>> 16)&0xff) != AF_INET) return -EAFNOSUPPORT;
818         int port = word1 & 0xffff;
819         byte[] ip = new byte[4];
820         copyin(addr+4,ip,4);
821         
822         InetAddress inetAddr;
823         try {
824             inetAddr = Platform.inetAddressFromBytes(ip);
825         } catch(UnknownHostException e) {
826             return -EADDRNOTAVAIL;
827         }
828         
829         fd.connectAddr = inetAddr;
830         fd.connectPort = port;
831         
832         try {
833             switch(fd.type()) {
834                 case SocketFD.TYPE_STREAM: {
835                     Socket s = new Socket(inetAddr,port);
836                     fd.s = s;
837                     fd.setOptions();
838                     fd.is = s.getInputStream();
839                     fd.os = s.getOutputStream();
840                     break;
841                 }
842                 case SocketFD.TYPE_DGRAM:
843                     break;
844                 default:
845                     throw new Error("should never happen");
846             }
847         } catch(IOException e) {
848             return -ECONNREFUSED;
849         }
850         
851         return 0;
852     }
853     
854     private int sys_resolve_hostname(int chostname, int addr, int sizeAddr) throws FaultException {
855         String hostname = cstring(chostname);
856         int size = memRead(sizeAddr);
857         InetAddress[] inetAddrs;
858         try {
859             inetAddrs = InetAddress.getAllByName(hostname);
860         } catch(UnknownHostException e) {
861             return HOST_NOT_FOUND;
862         }
863         int count = min(size/4,inetAddrs.length);
864         for(int i=0;i<count;i++,addr+=4) {
865             byte[] b = inetAddrs[i].getAddress();
866             copyout(b,addr,4);
867         }
868         memWrite(sizeAddr,count*4);
869         return 0;
870     }
871     
872     private int sys_setsockopt(int fdn, int level, int name, int valaddr, int len) throws ReadFaultException, ErrnoException {
873         SocketFD fd = getSocketFD(fdn);
874         switch(level) {
875             case SOL_SOCKET:
876                 switch(name) {
877                     case SO_REUSEADDR:
878                     case SO_KEEPALIVE: {
879                         if(len != 4) return -EINVAL;
880                         int val = memRead(valaddr);
881                         if(val != 0) fd.options |= name;
882                         else fd.options &= ~name;
883                         fd.setOptions();
884                         return 0;
885                     }
886                     default:
887                         if(STDERR_DIAG) System.err.println("Unknown setsockopt name passed: " + name);
888                         return -ENOPROTOOPT;
889                 }
890             default:
891                 if(STDERR_DIAG) System.err.println("Unknown setsockopt leve passed: " + level);
892                 return -ENOPROTOOPT;
893         }                   
894     }
895     
896     private int sys_getsockopt(int fdn, int level, int name, int valaddr, int lenaddr) throws ErrnoException, FaultException {
897         SocketFD fd = getSocketFD(fdn);
898         switch(level) {
899             case SOL_SOCKET:
900                 switch(name) {
901                     case SO_REUSEADDR:
902                     case SO_KEEPALIVE: {
903                         int len = memRead(lenaddr);
904                         if(len < 4) return -EINVAL;
905                         int val = (fd.options & name) != 0 ? 1 : 0;
906                         memWrite(valaddr,val);
907                         memWrite(lenaddr,4);
908                         return 0;
909                     }
910                     default:
911                         if(STDERR_DIAG) System.err.println("Unknown setsockopt name passed: " + name);
912                         return -ENOPROTOOPT;
913                 }
914             default:
915                 if(STDERR_DIAG) System.err.println("Unknown setsockopt leve passed: " + level);
916                 return -ENOPROTOOPT;
917         } 
918     }
919     
920     private int sys_bind(int fdn, int addr, int namelen) throws FaultException, ErrnoException {
921         SocketFD fd = getSocketFD(fdn);
922         
923         if(fd.type() == SocketFD.TYPE_STREAM && (fd.s != null || fd.ss != null)) return -EISCONN;
924         int word1 = memRead(addr);
925         if( ((word1 >>> 16)&0xff) != AF_INET) return -EAFNOSUPPORT;
926         int port = word1 & 0xffff;
927         InetAddress inetAddr = null;
928         if(memRead(addr+4) != 0) {
929             byte[] ip = new byte[4];
930             copyin(addr+4,ip,4);
931         
932             try {
933                 inetAddr = Platform.inetAddressFromBytes(ip);
934             } catch(UnknownHostException e) {
935                 return -EADDRNOTAVAIL;
936             }
937         }
938         
939         switch(fd.type()) {
940             case SocketFD.TYPE_STREAM: {
941                 fd.bindAddr = inetAddr;
942                 fd.bindPort = port;
943                 return 0;
944             }
945             case SocketFD.TYPE_DGRAM: {
946                 if(fd.ds != null) fd.ds.close();
947                 try {
948                     fd.ds = inetAddr != null ? new DatagramSocket(port,inetAddr) : new DatagramSocket(port);
949                 } catch(IOException e) {
950                     return -EADDRINUSE;
951                 }
952                 return 0;
953             }
954             default:
955                 throw new Error("should never happen");
956         }
957     }
958     
959     private int sys_listen(int fdn, int backlog) throws ErrnoException {
960         SocketFD fd = getSocketFD(fdn);
961         if(fd.type() != SocketFD.TYPE_STREAM) return -EOPNOTSUPP;
962         if(fd.ss != null || fd.s != null) return -EISCONN;
963         if(fd.bindPort < 0) return -EOPNOTSUPP;
964         
965         try {
966             fd.ss = new ServerSocket(fd.bindPort,backlog,fd.bindAddr);
967             fd.flags |= SocketFD.LISTEN;
968             return 0;
969         } catch(IOException e) {
970             return -EADDRINUSE;
971         }
972         
973     }
974     
975     private int sys_accept(int fdn, int addr, int lenaddr) throws ErrnoException, FaultException {
976         SocketFD fd = getSocketFD(fdn);
977         if(fd.type() != SocketFD.TYPE_STREAM) return -EOPNOTSUPP;
978         if(!fd.listen()) return -EOPNOTSUPP;
979
980         int size = memRead(lenaddr);
981         
982         ServerSocket s = fd.ss;
983         Socket client;
984         try {
985             client = s.accept();
986         } catch(IOException e) {
987             return -EIO;
988         }
989         
990         if(size >= 8) {
991             memWrite(addr,(6 << 24) | (AF_INET << 16) | client.getPort());
992             byte[] b = client.getInetAddress().getAddress();
993             copyout(b,addr+4,4);
994             memWrite(lenaddr,8);
995         }
996         
997         SocketFD clientFD = new SocketFD(SocketFD.TYPE_STREAM);
998         clientFD.s = client;
999         try {
1000             clientFD.is = client.getInputStream();
1001             clientFD.os = client.getOutputStream();
1002         } catch(IOException e) {
1003             return -EIO;
1004         }
1005         int n = addFD(clientFD);
1006         if(n == -1) { clientFD.close(); return -ENFILE; }
1007         return n;
1008     }
1009     
1010     private int sys_shutdown(int fdn, int how) throws ErrnoException {
1011         SocketFD fd = getSocketFD(fdn);
1012         if(fd.type() != SocketFD.TYPE_STREAM || fd.listen()) return -EOPNOTSUPP;
1013         if(fd.s == null) return -ENOTCONN;
1014         
1015         Socket s = fd.s;
1016         
1017         try {
1018             if(how == SHUT_RD || how == SHUT_RDWR) Platform.socketHalfClose(s,false);
1019             if(how == SHUT_WR || how == SHUT_RDWR) Platform.socketHalfClose(s,true);
1020         } catch(IOException e) {
1021             return -EIO;
1022         }
1023         
1024         return 0;
1025     }
1026     
1027     private int sys_sendto(int fdn, int addr, int count, int flags, int destAddr, int socklen) throws ErrnoException,ReadFaultException {
1028         SocketFD fd = getSocketFD(fdn);
1029         if(flags != 0) throw new ErrnoException(EINVAL);
1030         
1031         int word1 = memRead(destAddr);
1032         if( ((word1 >>> 16)&0xff) != AF_INET) return -EAFNOSUPPORT;
1033         int port = word1 & 0xffff;
1034         InetAddress inetAddr;
1035                 byte[] ip = new byte[4];
1036                 copyin(destAddr+4,ip,4);
1037                 try {
1038                         inetAddr = Platform.inetAddressFromBytes(ip);
1039                 } catch(UnknownHostException e) {
1040                         return -EADDRNOTAVAIL;
1041                 }
1042         
1043         count = Math.min(count,MAX_CHUNK);
1044         byte[] buf = byteBuf(count);
1045         copyin(addr,buf,count);
1046         try {
1047                 return fd.sendto(buf,0,count,inetAddr,port);
1048         } catch(ErrnoException e) {
1049                 if(e.errno == EPIPE) exit(128+13,true);
1050                 throw e;
1051         }
1052     }
1053     
1054     private int sys_recvfrom(int fdn, int addr, int count, int flags, int sourceAddr, int socklenAddr) throws ErrnoException, FaultException {
1055         SocketFD fd = getSocketFD(fdn);
1056         if(flags != 0) throw new ErrnoException(EINVAL);
1057         
1058         InetAddress[] inetAddr = sourceAddr == 0 ? null : new InetAddress[1];
1059         int[] port = sourceAddr == 0 ? null : new int[1];
1060         
1061         count = Math.min(count,MAX_CHUNK);
1062         byte[] buf = byteBuf(count);
1063         int n = fd.recvfrom(buf,0,count,inetAddr,port);
1064         copyout(buf,addr,n);
1065         
1066         if(sourceAddr != 0) {
1067                 memWrite(sourceAddr,(AF_INET << 16) | port[0]);
1068                 byte[] ip = inetAddr[0].getAddress();
1069                 copyout(ip,sourceAddr+4,4);
1070         }
1071         
1072         return n;
1073     }
1074     
1075     private int sys_select(int n, int readFDs, int writeFDs, int exceptFDs, int timevalAddr) throws ReadFaultException, ErrnoException {
1076         return -ENOSYS;
1077     }
1078     
1079     private static String hostName() {
1080         try {
1081             return InetAddress.getLocalHost().getHostName();
1082         } catch(UnknownHostException e) {
1083             return "darkstar";
1084         }
1085     }
1086     
1087     private int sys_sysctl(int nameaddr, int namelen, int oldp, int oldlenaddr, int newp, int newlen) throws FaultException {
1088         if(newp != 0) return -EPERM;
1089         if(namelen == 0) return -ENOENT;
1090         if(oldp == 0) return 0;
1091         
1092         Object o = null;
1093         switch(memRead(nameaddr)) {
1094             case CTL_KERN:
1095                 if(namelen != 2) break;
1096                 switch(memRead(nameaddr+4)) {
1097                     case KERN_OSTYPE: o = "NestedVM"; break;
1098                     case KERN_HOSTNAME: o = hostName(); break;
1099                     case KERN_OSRELEASE: o = VERSION; break;
1100                     case KERN_VERSION: o = "NestedVM Kernel Version " + VERSION; break;
1101                 }
1102                 break;
1103             case CTL_HW:
1104                 if(namelen != 2) break;
1105                 switch(memRead(nameaddr+4)) {
1106                     case HW_MACHINE: o = "NestedVM Virtual Machine"; break;
1107                 }
1108                 break;
1109         }
1110         if(o == null) return -ENOENT;
1111         int len = memRead(oldlenaddr);
1112         if(o instanceof String) {
1113             byte[] b = getNullTerminatedBytes((String)o);
1114             if(len < b.length) return -ENOMEM;
1115             len = b.length;
1116             copyout(b,oldp,len);
1117             memWrite(oldlenaddr,len);
1118         } else if(o instanceof Integer) {
1119             if(len < 4) return -ENOMEM;
1120             memWrite(oldp,((Integer)o).intValue());
1121         } else {
1122             throw new Error("should never happen");
1123         }
1124         return 0;
1125     }
1126     
1127     public static final class GlobalState {
1128         Hashtable execCache = new Hashtable();
1129         
1130         final UnixRuntime[] tasks;
1131         int nextPID = 1;
1132         
1133         private MP[] mps = new MP[0];
1134         private FS root;
1135         
1136         public GlobalState() { this(255); }
1137         public GlobalState(int maxProcs) { this(maxProcs,true); }
1138         public GlobalState(int maxProcs, boolean defaultMounts) {
1139             tasks = new UnixRuntime[maxProcs+1];
1140             if(defaultMounts) {
1141                 addMount("/",new HostFS());
1142                 addMount("/dev",new DevFS());
1143             }
1144         }
1145         
1146         static class MP implements Sort.Comparable {
1147             public MP(String path, FS fs) { this.path = path; this.fs = fs; }
1148             public String path;
1149             public FS fs;
1150             public int compareTo(Object o) {
1151                 if(!(o instanceof MP)) return 1;
1152                 return -path.compareTo(((MP)o).path);
1153             }
1154         }
1155         
1156         public synchronized FS getMount(String path) {
1157             if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /");
1158             if(path.equals("/")) return root;
1159             path  = path.substring(1);
1160             for(int i=0;i<mps.length;i++)
1161                 if(mps[i].path.equals(path)) return mps[i].fs;
1162             return null;
1163         }
1164         
1165         public synchronized void addMount(String path, FS fs) {
1166             if(getMount(path) != null) throw new IllegalArgumentException("mount point already exists");
1167             if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /");
1168             
1169             if(fs.owner != null) fs.owner.removeMount(fs);
1170             fs.owner = this;
1171             
1172             if(path.equals("/")) { root = fs; fs.devno = 1; return; }
1173             path = path.substring(1);
1174             int oldLength = mps.length;
1175             MP[] newMPS = new MP[oldLength + 1];
1176             if(oldLength != 0) System.arraycopy(mps,0,newMPS,0,oldLength);
1177             newMPS[oldLength] = new MP(path,fs);
1178             Sort.sort(newMPS);
1179             mps = newMPS;
1180             int highdevno = 0;
1181             for(int i=0;i<mps.length;i++) highdevno = max(highdevno,mps[i].fs.devno);
1182             fs.devno = highdevno + 2;
1183         }
1184         
1185         public synchronized void removeMount(FS fs) {
1186             for(int i=0;i<mps.length;i++) if(mps[i].fs == fs) { removeMount(i); return; }
1187             throw new IllegalArgumentException("mount point doesn't exist");
1188         }
1189         
1190         public synchronized void removeMount(String path) {
1191             if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /");
1192             if(path.equals("/")) {
1193                 removeMount(-1);
1194             } else {
1195                 path = path.substring(1);
1196                 int p;
1197                 for(p=0;p<mps.length;p++) if(mps[p].path.equals(path)) break;
1198                 if(p == mps.length) throw new IllegalArgumentException("mount point doesn't exist");
1199                 removeMount(p);
1200             }
1201         }
1202         
1203         private void removeMount(int index) {
1204             if(index == -1) { root.owner = null; root = null; return; }
1205             MP[] newMPS = new MP[mps.length - 1];
1206             System.arraycopy(mps,0,newMPS,0,index);
1207             System.arraycopy(mps,0,newMPS,index,mps.length-index-1);
1208             mps = newMPS;
1209         }
1210         
1211         private Object fsop(int op, UnixRuntime r, String normalizedPath, int arg1, int arg2) throws ErrnoException {
1212             int pl = normalizedPath.length();
1213             if(pl != 0) {
1214                 MP[] list;
1215                 synchronized(this) { list = mps; }
1216                 for(int i=0;i<list.length;i++) {
1217                     MP mp = list[i];
1218                     int mpl = mp.path.length();
1219                     if(normalizedPath.startsWith(mp.path) && (pl == mpl || normalizedPath.charAt(mpl) == '/'))
1220                         return mp.fs.dispatch(op,r,pl == mpl ? "" : normalizedPath.substring(mpl+1),arg1,arg2);
1221                 }
1222             }
1223             return root.dispatch(op,r,normalizedPath,arg1,arg2);
1224         }
1225         
1226         public final FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException { return (FD) fsop(FS.OPEN,r,path,flags,mode); }
1227         public final FStat stat(UnixRuntime r, String path) throws ErrnoException { return (FStat) fsop(FS.STAT,r,path,0,0); }
1228         public final FStat lstat(UnixRuntime r, String path) throws ErrnoException { return (FStat) fsop(FS.LSTAT,r,path,0,0); }
1229         public final void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { fsop(FS.MKDIR,r,path,mode,0); }
1230         public final void unlink(UnixRuntime r, String path) throws ErrnoException { fsop(FS.UNLINK,r,path,0,0); }
1231         
1232         private static class CacheEnt {
1233             public final long time;
1234             public final long size;
1235             public final Object o;
1236             public CacheEnt(long time, long size, Object o) { this.time = time; this.size = size; this.o = o; }
1237         }
1238     }
1239     
1240     public abstract static class FS {
1241         static final int OPEN = 1;
1242         static final int STAT = 2;
1243         static final int LSTAT = 3;
1244         static final int MKDIR = 4;
1245         static final int UNLINK = 5;
1246         
1247         GlobalState owner;
1248         int devno;
1249         
1250         Object dispatch(int op, UnixRuntime r, String path, int arg1, int arg2) throws ErrnoException {
1251             switch(op) {
1252                 case OPEN: return open(r,path,arg1,arg2);
1253                 case STAT: return stat(r,path);
1254                 case LSTAT: return lstat(r,path);
1255                 case MKDIR: mkdir(r,path,arg1); return null;
1256                 case UNLINK: unlink(r,path); return null;
1257                 default: throw new Error("should never happen");
1258             }
1259         }
1260         
1261         public FStat lstat(UnixRuntime r, String path) throws ErrnoException { return stat(r,path); }
1262
1263         // If this returns null it'll be truned into an ENOENT
1264         public abstract FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException;
1265         // If this returns null it'll be turned into an ENOENT
1266         public abstract FStat stat(UnixRuntime r, String path) throws ErrnoException;
1267         public abstract void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException;
1268         public abstract void unlink(UnixRuntime r, String path) throws ErrnoException;
1269     }
1270         
1271     // chroot support should go in here if it is ever implemented chroot support in here
1272     private String normalizePath(String path) {
1273         boolean absolute = path.startsWith("/");
1274         int cwdl = cwd.length();
1275         
1276         // NOTE: This isn't just a fast path, it handles cases the code below doesn't
1277         if(!path.startsWith(".") && path.indexOf("./") == -1 && path.indexOf("//") == -1 && !path.endsWith("."))
1278             return absolute ? path.substring(1) : cwdl == 0 ? path : path.length() == 0 ? cwd : cwd + "/" + path;
1279         
1280         char[] in = new char[path.length()+1];
1281         char[] out = new char[in.length + (absolute ? -1 : cwd.length())];
1282         int inp=0, outp=0;
1283         
1284         if(absolute) {
1285             do { inp++; } while(in[inp] == '/');
1286         } else if(cwdl != 0) {
1287             cwd.getChars(0,cwdl,out,0);
1288             outp = cwdl;
1289         }
1290
1291         path.getChars(0,path.length(),in,0);
1292         while(in[inp] != 0) {
1293             if(inp != 0) {
1294                 while(in[inp] != 0 && in[inp] != '/') { out[outp++] = in[inp++]; }
1295                 if(in[inp] == '\0') break;
1296                 while(in[inp] == '/') inp++;
1297             }
1298             
1299             // Just read a /
1300             if(in[inp] == '\0') break;
1301             if(in[inp] != '.') { out[outp++] = '/'; out[outp++] = in[inp++]; continue; }
1302             // Just read a /.
1303             if(in[inp+1] == '\0' || in[inp+1] == '/') { inp++; continue; }
1304             if(in[inp+1] == '.' && (in[inp+2] == '\0' || in[inp+2] == '/')) { // ..
1305                 // Just read a /..{$,/}
1306                 inp += 2;
1307                 if(outp > 0) outp--;
1308                 while(outp > 0 && out[outp] != '/') outp--;
1309                 //System.err.println("After ..: " + new String(out,0,outp));
1310                 continue;
1311             }
1312             // Just read a /.[^.] or /..[^/$]
1313             inp++;
1314             out[outp++] = '/';
1315             out[outp++] = '.';
1316         }
1317         if(outp > 0 && out[outp-1] == '/') outp--;
1318         //System.err.println("normalize: " + path + " -> " + new String(out,0,outp) + " (cwd: " + cwd + ")");
1319         return new String(out,0,outp);
1320     }
1321     
1322     FStat hostFStat(final File f, Object data) {
1323         boolean e = false;
1324         try {
1325             FileInputStream fis = new FileInputStream(f);
1326             switch(fis.read()) {
1327                 case '\177': e = fis.read() == 'E' && fis.read() == 'L' && fis.read() == 'F'; break;
1328                 case '#': e = fis.read() == '!';
1329             }
1330             fis.close();
1331         } catch(IOException e2) { } 
1332         HostFS fs = (HostFS) data;
1333         final int inode = fs.inodes.get(f.getAbsolutePath());
1334         final int devno = fs.devno;
1335         return new HostFStat(f,e) {
1336             public int inode() { return inode; }
1337             public int dev() { return devno; }
1338         };
1339     }
1340
1341     FD hostFSDirFD(File f, Object _fs) {
1342         HostFS fs = (HostFS) _fs;
1343         return fs.new HostDirFD(f);
1344     }
1345     
1346     public static class HostFS extends FS {
1347         InodeCache inodes = new InodeCache(4000);
1348         protected File root;
1349         public File getRoot() { return root; }
1350         
1351         static File hostRootDir() {
1352             if(Platform.getProperty("nestedvm.root") != null) {
1353                 File f = new File(Platform.getProperty("nestedvm.root"));
1354                 if(f.isDirectory()) return f;
1355                 // fall through to case below
1356             }
1357             String cwd = Platform.getProperty("user.dir");
1358             File f = new File(cwd != null ? cwd : ".");
1359             if(!f.exists()) throw new Error("Couldn't get File for cwd");
1360             f = new File(f.getAbsolutePath());
1361             while(f.getParent() != null) f = new File(f.getParent());
1362             // This works around a bug in some versions of ClassPath
1363             if(f.getPath().length() == 0) f = new File("/");
1364             return f;
1365         }
1366         
1367         private File hostFile(String path) {
1368             char sep = File.separatorChar;
1369             if(sep != '/') {
1370                 char buf[] = path.toCharArray();
1371                 for(int i=0;i<buf.length;i++) {
1372                     char c = buf[i];
1373                     if(c == '/') buf[i] = sep;
1374                     else if(c == sep) buf[i] = '/';
1375                 }
1376                 path = new String(buf);
1377             }
1378             return new File(root,path);
1379         }
1380         
1381         public HostFS() { this(hostRootDir()); }
1382         public HostFS(String root) { this(new File(root)); }
1383         public HostFS(File root) { this.root = root; }
1384         
1385         
1386         public FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException {
1387             final File f = hostFile(path);
1388             return r.hostFSOpen(f,flags,mode,this);
1389         }
1390         
1391         public void unlink(UnixRuntime r, String path) throws ErrnoException {
1392             File f = hostFile(path);
1393             if(r.sm != null && !r.sm.allowUnlink(f)) throw new ErrnoException(EPERM);
1394             if(!f.exists()) throw new ErrnoException(ENOENT);
1395             if(!f.delete()) throw new ErrnoException(EPERM);
1396         }
1397         
1398         public FStat stat(UnixRuntime r, String path) throws ErrnoException {
1399             File f = hostFile(path);
1400             if(r.sm != null && !r.sm.allowStat(f)) throw new ErrnoException(EACCES);
1401             if(!f.exists()) return null;
1402             return r.hostFStat(f,this);
1403         }
1404         
1405         public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException {
1406             File f = hostFile(path);
1407             if(r.sm != null && !r.sm.allowWrite(f)) throw new ErrnoException(EACCES);
1408             if(f.exists() && f.isDirectory()) throw new ErrnoException(EEXIST);
1409             if(f.exists()) throw new ErrnoException(ENOTDIR);
1410             File parent = getParentFile(f);
1411             if(parent!=null && (!parent.exists() || !parent.isDirectory())) throw new ErrnoException(ENOTDIR);
1412             if(!f.mkdir()) throw new ErrnoException(EIO);            
1413         }
1414         
1415         private static File getParentFile(File f) {
1416             String p = f.getParent();
1417             return p == null ? null : new File(f,p);
1418         }
1419         
1420         public class HostDirFD extends DirFD {
1421             private final File f;
1422             private final File[] children;
1423             public HostDirFD(File f) {
1424                 this.f = f;
1425                 String[] l = f.list();
1426                 children = new File[l.length];
1427                 for(int i=0;i<l.length;i++) children[i] = new File(f,l[i]);
1428             }
1429             public int size() { return children.length; }
1430             public String name(int n) { return children[n].getName(); }
1431             public int inode(int n) { return inodes.get(children[n].getAbsolutePath()); }
1432             public int parentInode() {
1433                 File parent = getParentFile(f);
1434                 // HACK: myInode() isn't really correct  if we're not the root
1435                 return parent == null ? myInode() : inodes.get(parent.getAbsolutePath());
1436             }
1437             public int myInode() { return inodes.get(f.getAbsolutePath()); }
1438             public int myDev() { return devno; } 
1439         }
1440     }
1441     
1442     private static void putInt(byte[] buf, int off, int n) {
1443         buf[off+0] = (byte)((n>>>24)&0xff);
1444         buf[off+1] = (byte)((n>>>16)&0xff);
1445         buf[off+2] = (byte)((n>>> 8)&0xff);
1446         buf[off+3] = (byte)((n>>> 0)&0xff);
1447     }
1448     
1449     public static abstract class DirFD extends FD {
1450         private int pos = -2;
1451         
1452         protected abstract int size();
1453         protected abstract String name(int n);
1454         protected abstract int inode(int n);
1455         protected abstract int myDev();
1456         protected abstract int parentInode();
1457         protected abstract int myInode();
1458         public int flags() { return O_RDONLY; }
1459
1460         public int getdents(byte[] buf, int off, int len) {
1461             int ooff = off;
1462             int ino;
1463             int reclen;
1464             OUTER: for(;len > 0 && pos < size();pos++){
1465                 switch(pos) {
1466                     case -2:
1467                     case -1:
1468                         ino = pos == -1 ? parentInode() : myInode();
1469                         if(ino == -1) continue;
1470                         reclen = 9 + (pos == -1 ? 2 : 1);
1471                         if(reclen > len) break OUTER;
1472                         buf[off+8] = '.';
1473                         if(pos == -1) buf[off+9] = '.';
1474                         break;
1475                     default: {
1476                         String f = name(pos);
1477                         byte[] fb = getBytes(f);
1478                         reclen = fb.length + 9;
1479                         if(reclen > len) break OUTER;
1480                         ino = inode(pos);
1481                         System.arraycopy(fb,0,buf,off+8,fb.length);
1482                     }
1483                 }
1484                 buf[off+reclen-1] = 0; // null terminate
1485                 reclen = (reclen + 3) & ~3; // add padding
1486                 putInt(buf,off,reclen);
1487                 putInt(buf,off+4,ino);
1488                 off += reclen;
1489                 len -= reclen;    
1490             }
1491             return off-ooff;
1492         }
1493         
1494         protected FStat _fstat() {
1495             return new FStat() { 
1496                 public int type() { return S_IFDIR; }
1497                 public int inode() { return myInode(); }
1498                 public int dev() { return myDev(); }
1499             };
1500         }
1501     }
1502         
1503     public static class DevFS extends FS {
1504         private static final int ROOT_INODE = 1;
1505         private static final int NULL_INODE = 2;
1506         private static final int ZERO_INODE = 3;
1507         private static final int FD_INODE = 4;
1508         private static final int FD_INODES = 32;
1509         
1510         private abstract class DevFStat extends FStat {
1511             public int dev() { return devno; }
1512             public int mode() { return 0666; }
1513             public int type() { return S_IFCHR; }
1514             public int nlink() { return 1; }
1515             public abstract int inode();
1516         }
1517         
1518         private abstract class DevDirFD extends DirFD {
1519             public int myDev() { return devno; }
1520         }
1521         
1522         private FD devZeroFD = new FD() {
1523             public int read(byte[] a, int off, int length) { 
1524                 /*Arrays.fill(a,off,off+length,(byte)0);*/
1525                 for(int i=off;i<off+length;i++) a[i] = 0;
1526                 return length;
1527             }
1528             public int write(byte[] a, int off, int length) { return length; }
1529             public int seek(int n, int whence) { return 0; }
1530             public FStat _fstat() { return new DevFStat(){ public int inode() { return ZERO_INODE; } }; }
1531             public int flags() { return O_RDWR; }
1532         };
1533         private FD devNullFD = new FD() {
1534             public int read(byte[] a, int off, int length) { return 0; }
1535             public int write(byte[] a, int off, int length) { return length; }
1536             public int seek(int n, int whence) { return 0; }
1537             public FStat _fstat() { return new DevFStat(){ public int inode() { return NULL_INODE; } }; }
1538             public int flags() { return O_RDWR; }
1539         }; 
1540         
1541         public FD open(UnixRuntime r, String path, int mode, int flags) throws ErrnoException {
1542             if(path.equals("null")) return devNullFD;
1543             if(path.equals("zero")) return devZeroFD;
1544             if(path.startsWith("fd/")) {
1545                 int n;
1546                 try {
1547                     n = Integer.parseInt(path.substring(4));
1548                 } catch(NumberFormatException e) {
1549                     return null;
1550                 }
1551                 if(n < 0 || n >= OPEN_MAX) return null;
1552                 if(r.fds[n] == null) return null;
1553                 return r.fds[n].dup();
1554             }
1555             if(path.equals("fd")) {
1556                 int count=0;
1557                 for(int i=0;i<OPEN_MAX;i++) if(r.fds[i] != null) { count++; }
1558                 final int[] files = new int[count];
1559                 count = 0;
1560                 for(int i=0;i<OPEN_MAX;i++) if(r.fds[i] != null) files[count++] = i;
1561                 return new DevDirFD() {
1562                     public int myInode() { return FD_INODE; }
1563                     public int parentInode() { return ROOT_INODE; }
1564                     public int inode(int n) { return FD_INODES + n; }
1565                     public String name(int n) { return Integer.toString(files[n]); }
1566                     public int size() { return files.length; }
1567                 };
1568             }
1569             if(path.equals("")) {
1570                 return new DevDirFD() {
1571                     public int myInode() { return ROOT_INODE; }
1572                     // HACK: We don't have any clean way to get the parent inode
1573                     public int parentInode() { return ROOT_INODE; }
1574                     public int inode(int n) {
1575                         switch(n) {
1576                             case 0: return NULL_INODE;
1577                             case 1: return ZERO_INODE;
1578                             case 2: return FD_INODE;
1579                             default: return -1;
1580                         }
1581                     }
1582                     
1583                     public String name(int n) {
1584                         switch(n) {
1585                             case 0: return "null";
1586                             case 1: return "zero";
1587                             case 2: return "fd";
1588                             default: return null;
1589                         }
1590                     }
1591                     public int size() { return 3; }
1592                 };
1593             }
1594             return null;
1595         }
1596         
1597         public FStat stat(UnixRuntime r,String path) throws ErrnoException {
1598             if(path.equals("null")) return devNullFD.fstat();
1599             if(path.equals("zero")) return devZeroFD.fstat();            
1600             if(path.startsWith("fd/")) {
1601                 int n;
1602                 try {
1603                     n = Integer.parseInt(path.substring(3));
1604                 } catch(NumberFormatException e) {
1605                     return null;
1606                 }
1607                 if(n < 0 || n >= OPEN_MAX) return null;
1608                 if(r.fds[n] == null) return null;
1609                 return r.fds[n].fstat();
1610             }
1611             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; }};
1612             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; }};
1613             return null;
1614         }
1615         
1616         public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { throw new ErrnoException(EROFS); }
1617         public void unlink(UnixRuntime r, String path) throws ErrnoException { throw new ErrnoException(EROFS); }
1618     }    
1619 }