Home | History | Annotate | Download | only in 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 	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 }
    328