From: David Crawshaw Date: Sat, 18 Nov 2006 02:07:51 +0000 (-0800) Subject: support F_SETLK / F_GETLK commands in the fcntl() syscall X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=commitdiff_plain;h=3e206c53f6ee3d09c3bb781526c0fe01bf8f6c75 support F_SETLK / F_GETLK commands in the fcntl() syscall darcs-hash:20061118020751-0c629-ff9f662ddf77e3779c0bc58295e699bec19ab5a0.gz --- diff --git a/src/org/ibex/nestedvm/Runtime.java b/src/org/ibex/nestedvm/Runtime.java index f286620..4618dc3 100644 --- a/src/org/ibex/nestedvm/Runtime.java +++ b/src/org/ibex/nestedvm/Runtime.java @@ -76,6 +76,10 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { /** Table containing all open file descriptors. (Entries are null if the fd is not in use */ FD[] fds; // package-private for UnixRuntime boolean closeOnExec[]; + + /** Table of all current file locks held by this process. */ + Seekable.Lock[] locks; + public static final int LOCK_MAX = 8; /** Pointer to a SecurityManager for this process */ SecurityManager sm; @@ -122,6 +126,7 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { r.startTime = 0; r.fds = new FD[OPEN_MAX]; for(int i=0;i= OPEN_MAX) return -EBADFD; @@ -1028,11 +1033,124 @@ public abstract class Runtime implements UsermodeConstants,Registers,Cloneable { return 0; case F_GETFD: return closeOnExec[fdn] ? 1 : 0; + case F_GETLK: + case F_SETLK: + try { + return sys_fcntl_lock(fd, cmd, arg); + } catch (IOException e) { throw new RuntimeException(e);} default: if(STDERR_DIAG) System.err.println("WARNING: Unknown fcntl command: " + cmd); return -ENOSYS; } } + + /** 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. + 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(FD fd, int cmd, int arg) + throws FaultException, IOException { + if (arg == 0) { System.out.println("BAD ARG"); 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 s = fd.seekable(); + if (s == null) return -EINVAL; + + 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 && cmd != F_SETLK) return -EINVAL; + + if (cmd == F_GETLK) { + // Check if an l_type lock can be aquired. The only way to + // do this within the Java API is to try and create 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 < LOCK_MAX; i++) { + if (locks[i] == null || !s.equals(locks[i].seekable())) + 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 < LOCK_MAX; i++) { + if (locks[i] == null || !s.equals(locks[i].seekable())) + continue; + int pos = (int)locks[i].position(); + int size = (int)locks[i].size(); + if (l_start < pos && pos + size < l_start + l_len) { + // found a lock contained in the new requested lock + locks[i].release(); + locks[i] = null; + + } else if (l_start >= pos && pos + size >= l_start + l_len) { + // found a lock that contains the requested lock + if (locks[i].isShared() == (l_type == F_RDLCK)) { + memWrite(arg+4, pos); + memWrite(arg+8, size); + return 0; + } else { + locks[i].release(); + locks[i] = null; + } + } + } + + // create the lock + Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK); + if (lock == null) return -EAGAIN; + + int i; + for (i=0; i < LOCK_MAX; i++) + if (locks[i] == null) break; + if (i == LOCK_MAX) return -ENOLCK; + locks[i] = lock; + return 0; + + } else { + return -EINVAL; + } + } + /** The syscall dispatcher. The should be called by subclasses when the syscall instruction is invoked. diff --git a/src/org/ibex/nestedvm/util/Platform.java b/src/org/ibex/nestedvm/util/Platform.java index b81e3ac..a8db074 100644 --- a/src/org/ibex/nestedvm/util/Platform.java +++ b/src/org/ibex/nestedvm/util/Platform.java @@ -5,6 +5,7 @@ package org.ibex.nestedvm.util; import java.io.*; +import java.nio.channels.*; import java.net.*; import java.util.*; @@ -58,6 +59,10 @@ public abstract class Platform { abstract boolean _atomicCreateFile(File f) throws IOException; public static boolean atomicCreateFile(File f) throws IOException { return p._atomicCreateFile(f); } + + abstract Seekable.Lock _lockFile(Seekable s, RandomAccessFile raf, long pos, long size, boolean shared) throws IOException; + public static Seekable.Lock lockFile(Seekable s, RandomAccessFile raf, long pos, long size, boolean shared) throws IOException { + return p._lockFile(s, raf, pos, size, shared); } abstract void _socketHalfClose(Socket s, boolean output) throws IOException; public static void socketHalfClose(Socket s, boolean output) throws IOException { p._socketHalfClose(s,output); } @@ -88,6 +93,9 @@ public abstract class Platform { new FileOutputStream(f).close(); return true; } + Seekable.Lock _lockFile(Seekable s, RandomAccessFile raf, long p, long size, boolean shared) throws IOException { + throw new IOException("file locking requires jdk 1.4+"); + } void _socketHalfClose(Socket s, boolean output) throws IOException { throw new IOException("half closing sockets not supported"); } @@ -185,5 +193,29 @@ public abstract class Platform { static class Jdk14 extends Jdk13 { InetAddress _inetAddressFromBytes(byte[] a) throws UnknownHostException { return InetAddress.getByAddress(a); } + + Seekable.Lock _lockFile(Seekable s, RandomAccessFile r, long pos, long size, boolean shared) throws IOException { + FileLock flock; + try { + flock = pos == 0 && size == 0 ? r.getChannel().lock() : + r.getChannel().tryLock(pos, size, shared); + } catch (OverlappingFileLockException e) { flock = null; } + if (flock == null) return null; // region already locked + return new Jdk14FileLock(s, flock); + } + } + + private static final class Jdk14FileLock implements Seekable.Lock { + private final Seekable s; + private final FileLock l; + + Jdk14FileLock(Seekable sk, FileLock flock) { s = sk; l = flock; } + public Seekable seekable() { return s; } + public boolean isShared() { return l.isShared(); } + public boolean isValid() { return l.isValid(); } + public void release() throws IOException { l.release(); } + public long position() { return l.position(); } + public long size() { return l.size(); } + public String toString() { return l.toString(); } } } diff --git a/src/org/ibex/nestedvm/util/Seekable.java b/src/org/ibex/nestedvm/util/Seekable.java index cacca0a..964ebc2 100644 --- a/src/org/ibex/nestedvm/util/Seekable.java +++ b/src/org/ibex/nestedvm/util/Seekable.java @@ -17,6 +17,10 @@ public abstract class Seekable { public void resize(long length) throws IOException { throw new IOException("resize not implemented for " + getClass()); } + /** If pos == 0 and size == 0 lock covers whole file. */ + public Lock lock(long pos, long size, boolean shared) throws IOException { + throw new IOException("lock not implemented for " + getClass()); + } public int read() throws IOException { byte[] buf = new byte[1]; @@ -71,12 +75,14 @@ public abstract class Seekable { } public static class File extends Seekable { + private final java.io.File file; private final RandomAccessFile raf; public File(String fileName) throws IOException { this(fileName,false); } public File(String fileName, boolean writable) throws IOException { this(new java.io.File(fileName),writable,false); } public File(java.io.File file, boolean writable, boolean truncate) throws IOException { + this.file = file; String mode = writable ? "rw" : "r"; raf = truncate ? Platform.truncatedRandomAccessFile(file,mode) : new RandomAccessFile(file,mode); } @@ -88,6 +94,14 @@ public abstract class Seekable { public int length() throws IOException { return (int)raf.length(); } public void close() throws IOException { raf.close(); } public void resize(long length) throws IOException { raf.setLength(length); } + public boolean equals(Object o) { + return o != null && o instanceof File + && file.equals(((File)o).file); + } + public Lock lock(long pos, long size, boolean shared) + throws IOException { + return Platform.lockFile(this, raf, pos, size, shared); + } } public static class InputStream extends Seekable { @@ -135,4 +149,12 @@ public abstract class Seekable { public void close() throws IOException { is.close(); } } + public interface Lock { + public Seekable seekable(); + public boolean isShared(); + public boolean isValid(); + public void release() throws IOException; + public long position(); + public long size(); + } }