+// Copyright 2000-2005 the Contributors, as shown in the revision logs.
+// Licensed under the Apache License 2.0 ("the License").
+// You may not use this file except in compliance with the License.
+
package org.ibex.nestedvm;
import org.ibex.nestedvm.util.*;
-// HACK: This is ugly, this stuff needs to be in org.ibex.util or something
-import org.ibex.classgen.util.Sort;
import java.io.*;
import java.util.*;
import java.net.*;
+import java.lang.reflect.*; // For lazily linked RuntimeCompiler
// FEATURE: vfork
private GlobalState gs;
public void setGlobalState(GlobalState gs) {
if(state != STOPPED) throw new IllegalStateException("can't change GlobalState when running");
+ if(gs == null) throw new NullPointerException("gs is null");
this.gs = gs;
}
if(!exec) {
gs = defaultGS;
String userdir = Platform.getProperty("user.dir");
- String nvroot = Platform.getProperty("nestedvm.root");
- cwd = "";
- if(userdir != null && nvroot == null) {
- if(userdir.startsWith("/") && File.separatorChar == '/') {
- cwd = userdir.substring(1);
- } else {
- Vector vec = new Vector();
- File root = HostFS.hostRootDir();
- String s = new File(userdir).getAbsolutePath();
- File d = new File(s);
- System.err.println(s);
- System.err.println(d);
- while(!d.equals(root)) {
- System.err.println("Got " + d.getName());
- vec.addElement(d.getName());
- if((s = d.getParent()) == null) break;
- d = new File(s);
- }
- if(s != null)
- for(int i=vec.size()-1;i>=0;i--) cwd += (String) vec.elementAt(i) + (i==0?"":"/");
- }
- }
+ cwd = userdir == null ? null : gs.mapHostPath(userdir);
+ if(cwd == null) cwd = "/";
+ cwd = cwd.substring(1);
}
}
}
String[] createEnv(String[] extra) {
- String[] defaults = new String[6];
+ String[] defaults = new String[7];
int n=0;
if(extra == null) extra = new String[0];
+ String tmp;
if(!envHas("USER",extra) && Platform.getProperty("user.name") != null)
defaults[n++] = "USER=" + Platform.getProperty("user.name");
- if(!envHas("HOME",extra) && Platform.getProperty("user.home") != null)
- defaults[n++] = "HOME=" + Platform.getProperty("user.home");
+ if(!envHas("HOME",extra) && (tmp=Platform.getProperty("user.home")) != null && (tmp=gs.mapHostPath(tmp)) != null)
+ defaults[n++] = "HOME=" + tmp;
+ if(!envHas("TMPDIR",extra) && (tmp=Platform.getProperty("java.io.tmpdir")) != null && (tmp=gs.mapHostPath(tmp)) != null)
+ defaults[n++] = "TMPDIR=" + tmp;
if(!envHas("SHELL",extra)) defaults[n++] = "SHELL=/bin/sh";
if(!envHas("TERM",extra) && !win32Hacks) defaults[n++] = "TERM=vt100";
if(!envHas("TZ",extra)) defaults[n++] = "TZ=" + posixTZ();
}
}
- int _syscall(int syscall, int a, int b, int c, int d, int e, int f) throws ErrnoException, FaultException {
+ protected int _syscall(int syscall, int a, int b, int c, int d, int e, int f) throws ErrnoException, FaultException {
switch(syscall) {
case SYS_kill: return sys_kill(a,b);
case SYS_fork: return sys_fork();
case SYS_sendto: return sys_sendto(a,b,c,d,e,f);
case SYS_recvfrom: return sys_recvfrom(a,b,c,d,e,f);
case SYS_select: return sys_select(a,b,c,d,e);
-
+ case SYS_access: return sys_access(a,b);
+ case SYS_realpath: return sys_realpath(a,b);
+ case SYS_chown: return sys_chown(a,b,c);
+ case SYS_lchown: return sys_chown(a,b,c);
+ case SYS_fchown: return sys_fchown(a,b,c);
+ case SYS_chmod: return sys_chmod(a,b,c);
+ case SYS_fchmod: return sys_fchmod(a,b,c);
+ case SYS_fcntl: return sys_fcntl_lock(a,b,c);
+ case SYS_umask: return sys_umask(a);
+
default: return super._syscall(syscall,a,b,c,d,e,f);
}
}
FD _open(String path, int flags, int mode) throws ErrnoException {
- return gs.open(this,normalizePath(path),flags,mode);
+ path = normalizePath(path);
+ FD fd = gs.open(this,path,flags,mode);
+ if (fd != null && path != null) fd.setNormalizedPath(path);
+ return fd;
}
private int sys_getppid() {
return parent == null ? 1 : parent.pid;
}
+
+ private int sys_chown(int fileAddr, int uid, int gid) {
+ return 0;
+ }
+ private int sys_lchown(int fileAddr, int uid, int gid) {
+ return 0;
+ }
+ private int sys_fchown(int fd, int uid, int gid) {
+ return 0;
+ }
+ private int sys_chmod(int fileAddr, int uid, int gid) {
+ return 0;
+ }
+ private int sys_fchmod(int fd, int uid, int gid) {
+ return 0;
+ }
+ private int sys_umask(int mask) {
+ return 0;
+ }
+
+ private int sys_access(int cstring, int mode) throws ErrnoException, ReadFaultException {
+ // FEATURE: sys_access
+ return gs.stat(this,normalizePath(cstring(cstring))) == null ? -ENOENT : 0;
+ }
+
+ private int sys_realpath(int inAddr, int outAddr) throws FaultException {
+ String s = normalizePath(cstring(inAddr));
+ byte[] b = getNullTerminatedBytes(s);
+ if(b.length > PATH_MAX) return -ERANGE;
+ copyout(b,outAddr,b.length);
+ return 0;
+ }
// FEATURE: Signal handling
// check flag only on backwards jumps to basic blocks without compulsatory checks
private int sys_exec(int cpath, int cargv, int cenvp) throws ErrnoException, FaultException {
return exec(normalizePath(cstring(cpath)),readStringArray(cargv),readStringArray(cenvp));
}
+
+ private final static Method runtimeCompilerCompile;
+ static {
+ Method m;
+ try {
+ m = Class.forName("org.ibex.nestedvm.RuntimeCompiler").getMethod("compile",new Class[]{Seekable.class,String.class,String.class});
+ } catch(NoSuchMethodException e) {
+ m = null;
+ } catch(ClassNotFoundException e) {
+ m = null;
+ }
+ runtimeCompilerCompile = m;
+ }
+
+ public Class runtimeCompile(Seekable s, String sourceName) throws IOException {
+ if(runtimeCompilerCompile == null) {
+ if(STDERR_DIAG) System.err.println("WARNING: Exec attempted but RuntimeCompiler not found!");
+ return null;
+ }
- private int exec(String normalizedPath, String[] argv, String[] envp) throws ErrnoException {
+ try {
+ return (Class) runtimeCompilerCompile.invoke(null,new Object[]{s,"unixruntime,maxinsnpermethod=256,lessconstants",sourceName});
+ } catch(IllegalAccessException e) {
+ e.printStackTrace();
+ return null;
+ } catch(InvocationTargetException e) {
+ Throwable t = e.getTargetException();
+ if(t instanceof IOException) throw (IOException) t;
+ if(t instanceof RuntimeException) throw (RuntimeException) t;
+ if(t instanceof Error) throw (Error) t;
+ if(STDERR_DIAG) t.printStackTrace();
+ return null;
+ }
+ }
+
+ private int exec(String path, String[] argv, String[] envp) throws ErrnoException {
if(argv.length == 0) argv = new String[]{""};
-
+ // HACK: Hideous hack to make a standalone busybox possible
+ if(path.equals("bin/busybox") && getClass().getName().endsWith("BusyBox"))
+ return execClass(getClass(),argv,envp);
+
// NOTE: For this little hack to work nestedvm.root MUST be "."
/*try {
System.err.println("Execing normalized path: " + normalizedPath);
if(true) return exec(new Interpreter(normalizedPath),argv,envp);
} catch(IOException e) { throw new Error(e); }*/
- Object o = gs.exec(this,normalizedPath);
- if(o == null) return -ENOENT;
-
- if(o instanceof Class) {
- Class c = (Class) o;
- try {
- UnixRuntime r = (UnixRuntime) c.getDeclaredConstructor(new Class[]{Boolean.TYPE}).newInstance(new Object[]{Boolean.TRUE});
- return exec(r,argv,envp);
- } catch(Exception e) {
- e.printStackTrace();
- return -ENOEXEC;
+ FStat fstat = gs.stat(this,path);
+ if(fstat == null) return -ENOENT;
+ GlobalState.CacheEnt ent = (GlobalState.CacheEnt) gs.execCache.get(path);
+ long mtime = fstat.mtime();
+ long size = fstat.size();
+ if(ent != null) {
+ //System.err.println("Found cached entry for " + path);
+ if(ent.time ==mtime && ent.size == size) {
+ if(ent.o instanceof Class)
+ return execClass((Class) ent.o,argv,envp);
+ if(ent.o instanceof String[])
+ return execScript(path,(String[]) ent.o,argv,envp);
+ throw new Error("should never happen");
}
- } else {
- String[] command = (String[]) o;
- String[] newArgv = new String[argv.length + command[1] != null ? 2 : 1];
- int p = command[0].lastIndexOf('/');
- newArgv[0] = p == -1 ? command[0] : command[0].substring(p+1);
- p = 1;
- if(command[1] != null) newArgv[p++] = command[1];
- newArgv[p++] = "/" + normalizedPath;
- for(int i=1;i<argv.length;i++) newArgv[p++] = argv[i];
- return exec(command[0],newArgv,envp);
+ //System.err.println("Cache was out of date");
+ gs.execCache.remove(path);
+ }
+
+ FD fd = gs.open(this,path,RD_ONLY,0);
+ if(fd == null) throw new ErrnoException(ENOENT);
+ Seekable s = fd.seekable();
+ 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);
+ if(buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F') return -ENOEXEC;
+ s.seek(0);
+ if(STDERR_DIAG) System.err.println("Running RuntimeCompiler for " + path);
+ Class c = runtimeCompile(s,path);
+ if(STDERR_DIAG) System.err.println("RuntimeCompiler finished for " + path);
+ if(c == null) throw new ErrnoException(ENOEXEC);
+ gs.execCache.put(path,new GlobalState.CacheEnt(mtime,size,c));
+ return execClass(c,argv,envp);
+ case '#':
+ if(n == 1) {
+ int n2 = s.read(buf,1,buf.length-1);
+ if(n2 == -1) return -ENOEXEC;
+ n += n2;
+ }
+ if(buf[1] != '!') return -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);
+ }
+ int cmdStart = 2;
+ for(;cmdStart<p;cmdStart++) if(buf[cmdStart]!=' ') break;
+ if(cmdStart == p) throw new ErrnoException(ENOEXEC);
+ int argStart = cmdStart;
+ for(;argStart<p;argStart++) if(buf[argStart] == ' ') break;
+ int cmdEnd = argStart;
+ while(argStart < p && buf[argStart] == ' ') argStart++;
+ String[] command = new String[] {
+ new String(buf,cmdStart,cmdEnd-cmdStart),
+ argStart < p ? new String(buf,argStart,p-argStart) : null
+ };
+ gs.execCache.put(path,new GlobalState.CacheEnt(mtime,size,command));
+ return execScript(path,command,argv,envp);
+ default:
+ return -ENOEXEC;
+ }
+ } catch(IOException e) {
+ return -EIO;
+ } finally {
+ fd.close();
+ }
+ }
+
+ public int execScript(String path, String[] command, String[] argv, String[] envp) throws ErrnoException {
+ String[] newArgv = new String[argv.length-1 + (command[1] != null ? 3 : 2)];
+ int p = command[0].lastIndexOf('/');
+ newArgv[0] = p == -1 ? command[0] : command[0].substring(p+1);
+ newArgv[1] = "/" + path;
+ p = 2;
+ if(command[1] != null) newArgv[p++] = command[1];
+ for(int i=1;i<argv.length;i++) newArgv[p++] = argv[i];
+ if(p != newArgv.length) throw new Error("p != newArgv.length");
+ System.err.println("Execing: " + command[0]);
+ for(int i=0;i<newArgv.length;i++) System.err.println("execing [" + i + "] " + newArgv[i]);
+ return exec(command[0],newArgv,envp);
+ }
+
+ public int execClass(Class c,String[] argv, String[] envp) {
+ try {
+ UnixRuntime r = (UnixRuntime) c.getDeclaredConstructor(new Class[]{Boolean.TYPE}).newInstance(new Object[]{Boolean.TRUE});
+ return exec(r,argv,envp);
+ } catch(Exception e) {
+ e.printStackTrace();
+ return -ENOEXEC;
}
}
private int exec(UnixRuntime r, String[] argv, String[] envp) {
-
//System.err.println("Execing " + r);
for(int i=0;i<OPEN_MAX;i++) if(closeOnExec[i]) closeFD(i);
r.fds = fds;
return 0;
}
- static class Pipe {
+ public static class Pipe {
private final byte[] pipebuf = new byte[PIPE_BUF*4];
private int readPos;
private int writePos;
copyout(buf,addr,n);
return n;
}
-
+
+ void _preCloseFD(FD fd) {
+ // release all fcntl locks on this file
+ Seekable s = fd.seekable();
+ if (s == null) return;
+
+ try {
+ for (int i=0; i < gs.locks.length; i++) {
+ Seekable.Lock l = gs.locks[i];
+ if (l == null) continue;
+ if (s.equals(l.seekable()) && l.getOwner() == this) {
+ l.release();
+ gs.locks[i] = null;
+ }
+ }
+ } catch (IOException e) { throw new RuntimeException(e); }
+ }
+
+ void _postCloseFD(FD fd) {
+ if (fd.isMarkedForDeleteOnClose()) {
+ try { gs.unlink(this, fd.getNormalizedPath()); }
+ catch (Throwable t) {}
+ }
+ }
+
+ /** Implements the F_GETLK and F_SETLK cases of fcntl syscall.
+ * If l_start = 0 and l_len = 0 the lock refers to the entire file.
+ * Uses GlobalState to ensure locking across processes in the same JVM.
+ struct flock {
+ short l_type; // lock type: F_UNLCK, F_RDLCK, F_WRLCK
+ short l_whence; // type of l_start: SEEK_SET, SEEK_CUR, SEEK_END
+ long l_start; // starting offset, bytes
+ long l_len; // len = 0 means until EOF
+ short l_pid; // lock owner
+ short l_xxx; // padding
+ };
+ */
+ private int sys_fcntl_lock(int fdn, int cmd, int arg) throws FaultException{
+ if (cmd != F_GETLK && cmd != F_SETLK) return sys_fcntl(fdn, cmd, arg);
+
+ if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
+ if(fds[fdn] == null) return -EBADFD;
+ FD fd = fds[fdn];
+
+ if (arg == 0) return -EINVAL;
+ int word = memRead(arg);
+ int l_start = memRead(arg+4);
+ int l_len = memRead(arg+8);
+ int l_type = word>>16;
+ int l_whence = word&0x00ff;
+
+ Seekable.Lock[] locks = gs.locks;
+ Seekable s = fd.seekable();
+ if (s == null) return -EINVAL;
+
+ try {
+
+ switch (l_whence) {
+ case SEEK_SET: break;
+ case SEEK_CUR: l_start += s.pos(); break;
+ case SEEK_END: l_start += s.length(); break;
+ default: return -1;
+ }
+
+ if (cmd == F_GETLK) {
+ // The simple Java file locking below will happily return
+ // a lock that overlaps one already held by the JVM. Thus
+ // we must check over all the locks held by other Runtimes
+ for (int i=0; i < locks.length; i++) {
+ if (locks[i] == null || !s.equals(locks[i].seekable()))
+ continue;
+ if (!locks[i].overlaps(l_start, l_len))
+ continue;
+ if (locks[i].getOwner() == this)
+ continue;
+ if (locks[i].isShared() && l_type == F_RDLCK)
+ continue;
+
+ // overlapping lock held by another process
+ return 0;
+ }
+
+ // check if an area is lockable by attempting to obtain a lock
+ Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK);
+
+ if (lock != null) { // no lock exists
+ memWrite(arg, SEEK_SET|(F_UNLCK<<16));
+ lock.release();
+ }
+
+ return 0;
+ }
+
+ // now processing F_SETLK
+ if (cmd != F_SETLK) return -EINVAL;
+
+ if (l_type == F_UNLCK) {
+ // release all locks that fall within the boundaries given
+ for (int i=0; i < locks.length; i++) {
+ if (locks[i] == null || !s.equals(locks[i].seekable()))
+ continue;
+ if (locks[i].getOwner() != this) continue;
+
+ int pos = (int)locks[i].position();
+ if (pos < l_start) continue;
+ if (l_start != 0 && l_len != 0) // start/len 0 means unlock all
+ if (pos + locks[i].size() > l_start + l_len)
+ continue;
+
+ locks[i].release();
+ locks[i] = null;
+ }
+ return 0;
+
+ } else if (l_type == F_RDLCK || l_type == F_WRLCK) {
+ // first see if a lock already exists
+ for (int i=0; i < locks.length; i++) {
+ if (locks[i] == null || !s.equals(locks[i].seekable()))
+ continue;
+
+ if (locks[i].getOwner() == this) {
+ // if this Runtime owns an overlapping lock work with it
+ if (locks[i].contained(l_start, l_len)) {
+ locks[i].release();
+ locks[i] = null;
+ } else if (locks[i].contains(l_start, l_len)) {
+ if (locks[i].isShared() == (l_type == F_RDLCK)) {
+ // return this more general lock
+ memWrite(arg+4, (int)locks[i].position());
+ memWrite(arg+8, (int)locks[i].size());
+ return 0;
+ } else {
+ locks[i].release();
+ locks[i] = null;
+ }
+ }
+ } else {
+ // if another Runtime has an lock and it is exclusive or
+ // we want an exclusive lock then fail
+ if (locks[i].overlaps(l_start, l_len)
+ && (!locks[i].isShared() || l_type == F_WRLCK))
+ return -EAGAIN;
+ }
+ }
+
+ // create the lock
+ Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK);
+ if (lock == null) return -EAGAIN;
+ lock.setOwner(this);
+
+ int i;
+ for (i=0; i < locks.length; i++)
+ if (locks[i] == null) break;
+ if (i == locks.length) return -ENOLCK;
+ locks[i] = lock;
+ return 0;
+
+ } else {
+ return -EINVAL;
+ }
+
+ } catch (IOException e) { throw new RuntimeException(e); }
+ }
+
static class SocketFD extends FD {
public static final int TYPE_STREAM = 0;
public static final int TYPE_DGRAM = 1;
return 0;
}
- public static class GlobalState {
+ public static final class GlobalState {
+ Hashtable execCache = new Hashtable();
+
final UnixRuntime[] tasks;
int nextPID = 1;
+ /** Table of all current file locks held by this process. */
+ Seekable.Lock[] locks = new Seekable.Lock[16];
+
private MP[] mps = new MP[0];
private FS root;
public GlobalState(int maxProcs, boolean defaultMounts) {
tasks = new UnixRuntime[maxProcs+1];
if(defaultMounts) {
- addMount("/",new HostFS());
+ File root = null;
+ if(Platform.getProperty("nestedvm.root") != null) {
+ root = new File(Platform.getProperty("nestedvm.root"));
+ if(!root.isDirectory()) throw new IllegalArgumentException("nestedvm.root is not a directory");
+ } else {
+ String cwd = Platform.getProperty("user.dir");
+ root = Platform.getRoot(new File(cwd != null ? cwd : "."));
+ }
+
+ addMount("/",new HostFS(root));
+
+ if(Platform.getProperty("nestedvm.root") == null) {
+ File[] roots = Platform.listRoots();
+ for(int i=0;i<roots.length;i++) {
+ String name = roots[i].getPath();
+ if(name.endsWith(File.separator))
+ name = name.substring(0,name.length()-1);
+ if(name.length() == 0 || name.indexOf('/') != -1) continue;
+ addMount("/" + name.toLowerCase(),new HostFS(roots[i]));
+ }
+ }
+
addMount("/dev",new DevFS());
+ addMount("/resource",new ResourceFS());
+ addMount("/cygdrive",new CygdriveFS());
}
}
- private static class MP implements Sort.Comparable {
+ public String mapHostPath(String s) { return mapHostPath(new File(s)); }
+ public String mapHostPath(File f) {
+ MP[] list;
+ FS root;
+ synchronized(this) { mps = this.mps; root = this.root; }
+ if(!f.isAbsolute()) f = new File(f.getAbsolutePath());
+ for(int i=mps.length;i>=0;i--) {
+ FS fs = i == mps.length ? root : mps[i].fs;
+ String path = i == mps.length ? "" : mps[i].path;
+ if(!(fs instanceof HostFS)) continue;
+ File fsroot = ((HostFS)fs).getRoot();
+ if(!fsroot.isAbsolute()) fsroot = new File(fsroot.getAbsolutePath());
+ if(f.getPath().startsWith(fsroot.getPath())) {
+ char sep = File.separatorChar;
+ String child = f.getPath().substring(fsroot.getPath().length());
+ if(sep != '/') {
+ char[] child_ = child.toCharArray();
+ for(int j=0;j<child_.length;j++) {
+ if(child_[j] == '/') child_[j] = sep;
+ else if(child_[j] == sep) child_[j] = '/';
+ }
+ child = new String(child_);
+ }
+ String mapped = "/" + (path.length()==0?"":path+"/") + child;
+ return mapped;
+ }
+ }
+ return null;
+ }
+
+ static class MP implements Sort.Comparable {
public MP(String path, FS fs) { this.path = path; this.fs = fs; }
public String path;
public FS fs;
System.arraycopy(mps,0,newMPS,index,mps.length-index-1);
mps = newMPS;
}
-
+
private Object fsop(int op, UnixRuntime r, String normalizedPath, int arg1, int arg2) throws ErrnoException {
int pl = normalizedPath.length();
if(pl != 0) {
public final void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { fsop(FS.MKDIR,r,path,mode,0); }
public final void unlink(UnixRuntime r, String path) throws ErrnoException { fsop(FS.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") && r.getClass().getName().endsWith("BusyBox")) 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;
- }
}
public abstract static class FS {
public abstract void unlink(UnixRuntime r, String path) throws ErrnoException;
}
- // chroot support should go in here if it is ever implemented chroot support in here
+ // chroot support should go in here if it is ever implemented
private String normalizePath(String path) {
boolean absolute = path.startsWith("/");
int cwdl = cwd.length();
char[] in = new char[path.length()+1];
char[] out = new char[in.length + (absolute ? -1 : cwd.length())];
+ path.getChars(0,path.length(),in,0);
int inp=0, outp=0;
if(absolute) {
outp = cwdl;
}
- path.getChars(0,path.length(),in,0);
while(in[inp] != 0) {
if(inp != 0) {
while(in[inp] != 0 && in[inp] != '/') { out[outp++] = in[inp++]; }
}
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);
+ int outStart = out[0] == '/' ? 1 : 0;
+ return new String(out,outStart,outp - outStart);
}
FStat hostFStat(final File f, Object data) {
protected File root;
public File getRoot() { return root; }
- static File hostRootDir() {
- if(Platform.getProperty("nestedvm.root") != null) {
- File f = new File(Platform.getProperty("nestedvm.root"));
- if(f.isDirectory()) return f;
- // fall through to case below
- }
- String cwd = Platform.getProperty("user.dir");
- File f = new File(cwd != null ? cwd : ".");
- if(!f.exists()) throw new Error("Couldn't get File for cwd");
- f = new File(f.getAbsolutePath());
- while(f.getParent() != null) f = new File(f.getParent());
- // This works around a bug in some versions of ClassPath
- if(f.getPath().length() == 0) f = new File("/");
- return f;
- }
-
- private File hostFile(String path) {
+ protected File hostFile(String path) {
char sep = File.separatorChar;
if(sep != '/') {
char buf[] = path.toCharArray();
return new File(root,path);
}
- public HostFS() { this(hostRootDir()); }
public HostFS(String root) { this(new File(root)); }
public HostFS(File root) { this.root = root; }
-
public FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException {
final File f = hostFile(path);
return r.hostFSOpen(f,flags,mode,this);
File f = hostFile(path);
if(r.sm != null && !r.sm.allowUnlink(f)) throw new ErrnoException(EPERM);
if(!f.exists()) throw new ErrnoException(ENOENT);
- if(!f.delete()) throw new ErrnoException(EPERM);
+ if(!f.delete()) {
+ // Can't delete file immediately, so mark for
+ // delete on close all matching FDs
+ boolean marked = false;
+ for(int i=0;i<OPEN_MAX;i++) {
+ if(r.fds[i] != null) {
+ String fdpath = r.fds[i].getNormalizedPath();
+ if(fdpath != null && fdpath.equals(path)) {
+ r.fds[i].markDeleteOnClose();
+ marked = true;
+ }
+ }
+ }
+ if(!marked) throw new ErrnoException(EPERM);
+ }
}
public FStat stat(UnixRuntime r, String path) throws ErrnoException {
private static File getParentFile(File f) {
String p = f.getParent();
- return p == null ? null : new File(f,p);
+ return p == null ? null : new File(p);
}
public class HostDirFD extends DirFD {
public int myDev() { return devno; }
}
}
+
+ /* Implements the Cygwin notation for accessing MS Windows drive letters
+ * in a unix path. The path /cygdrive/c/myfile is converted to C:\file.
+ * As there is no POSIX standard for this, little checking is done. */
+ public static class CygdriveFS extends HostFS {
+ protected File hostFile(String path) {
+ final char drive = path.charAt(0);
+
+ if (drive < 'a' || drive > 'z' || path.charAt(1) != '/')
+ return null;
+
+ path = drive + ":" + path.substring(1).replace('/', '\\');
+ return new File(path);
+ }
+
+ public CygdriveFS() { super("/"); }
+ }
private static void putInt(byte[] buf, int off, int n) {
buf[off+0] = (byte)((n>>>24)&0xff);
if(path.startsWith("fd/")) {
int n;
try {
- n = Integer.parseInt(path.substring(4));
+ n = Integer.parseInt(path.substring(3));
} catch(NumberFormatException e) {
return null;
}
public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { throw new ErrnoException(EROFS); }
public void unlink(UnixRuntime r, String path) throws ErrnoException { throw new ErrnoException(EROFS); }
- }
+ }
+
+
+ public static class ResourceFS extends FS {
+ final InodeCache inodes = new InodeCache(500);
+
+ public FStat lstat(UnixRuntime r, String path) throws ErrnoException { return stat(r,path); }
+ public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { throw new ErrnoException(EROFS); }
+ public void unlink(UnixRuntime r, String path) throws ErrnoException { throw new ErrnoException(EROFS); }
+
+ FStat connFStat(final URLConnection conn) {
+ return new FStat() {
+ public int type() { return S_IFREG; }
+ public int nlink() { return 1; }
+ public int mode() { return 0444; }
+ public int size() { return conn.getContentLength(); }
+ public int mtime() { return (int)(conn.getDate() / 1000); }
+ public int inode() { return inodes.get(conn.getURL().toString()); }
+ public int dev() { return devno; }
+ };
+ }
+
+ public FStat stat(UnixRuntime r, String path) throws ErrnoException {
+ URL url = r.getClass().getResource("/" + path);
+ if(url == null) return null;
+ try {
+ return connFStat(url.openConnection());
+ } catch(IOException e) {
+ throw new ErrnoException(EIO);
+ }
+ }
+
+ public FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException {
+ if((flags & ~3) != 0) {
+ if(STDERR_DIAG)
+ System.err.println("WARNING: Unsupported flags passed to ResourceFS.open(\"" + path + "\"): " + toHex(flags & ~3));
+ throw new ErrnoException(ENOTSUP);
+ }
+ if((flags&3) != RD_ONLY) throw new ErrnoException(EROFS);
+ URL url = r.getClass().getResource("/" + path);
+ if(url == null) return null;
+ try {
+ final URLConnection conn = url.openConnection();
+ Seekable.InputStream si = new Seekable.InputStream(conn.getInputStream());
+ return new SeekableFD(si,flags) { protected FStat _fstat() { return connFStat(conn); } };
+ } catch(FileNotFoundException e) {
+ if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) throw new ErrnoException(EACCES);
+ return null;
+ } catch(IOException e) { throw new ErrnoException(EIO); }
+ }
+ }
}