+
+ // FEATURE: We shouldn't need to special case the root dir, it should work in the MP array
+ private Object fsop(int op, UnixRuntime r, String normalizedPath, int arg1, int arg2) throws ErrnoException {
+ int pl = normalizedPath.length();
+ if(pl != 0) {
+ MP[] list;
+ synchronized(this) { list = mps; }
+ for(int i=0;i<list.length;i++) {
+ MP mp = list[i];
+ int mpl = mp.path.length();
+ if(normalizedPath.startsWith(mp.path) && (pl == mpl || normalizedPath.charAt(mpl) == '/'))
+ return dispatch(mp.fs,op,r,pl == mpl ? "" : normalizedPath.substring(mpl+1),arg1,arg2);
+ }
+ }
+ return dispatch(root,op,r,normalizedPath,arg1,arg2);
+ }
+
+ // 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);
+ //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;
+ }
+ }
+
+ public abstract static class FS {
+ GlobalState owner;
+ int devno;
+
+ public FStat lstat(UnixRuntime r, String path) throws ErrnoException { return stat(r,path); }
+
+ // If this returns null it'll be truned into an ENOENT
+ public abstract FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException;
+ // If this returns null it'll be turned into an ENOENT
+ public abstract FStat stat(UnixRuntime r, String path) throws ErrnoException;
+ public abstract void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException;
+ public abstract void unlink(UnixRuntime r, String path) throws ErrnoException;
+ }
+
+ // FEATURE: chroot support in here
+ private String normalizePath(String path) {
+ boolean absolute = path.startsWith("/");
+ int cwdl = cwd.length();
+ // NOTE: This isn't just a fast path, it handles cases the code below doesn't
+ if(!path.startsWith(".") && path.indexOf("./") == -1 && path.indexOf("//") == -1 && !path.endsWith("."))
+ return absolute ? path.substring(1) : cwdl == 0 ? path : path.length() == 0 ? cwd : cwd + "/" + path;
+
+ char[] in = new char[path.length()+1];
+ char[] out = new char[in.length + (absolute ? -1 : cwd.length())];
+ int inp=0, outp=0;
+
+ if(absolute) {
+ do { inp++; } while(in[inp] == '/');
+ } else if(cwdl != 0) {
+ cwd.getChars(0,cwdl,out,0);
+ outp = cwdl;
+ }
+
+ path.getChars(0,path.length(),in,0);
+ while(in[inp] != 0) {
+ if(inp != 0 || cwdl==0) {
+ if(in[inp] != '/') { out[outp++] = in[inp++]; continue; }
+ while(in[inp] == '/') inp++;
+ }
+ if(in[inp] == '\0') continue;
+ if(in[inp] != '.') { out[outp++] = '/'; out[outp++] = in[inp++]; continue; }
+ if(in[inp+1] == '\0' || in[inp+1] == '/') { inp++; continue; }
+ if(in[inp+1] == '.' && (in[inp+2] == '\0' || in[inp+2] == '/')) { // ..
+ inp += 2;
+ if(outp > 0) outp--;
+ while(outp > 0 && out[outp] != '/') outp--;
+ //System.err.println("After ..: " + new String(out,0,outp));
+ continue;
+ }
+ inp++;
+ out[outp++] = '/';
+ out[outp++] = '.';
+ }
+ if(outp > 0 && out[outp-1] == '/') outp--;
+ //System.err.println("normalize: " + path + " -> " + new String(out,0,outp) + " (cwd: " + cwd + ")");
+ return new String(out,0,outp);
+ }
+
+ FStat hostFStat(final File f, Object data) {
+ boolean e = false;
+ try {
+ FileInputStream fis = new FileInputStream(f);
+ switch(fis.read()) {
+ case '\177': e = fis.read() == 'E' && fis.read() == 'L' && fis.read() == 'F'; break;
+ case '#': e = fis.read() == '!';
+ }
+ fis.close();
+ } catch(IOException e2) { }
+ HostFS fs = (HostFS) data;
+ final int inode = fs.inodes.get(f.getAbsolutePath());
+ final int devno = fs.devno;
+ return new HostFStat(f,e) {
+ public int inode() { return inode; }
+ public int dev() { return devno; }
+ };
+ }
+
+ FD hostFSDirFD(File f, Object _fs) {
+ HostFS fs = (HostFS) _fs;
+ return fs.new HostDirFD(f);