...
Run Format

Source file src/syscall/fd_nacl.go

Documentation: syscall

     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  	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, io.SeekCurrent)
   256  	if err != nil {
   257  		return 0, err
   258  	}
   259  	if _, err := f.seek(offset, io.SeekStart); err != nil {
   260  		return 0, err
   261  	}
   262  	n, err := rw(b)
   263  	f.seek(old, io.SeekStart)
   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  }
   327  

View as plain text