+
+ // FEATURE: move this into FS so some filesystem can override it directly (devfs)
+ private static Object dispatch(FS fs, int op, UnixRuntime r, String path, int arg1, int arg2) throws ErrnoException {
+ switch(op) {
+ case OPEN: return fs.open(r,path,arg1,arg2);
+ case STAT: return fs.stat(r,path);
+ case LSTAT: return fs.lstat(r,path);
+ case MKDIR: fs.mkdir(r,path,arg1); return null;
+ case UNLINK: fs.unlink(r,path); return null;
+ default: throw new Error("should never happen");
+ }
+ }
+
+ public final FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException { return (FD) fsop(OPEN,r,path,flags,mode); }
+ public final FStat stat(UnixRuntime r, String path) throws ErrnoException { return (FStat) fsop(STAT,r,path,0,0); }
+ public final FStat lstat(UnixRuntime r, String path) throws ErrnoException { return (FStat) fsop(LSTAT,r,path,0,0); }
+ public final void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { fsop(MKDIR,r,path,mode,0); }
+ public final void unlink(UnixRuntime r, String path) throws ErrnoException { fsop(UNLINK,r,path,0,0); }
+
+ private Hashtable execCache = new Hashtable();
+ private static class CacheEnt {
+ public final long time;
+ public final long size;
+ public final Object o;
+ public CacheEnt(long time, long size, Object o) { this.time = time; this.size = size; this.o = o; }
+ }
+
+ public synchronized Object exec(UnixRuntime r, String path) throws ErrnoException {
+ // HACK: Hideous hack to make a standalone busybox possible
+ if(path.equals("bin/busybox") && Boolean.valueOf(getSystemProperty("nestedvm.busyboxhack")).booleanValue())
+ return r.getClass();
+ FStat fstat = stat(r,path);
+ if(fstat == null) return null;
+ long mtime = fstat.mtime();
+ long size = fstat.size();
+ CacheEnt ent = (CacheEnt) execCache.get(path);
+ if(ent != null) {
+ //System.err.println("Found cached entry for " + path);
+ if(ent.time == mtime && ent.size == size) return ent.o;
+ //System.err.println("Cache was out of date");
+ execCache.remove(path);
+ }
+ FD fd = open(r,path,RD_ONLY,0);
+ if(fd == null) return null;
+ Seekable s = fd.seekable();
+
+ String[] command = null;
+
+ if(s == null) throw new ErrnoException(EACCES);
+ byte[] buf = new byte[4096];
+
+ try {
+ int n = s.read(buf,0,buf.length);
+ if(n == -1) throw new ErrnoException(ENOEXEC);
+
+ switch(buf[0]) {
+ case '\177': // possible ELF
+ if(n < 4 && s.tryReadFully(buf,n,4-n) != 4-n) throw new ErrnoException(ENOEXEC);
+ if(buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F') throw new ErrnoException(ENOEXEC);
+ break;
+ case '#':
+ if(n == 1) {
+ int n2 = s.read(buf,1,buf.length-1);
+ if(n2 == -1) throw new ErrnoException(ENOEXEC);
+ n += n2;
+ }
+ if(buf[1] != '!') throw new ErrnoException(ENOEXEC);
+ int p = 2;
+ n -= 2;
+ OUTER: for(;;) {
+ for(int i=p;i<p+n;i++) if(buf[i] == '\n') { p = i; break OUTER; }
+ p += n;
+ if(p == buf.length) break OUTER;
+ n = s.read(buf,p,buf.length-p);
+ }
+ command = new String[2];
+ int arg;
+ for(arg=2;arg<p;arg++) if(buf[arg] == ' ') break;
+ if(arg < p) {
+ int cmdEnd = arg;
+ while(arg < p && buf[arg] == ' ') arg++;
+ command[0] = new String(buf,2,cmdEnd);
+ command[1] = arg < p ? new String(buf,arg,p-arg) : null;
+ } else {
+ command[0] = new String(buf,2,p-2);
+ }
+ //System.err.println("command[0]: " + command[0] + " command[1]: " + command[1]);
+ break;
+ default:
+ throw new ErrnoException(ENOEXEC);
+ }
+ } catch(IOException e) {
+ fd.close();
+ throw new ErrnoException(EIO);
+ }
+
+ if(command == null) {
+ // its an elf binary
+ try {
+ s.seek(0);
+ Class c = RuntimeCompiler.compile(s,"unixruntime");
+ //System.err.println("Compile succeeded: " + c);
+ ent = new CacheEnt(mtime,size,c);
+ } catch(Compiler.Exn e) {
+ if(STDERR_DIAG) e.printStackTrace();
+ throw new ErrnoException(ENOEXEC);
+ } catch(IOException e) {
+ if(STDERR_DIAG) e.printStackTrace();
+ throw new ErrnoException(EIO);
+ }
+ } else {
+ ent = new CacheEnt(mtime,size,command);
+ }
+
+ fd.close();
+
+ execCache.put(path,ent);
+ return ent.o;