...
Run Format

Source file src/syscall/fd_nacl.go

Documentation: syscall

  // Copyright 2013 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  // File descriptor support for Native Client.
  // We want to provide access to a broader range of (simulated) files than
  // Native Client allows, so we maintain our own file descriptor table exposed
  // to higher-level packages.
  
  package syscall
  
  import (
  	"io"
  	"sync"
  )
  
  // files is the table indexed by a file descriptor.
  var files struct {
  	sync.RWMutex
  	tab []*file
  }
  
  // A file is an open file, something with a file descriptor.
  // A particular *file may appear in files multiple times, due to use of Dup or Dup2.
  type file struct {
  	fdref int      // uses in files.tab
  	impl  fileImpl // underlying implementation
  }
  
  // A fileImpl is the implementation of something that can be a file.
  type fileImpl interface {
  	// Standard operations.
  	// These can be called concurrently from multiple goroutines.
  	stat(*Stat_t) error
  	read([]byte) (int, error)
  	write([]byte) (int, error)
  	seek(int64, int) (int64, error)
  	pread([]byte, int64) (int, error)
  	pwrite([]byte, int64) (int, error)
  
  	// Close is called when the last reference to a *file is removed
  	// from the file descriptor table. It may be called concurrently
  	// with active operations such as blocked read or write calls.
  	close() error
  }
  
  // newFD adds impl to the file descriptor table,
  // returning the new file descriptor.
  // Like Unix, it uses the lowest available descriptor.
  func newFD(impl fileImpl) int {
  	files.Lock()
  	defer files.Unlock()
  	f := &file{impl: impl, fdref: 1}
  	for fd, oldf := range files.tab {
  		if oldf == nil {
  			files.tab[fd] = f
  			return fd
  		}
  	}
  	fd := len(files.tab)
  	files.tab = append(files.tab, f)
  	return fd
  }
  
  // Install Native Client stdin, stdout, stderr.
  func init() {
  	newFD(&naclFile{naclFD: 0})
  	newFD(&naclFile{naclFD: 1})
  	newFD(&naclFile{naclFD: 2})
  }
  
  // fdToFile retrieves the *file corresponding to a file descriptor.
  func fdToFile(fd int) (*file, error) {
  	files.Lock()
  	defer files.Unlock()
  	if fd < 0 || fd >= len(files.tab) || files.tab[fd] == nil {
  		return nil, EBADF
  	}
  	return files.tab[fd], nil
  }
  
  func Close(fd int) error {
  	files.Lock()
  	if fd < 0 || fd >= len(files.tab) || files.tab[fd] == nil {
  		files.Unlock()
  		return EBADF
  	}
  	f := files.tab[fd]
  	files.tab[fd] = nil
  	f.fdref--
  	fdref := f.fdref
  	files.Unlock()
  	if fdref > 0 {
  		return nil
  	}
  	return f.impl.close()
  }
  
  func CloseOnExec(fd int) {
  	// nothing to do - no exec
  }
  
  func Dup(fd int) (int, error) {
  	files.Lock()
  	defer files.Unlock()
  	if fd < 0 || fd >= len(files.tab) || files.tab[fd] == nil {
  		return -1, EBADF
  	}
  	f := files.tab[fd]
  	f.fdref++
  	for newfd, oldf := range files.tab {
  		if oldf == nil {
  			files.tab[newfd] = f
  			return newfd, nil
  		}
  	}
  	newfd := len(files.tab)
  	files.tab = append(files.tab, f)
  	return newfd, nil
  }
  
  func Dup2(fd, newfd int) error {
  	files.Lock()
  	defer files.Unlock()
  	if fd < 0 || fd >= len(files.tab) || files.tab[fd] == nil || newfd < 0 || newfd >= len(files.tab)+100 {
  		files.Unlock()
  		return EBADF
  	}
  	f := files.tab[fd]
  	f.fdref++
  	for cap(files.tab) <= newfd {
  		files.tab = append(files.tab[:cap(files.tab)], nil)
  	}
  	oldf := files.tab[newfd]
  	var oldfdref int
  	if oldf != nil {
  		oldf.fdref--
  		oldfdref = oldf.fdref
  	}
  	files.tab[newfd] = f
  	files.Unlock()
  	if oldf != nil {
  		if oldfdref == 0 {
  			oldf.impl.close()
  		}
  	}
  	return nil
  }
  
  func Fstat(fd int, st *Stat_t) error {
  	f, err := fdToFile(fd)
  	if err != nil {
  		return err
  	}
  	return f.impl.stat(st)
  }
  
  func Read(fd int, b []byte) (int, error) {
  	f, err := fdToFile(fd)
  	if err != nil {
  		return 0, err
  	}
  	return f.impl.read(b)
  }
  
  var zerobuf [0]byte
  
  func Write(fd int, b []byte) (int, error) {
  	if b == nil {
  		// avoid nil in syscalls; nacl doesn't like that.
  		b = zerobuf[:]
  	}
  	f, err := fdToFile(fd)
  	if err != nil {
  		return 0, err
  	}
  	return f.impl.write(b)
  }
  
  func Pread(fd int, b []byte, offset int64) (int, error) {
  	f, err := fdToFile(fd)
  	if err != nil {
  		return 0, err
  	}
  	return f.impl.pread(b, offset)
  }
  
  func Pwrite(fd int, b []byte, offset int64) (int, error) {
  	f, err := fdToFile(fd)
  	if err != nil {
  		return 0, err
  	}
  	return f.impl.pwrite(b, offset)
  }
  
  func Seek(fd int, offset int64, whence int) (int64, error) {
  	f, err := fdToFile(fd)
  	if err != nil {
  		return 0, err
  	}
  	return f.impl.seek(offset, whence)
  }
  
  // defaulFileImpl implements fileImpl.
  // It can be embedded to complete a partial fileImpl implementation.
  type defaultFileImpl struct{}
  
  func (*defaultFileImpl) close() error                      { return nil }
  func (*defaultFileImpl) stat(*Stat_t) error                { return ENOSYS }
  func (*defaultFileImpl) read([]byte) (int, error)          { return 0, ENOSYS }
  func (*defaultFileImpl) write([]byte) (int, error)         { return 0, ENOSYS }
  func (*defaultFileImpl) seek(int64, int) (int64, error)    { return 0, ENOSYS }
  func (*defaultFileImpl) pread([]byte, int64) (int, error)  { return 0, ENOSYS }
  func (*defaultFileImpl) pwrite([]byte, int64) (int, error) { return 0, ENOSYS }
  
  // naclFile is the fileImpl implementation for a Native Client file descriptor.
  type naclFile struct {
  	defaultFileImpl
  	naclFD int
  }
  
  func (f *naclFile) stat(st *Stat_t) error {
  	return naclFstat(f.naclFD, st)
  }
  
  func (f *naclFile) read(b []byte) (int, error) {
  	n, err := naclRead(f.naclFD, b)
  	if err != nil {
  		n = 0
  	}
  	return n, err
  }
  
  // implemented in package runtime, to add time header on playground
  func naclWrite(fd int, b []byte) int
  
  func (f *naclFile) write(b []byte) (int, error) {
  	n := naclWrite(f.naclFD, b)
  	if n < 0 {
  		return 0, Errno(-n)
  	}
  	return n, nil
  }
  
  func (f *naclFile) seek(off int64, whence int) (int64, error) {
  	old := off
  	err := naclSeek(f.naclFD, &off, whence)
  	if err != nil {
  		return old, err
  	}
  	return off, nil
  }
  
  func (f *naclFile) prw(b []byte, offset int64, rw func([]byte) (int, error)) (int, error) {
  	// NaCl has no pread; simulate with seek and hope for no races.
  	old, err := f.seek(0, io.SeekCurrent)
  	if err != nil {
  		return 0, err
  	}
  	if _, err := f.seek(offset, io.SeekStart); err != nil {
  		return 0, err
  	}
  	n, err := rw(b)
  	f.seek(old, io.SeekStart)
  	return n, err
  }
  
  func (f *naclFile) pread(b []byte, offset int64) (int, error) {
  	return f.prw(b, offset, f.read)
  }
  
  func (f *naclFile) pwrite(b []byte, offset int64) (int, error) {
  	return f.prw(b, offset, f.write)
  }
  
  func (f *naclFile) close() error {
  	err := naclClose(f.naclFD)
  	f.naclFD = -1
  	return err
  }
  
  // A pipeFile is an in-memory implementation of a pipe.
  // The byteq implementation is in net_nacl.go.
  type pipeFile struct {
  	defaultFileImpl
  	rd *byteq
  	wr *byteq
  }
  
  func (f *pipeFile) close() error {
  	if f.rd != nil {
  		f.rd.close()
  	}
  	if f.wr != nil {
  		f.wr.close()
  	}
  	return nil
  }
  
  func (f *pipeFile) read(b []byte) (int, error) {
  	if f.rd == nil {
  		return 0, EINVAL
  	}
  	n, err := f.rd.read(b, 0)
  	if err == EAGAIN {
  		err = nil
  	}
  	return n, err
  }
  
  func (f *pipeFile) write(b []byte) (int, error) {
  	if f.wr == nil {
  		return 0, EINVAL
  	}
  	n, err := f.wr.write(b, 0)
  	if err == EAGAIN {
  		err = EPIPE
  	}
  	return n, err
  }
  
  func Pipe(fd []int) error {
  	q := newByteq()
  	fd[0] = newFD(&pipeFile{rd: q})
  	fd[1] = newFD(&pipeFile{wr: q})
  	return nil
  }
  

View as plain text