1 package org.ibex.nestedvm;
3 import org.ibex.nestedvm.util.*;
7 // FEATURE: Remove System.{out,err}.printlns and throw Errors where applicable
9 // FEATURE: BusyBox's ASH doesn't like \r\n at the end of lines
10 // is ash just broken or are many apps like this? if so workaround in nestedvm
12 public abstract class UnixRuntime extends Runtime implements Cloneable {
13 /** The pid of this "process" */
15 private UnixRuntime parent;
16 protected int getPid() { return pid; }
18 private static final GlobalState defaultGD = new GlobalState();
19 private GlobalState gd = defaultGD;
21 /** proceses' current working directory - absolute path WITHOUT leading slash
22 "" = root, "bin" = /bin "usr/bin" = /usr/bin */
25 /** The runtime that should be run next when in state == EXECED */
26 private UnixRuntime execedRuntime;
28 private Object children; // used only for synchronizatin
29 private Vector activeChildren;
30 private Vector exitedChildren;
32 public UnixRuntime(int pageSize, int totalPages) {
33 super(pageSize,totalPages);
35 // FEATURE: Do the proper mangling for non-unix hosts
36 String userdir = getSystemProperty("user.dir");
37 cwd = userdir != null && userdir.startsWith("/") && File.separatorChar == '/' ? userdir.substring(1) : "";
40 // NOTE: getDisplayName() is a Java2 function
41 private static String posixTZ() {
42 StringBuffer sb = new StringBuffer();
43 TimeZone zone = TimeZone.getDefault();
44 int off = zone.getRawOffset() / 1000;
45 sb.append(zone.getDisplayName(false,TimeZone.SHORT));
46 if(off > 0) sb.append("-");
48 sb.append(off/3600); off = off%3600;
49 if(off > 0) sb.append(":").append(off/60); off=off%60;
50 if(off > 0) sb.append(":").append(off);
51 if(zone.useDaylightTime())
52 sb.append(zone.getDisplayName(true,TimeZone.SHORT));
56 private static boolean envHas(String key,String[] environ) {
57 for(int i=0;i<environ.length;i++)
58 if(environ[i]!=null && environ[i].startsWith(key + "=")) return true;
62 protected String[] createEnv(String[] extra) {
63 String[] defaults = new String[5];
65 if(extra == null) extra = new String[0];
66 if(!envHas("USER",extra) && getSystemProperty("user.name") != null)
67 defaults[n++] = "USER=" + getSystemProperty("user.name");
68 if(!envHas("HOME",extra) && getSystemProperty("user.name") != null)
69 defaults[n++] = "HOME=" + getSystemProperty("user.home");
70 if(!envHas("SHELL",extra)) defaults[n++] = "SHELL=/bin/sh";
71 if(!envHas("TERM",extra)) defaults[n++] = "TERM=vt100";
72 if(!envHas("TZ",extra)) defaults[n++] = "TZ=" + posixTZ();
73 String[] env = new String[extra.length+n];
74 for(int i=0;i<n;i++) env[i] = defaults[i];
75 for(int i=0;i<extra.length;i++) env[n++] = extra[i];
79 private static class ProcessTableFullExn extends RuntimeException { }
81 protected void _started() {
82 UnixRuntime[] tasks = gd.tasks;
85 if(tasks[pid] == null || tasks[pid].pid != pid) throw new Error("should never happen");
88 int nextPID = gd.nextPID;
89 for(int i=nextPID;i<tasks.length;i++) if(tasks[i] == null) { newpid = i; break; }
90 if(newpid == -1) for(int i=1;i<nextPID;i++) if(tasks[i] == null) { newpid = i; break; }
91 if(newpid == -1) throw new ProcessTableFullExn();
93 gd.nextPID = newpid + 1;
99 int _syscall(int syscall, int a, int b, int c, int d) throws ErrnoException, FaultException {
101 case SYS_kill: return sys_kill(a,b);
102 case SYS_fork: return sys_fork();
103 case SYS_pipe: return sys_pipe(a);
104 case SYS_dup2: return sys_dup2(a,b);
105 case SYS_waitpid: return sys_waitpid(a,b,c);
106 case SYS_stat: return sys_stat(a,b);
107 case SYS_lstat: return sys_lstat(a,b);
108 case SYS_mkdir: return sys_mkdir(a,b);
109 case SYS_getcwd: return sys_getcwd(a,b);
110 case SYS_chdir: return sys_chdir(a);
111 case SYS_exec: return sys_exec(a,b,c);
113 default: return super._syscall(syscall,a,b,c,d);
117 protected FD _open(String path, int flags, int mode) throws ErrnoException {
118 return gd.open(this,normalizePath(path),flags,mode);
121 /** The kill syscall.
122 SIGSTOP, SIGTSTO, SIGTTIN, and SIGTTOUT pause the process.
123 SIGCONT, SIGCHLD, SIGIO, and SIGWINCH are ignored.
124 Anything else terminates the process. */
125 private int sys_kill(int pid, int signal) {
126 // This will only be called by raise() in newlib to invoke the default handler
127 // We don't have to worry about actually delivering the signal
128 if(pid != pid) return -ESRCH;
129 if(signal < 0 || signal >= 32) return -EINVAL;
144 return syscall(SYS_exit,128+signal,0,0,0);
149 private int sys_waitpid(int pid, int statusAddr, int options) throws FaultException, ErrnoException {
150 System.err.println("PID: " + this.pid + " is waiting on " + pid);
151 final int WNOHANG = 1;
152 if((options & ~(WNOHANG)) != 0) return -EINVAL;
153 if(pid == 0 || pid < -1) {
154 System.err.println("WARNING: waitpid called with a pid of " + pid);
157 boolean blocking = (options&WNOHANG)==0;
159 if(pid !=-1 && (pid <= 0 || pid >= gd.tasks.length)) return -ECHILD;
160 if(children == null) return blocking ? -ECHILD : 0;
162 UnixRuntime done = null;
164 synchronized(children) {
167 if(exitedChildren.size() > 0) done = (UnixRuntime)exitedChildren.remove(exitedChildren.size() - 1);
169 UnixRuntime t = gd.tasks[pid];
170 if(t.parent != this) return -ECHILD;
171 if(t.state == EXITED) {
172 if(!exitedChildren.remove(t)) throw new Error("should never happen");
176 // process group stuff, EINVAL returned above
177 throw new Error("should never happen");
180 if(!blocking) return 0;
181 try { children.wait(); } catch(InterruptedException e) {}
182 System.err.println("waitpid woke up: " + exitedChildren.size());
184 gd.tasks[done.pid] = null;
189 if(statusAddr!=0) memWrite(statusAddr,done.exitStatus()<<8);
194 protected void _exited() {
195 if(children != null) synchronized(children) {
196 for(Enumeration e = exitedChildren.elements(); e.hasMoreElements(); ) {
197 UnixRuntime child = (UnixRuntime) e.nextElement();
198 gd.tasks[child.pid] = null;
200 exitedChildren.clear();
201 for(Enumeration e = activeChildren.elements(); e.hasMoreElements(); ) {
202 UnixRuntime child = (UnixRuntime) e.nextElement();
205 activeChildren.clear();
208 UnixRuntime _parent = parent;
209 if(_parent == null) {
210 gd.tasks[pid] = null;
212 synchronized(_parent.children) {
214 gd.tasks[pid] = null;
216 parent.activeChildren.remove(this);
217 parent.exitedChildren.add(this);
218 parent.children.notify();
224 protected Object clone() throws CloneNotSupportedException {
225 UnixRuntime r = (UnixRuntime) super.clone();
229 r.activeChildren = r.exitedChildren = null;
233 private int sys_fork() {
234 CPUState state = new CPUState();
236 int sp = state.r[SP];
240 r = (UnixRuntime) clone();
241 } catch(Exception e) {
250 } catch(ProcessTableFullExn e) {
254 System.err.println("fork " + pid + " -> " + r.pid + " tasks[" + r.pid + "] = " + gd.tasks[r.pid]);
255 if(children == null) {
256 children = new Object();
257 activeChildren = new Vector();
258 exitedChildren = new Vector();
260 activeChildren.add(r);
262 state.r[V0] = 0; // return 0 to child
263 state.pc += 4; // skip over syscall instruction
264 r.setCPUState(state);
267 new ForkedProcess(r);
272 public static final class ForkedProcess extends Thread {
273 private final UnixRuntime initial;
274 public ForkedProcess(UnixRuntime initial) { this.initial = initial; start(); }
275 public void run() { UnixRuntime.executeAndExec(initial); }
278 public static int runAndExec(UnixRuntime r, String argv0, String[] rest) { return runAndExec(r,concatArgv(argv0,rest)); }
279 public static int runAndExec(UnixRuntime r, String[] argv) { r.start(argv); return executeAndExec(r); }
281 public static int executeAndExec(UnixRuntime r) {
284 if(r.execute()) break;
285 System.err.println("WARNING: Pause requested while executing runAndExec()");
287 if(r.state != EXECED) return r.exitStatus();
292 private String[] readStringArray(int addr) throws ReadFaultException {
294 for(int p=addr;memRead(p) != 0;p+=4) count++;
295 String[] a = new String[count];
296 for(int i=0,p=addr;i<count;i++,p+=4) a[i] = cstring(memRead(p));
300 private int sys_exec(int cpath, int cargv, int cenvp) throws ErrnoException, FaultException {
301 return exec(normalizePath(cstring(cpath)),readStringArray(cargv),readStringArray(cenvp));
304 private int exec(String normalizedPath, String[] argv, String[] envp) throws ErrnoException {
305 if(argv.length == 0) argv = new String[]{""};
307 Object o = gd.exec(this,normalizedPath);
308 if(o == null) return -ENOENT;
310 if(o instanceof Class) {
313 return exec((UnixRuntime) c.newInstance(),argv,envp);
314 } catch(Exception e) {
319 String[] command = (String[]) o;
320 String[] newArgv = new String[argv.length + command[1] != null ? 2 : 1];
321 int p = command[0].lastIndexOf('/');
322 newArgv[0] = p == -1 ? command[0] : command[0].substring(p+1);
324 if(command[1] != null) newArgv[p++] = command[1];
325 newArgv[p++] = "/" + normalizedPath;
326 for(int i=1;i<argv.length;i++) newArgv[p++] = argv[i];
327 return exec(command[0],newArgv,envp);
331 private int exec(UnixRuntime r, String[] argv, String[] envp) {
333 System.err.println("Execing " + r);
334 for(int i=0;i<OPEN_MAX;i++) if(closeOnExec[i]) closeFD(i);
336 r.closeOnExec = closeOnExec;
337 // make sure this doesn't get messed with these since we didn't copy them
354 // FEATURE: Use custom PipeFD - be sure to support PIPE_BUF of data
355 private int sys_pipe(int addr) throws FaultException {
356 PipedOutputStream writerStream = new PipedOutputStream();
357 PipedInputStream readerStream;
359 readerStream = new PipedInputStream(writerStream);
360 } catch(IOException e) {
363 FD reader = new InputStreamFD(readerStream);
364 FD writer = new OutputStreamFD(writerStream);
365 int fd1 = addFD(reader);
366 if(fd1 < 0) return -ENFILE;
367 int fd2 = addFD(writer);
368 if(fd2 < 0) { closeFD(fd1); return -ENFILE; }
371 memWrite(addr+4,fd2);
372 } catch(FaultException e) {
380 private int sys_dup2(int oldd, int newd) {
381 if(oldd == newd) return 0;
382 if(oldd < 0 || oldd >= OPEN_MAX) return -EBADFD;
383 if(newd < 0 || newd >= OPEN_MAX) return -EBADFD;
384 if(fds[oldd] == null) return -EBADFD;
385 if(fds[newd] != null) fds[newd].close();
386 fds[newd] = fds[oldd].dup();
390 private int sys_stat(int cstring, int addr) throws FaultException, ErrnoException {
391 FStat s = gd.stat(this,normalizePath(cstring(cstring)));
392 if(s == null) return -ENOENT;
396 private int sys_lstat(int cstring, int addr) throws FaultException, ErrnoException {
397 FStat s = gd.lstat(this,normalizePath(cstring(cstring)));
398 if(s == null) return -ENOENT;
402 private int sys_mkdir(int cstring, int mode) throws FaultException, ErrnoException {
403 gd.mkdir(this,normalizePath(cstring(cstring)),mode);
408 private int sys_getcwd(int addr, int size) throws FaultException, ErrnoException {
409 byte[] b = getBytes(cwd);
410 if(size == 0) return -EINVAL;
411 if(size < b.length+2) return -ERANGE;
413 copyout(b,addr+1,b.length);
414 memset(addr+b.length+1,0,1);
418 private int sys_chdir(int addr) throws ErrnoException, FaultException {
419 String path = normalizePath(cstring(addr));
420 System.err.println("Chdir: " + cstring(addr) + " -> " + path + " pwd: " + cwd);
421 if(gd.stat(this,path).type() != FStat.S_IFDIR) return -ENOTDIR;
423 System.err.println("Now: [" + cwd + "]");
427 // FEATURE: Run through the fork/wait stuff one more time
428 public static class GlobalState {
429 protected static final int OPEN = 1;
430 protected static final int STAT = 2;
431 protected static final int LSTAT = 3;
432 protected static final int MKDIR = 4;
434 // Make this protected (need to fix _exit)
435 private final UnixRuntime[] tasks;
436 private int nextPID = 1;
438 private final MP[][] mps = new MP[128][];
441 public GlobalState() { this(255); }
442 public GlobalState(int maxProcs) { this(maxProcs,true); }
443 public GlobalState(int maxProcs, boolean defaultMounts) {
444 tasks = new UnixRuntime[maxProcs+1];
447 addMount("/dev",new DevFS());
451 private static class MP {
452 public MP(String path, FS fs) { this.path = path; this.fs = fs; }
455 public int compareTo(Object o) {
456 if(!(o instanceof MP)) return 1;
457 return -path.compareTo(((MP)o).path);
461 public synchronized FS getMount(String path) {
462 if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /");
463 if(path.equals("/")) return root;
464 path = path.substring(1);
465 int f = path.charAt(0) & 0x7f;
466 for(int i=0;mps[f] != null && i < mps[f].length;i++)
467 if(mps[f][i].path.equals(path)) return mps[f][i].fs;
471 public synchronized void addMount(String path, FS fs) {
472 if(getMount(path) != null) throw new IllegalArgumentException("mount point already exists");
473 if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /");
474 if(path.equals("/")) { root = fs; return; }
475 path = path.substring(1);
476 int f = path.charAt(0) & 0x7f;
477 int oldLength = mps[f] == null ? 0 : mps[f].length;
478 MP[] newList = new MP[oldLength + 1];
479 if(oldLength != 0) System.arraycopy(mps[f],0,newList,0,oldLength);
480 newList[oldLength] = new MP(path,fs);
481 Arrays.sort(newList);
485 public synchronized void removeMount(String path) {
486 if(getMount(path) == null) throw new IllegalArgumentException("mount point doesn't exist");
487 if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /");
488 if(path.equals("/")) { root = null; return; }
489 path = path.substring(1);
490 int f = path.charAt(0) & 0x7f;
491 MP[] oldList = mps[f];
492 MP[] newList = new MP[oldList.length - 1];
494 for(p=0;p<oldList.length;p++) if(oldList[p].path.equals(path)) break;
495 if(p == oldList.length) throw new Error("should never happen");
496 System.arraycopy(oldList,0,newList,0,p);
497 System.arraycopy(oldList,0,newList,p,oldList.length-p-1);
501 private Object fsop(int op, UnixRuntime r, String path, int arg1, int arg2) throws ErrnoException {
502 int pl = path.length();
504 MP[] list = mps[path.charAt(0) & 0x7f];
506 for(int i=0;i<list.length;i++) {
508 int mpl = mp.path.length();
509 if(path.startsWith(mp.path) && (pl == mpl || (pl < mpl && path.charAt(mpl) == '/')))
510 return dispatch(mp.fs,op,r,pl == mpl ? "" : path.substring(mpl+1),arg1,arg2);
514 return dispatch(root,op,r,path,arg1,arg2);
517 private static Object dispatch(FS fs, int op, UnixRuntime r, String path, int arg1, int arg2) throws ErrnoException {
519 case OPEN: return fs.open(r,path,arg1,arg2);
520 case STAT: return fs.stat(r,path);
521 case LSTAT: return fs.lstat(r,path);
522 case MKDIR: fs.mkdir(r,path,arg1); return null;
523 default: throw new Error("should never happen");
527 public final FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException { return (FD) fsop(OPEN,r,path,flags,mode); }
528 public final FStat stat(UnixRuntime r, String path) throws ErrnoException { return (FStat) fsop(STAT,r,path,0,0); }
529 public final FStat lstat(UnixRuntime r, String path) throws ErrnoException { return (FStat) fsop(LSTAT,r,path,0,0); }
530 public final void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { fsop(MKDIR,r,path,mode,0); }
532 private Hashtable execCache = new Hashtable();
533 private static class CacheEnt {
534 public final long time;
535 public final long size;
536 public final Object o;
537 public CacheEnt(long time, long size, Object o) { this.time = time; this.size = size; this.o = o; }
540 public synchronized Object exec(UnixRuntime r, String path) throws ErrnoException {
541 FStat fstat = stat(r,path);
542 if(fstat == null) return null;
543 long mtime = fstat.mtime();
544 long size = fstat.size();
545 CacheEnt ent = (CacheEnt) execCache.get(path);
547 System.err.println("Found cached entry for " + path);
548 if(ent.time == mtime && ent.size == size) return ent.o;
549 System.err.println("Cache was out of date");
550 execCache.remove(path);
552 FD fd = open(r,path,RD_ONLY,0);
553 if(fd == null) return null;
554 Seekable s = fd.seekable();
556 String[] command = null;
558 if(s == null) throw new ErrnoException(EACCES);
559 byte[] buf = new byte[4096];
562 int n = s.read(buf,0,buf.length);
563 if(n == -1) throw new Error("zero length file");
566 case '\177': // possible ELF
567 if(n < 4 && s.tryReadFully(buf,n,4-n) != 4-n) throw new ErrnoException(ENOEXEC);
568 if(buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F') throw new ErrnoException(ENOEXEC);
572 int n2 = s.read(buf,1,buf.length-1);
573 if(n2 == -1) throw new ErrnoException(ENOEXEC);
576 if(buf[1] != '!') throw new ErrnoException(ENOEXEC);
580 for(int i=p;i<p+n;i++) if(buf[i] == '\n') { p = i; break OUTER; }
582 if(p == buf.length) break OUTER;
583 n = s.read(buf,p,buf.length-p);
585 command = new String[2];
587 for(arg=2;arg<p;arg++) if(buf[arg] == ' ') break;
590 while(arg < p && buf[arg] == ' ') arg++;
591 command[0] = new String(buf,2,cmdEnd);
592 command[1] = arg < p ? new String(buf,arg,p-arg) : null;
594 command[0] = new String(buf,2,p-2);
596 System.err.println("command[0]: " + command[0] + " command[1]: " + command[1]);
599 throw new ErrnoException(ENOEXEC);
601 } catch(IOException e) {
603 throw new ErrnoException(EIO);
606 if(command == null) {
610 Class c = RuntimeCompiler.compile(s);
611 System.err.println("Compile succeeded: " + c);
612 ent = new CacheEnt(mtime,size,c);
613 } catch(Compiler.Exn e) {
614 throw new ErrnoException(ENOEXEC);
615 } catch(IOException e) {
616 throw new ErrnoException(EIO);
619 ent = new CacheEnt(mtime,size,command);
624 execCache.put(path,ent);
629 public abstract static class FS {
630 // FEATURE: inode stuff
631 // FEATURE: Implement whatever is needed to get newlib's opendir and friends to work - that patch is a pain
632 protected static FD directoryFD(String[] files, int hashCode) {
633 ByteArrayOutputStream bos = new ByteArrayOutputStream();
634 DataOutputStream dos = new DataOutputStream(bos);
636 for(int i=0;i<files.length;i++) {
637 byte[] b = getBytes(files[i]);
638 int inode = (files[i].hashCode() ^ hashCode) & 0xfffff;
640 dos.writeInt(b.length);
641 dos.write(b,0,b.length);
643 } catch(IOException e) {
646 final byte[] data = bos.toByteArray();
647 return new SeekableFD(new Seekable.ByteArray(data,false),RD_ONLY) {
648 protected FStat _fstat() { return new FStat() {
649 public int length() { return data.length; }
650 public int type() { return S_IFDIR; }
655 public FStat lstat(UnixRuntime r, String path) throws ErrnoException { return stat(r,path); }
657 // If this returns null it'll be truned into an ENOENT
658 public abstract FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException;
659 // If this returns null it'll be turned into an ENOENT
660 public abstract FStat stat(UnixRuntime r, String path) throws ErrnoException;
661 public abstract void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException;
664 private String normalizePath(String path) {
665 boolean absolute = path.startsWith("/");
666 int cwdl = cwd.length();
667 // NOTE: This isn't just a fast path, it handles cases the code below doesn't
668 if(!path.startsWith(".") && path.indexOf("./") == -1 && path.indexOf("//") == -1 && !path.endsWith("."))
669 return absolute ? path.substring(1) : cwdl == 0 ? path : path.length() == 0 ? cwd : cwd + "/" + path;
671 char[] in = new char[path.length()+1];
672 char[] out = new char[in.length + (absolute ? -1 : cwd.length())];
676 do { inp++; } while(in[inp] == '/');
677 } else if(cwdl != 0) {
678 cwd.getChars(0,cwdl,out,0);
682 path.getChars(0,path.length(),in,0);
683 while(in[inp] != 0) {
685 if(in[inp] != '/') { out[outp++] = in[inp++]; continue; }
686 while(in[inp] == '/') inp++;
688 if(in[inp] == '\0') continue;
689 if(in[inp] != '.') { out[outp++] = '/'; out[outp++] = in[inp++]; continue; }
690 if(in[inp+1] == '\0' || in[inp+1] == '/') { inp++; continue; }
691 if(in[inp+1] == '.' && (in[inp+2] == '\0' || in[inp+2] == '/')) { // ..
694 while(outp > 0 && out[outp] != '/') outp--;
695 System.err.println("After ..: " + new String(out,0,outp));
702 if(outp > 0 && out[outp-1] == '/') outp--;
703 //System.err.println("normalize: " + path + " -> " + new String(out,0,outp) + " (cwd: " + cwd + ")");
704 return new String(out,0,outp);
707 // FEATURE: override Runtime.hostFStat to add executable type checking
710 FileInputStream fis = new FileInputStream(f);
712 case '\177': _executable = fis.read() == 'E' && fis.read() == 'L' && fis.read() == 'F'; break;
713 case '#': _executable = fis.read() == '!';
716 } catch(IOException e) { }
719 // FEATURE: inode stuff
720 FD hostFSDirFD(File f) { return FS.directoryFD(f.list(),f.hashCode()); }
722 public static class HostFS extends FS {
724 public File getRoot() { return root; }
726 private static File hostRootDir() {
727 String cwd = getSystemProperty("user.dir");
728 File f = new File(cwd != null ? cwd : ".");
729 f = new File(f.getAbsolutePath());
730 while(f.getParent() != null) f = new File(f.getParent());
734 private File hostFile(String path) {
735 char sep = File.separatorChar;
737 char buf[] = path.toCharArray();
738 for(int i=0;i<buf.length;i++) {
740 if(c == '/') buf[i] = sep;
741 else if(c == sep) buf[i] = '/';
743 path = new String(buf);
745 return new File(root,path);
748 public HostFS() { this(hostRootDir()); }
749 public HostFS(String root) { this(new File(root)); }
750 public HostFS(File root) { this.root = root; }
753 public FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException {
754 final File f = hostFile(path);
755 return r.hostFSOpen(f,flags,mode);
758 public FStat stat(UnixRuntime r, String path) throws ErrnoException {
759 File f = hostFile(path);
760 if(r.sm != null && !r.sm.allowStat(f)) throw new ErrnoException(EACCES);
761 if(!f.exists()) return null;
762 return r.hostFStat(f);
765 public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException {
766 File f = hostFile(path);
767 if(r.sm != null && !r.sm.allowWrite(f)) throw new ErrnoException(EACCES);
768 if(f.exists() && f.isDirectory()) throw new ErrnoException(EEXIST);
769 if(f.exists()) throw new ErrnoException(ENOTDIR);
770 File parent = f.getParentFile();
771 if(parent!=null && (!parent.exists() || !parent.isDirectory())) throw new ErrnoException(ENOTDIR);
772 if(!f.mkdir()) throw new ErrnoException(EIO);
776 public static class DevFS extends FS {
777 private static class DevFStat extends FStat {
778 public int dev() { return 1; }
779 public int mode() { return 0666; }
780 public int type() { return S_IFCHR; }
781 public int nlink() { return 1; }
783 private static FD devZeroFD = new FD() {
784 public boolean readable() { return true; }
785 public boolean writable() { return true; }
786 public int read(byte[] a, int off, int length) { Arrays.fill(a,off,off+length,(byte)0); return length; }
787 public int write(byte[] a, int off, int length) { return length; }
788 public int seek(int n, int whence) { return 0; }
789 public FStat _fstat() { return new DevFStat(); }
791 private static FD devNullFD = new FD() {
792 public boolean readable() { return true; }
793 public boolean writable() { return true; }
794 public int read(byte[] a, int off, int length) { return 0; }
795 public int write(byte[] a, int off, int length) { return length; }
796 public int seek(int n, int whence) { return 0; }
797 public FStat _fstat() { return new DevFStat(); }
800 public FD open(UnixRuntime r, String path, int mode, int flags) throws ErrnoException {
801 if(path.equals("null")) return devNullFD;
802 if(path.equals("zero")) return devZeroFD;
803 if(path.startsWith("fd/")) {
806 n = Integer.parseInt(path.substring(4));
807 } catch(NumberFormatException e) {
810 if(n < 0 || n >= OPEN_MAX) return null;
811 if(r.fds[n] == null) return null;
812 return r.fds[n].dup();
814 if(path.equals("fd")) {
816 for(int i=0;i<OPEN_MAX;i++) if(r.fds[i] != null) count++;
817 String[] files = new String[count];
819 for(int i=0;i<OPEN_MAX;i++) if(r.fds[i] != null) files[count++] = Integer.toString(i);
820 return directoryFD(files,hashCode());
822 if(path.equals("")) {
823 String[] files = { "null", "zero", "fd" };
824 return directoryFD(files,hashCode());
829 public FStat stat(UnixRuntime r,String path) throws ErrnoException {
830 if(path.equals("null")) return devNullFD.fstat();
831 if(path.equals("zero")) return devZeroFD.fstat();
832 if(path.startsWith("fd/")) {
835 n = Integer.parseInt(path.substring(4));
836 } catch(NumberFormatException e) {
839 if(n < 0 || n >= OPEN_MAX) return null;
840 if(r.fds[n] == null) return null;
841 return r.fds[n].fstat();
843 if(path.equals("fd")) return new FStat() { public int type() { return S_IFDIR; } public int mode() { return 0444; }};
844 if(path.equals("")) return new FStat() { public int type() { return S_IFDIR; } public int mode() { return 0444; }};
848 public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { throw new ErrnoException(EACCES); }