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 	"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 }
    327