Skip to content
Permalink
2197321db1
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
326 lines (288 sloc) 7.08 KB
// 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()
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
}