blob: 74324142a7ac82378b950d47cf0fb08ba58b0f5e [file] [log] [blame]
Dave Cheney7c8280c2014-02-25 09:47:42 -05001// 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
10package syscall
11
12import (
13 "sync"
14)
15
16// files is the table indexed by a file descriptor.
17var 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.
24type 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.
30type 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.
49func 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.
65func 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.
72func 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
81func 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
98func CloseOnExec(fd int) {
99 // nothing to do - no exec
100}
101
102func 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
121func 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
149func 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
157func 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
Russ Cox82854d72014-05-20 11:38:34 -0400165var zerobuf [0]byte
166
Dave Cheney7c8280c2014-02-25 09:47:42 -0500167func Write(fd int, b []byte) (int, error) {
Russ Cox82854d72014-05-20 11:38:34 -0400168 if b == nil {
169 // avoid nil in syscalls; nacl doesn't like that.
170 b = zerobuf[:]
171 }
Dave Cheney7c8280c2014-02-25 09:47:42 -0500172 f, err := fdToFile(fd)
173 if err != nil {
174 return 0, err
175 }
176 return f.impl.write(b)
177}
178
179func 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
187func 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
195func 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
Robert Griesemerf3913622014-05-02 13:17:55 -0700203// defaulFileImpl implements fileImpl.
Dave Cheney7c8280c2014-02-25 09:47:42 -0500204// It can be embedded to complete a partial fileImpl implementation.
205type defaultFileImpl struct{}
206
207func (*defaultFileImpl) close() error { return nil }
208func (*defaultFileImpl) stat(*Stat_t) error { return ENOSYS }
209func (*defaultFileImpl) read([]byte) (int, error) { return 0, ENOSYS }
210func (*defaultFileImpl) write([]byte) (int, error) { return 0, ENOSYS }
211func (*defaultFileImpl) seek(int64, int) (int64, error) { return 0, ENOSYS }
212func (*defaultFileImpl) pread([]byte, int64) (int, error) { return 0, ENOSYS }
213func (*defaultFileImpl) pwrite([]byte, int64) (int, error) { return 0, ENOSYS }
214
215// naclFile is the fileImpl implementation for a Native Client file descriptor.
216type naclFile struct {
217 defaultFileImpl
218 naclFD int
219}
220
221func (f *naclFile) stat(st *Stat_t) error {
222 return naclFstat(f.naclFD, st)
223}
224
225func (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
234func naclWrite(fd int, b []byte) int
235
236func (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
244func (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
253func (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
267func (f *naclFile) pread(b []byte, offset int64) (int, error) {
268 return f.prw(b, offset, f.read)
269}
270
271func (f *naclFile) pwrite(b []byte, offset int64) (int, error) {
272 return f.prw(b, offset, f.write)
273}
274
275func (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.
283type pipeFile struct {
284 defaultFileImpl
285 rd *byteq
286 wr *byteq
287}
288
289func (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
299func (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
310func (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
321func 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}