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