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