...

Source file src/syscall/fd_nacl.go

     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