Package erjang.driver.efile

Source Code of erjang.driver.efile.EFile

/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2009 by Trifork
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

package erjang.driver.efile;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectableChannel;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NotLinkException;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.logging.Level;
import java.util.logging.Logger;

import kilim.Pausable;
import erjang.EBinList;
import erjang.EBinary;
import erjang.EHandle;
import erjang.ERT;
import erjang.ERef;
import erjang.EString;
import erjang.NotImplemented;
import erjang.driver.EAsync;
import erjang.driver.EDriverInstance;
import erjang.driver.IO;

/**
* Java does nor support proper non-blocking IO for files (i.e., you cannot
* select on a file). Select is only supported for sockets (and server sockets).
*/
public class EFile extends EDriverInstance {
 
  static Logger log = Logger.getLogger("erjang.driver.file");

  public static final String RESOURCE_PREFIX = "/~resource/";
  private static Field FileDescriptor_FD;
 
  static {
    try {
      FileDescriptor_FD = FileDescriptor.class.getDeclaredField("fd");
      FileDescriptor_FD.setAccessible(true);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  private int getFDnumber(FileDescriptor fd) throws IllegalAccessException {
    int res = FileDescriptor_FD.getInt(fd);
    // Windows doesn't provide FD numbers - fallback to a usable dummy:
    if (res == -1) res = 255;
    return res;
  }

  /**
   *
   */
  private FileChannel fd;
  private int flags;
  private TimerState timer_state;
  private FileAsync invoke;
  private Queue<FileAsync> cq;
  private int level;
  private Lock q_mtx;
  private int write_buffered;
  private int write_bufsize;

  public int posix_errno;
  public boolean write_error;
  private long write_delay;
  private ByteBuffer read_binp;
  protected File name;

  /**
   *
   */
  private final class WriteAsync extends FileAsync {
    Lock q_mtx;
    int size, free_size, reply_size;

    /**
     * @param xreplySize
     * @param efile
     */
    private WriteAsync(boolean reply, int reply_size) {

      EFile efile = EFile.this;

      super.command = FILE_WRITE;
      super.fd = efile.fd;
      super.flags = efile.flags;
      super.level = 1;

      this.q_mtx = efile.q_mtx;
      this.size = efile.write_buffered;

      super.reply = reply;

      this.free_size = 0;
      this.reply_size = reply_size;
    }

    /** invoke_writev */
    @Override
    public void async() {
      int size;

      boolean segment = again && this.size >= 2 * FILE_SEGMENT_WRITE;
      if (segment) {
        size = FILE_SEGMENT_WRITE;
      } else {
        size = this.size;
      }

      q_mtx.lock();
      ByteBuffer[] iov0 = driver_peekq();
      if (iov0 != null) {

        // copy the buffers
        ByteBuffer[] iov = iov0.clone();
        q_mtx.unlock();

        // figure out how much data we have available for writing
        long p = 0;
        int iovcnt = 0;
        while (p < size && iovcnt < iov.length) {
          p += iov[iovcnt++].remaining();
        }

        if (iov.length > 0) {
          // What is this good for?
          assert iov[iovcnt - 1].limit() > p - size;

          if ((flags & EFILE_COMPRESSED) == EFILE_COMPRESSED) {
            for (int i = 0; i < iovcnt; i++) {
              try {
                free_size += IO.gzwrite(fd, iov[i]);
                super.result_ok = true;
              } catch (IOException e) {
                posix_errno = IO.exception_to_posix_code(e);
                super.result_ok = false;
              }
            }
          } else {
            try {

              long written_bytes = IO.writev(fd, iov);
             
              if (log.isLoggable(Level.FINER)) {
                log.finer(""+EFile.this+" :: wrote "+written_bytes);
              }
             
              free_size += written_bytes;
              result_ok = true;
            } catch (java.nio.channels.NonWritableChannelException e) {
              posix_errno = Posix.EBADF;
              super.result_ok = false;
            } catch (IOException e) {
              posix_errno = IO.exception_to_posix_code(e);
              super.result_ok = false;

            }
          }
        } else if (iov.length == 0) {
          result_ok = true;
        }
      } else {
        q_mtx.unlock();
        posix_errno = Posix.EINVAL;
        result_ok = false;
      }

      if (!result_ok) {
        again = false;
      } else if (!segment) {
        again = false;
      }
    }

    // called from the DriverTask
    @Override
    public void ready() throws Pausable {
      if (reply) {
        if (!result_ok) {
          reply_posix_error(posix_errno);
        } else {
          reply_Uint(reply_size);
        }

      } else {
        if (!result_ok) {
          EFile.this.write_error = true;
          EFile.this.posix_errno = posix_errno;
        }

      }
    }

    /*
     * (non-Javadoc)
     *
     * @see erjang.driver.efile.FileAsync#deq_free_size()
     */
    @Override
    public void deq_free_size() {
      driver_deq(free_size);
    }
  }

  /**
   *
   */
  public enum TimerState {
    IDLE, AGAIN, WRITE
  }

  /*
   * This structure contains date and time.
   */
  public static class Time {
    short year; /* (4 digits). */
    short month; /* (1..12). */
    short day; /* (1..31). */
    short hour; /* (0..23). */
    short minute; /* (0..59). */
    short second; /* (0..59). */
  }

  /** stat info about file */
  public static class Info {
    int size_low; /* Size of file, lower 32 bits.. */
    int size_high; /* Size of file, higher 32 bits. */
    int type; /* Type of file -- one of FT_*. */
    int access; /* Access to file -- one of FA_*. */
    int mode; /* Access permissions -- bit field. */
    int links; /* Number of links to file. */
    int major_device; /* Major device or file system. */
    int minor_device; /* Minor device (for devices). */
    int inode; /* Inode number. */
    int uid; /* User id of owner. */
    int gid; /* Group id of owner. */
    Time accessTime; /* Last time the file was accessed. */
    Time modifyTime; /* Last time the file was modified. */
    Time cTime; /* Creation time (Windows) or last */
  }

  /*
   * Open modes for efile_openfile().
   */
  public static final int EFILE_MODE_READ = 1;
  public static final int EFILE_MODE_WRITE = 2; /*
                         * Implies truncating file when
                         * used alone.
                         */
  public static final int EFILE_MODE_READ_WRITE = 3;
  public static final int EFILE_MODE_APPEND = 4;
  public static final int EFILE_COMPRESSED = 8;
  public static final int EFILE_MODE_EXCL = 16; /*
                           * Special for reopening on
                           * VxWorks
                           */

  /*
   * Seek modes for efile_seek().
   */
  public static final int EFILE_SEEK_SET = 0;
  public static final int EFILE_SEEK_CUR = 1;
  public static final int EFILE_SEEK_END = 2;

  /*
   * File types returned by efile_fileinfo().
   */
  public static final int FT_DEVICE = 1;
  public static final int FT_DIRECTORY = 2;
  public static final int FT_REGULAR = 3;
  public static final int FT_SYMLINK = 4;
  public static final int FT_OTHER = 5;

  /*
   * Access attributes returned by efile_fileinfo() (the bits can be ORed
   * together).
   */
  public static final int FA_NONE = 0;
  public static final int FA_WRITE = 1;
  public static final int FA_READ = 2;
  public static final int FA_EXECUTE = 4;

  /* commands sent via efile_output(v) */

  public static final int FILE_OPEN = 1; /* Essential for startup */
  public static final int FILE_READ = 2;
  public static final int FILE_LSEEK = 3;
  public static final int FILE_WRITE = 4;
  public static final int FILE_FSTAT = 5; /* Essential for startup */
  public static final int FILE_PWD = 6; /* Essential for startup */
  public static final int FILE_READDIR = 7; /* Essential for startup */
  public static final int FILE_CHDIR = 8;
  public static final int FILE_FSYNC = 9;
  public static final int FILE_MKDIR = 10;
  public static final int FILE_DELETE = 11;
  public static final int FILE_RENAME = 12;
  public static final int FILE_RMDIR = 13;
  public static final int FILE_TRUNCATE = 14;
  public static final int FILE_READ_FILE = 15; /* Essential for startup */
  public static final int FILE_WRITE_INFO = 16;
  public static final int FILE_LSTAT = 19;
  public static final int FILE_READLINK = 20;
  public static final int FILE_LINK = 21;
  public static final int FILE_SYMLINK = 22;
  public static final int FILE_CLOSE = 23;
  public static final int FILE_PWRITEV = 24;
  public static final int FILE_PREADV = 25;
  public static final int FILE_SETOPT = 26;
  public static final int FILE_IPREAD = 27;
  public static final int FILE_ALTNAME = 28;
  public static final int FILE_READ_LINE = 29;
  public static final int FILE_FDATASYNC = 30;
  public static final int FILE_FADVISE = 31;


  /* Return codes */

  public static final byte FILE_RESP_OK = 0;
  /**
   *
   */
  private static final byte[] FILE_RESP_OK_HEADER = new byte[]{ FILE_RESP_OK };
  public static final byte FILE_RESP_ERROR = 1;
  public static final byte FILE_RESP_DATA = 2;
  public static final byte FILE_RESP_NUMBER = 3;
  public static final byte FILE_RESP_INFO = 4;
  public static final byte FILE_RESP_NUMERR = 5;
  public static final byte FILE_RESP_LDATA = 6;
  public static final byte FILE_RESP_N2DATA = 7;
  public static final byte FILE_RESP_EOF = 8;
  public static final byte FILE_RESP_FNAME = 9;
  public static final byte FILE_RESP_ALL_DATA = 10;
        public static final byte FILE_RESP_LFNAME = 11;
  private static final byte[] FILE_RESP_ALL_DATA_HEADER = new byte[]{ FILE_RESP_ALL_DATA };

  /* Options */

  public static final int FILE_OPT_DELAYED_WRITE = 0;
  public static final int FILE_OPT_READ_AHEAD = 1;

  /* IPREAD variants */

  public static final int IPREAD_S32BU_P32BU = 0;
 
  /* POSIX file advises */
  public static final int POSIX_FADV_NORMAL    = 0;
  public static final int POSIX_FADV_RANDOM    = 1;
  public static final int POSIX_FADV_SEQUENTIAL  = 2;
  public static final int POSIX_FADV_WILLNEED    = 3;
  public static final int POSIX_FADV_DONTNEED    = 4;
  public static final int POSIX_FADV_NOREUSE    = 5;

  /* Limits */

  public static final int FILE_SEGMENT_READ = (256 * 1024);
  public static final int FILE_SEGMENT_WRITE = (256 * 1024);

  public static final int FILE_TYPE_DEVICE = 0;
  public static final int FILE_TYPE_DIRECTORY = 2;
  public static final int FILE_TYPE_REGULAR = 3;
  public static final int FILE_TYPE_SYMLINK = 4;

  public static final int FILE_ACCESS_NONE = 0;
  public static final int FILE_ACCESS_WRITE = 1;
  public static final int FILE_ACCESS_READ = 2;
  public static final int FILE_ACCESS_READ_WRITE = 3;
//  private static final String SYS_INFO = null;

  private static final int THREAD_SHORT_CIRCUIT;

  /** initialize value of thread_short_circuit */
  static {
    String buf = System.getenv("ERL_EFILE_THREAD_SHORT_CIRCUIT");
    if (buf == null) {
      THREAD_SHORT_CIRCUIT = 0;
    } else {
      THREAD_SHORT_CIRCUIT = Integer.parseInt(buf);
    }
  }

  /**
   * @param command
   * @param driver
   */
  public EFile(EString command, Driver driver) {
    super(driver);

    this.fd = (FileChannel) null;
    this.flags = 0;
    this.invoke = null;
    this.cq = new LinkedList<FileAsync>();
    this.timer_state = TimerState.IDLE;
    // this.read_bufsize = 0;
    this.read_binp = (ByteBuffer) null;
    // this.read_offset = 0;
    //this.read_size = 0;
    this.write_delay = 0L;
    this.write_bufsize = 0;
    // this.write_error = 0;
    this.q_mtx = driver_pdl_create();
    this.write_buffered = 0;

  }

  /**
   * @param replySize
   * @throws Pausable
   */
  public void reply_Uint(int value) throws Pausable {
    if (isUnicodeDriverInterface()) {
      ByteBuffer response = ByteBuffer.allocate(9);
      response.put(FILE_RESP_NUMBER);
      response.putLong(value);
      driver_output2(response, null);
    } else {
      ByteBuffer response = ByteBuffer.allocate(4);
      response.putInt(value);
      driver_output2(response, null);
    }
  }

  public void reply_Uint_error(int value, int posix_error) throws Pausable {
    ByteBuffer response = ByteBuffer.allocate(256);
    String err = Posix.errno_id(posix_error);   
    response.putInt(value);
    IO.putstr(response, err, false);
    driver_output2(response, null);
  }

  /**
   * @param error
   * @throws Pausable
   */
  public void reply_posix_error(int posix_errno) throws Pausable {
    ByteBuffer response = ByteBuffer.allocate(256);
    response.put(FILE_RESP_ERROR);
    String err = Posix.errno_id(posix_errno);
    IO.putstr(response, err, false);

    driver_output2(response, null);
  }


  @Override
  protected void flush() throws Pausable {
    int r = flush_write(null);
    assert (r == 0);
    cq_execute();
  }

  private abstract class SimpleFileAsync extends FileAsync {

    protected final File file;
    protected final String name;

    {
      level = 2;
      reply = true;
    }

    /**
     * @param path
     * @param cmd
     */
    public SimpleFileAsync(byte command, String path) {
      this.command = command;
      this.name = path;
      this.file = ERT.newFile(path);
    }

    @Override
    public final void async() {
      try {
        this.result_ok = false;
        run();
      } catch (OutOfMemoryError e) {
        this.result_ok = false;
        posix_errno = Posix.ENOMEM;
      } catch (SecurityException e) {
        this.result_ok = false;
        posix_errno = Posix.EPERM;
      } catch (IOException e) {
        this.result_ok = false;
        posix_errno = IO.exception_to_posix_code(e);
      } catch (Throwable e) {
        e.printStackTrace();
        this.result_ok = false;
        posix_errno = Posix.EUNKNOWN;
      }
    }

    /**
     * This is what does the real operation
     */
    protected abstract void run() throws IOException;

    public void ready() throws Pausable {
      reply(EFile.this);
    }

  }

  /*
   * (non-Javadoc)
   *
   * @see erjang.driver.EDriverInstance#outputv(java.nio.ByteBuffer[])
   */
  @Override
  protected void outputv(EHandle caller, ByteBuffer[] ev) throws Pausable {

    if (ev.length == 0 || ev[0].remaining() == 0) {
      reply_posix_error(Posix.EINVAL);
      return;
    }

    byte command = ev[0].get();
    switch (command) {
    case FILE_PREADV: {
     
      ByteBuffer evin = flatten(ev);
     
      evin.getInt(); // skip first 4-byte     
      final int n = evin.getInt();
     
      final long[] offsets = new long[n];
      final ByteBuffer[] res_ev = new ByteBuffer[n+1];
      for (int i = 1; i < n+1; i++) {
        offsets[i-1] = evin.getLong();
        int len = (int) (evin.getLong() & 0x7fffffff);
     
        res_ev[i] = ByteBuffer.allocate(len);
       
        if (log.isLoggable(Level.FINER)) {
          log.finer(EFile.this+" :: pread "+len+" @ "+offsets[i-1]);
        }
      }
     
      res_ev[0] = ByteBuffer.allocate(4+4+8*n);
      res_ev[0].putInt(0);
      res_ev[0].putInt(n);
     
      if (n == 0) {
        reply_ev(FILE_RESP_LDATA, offsets, res_ev);
       
      } else {
       
        cq_enq(new FileAsync() {
         
          int cnt;
         
          {
            this.level = 1;
            this.fd = EFile.this.fd;
            this.cnt = 0;
            this.result_ok = true;
          }
         
          @Override
          public void async() {     
           
            // TODO: handle CHOP!
           
            for (int i = cnt; i < n; i++) {
              ByteBuffer res = res_ev[i+1];
              long pos = offsets[i] + res.position();

              int rem = res.remaining();
              if (rem > 0) {
               
                int bytes_read;
                try {
                  fd.position(pos);
                  bytes_read = fd.read(res);
                 
                  if (log.isLoggable(Level.FINER)) {
                    log.finer(EFile.this +
                              ":: did pread "+ bytes_read+ "@"+pos
                              +" into res["+i+"]; missing="+res.remaining());
                  }
                } catch (IOException e) {
                  result_ok = false;
                  this.posix_errno = IO.exception_to_posix_code(e);
                  this.again = false;
                  return;
                }
               
                if (bytes_read >= 0 && res.hasRemaining()) {
                  this.again = true;
                  return;
                }
               
                res_ev[0].putLong(res.position());
                cnt += 1;
              }
             
            }
           
            this.again = false;
          }
         
          @Override
          public void ready() throws Pausable {
            if (!result_ok) {
              reply_posix_error(posix_errno);
            } else {
              reply_ev(FILE_RESP_LDATA, offsets, res_ev);
            }
          }
         

        });
      }
     
    } break;
   
   
    case FILE_CLOSE: {
      if (ev.length > 1 && ev[0].hasRemaining()) {
        reply_posix_error(Posix.EINVAL);
        return;
      }
      ByteBuffer last = ev[ev.length - 1];

      if (last.hasRemaining()) {
        reply_posix_error(Posix.EINVAL);
        return;
      }
     
      if (log.isLoggable(Level.FINE))
        log.fine(""+this+" :: close");

      //TODO: Flush & check.

      // Is this check necessary?
      /*
      if (fd == null) {
        reply_posix_error(Posix.BADF);
        return;
      }
      */

      FileAsync d = new FileAsync() {
        {
          this.fd = EFile.this.fd;
        }
        public void async() {
          try {
            fd.close();
            result_ok = true;
          } catch (IOException e) {
            result_ok = false;
            posix_errno = IO.exception_to_posix_code(e);
          }
        }

        @Override
        public void ready() throws Pausable {
          if (result_ok) {
            reply_ok();
          } else {
            reply_posix_error(posix_errno);
          }
        }
      };
      cq_enq(d);
      break;
    }

    case FILE_READ: {
     
      int[] errp = new int[1];
      if (flush_write_check_error(errp) < 0) {
        reply_posix_error(errp[0]);
        return;
      }
     
      if (ev.length > 1 && ev[0].hasRemaining()) {
        reply_posix_error(Posix.EINVAL);
        return;
      }
      ByteBuffer last = ev[ev.length - 1];

      if (last.remaining() != 8) {
        reply_posix_error(Posix.EINVAL);
        return;
      }

      final long size = last.getLong();
      if (size > Integer.MAX_VALUE || size < 0) {
        reply_posix_error(Posix.ENOMEM);
        return;
      }

      //TODO: Write-flush & check.

      FileAsync d = new FileAsync() {
        private ByteBuffer binp = null;
        {
          super.level = 2;
          super.again = true;
          this.fd = EFile.this.fd;
        }

        @Override
        public void async() {
          // first time only, initialize binp
          if (binp == null) {
            try {
              binp = ByteBuffer.allocate((int) size);
            } catch (OutOfMemoryError e) {
              result_ok = false;
              posix_errno = Posix.ENOMEM;
              return;
            }
          }

          if (binp != null && binp.hasRemaining()) {
            try {
              int want = binp.remaining();
             
              int bytes = fd.read(binp);

              if (log.isLoggable(Level.FINER)) {
                log.finer
                   (EFile.this + ":: did read "+bytes+" bytes of "+want);
              }
             
              if (bytes == -1) {
                result_ok = true;
                again = false;
                return;
              }

              if (binp.hasRemaining()) {
                again = true;
                return;
              } else {
                result_ok = true;
              }
            } catch (IOException e) {
              result_ok = false;
              posix_errno = IO.exception_to_posix_code(e);
            }
          }

          again = false;
        }

        @Override
        public void ready() throws Pausable {
          if (!result_ok) {
            reply_posix_error(posix_errno);
            return;
          }

          reply_buf(binp);
          binp.flip();
        }
      };
      cq_enq(d);
      break;
    } //break;
    case FILE_READ_FILE: {
      if (ev.length > 1 && ev[0].hasRemaining()) {
        reply_posix_error(Posix.EINVAL);
        return;
      }

      ByteBuffer last = ev[ev.length - 1];
      final String name = IO.getstr(last, true);

      if (name.length() == 0) {
        reply_posix_error(Posix.ENOENT);
        return;
      }
     
      if (ClassPathResource.isResource(name)) {
        EBinary data = ClassPathResource.read_file(name);
        if (data == null) {
          reply_posix_error(Posix.ENOENT);
        } else {
                                    if (isUnicodeDriverInterface()) {

          task.output_from_driver(new EBinList(FILE_RESP_ALL_DATA_HEADER, data));
                                    } else {
          task.output_from_driver(new EBinList(FILE_RESP_OK_HEADER, data));
                                    }
        }

        return;
      }

      FileAsync d = new FileAsync() {
        private ByteBuffer binp = null;
        private long size = 0;
        {
          super.level = 2;
          super.again = true;
        }

        @Override
        public void async() {

          // first time only, initialize binp
          if (binp == null) {
            File file = ERT.newFile(name);
            try {
              this.fd = new FileInputStream(file).getChannel();
            } catch (FileNotFoundException e) {
              this.again = false;
              result_ok = false;
              posix_errno = fileNotFound_to_posixErrno(file, EFILE_MODE_READ);

              this.again = false;
              return;
            }
            this.size = file.length();

            if (size > Integer.MAX_VALUE || size < 0) {
              result_ok = false;
              posix_errno = Posix.ENOMEM;
            } else {
              try {
                binp = ByteBuffer.allocate((int) size);
              } catch (OutOfMemoryError e) {
                result_ok = false;
                posix_errno = Posix.ENOMEM;
              }

              if (this.size == 0) {
                result_ok = true;
                this.again = false;
              }
            }
          }

          if (binp != null && binp.hasRemaining()) {

            try {
              int bytes = fd.read(binp);
              if (bytes == -1 && binp.hasRemaining()) {
                // urgh, file change size under our feet!
                result_ok = false;
                posix_errno = Posix.EIO;
              }

              if (binp.hasRemaining()) {
                again = true;
                return;
              } else {
                result_ok = true;
              }
            } catch (IOException e) {
              result_ok = false;
              posix_errno = IO.exception_to_posix_code(e);
            }

          }

          try {
            fd.close();
          } catch (IOException e) {
            result_ok = false;
            posix_errno = IO.exception_to_posix_code(e);
          }
          again = false;

        }

        @Override
        public void ready() throws Pausable {
         
          if (!result_ok) {
            reply_posix_error(posix_errno);
            return;
          }
         
          binp.flip();
          driver_output_binary(isUnicodeDriverInterface() ? FILE_RESP_ALL_DATA_HEADER : FILE_RESP_OK_HEADER, binp);
        }

      };

      cq_enq(d);
      break;
    }
   
    case FILE_PWRITEV: {
      int[] errp = new int[1];

      if (lseek_flush_read(errp) < 0) {
        reply_posix_error(errp[0]);
        return;
      }
     
      if (flush_write_check_error(errp) < 0) {
        reply_posix_error(errp[0]);
        return;
      }
     
      final int n = ev[0].getInt();
     
      if (n == 0) {
        if (ev.length > 1 || ev[0].hasRemaining()) {
          reply_posix_error(Posix.EINVAL);
        } else {
          reply_Uint(0);
        }
        return;
      }

     
      if (ev[0].remaining() != (8*2*n)) {
        reply_posix_error(Posix.EINVAL);
        return;
      }
     
      final long[] offsets = new long[n];
      final long[] sizes = new long[n];
     
      long total = 0;
      for (int i = 0; i < n; i++) {
        offsets[i] = ev[0].getLong();
        sizes[i] = ev[0].getLong();
        total += sizes[i];
      }

      if (total == 0) {
        reply_Uint(0);
        return;
      }
     
      q_mtx.lock();
      try {
        driver_enqv(ev);       
      } finally {
        q_mtx.unlock();
      }
     
      FileAsync d = new FileAsync() {
       
        int cnt = 0;
       
        {
          this.level = 1;
          this.fd = EFile.this.fd;
        }
       
        @Override
        public void async() {

          q_mtx.lock();
          ByteBuffer[] iov0 = driver_peekq();
          if (iov0 != null) {

            // copy the buffer list
            ByteBuffer[] iov = iov0.clone();
            q_mtx.unlock();
            int ip = 0;
           
            while (cnt < offsets.length && ip < iov.length) {
             
              if (sizes[cnt] == 0)
              { cnt += 1; continue; }
             
              if(!iov[ip].hasRemaining()) { ip++; continue; }
             
              ByteBuffer o = iov[ip];
              if (o.remaining() > sizes[cnt]) {
                o = o.slice();
                o.limit((int) sizes[cnt]);
              }
             
              int bytes;
              try {
                fd.position(offsets[cnt]);
                bytes = fd.write(o);
               
                if (log.isLoggable(Level.FINE)) {
                  log.fine(""+EFile.this+" :: wrote "+bytes);
                }

              } catch (IOException e) {
                EFile.this.posix_errno = IO.exception_to_posix_code(e);
                this.result_ok = false;
                return;
              }
             
              offsets[cnt] += bytes;
              sizes[cnt] -= bytes;
             
              if (o.hasRemaining())
              {
                this.again = true;
                return;
              }
             
              if (sizes[cnt] == 0) {
                cnt += 1;
              }
            }
           
            if (cnt != n) {
              this.result_ok = false;
              EFile.this.posix_errno = Posix.EINVAL;
              this.again = false;
            } else {
              this.again = false;
              this.result_ok = true;
            }
          }
         
        }

        @Override
        public void ready() throws Pausable {
          if (!result_ok) {
            reply_Uint_error(cnt, EFile.this.posix_errno);
          } else {
            reply_Uint(n);
          }
        }
       
      };

      cq_enq(d);
      break;
    }


   
    case FILE_WRITE: {
      int[] errp;
      int reply_size = 0;
     
      for (int i = 0; i < ev.length; i++) {
        reply_size += ev[i].remaining();
        if (log.isLoggable(Level.FINER))
          log.finer(""+this+" :: write "+ev[i].remaining());
      }
     
      q_mtx.lock();
      driver_enqv(ev);
      write_buffered += reply_size;
     
      if (write_buffered < write_bufsize) {
        q_mtx.unlock();
        reply_Uint(reply_size);
       
        if (timer_state == TimerState.IDLE) {
          timer_state = TimerState.WRITE;
          driver_set_timer(write_delay);
        }         
      } else if (async_write(errp = new int[1], true, reply_size) != 0){
        reply_posix_error(errp[0]);
        q_mtx.unlock();
      } else {
        q_mtx.unlock();
      }
       
      break;
    }

    default:
      // undo the get() we did to find command
      ev[0].position(ev[0].position() - 1);
      output(caller, flatten(ev));
    }

    cq_execute();

  }
 
  private int flush_write_check_error(int[] errp) {
      int r;
      if ( (r = flush_write(errp)) != 0) {
      check_write_error(null);
      return r;
      } else {
        return check_write_error(errp);
      }
  }

  private int check_write_error(int[] errp) {
      if (write_error) {
        if (errp != null) {
          errp[0] = this.posix_errno;
        }
        return -1;
        }
        return 0;
  }

  void flush_read() {
    this.read_binp = null;
  }

  private int lseek_flush_read(int[] errp) {
    int r = 0;
    int read_size = (read_binp == null ? 0 : read_binp.remaining());
    flush_read();
    if (read_size != 0) {
      if ((r = async_lseek(errp, false, -read_size, EFILE_SEEK_CUR)) < 0) {
          return r;
      }
    }
    return r;
  }

  private int async_lseek(int[] errp, final boolean do_reply, final long off, final int whence) {
   
    try {
      FileAsync d = new FileAsync() {
 
        {
          this.reply = do_reply;
          this.level = 1;
          this.fd = EFile.this.fd;
        }
       
        long out_pos;
       
        @Override
        public void async() {
 
          if ((flags & EFILE_COMPRESSED) != 0) {
            this.result_ok = false;
            this.posix_errno = Posix.EINVAL;
          }
         
          try {
         
            switch (whence) {
            case EFILE_SEEK_SET:
              out_pos=off;
              break;
             
            case EFILE_SEEK_CUR:
              long cur = fd.position();
              out_pos= cur + off;
              break;
             
            case EFILE_SEEK_END:
              cur = fd.size();
              out_pos = cur - off;
              break;
             
            default:
              this.result_ok = false;
              this.posix_errno = Posix.EINVAL;
              return;
            }
 
            fd.position(out_pos);
            if (log.isLoggable(Level.FINE)) {
              log.fine(""+EFile.this+" :: seek "+out_pos);
            }
           
          } catch (IOException e) {
            this.result_ok = false;
            this.posix_errno = IO.exception_to_posix_code(e);
          }
         
          this.result_ok = true;
        }
 
       
        @Override
        public void ready() throws Pausable {
          if (reply) {
            if (result_ok) {
              EFile.this.fd = fd;
              ByteBuffer response = ByteBuffer.allocate(9);
              response.put(FILE_RESP_NUMBER);
              response.putLong(out_pos);
              driver_output2(response, null);
            } else {
              reply_posix_error(posix_errno);
            }
          }
        }
       
 
      };
     
      cq_enq(d);
    } catch (OutOfMemoryError e) {
      if (errp != null) {
        errp[0] = Posix.ENOMEM;
      }
      return -1;
    }
    return 0;
  }

  private void reply_ev(byte response, long[] offsets, ByteBuffer[] res_ev) throws Pausable {
    ByteBuffer tmp = ByteBuffer.allocate(1);
    tmp.put(response);
    driver_outputv(tmp, res_ev);
  }

  static FilenameFilter READDIR_FILTER = new FilenameFilter() {
   
    @Override
    public boolean accept(File dir, String name) {
      return !".".equals(name) && !"..".equals(name);
    }
  };

  @Override
  protected void output(EHandle caller, ByteBuffer buf) throws Pausable {
    FileAsync d;
    final byte cmd = buf.get();
    switch (cmd) {
   
    case FILE_TRUNCATE: {
      d = new FileAsync() {
   
        {
          level = 2;
          fd = EFile.this.fd;
          command = FILE_TRUNCATE;
        }
       
        @Override
        public void async() {
          again = false;
          try {
            long pos = fd.position();
            fd.truncate(pos);         
            result_ok = true;
          } catch (IOException e) {
            result_ok = false;
            posix_errno = IO.exception_to_posix_code(e);
          }
        }

        @Override
        public void ready() throws Pausable {
          reply(EFile.this);
        }
       
      };
    } break;
   
    case FILE_FDATASYNC:
    case FILE_FSYNC: {
      d = new FileAsync() {
   
        {
          level = 2;
          fd = EFile.this.fd;
          command = cmd;
        }
       
        @Override
        public void async() {
          again = false;
          try {
            fd.force(true);
            result_ok = true;
          } catch (IOException e) {
            result_ok = false;
            posix_errno = IO.exception_to_posix_code(e);
          }
        }

        @Override
        public void ready() throws Pausable {
          reply(EFile.this);
        }
       
      };
    } break;
   
   
    case FILE_LINK:
    case FILE_SYMLINK: {
      final String old_file = IO.strcpy(buf);
      final String new_file = IO.strcpy(buf);
     
     
      d = new FileAsync() {
       
        @Override
        public void async() {
          Path existing = ERT.newFile(old_file).toPath();
          Path link = new File(new_file).toPath();
          try {
            if (cmd == FILE_LINK) {
              Files.createLink(link, existing);
            } else if (cmd == FILE_SYMLINK) {
              Files.createSymbolicLink(link, existing);
            } else {
              posix_errno = Posix.EUNKNOWN;
              result_ok = false;
              return;
            }
           
            result_ok = true;
          } catch (IOException e) {
            posix_errno = IO.exception_to_posix_code(e);
            result_ok = false;
          }
        }

        @Override
        public void ready() throws Pausable {
          reply(EFile.this);
        }
      };
     
    } break;
   
    case FILE_READLINK: {
      d = new SimpleFileAsync(cmd, IO.strcpy(buf)) {
       
        String outfile = null;
       
        public void run() {
          Path p = this.file.toPath();
         
          if (!Files.exists(p, LinkOption.NOFOLLOW_LINKS)) {
            result_ok = false;
            posix_errno = Posix.ENOENT;
            return;
          }
         
          if (!Files.isSymbolicLink(p)) {
            result_ok = false;
            posix_errno = Posix.EINVAL;
            return;
          }
         
          try {
            p = Files.readSymbolicLink(p);
            outfile = p.toString();
            result_ok = true;
           
          } catch (IOException e) {
            result_ok = false;
            posix_errno = IO.exception_to_posix_code(e);
          }
        }
       
        @Override
        public void ready() throws Pausable {
          if (!result_ok) {
            super.ready();
          } else {
            ByteBuffer reply = null;
            ByteBuffer data = null;

            // prim_file interface from R14 on
            reply = ByteBuffer.allocate(1);
            data = ByteBuffer.allocate(outfile.length());
            reply.put(FILE_RESP_FNAME);
            IO.putstr(data, outfile, false);

            driver_output2(reply, data);           
          }
        }
      };
    } break;
   
    case FILE_MKDIR: {
      d = new SimpleFileAsync(cmd, IO.strcpy(buf)) {
        public void run() {
         
          result_ok = file.mkdir();
          if (!result_ok) {
            if (name.length() == 0) {
              posix_errno = Posix.ENOENT;
            } else if (file.exists()) {
              posix_errno = Posix.EEXIST;
            } else if (file.getParentFile() != null && !file.getParentFile().isDirectory()) {
              posix_errno = Posix.ENOTDIR;
            } else {
              posix_errno = Posix.EUNKNOWN;
            }
          }
        }

      };
      break;
    }

    case FILE_RMDIR: {
      d = new SimpleFileAsync(cmd, IO.strcpy(buf)) {
        public void run() {
          result_ok = file.isDirectory() && file.delete();
          if (!result_ok) {
            if (!file.exists()) {
              posix_errno = Posix.ENOENT;
            } else if (Posix.isCWD(name, file)) {
              posix_errno = Posix.EINVAL;
            } else if (!file.isDirectory()) {
              posix_errno = Posix.ENOTDIR;
            } else if (file.exists()) {
              posix_errno = Posix.EEXIST;
            } else {
              posix_errno = Posix.EUNKNOWN;
            }
          }
        }

      };
      break;
    }
   
    case FILE_CHDIR: {
      d = new SimpleFileAsync(cmd, IO.strcpy(buf)) {
        public void run() {
          result_ok = file.isDirectory();
          if (!result_ok) {
            if (!file.exists()) {
              posix_errno = Posix.ENOENT;
            } else if (!file.isDirectory()) {
              posix_errno = Posix.ENOTDIR;
            } else {
              posix_errno = Posix.EUNKNOWN;
            }
          } else {
            try {
              System.setProperty("user.dir", file.getCanonicalPath());
            } catch (IOException e) {
              posix_errno = Posix.EUNKNOWN;
            }
          }
        }
      };
     
      break;
    }

    case FILE_DELETE: {
      d = new SimpleFileAsync(cmd, IO.strcpy(buf)) {
        public void run() {
          result_ok = file.isFile() && file.delete();
          if (!result_ok) {
            if (!file.exists()) {
              posix_errno = Posix.ENOENT;
            } else if (!file.canWrite()) {
              posix_errno = Posix.EPERM;
            } else if (file.isDirectory()) {
              posix_errno = Posix.EEXIST;
            } else {
              posix_errno = Posix.EUNKNOWN;
            }
          }
        }

      };
      break;
    }

    case FILE_PWD: {
      d = new FileAsync() {

        private String pwd;

        {
          this.command = FILE_PWD;
          super.level = 2;
        }
       
        @Override
        public void async() {
          File pwd = Posix.getCWD();
          if (pwd.exists() && pwd.isDirectory()) {
            this.pwd = pwd.getAbsolutePath();
            result_ok = true;
          } else {
            result_ok = false;
            posix_errno = Posix.ENOENT;
          }
          again = false;
        }

        @Override
        public void ready() throws Pausable {
          if (!result_ok) {
            reply_posix_error(posix_errno);
          } else {
            ByteBuffer reply = null;
            ByteBuffer data = null;
            if (isUnicodeDriverInterface()) {
              // prim_file interface from R14 on
              reply = ByteBuffer.allocate(1);
              data = ByteBuffer.allocate(pwd.length());
              reply.put(FILE_RESP_FNAME);
              IO.putstr(data, pwd, false);
            }
            else {
              // prim_file interface up to R13B
              reply = ByteBuffer.allocate(1+pwd.length());
              reply.put(FILE_RESP_OK);
              IO.putstr(reply, pwd, false);
            }
            driver_output2(reply, data);
          }
        }

      };
      break;
    }

    case FILE_LSEEK: {
      final long off = buf.getLong();
      final int whence = buf.getInt();
     
      async_lseek(null, true, off, whence);
     
      return;
    }
   
    case FILE_OPEN: {
      final int mode = buf.getInt();
      final String file_name = IO.strcpy(buf);

      d = new SimpleFileAsync(cmd, file_name) {
       
        int res_fd = 1234;
       
        public void run() {
          boolean compressed = (mode & EFILE_COMPRESSED) > 0;
          if (compressed && log.isLoggable(Level.FINE)) {
            log.fine("EFile.open_compressed "+file_name);
          }
          boolean append = (mode & EFILE_MODE_APPEND) > 0;
          if ((mode & ~(EFILE_MODE_APPEND | EFILE_MODE_READ_WRITE | EFILE_MODE_EXCL)) > 0) {
            log.warning("ONLY APPEND AND READ_WRITE OPTIONS ARE IMPLEMENTED!  mode="+mode);
            throw new NotImplemented();
          }
          try {
           
            if (compressed) {
              if ((mode & EFILE_MODE_READ_WRITE) == EFILE_MODE_READ_WRITE && append) {
                posix_errno = Posix.EINVAL;
                return;
              }
              log.warning("COMPRESSED NOT IMPLEMENTED!");
              throw new NotImplemented();
            } else {
              if ((mode & EFILE_MODE_EXCL) == EFILE_MODE_EXCL) {
                file.createNewFile()
              }
              switch (mode & EFILE_MODE_READ_WRITE) {
              case EFILE_MODE_READ: {
                FileInputStream fo = new FileInputStream(file);
                fd = fo.getChannel();
                res_fd = getFDnumber(fo.getFD());
                break;
              }
              case EFILE_MODE_WRITE: {
                FileOutputStream fo = new FileOutputStream(file);
                fd = fo.getChannel();
                res_fd = getFDnumber(fo.getFD());
                break;
              }
              case EFILE_MODE_READ_WRITE: {
                RandomAccessFile rafff;
                fd = (rafff=new RandomAccessFile(file,"rw")).getChannel();
                res_fd = getFDnumber(rafff.getFD());
                break;
              }
              default:
                throw new NotImplemented();
              }//switch

              EFile.this.name = file;
              result_ok = true;
            }
           
          } catch (FileNotFoundException fnfe) {
            posix_errno = fileNotFound_to_posixErrno(file, mode);
          } catch (IOException e) {
            log.log(Level.WARNING, "failed to open file", e);
            posix_errno = fileNotFound_to_posixErrno(file, mode);           
          } catch (IllegalAccessException e) {
            log.log(Level.WARNING, "failed to open file", e);
            posix_errno = fileNotFound_to_posixErrno(file, mode);           
          }
        }

        @Override
        public void ready() throws Pausable {
          if (result_ok) {
            EFile.this.fd = fd;
            reply_Uint(res_fd); /* TODO: fd */
          } else {
            reply_posix_error(posix_errno);
          }
        }


      };
    } break;
   
    case FILE_WRITE_INFO: {
      final int file_mode = buf.getInt();
      final int file_uid = buf.getInt();
      final int file_gid = buf.getInt();
     
      final FileTime file_atime = FileTime.from( buf.getLong(), TimeUnit.SECONDS );
      final FileTime file_mtime = FileTime.from( buf.getLong(), TimeUnit.SECONDS );
      final FileTime file_ctime = FileTime.from( buf.getLong(), TimeUnit.SECONDS );
     
      final String file_name = IO.strcpy(buf);

      if (ClassPathResource.isResource(file_name)) {
        reply_posix_error(Posix.EPERM);
        return;
      }
     
      d = new SimpleFileAsync(cmd, file_name) {
       
        @Override
        protected void run() throws IOException {
         
          if (!file.exists()) {
            this.posix_errno = Posix.ENOENT;
            this.result_ok = false;
            return;
          }
         
          Path path = file.toPath();
         
          Files.setLastModifiedTime(path, file_mtime);

          Map<String,Object> atts = Files.readAttributes(path, "unix:mode,gid,uid", LinkOption.NOFOLLOW_LINKS);
         
          if (att(atts, "mode", file_mode) != file_mode) {
            Files.setAttribute(path, "unix:mode", new Integer(file_mode), LinkOption.NOFOLLOW_LINKS);
          }
         
          if (att(atts, "gid", file_gid) != file_gid) {
            Files.setAttribute(path, "unix:gid", new Integer(file_gid), LinkOption.NOFOLLOW_LINKS);
          }
         
          if (att(atts, "uid", file_uid) != file_uid) {
            Files.setAttribute(path, "unix:uid", new Integer(file_uid), LinkOption.NOFOLLOW_LINKS);
          }
         
          this.result_ok = true;
        }
       
        int att(Map<String,Object> atts, String name, int defaultValue) {
          Number att = (Number)atts.get(name);
          if (att == null) return defaultValue;
          return att.intValue();
        }


      };
       
    } break;
   
    case FILE_FSTAT:
    case FILE_LSTAT: {
     
      final String file_name = IO.strcpy(buf);
      final File file = ERT.newFile(file_name);
     
      if (ClassPathResource.isResource(file_name)) {
        ClassPathResource.fstat(this, file_name);
        return;
      }
     
      d = new FileAsync() {
       
        long file_size;
        int  file_type;
       
        long file_access_time;
        long file_modify_time;
        long file_create_time;
       
        int file_inode;
        int file_gid;
        int file_uid;
        int file_access;
        int file_mode;
        int file_nlink;
        int file_dev;
        int file_rdev;
       
        /** emulate fstat as close as possible */
        @Override
        public void async() {
          if (!file.exists()) {
            result_ok = false;
            posix_errno = Posix.ENOENT;
            return;
          }
         
          Path path = file.toPath();
          PosixFileAttributes attrs;
          try {
            attrs = Files.readAttributes(path, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS);

          } catch (IOException e) {
            posix_errno = IO.exception_to_posix_code(e);
            result_ok = false;
            return;
          }

          file_size = attrs.size();
         
          if (attrs.isDirectory()) {
            file_type = FT_DIRECTORY;
          } else if (attrs.isRegularFile()) {
            file_type = FT_REGULAR;
          } else if (attrs.isSymbolicLink()) {
            file_type = FT_SYMLINK;
          } else if (attrs.isOther()) {
            file_type = FT_OTHER;
          } else {
            file_type = FT_DEVICE;
          }
         
            file_access_time = attrs.lastAccessTime().to(TimeUnit.SECONDS);
            file_create_time = attrs.creationTime().to(TimeUnit.SECONDS);
            file_modify_time = attrs.lastModifiedTime().to(TimeUnit.SECONDS);

            file_access = 0;
            if (Files.isReadable(path)) file_access |= FILE_ACCESS_READ;
            if (Files.isWritable(path)) file_access |= FILE_ACCESS_WRITE;

          Map<String,Object> unix_attrs = null;
          try {
            unix_attrs = Files.readAttributes(path, "unix:mode,ino,uid,gid,nlink,dev,rdev", LinkOption.NOFOLLOW_LINKS);
          } catch (UnsupportedOperationException e) {
            // ok //
          } catch (IOException e) {
            posix_errno = IO.exception_to_posix_code(e);
            result_ok = false;
            return;
          }
         
          if (unix_attrs != null && unix_attrs.containsKey("mode")) {
           
            file_mode = att(unix_attrs, "mode", 0);
            file_inode = att(unix_attrs, "ino", 0);
            file_uid = att(unix_attrs, "uid", 0);
            file_gid = att(unix_attrs, "gid", 0);
            file_nlink = att(unix_attrs, "nlink", 1);
            file_dev = att(unix_attrs, "dev", 0);
            file_rdev = att(unix_attrs, "rdev", 0);
           
          } else {
            file_inode = file.hashCode();
            file_nlink = 1;
           
            file_mode = 0;
            for (PosixFilePermission p : attrs.permissions()) {
              switch (p) {
              case OTHERS_READ:
                file_mode |= 0000004;
                break;
              case OTHERS_WRITE:
                file_mode |= 0000002;
                break;
              case OTHERS_EXECUTE:
                file_mode |= 0000001;
                break;

              case GROUP_READ:
                file_mode |= 0000040;
                break;
              case GROUP_WRITE:
                file_mode |= 0000020;
                break;
              case GROUP_EXECUTE:
                file_mode |= 0000010;
                break;

              case OWNER_READ:
                file_mode |= 0000400;
                break;
              case OWNER_WRITE:
                file_mode |= 0000200;
                break;
              case OWNER_EXECUTE:
                file_mode |= 0000100;
                break;
              }
            }

            switch (file_type) {
            case FT_DIRECTORY:
              file_mode |= 0040000;
              break;
            case FT_REGULAR:
              file_mode |= 0100000;
              break;
            case FT_SYMLINK:
              file_mode |= 0120000;
              break;
            }
           
          }
           
           
          result_ok = true;
        }
       
        @Override
        public void ready() throws Pausable {
          if (!this.result_ok) {
            reply_posix_error(posix_errno);
            return;
          }
         
          final int RESULT_SIZE = (1 + (17 * 4));
         
          ByteBuffer res = ByteBuffer.allocate(RESULT_SIZE);
          res.order(ByteOrder.BIG_ENDIAN);
         
          res.put(FILE_RESP_INFO);
          res.putLong(file_size);
          res.putInt(file_type);
         
                    res.putLong(file_access_time);
                    res.putLong(file_modify_time);
                    res.putLong(file_create_time);

          res.putInt(file_mode);
          res.putInt(file_nlink);
          res.putInt(file_dev);
          res.putInt(file_rdev);
          res.putInt(file_inode);
          res.putInt(file_uid);
          res.putInt(file_gid);
          res.putInt(file_access);
         
          driver_output2(res, null);
        }

        int att(Map<String,Object> atts, String name, int defaultValue) {
          Number att = (Number)atts.get(name);
          if (att == null) return defaultValue;
          return att.intValue();
        }

      };
     
      break;
    }
   
    case FILE_READDIR: {
     
      final String dir_name = IO.strcpy(buf);
     
      if (dir_name.startsWith(RESOURCE_PREFIX)) {
        ClassPathResource.listdir(this, dir_name.substring(RESOURCE_PREFIX.length()));
        return;
      }
     
      //final File cwd = new File(System.getProperty("user.dir")).getAbsoluteFile();
      final File dir = ERT.newFile(/*cwd, */dir_name);
     
     
      d = new FileAsync() {
        {
          super.level = 2;
        }
       
        String[] files;

        @Override
        public void async() {
          if (!dir.exists()) {
            this.posix_errno = Posix.ENOENT;
            this.result_ok = false;
            return;
          }
          if (!dir.isDirectory()) {
            this.posix_errno = Posix.ENOTDIR;
            this.result_ok = false;
            return;
          }
         
          try {
            files = dir.list(READDIR_FILTER);
            this.result_ok = true;
           
          } catch (SecurityException e) {
            this.posix_errno = Posix.EPERM;
            this.result_ok = false;
            return;
          }
        }

        @Override
        public void ready() throws Pausable {
          if (!this.result_ok) {
            reply_posix_error(posix_errno);
            return;
          }
                   
          reply_list_directory(files);
        }
      };
      break;
    }
   
    case FILE_RENAME: {
      final String from_name = IO.getstr(buf, true);
      final File to_name   = ERT.newFile(IO.getstr(buf, true));
     
      if (log.isLoggable(Level.FINE))
        log.fine(""+this+"rename "+from_name+" -> "+to_name);
     
      d = new SimpleFileAsync(cmd, from_name) {
        public void run() {
          this.result_ok = file.renameTo(to_name);
          if (!result_ok) {
            if (!file.exists()) {
              posix_errno = Posix.ENOENT;
            } else if (to_name.exists()) {
              posix_errno = Posix.EEXIST;
            } else {
              posix_errno = Posix.EUNKNOWN;
            }
          }
        }

      };
      break;
    }
   
    case FILE_FADVISE: {
      // fadvice() is not available from Java,
      // so we simply ignore it and return success
      reply_ok();
      return;     
    }
   
    case FILE_SETOPT: {
      reply_ok();
      return;     
    }

   
    default:
      log.warning("invalid file_output cmd:" + ((int) cmd) + " "
      + EBinary.make(buf));
     
      driver_output_binary(new byte[]{ FILE_RESP_ERROR },
                   ByteBuffer.wrap( ( "unknown_cmd_"+((int)cmd)).getBytes() ));
      return;
      /** ignore everything else - let the caller hang */
      // return;
    }

    if (d != null) {
      cq_enq(d);
    }

  }

 
 
 
  @Override
  public void processExit(ERef monitor) throws Pausable {
    // TODO Auto-generated method stub

  }

  @Override
  protected void readyAsync(EAsync data) throws Pausable {
    FileAsync d = (FileAsync) data;

    if (try_again(d))
      return;

    // do whatever for this kind of async job
    d.ready();

    if (write_buffered != 0 && timer_state == TimerState.IDLE) {
      timer_state = TimerState.WRITE;
      driver_set_timer(write_delay);
    }

    cq_execute();
  }

  /**
   * @param d
   * @return
   */
  private boolean try_again(FileAsync d) {
    if (!d.again) {
      return false;
    }

    d.deq_free_size();

    if (timer_state != TimerState.IDLE) {
      driver_cancel_timer();
    }

    timer_state = TimerState.AGAIN;
    invoke = d;
    driver_set_timer(0);

    return true;
  }

  @Override
  protected void readyInput(SelectableChannel ch) throws Pausable {
    throw new InternalError("should not happen");
  }

  @Override
  protected void readyOutput(SelectableChannel evt) throws Pausable {
    throw new InternalError("should not happen");
  }

  @Override
  protected void timeout() throws Pausable {
    TimerState timer_state = this.timer_state;
    this.timer_state = TimerState.IDLE;
    switch (timer_state) {
    case IDLE:
      assert (false) : "timeout in idle state?";
      return;

    case AGAIN:
      assert (invoke != null);
      driver_async(invoke);
      break;

    case WRITE:
      int r = flush_write(null);
      assert (r == 0);
      cq_execute();
    }
  }

  /**
   * @return
   */
  private int flush_write(int[] errp) {
    int result;
    q_mtx.lock();
    try {
      if (this.write_buffered > 0) {
        result = async_write(null, false, 0);
      } else {
        result = 0;
      }
    } finally {
      q_mtx.unlock();
    }
    return result;
  }

  // // CQ OPERATIONS //

  /**
   * @param errp
   * @param reply
   *            true if we should send a reply
   * @param reply_size
   *            value to send in reply
   * @return
   */
  private int async_write(int[] errp, boolean reply, int reply_size) {
    try {
      FileAsync cmd = new WriteAsync(reply, reply_size);
      cq_enq(cmd);
      write_buffered = 0;
      return 0;
    } catch (OutOfMemoryError e) {
      if (errp == null) {
        throw e;
      }
      if (errp != null)
        errp[0] = Posix.ENOMEM;
      return -1;
    }
  }

  private void cq_enq(FileAsync d) {
    cq.add(d);
  }

  private FileAsync cq_deq() {
    return cq.poll();
  }

  private void cq_execute() throws Pausable {
    if (timer_state == TimerState.AGAIN)
      return;

    FileAsync d;
    if ((d = cq_deq()) == null)
      return;

    d.again = false; /* (SYS_INFO.async_threads == 0); */

    if (THREAD_SHORT_CIRCUIT >= level) {
      d.async();
      this.readyAsync(d);
    } else {
      driver_async(d);
    }
  }

  //

  void reply_buf(ByteBuffer buf) throws Pausable {
    ByteBuffer header = ByteBuffer.allocate(1 + 4 + 4);
    header.put(FILE_RESP_DATA);
    header.putLong(buf.position());
    driver_output2(header, buf);
  }

  void reply_eof() throws Pausable {
    ByteBuffer header = ByteBuffer.allocate(1);
    header.put(FILE_RESP_EOF);
    driver_output2(header, null);
  }

  /**
   * @throws Pausable
   *
   */
  public void reply_ok() throws Pausable {
    ByteBuffer header = ByteBuffer.allocate(1);
    header.put(FILE_RESP_OK);
    driver_output2(header, null);
  }

  protected static int fileNotFound_to_posixErrno(File file, int mode) {
    if (!file.exists() || !file.isFile())
      return Posix.ENOENT;
    else if ((mode & EFILE_MODE_READ) > 0 && !file.canRead())
      return Posix.EPERM;
    else if ((mode & EFILE_MODE_WRITE) > 0 && !file.canWrite())
      return Posix.EPERM;
    else
      return Posix.EUNKNOWN;
  }

  @Override
  public String toString() {
    String pos;
    try {
      pos = (fd==null?"?":"0x"+Long.toHexString (fd.position()));
    } catch (IOException e) {
      pos = "?";
    }
    return "EFile[name=\""+name+"\";pos="+pos+"]";
   
  }

  void reply_list_directory(String[] files) throws Pausable {
    for (int i = 0; i < files.length; i++) {
                    if (isLFNameDriverInterface()) {
        // prim_file interface from R15 on
        ByteBuffer reply = ByteBuffer.allocate(1);
        reply.put(FILE_RESP_LFNAME);
       
        ByteBuffer data = ByteBuffer.allocate(2+files[i].length());
        data.limit(data.capacity());
        data.position(0);
                                data.putShort((short)files[i].length());
        IO.putstr(data, files[i], false);
       
        driver_output2(reply, data);
                    } else if (isUnicodeDriverInterface()) {
        // prim_file interface from R14 on
        ByteBuffer reply = ByteBuffer.allocate(1);
        reply.put(FILE_RESP_FNAME);
       
        ByteBuffer data = ByteBuffer.allocate(files[i].length());
        data.limit(data.capacity());
        data.position(0);
        IO.putstr(data, files[i], false);
       
        driver_output2(reply, data);
      }
      else {
        // prim_file interface up to R13B
        ByteBuffer resbuf = ByteBuffer.allocate(files[i].length()+1);
        resbuf.put(FILE_RESP_OK);
        resbuf.limit(resbuf.capacity());
        resbuf.position(1);
       
        IO.putstr(resbuf, files[i], false);
       
        driver_output2(resbuf, null);
      }
    }

                if (isLFNameDriverInterface()) {
                    ByteBuffer resbuf = ByteBuffer.allocate(1);
                    resbuf.put(FILE_RESP_LFNAME);
                    driver_output2(resbuf, null);
                } else {
   
                    ByteBuffer resbuf = ByteBuffer.allocate(1);
                    resbuf.put(isUnicodeDriverInterface() ? FILE_RESP_FNAME : FILE_RESP_OK);
                    driver_output2(resbuf, null);

                }
  }
 
  /**
   * Determine whether to use the new unicode driver interface
   * from R14B01.
   *
   * @return <code>true</code>, if the new driver interface
   * from R14B01 is to be used, <code>false</code> for the older
   * driver interface up to R13
   *
   * @see http://www.erlang.org/doc/apps/stdlib/unicode_usage.html#id60205
   */
  private static boolean isUnicodeDriverInterface() {
    return ERT.runtime_info.unicodeDriverInterface;
  }

  private static boolean isLFNameDriverInterface() {
    return ERT.runtime_info.unicodeDriverInterface;
  }
}
TOP

Related Classes of erjang.driver.efile.EFile

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.