github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/syscall/fd_nacl.go (about)

     1  // Copyright 2013 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // File descriptor support for Native Client.
     6  // We want to provide access to a broader range of (simulated) files than
     7  // Native Client allows, so we maintain our own file descriptor table exposed
     8  // to higher-level packages.
     9  
    10  package syscall
    11  
    12  import (
    13  	"sync"
    14  )
    15  
    16  // files is the table indexed by a file descriptor.
    17  var files struct {
    18  	sync.RWMutex
    19  	tab []*file
    20  }
    21  
    22  // A file is an open file, something with a file descriptor.
    23  // A particular *file may appear in files multiple times, due to use of Dup or Dup2.
    24  type file struct {
    25  	fdref int      // uses in files.tab
    26  	impl  fileImpl // underlying implementation
    27  }
    28  
    29  // A fileImpl is the implementation of something that can be a file.
    30  type fileImpl interface {
    31  	// Standard operations.
    32  	// These can be called concurrently from multiple goroutines.
    33  	stat(*Stat_t) error
    34  	read([]byte) (int, error)
    35  	write([]byte) (int, error)
    36  	seek(int64, int) (int64, error)
    37  	pread([]byte, int64) (int, error)
    38  	pwrite([]byte, int64) (int, error)
    39  
    40  	// Close is called when the last reference to a *file is removed
    41  	// from the file descriptor table. It may be called concurrently
    42  	// with active operations such as blocked read or write calls.
    43  	close() error
    44  }
    45  
    46  // newFD adds impl to the file descriptor table,
    47  // returning the new file descriptor.
    48  // Like Unix, it uses the lowest available descriptor.
    49  func newFD(impl fileImpl) int {
    50  	files.Lock()
    51  	defer files.Unlock()
    52  	f := &file{impl: impl, fdref: 1}
    53  	for fd, oldf := range files.tab {
    54  		if oldf == nil {
    55  			files.tab[fd] = f
    56  			return fd
    57  		}
    58  	}
    59  	fd := len(files.tab)
    60  	files.tab = append(files.tab, f)
    61  	return fd
    62  }
    63  
    64  // Install Native Client stdin, stdout, stderr.
    65  func init() {
    66  	newFD(&naclFile{naclFD: 0})
    67  	newFD(&naclFile{naclFD: 1})
    68  	newFD(&naclFile{naclFD: 2})
    69  }
    70  
    71  // fdToFile retrieves the *file corresponding to a file descriptor.
    72  func fdToFile(fd int) (*file, error) {
    73  	files.Lock()
    74  	defer files.Unlock()
    75  	if fd < 0 || fd >= len(files.tab) || files.tab[fd] == nil {
    76  		return nil, EBADF
    77  	}
    78  	return files.tab[fd], nil
    79  }
    80  
    81  func Close(fd int) error {
    82  	files.Lock()
    83  	if fd < 0 || fd >= len(files.tab) || files.tab[fd] == nil {
    84  		files.Unlock()
    85  		return EBADF
    86  	}
    87  	f := files.tab[fd]
    88  	files.tab[fd] = nil
    89  	f.fdref--
    90  	fdref := f.fdref
    91  	files.Unlock()
    92  	if fdref > 0 {
    93  		return nil
    94  	}
    95  	return f.impl.close()
    96  }
    97  
    98  func CloseOnExec(fd int) {
    99  	// nothing to do - no exec
   100  }
   101  
   102  func Dup(fd int) (int, error) {
   103  	files.Lock()
   104  	defer files.Unlock()
   105  	if fd < 0 || fd >= len(files.tab) || files.tab[fd] == nil {
   106  		return -1, EBADF
   107  	}
   108  	f := files.tab[fd]
   109  	f.fdref++
   110  	for newfd, oldf := range files.tab {
   111  		if oldf == nil {
   112  			files.tab[newfd] = f
   113  			return newfd, nil
   114  		}
   115  	}
   116  	newfd := len(files.tab)
   117  	files.tab = append(files.tab, f)
   118  	return newfd, nil
   119  }
   120  
   121  func Dup2(fd, newfd int) error {
   122  	files.Lock()
   123  	defer files.Unlock()
   124  	if fd < 0 || fd >= len(files.tab) || files.tab[fd] == nil || newfd < 0 || newfd >= len(files.tab)+100 {
   125  		files.Unlock()
   126  		return EBADF
   127  	}
   128  	f := files.tab[fd]
   129  	f.fdref++
   130  	for cap(files.tab) <= newfd {
   131  		files.tab = append(files.tab[:cap(files.tab)], nil)
   132  	}
   133  	oldf := files.tab[newfd]
   134  	var oldfdref int
   135  	if oldf != nil {
   136  		oldf.fdref--
   137  		oldfdref = oldf.fdref
   138  	}
   139  	files.tab[newfd] = f
   140  	files.Unlock()
   141  	if oldf != nil {
   142  		if oldfdref == 0 {
   143  			oldf.impl.close()
   144  		}
   145  	}
   146  	return nil
   147  }
   148  
   149  func Fstat(fd int, st *Stat_t) error {
   150  	f, err := fdToFile(fd)
   151  	if err != nil {
   152  		return err
   153  	}
   154  	return f.impl.stat(st)
   155  }
   156  
   157  func Read(fd int, b []byte) (int, error) {
   158  	f, err := fdToFile(fd)
   159  	if err != nil {
   160  		return 0, err
   161  	}
   162  	return f.impl.read(b)
   163  }
   164  
   165  var zerobuf [0]byte
   166  
   167  func Write(fd int, b []byte) (int, error) {
   168  	if b == nil {
   169  		// avoid nil in syscalls; nacl doesn't like that.
   170  		b = zerobuf[:]
   171  	}
   172  	f, err := fdToFile(fd)
   173  	if err != nil {
   174  		return 0, err
   175  	}
   176  	return f.impl.write(b)
   177  }
   178  
   179  func Pread(fd int, b []byte, offset int64) (int, error) {
   180  	f, err := fdToFile(fd)
   181  	if err != nil {
   182  		return 0, err
   183  	}
   184  	return f.impl.pread(b, offset)
   185  }
   186  
   187  func Pwrite(fd int, b []byte, offset int64) (int, error) {
   188  	f, err := fdToFile(fd)
   189  	if err != nil {
   190  		return 0, err
   191  	}
   192  	return f.impl.pwrite(b, offset)
   193  }
   194  
   195  func Seek(fd int, offset int64, whence int) (int64, error) {
   196  	f, err := fdToFile(fd)
   197  	if err != nil {
   198  		return 0, err
   199  	}
   200  	return f.impl.seek(offset, whence)
   201  }
   202  
   203  // defaulFileImpl implements fileImpl.
   204  // It can be embedded to complete a partial fileImpl implementation.
   205  type defaultFileImpl struct{}
   206  
   207  func (*defaultFileImpl) close() error                      { return nil }
   208  func (*defaultFileImpl) stat(*Stat_t) error                { return ENOSYS }
   209  func (*defaultFileImpl) read([]byte) (int, error)          { return 0, ENOSYS }
   210  func (*defaultFileImpl) write([]byte) (int, error)         { return 0, ENOSYS }
   211  func (*defaultFileImpl) seek(int64, int) (int64, error)    { return 0, ENOSYS }
   212  func (*defaultFileImpl) pread([]byte, int64) (int, error)  { return 0, ENOSYS }
   213  func (*defaultFileImpl) pwrite([]byte, int64) (int, error) { return 0, ENOSYS }
   214  
   215  // naclFile is the fileImpl implementation for a Native Client file descriptor.
   216  type naclFile struct {
   217  	defaultFileImpl
   218  	naclFD int
   219  }
   220  
   221  func (f *naclFile) stat(st *Stat_t) error {
   222  	return naclFstat(f.naclFD, st)
   223  }
   224  
   225  func (f *naclFile) read(b []byte) (int, error) {
   226  	n, err := naclRead(f.naclFD, b)
   227  	if err != nil {
   228  		n = 0
   229  	}
   230  	return n, err
   231  }
   232  
   233  // implemented in package runtime, to add time header on playground
   234  func naclWrite(fd int, b []byte) int
   235  
   236  func (f *naclFile) write(b []byte) (int, error) {
   237  	n := naclWrite(f.naclFD, b)
   238  	if n < 0 {
   239  		return 0, Errno(-n)
   240  	}
   241  	return n, nil
   242  }
   243  
   244  func (f *naclFile) seek(off int64, whence int) (int64, error) {
   245  	old := off
   246  	err := naclSeek(f.naclFD, &off, whence)
   247  	if err != nil {
   248  		return old, err
   249  	}
   250  	return off, nil
   251  }
   252  
   253  func (f *naclFile) prw(b []byte, offset int64, rw func([]byte) (int, error)) (int, error) {
   254  	// NaCl has no pread; simulate with seek and hope for no races.
   255  	old, err := f.seek(0, 1)
   256  	if err != nil {
   257  		return 0, err
   258  	}
   259  	if _, err := f.seek(offset, 0); err != nil {
   260  		return 0, err
   261  	}
   262  	n, err := rw(b)
   263  	f.seek(old, 0)
   264  	return n, err
   265  }
   266  
   267  func (f *naclFile) pread(b []byte, offset int64) (int, error) {
   268  	return f.prw(b, offset, f.read)
   269  }
   270  
   271  func (f *naclFile) pwrite(b []byte, offset int64) (int, error) {
   272  	return f.prw(b, offset, f.write)
   273  }
   274  
   275  func (f *naclFile) close() error {
   276  	err := naclClose(f.naclFD)
   277  	f.naclFD = -1
   278  	return err
   279  }
   280  
   281  // A pipeFile is an in-memory implementation of a pipe.
   282  // The byteq implementation is in net_nacl.go.
   283  type pipeFile struct {
   284  	defaultFileImpl
   285  	rd *byteq
   286  	wr *byteq
   287  }
   288  
   289  func (f *pipeFile) close() error {
   290  	if f.rd != nil {
   291  		f.rd.close()
   292  	}
   293  	if f.wr != nil {
   294  		f.wr.close()
   295  	}
   296  	return nil
   297  }
   298  
   299  func (f *pipeFile) read(b []byte) (int, error) {
   300  	if f.rd == nil {
   301  		return 0, EINVAL
   302  	}
   303  	n, err := f.rd.read(b, 0)
   304  	if err == EAGAIN {
   305  		err = nil
   306  	}
   307  	return n, err
   308  }
   309  
   310  func (f *pipeFile) write(b []byte) (int, error) {
   311  	if f.wr == nil {
   312  		return 0, EINVAL
   313  	}
   314  	n, err := f.wr.write(b, 0)
   315  	if err == EAGAIN {
   316  		err = EPIPE
   317  	}
   318  	return n, err
   319  }
   320  
   321  func Pipe(fd []int) error {
   322  	q := newByteq()
   323  	fd[0] = newFD(&pipeFile{rd: q})
   324  	fd[1] = newFD(&pipeFile{wr: q})
   325  	return nil
   326  }