|  | // 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 | 
|  | } |