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