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