1
2
3
4
5 package os
6
7 import (
8 "runtime"
9 "sync"
10 "syscall"
11 )
12
13
14 type File struct {
15 fd syscall.Handle
16 name string
17 dirinfo *dirInfo
18 nepipe int
19 l sync.Mutex
20 }
21
22
23 func (file *File) Fd() syscall.Handle {
24 if file == nil {
25 return syscall.InvalidHandle
26 }
27 return file.fd
28 }
29
30
31 func NewFile(fd syscall.Handle, name string) *File {
32 if fd < 0 {
33 return nil
34 }
35 f := &File{fd: fd, name: name}
36 runtime.SetFinalizer(f, (*File).Close)
37 return f
38 }
39
40
41 type dirInfo struct {
42 stat syscall.Stat_t
43 usefirststat bool
44 }
45
46 const DevNull = "NUL"
47
48 func (file *File) isdir() bool { return file != nil && file.dirinfo != nil }
49
50 func openFile(name string, flag int, perm uint32) (file *File, err Error) {
51 r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, perm)
52 if e != 0 {
53 return nil, &PathError{"open", name, Errno(e)}
54 }
55
56
57
58 if syscall.O_CLOEXEC == 0 {
59 syscall.CloseOnExec(r)
60 }
61
62 return NewFile(r, name), nil
63 }
64
65 func openDir(name string) (file *File, err Error) {
66 d := new(dirInfo)
67 r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+"\\*"), &d.stat.Windata)
68 if e != 0 {
69 return nil, &PathError{"open", name, Errno(e)}
70 }
71 f := NewFile(r, name)
72 d.usefirststat = true
73 f.dirinfo = d
74 return f, nil
75 }
76
77
78
79
80
81
82 func OpenFile(name string, flag int, perm uint32) (file *File, err Error) {
83
84 r, e := openDir(name)
85 if e == nil {
86 if flag&O_WRONLY != 0 || flag&O_RDWR != 0 {
87 r.Close()
88 return nil, &PathError{"open", name, EISDIR}
89 }
90 return r, nil
91 }
92 r, e = openFile(name, flag, perm)
93 if e == nil {
94 return r, nil
95 }
96
97
98 if e2, ok := e.(*PathError); ok {
99 if e3, ok := e2.Error.(Errno); ok {
100 if e3 == Errno(syscall.ERROR_PATH_NOT_FOUND) {
101 return nil, &PathError{"open", name, ENOTDIR}
102 }
103 }
104 }
105 return nil, e
106 }
107
108
109
110 func (file *File) Close() Error {
111 if file == nil || file.fd < 0 {
112 return EINVAL
113 }
114 var e int
115 if file.isdir() {
116 e = syscall.FindClose(syscall.Handle(file.fd))
117 } else {
118 e = syscall.CloseHandle(syscall.Handle(file.fd))
119 }
120 var err Error
121 if e != 0 {
122 err = &PathError{"close", file.name, Errno(e)}
123 }
124 file.fd = syscall.InvalidHandle
125
126
127 runtime.SetFinalizer(file, nil)
128 return err
129 }
130
131 func (file *File) statFile(name string) (fi *FileInfo, err Error) {
132 var stat syscall.ByHandleFileInformation
133 e := syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &stat)
134 if e != 0 {
135 return nil, &PathError{"stat", file.name, Errno(e)}
136 }
137 return fileInfoFromByHandleInfo(new(FileInfo), file.name, &stat), nil
138 }
139
140
141
142 func (file *File) Stat() (fi *FileInfo, err Error) {
143 if file == nil || file.fd < 0 {
144 return nil, EINVAL
145 }
146 if file.isdir() {
147
148 return Stat(file.name)
149 }
150 return file.statFile(file.name)
151 }
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168 func (file *File) Readdir(n int) (fi []FileInfo, err Error) {
169 if file == nil || file.fd < 0 {
170 return nil, EINVAL
171 }
172 if !file.isdir() {
173 return nil, &PathError{"Readdir", file.name, ENOTDIR}
174 }
175 di := file.dirinfo
176 wantAll := n <= 0
177 size := n
178 if wantAll {
179 n = -1
180 size = 100
181 }
182 fi = make([]FileInfo, 0, size)
183 for n != 0 {
184 if di.usefirststat {
185 di.usefirststat = false
186 } else {
187 e := syscall.FindNextFile(syscall.Handle(file.fd), &di.stat.Windata)
188 if e != 0 {
189 if e == syscall.ERROR_NO_MORE_FILES {
190 break
191 } else {
192 err = &PathError{"FindNextFile", file.name, Errno(e)}
193 if !wantAll {
194 fi = nil
195 }
196 return
197 }
198 }
199 }
200 var f FileInfo
201 fileInfoFromWin32finddata(&f, &di.stat.Windata)
202 if f.Name == "." || f.Name == ".." {
203 continue
204 }
205 n--
206 fi = append(fi, f)
207 }
208 if !wantAll && len(fi) == 0 {
209 return fi, EOF
210 }
211 return fi, nil
212 }
213
214
215
216 func (f *File) read(b []byte) (n int, err int) {
217 f.l.Lock()
218 defer f.l.Unlock()
219 return syscall.Read(f.fd, b)
220 }
221
222
223
224
225 func (f *File) pread(b []byte, off int64) (n int, err int) {
226 f.l.Lock()
227 defer f.l.Unlock()
228 curoffset, e := syscall.Seek(f.fd, 0, 1)
229 if e != 0 {
230 return 0, e
231 }
232 defer syscall.Seek(f.fd, curoffset, 0)
233 o := syscall.Overlapped{
234 OffsetHigh: uint32(off >> 32),
235 Offset: uint32(off),
236 }
237 var done uint32
238 e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o)
239 if e != 0 {
240 return 0, e
241 }
242 return int(done), 0
243 }
244
245
246
247 func (f *File) write(b []byte) (n int, err int) {
248 f.l.Lock()
249 defer f.l.Unlock()
250 return syscall.Write(f.fd, b)
251 }
252
253
254
255 func (f *File) pwrite(b []byte, off int64) (n int, err int) {
256 f.l.Lock()
257 defer f.l.Unlock()
258 curoffset, e := syscall.Seek(f.fd, 0, 1)
259 if e != 0 {
260 return 0, e
261 }
262 defer syscall.Seek(f.fd, curoffset, 0)
263 o := syscall.Overlapped{
264 OffsetHigh: uint32(off >> 32),
265 Offset: uint32(off),
266 }
267 var done uint32
268 e = syscall.WriteFile(syscall.Handle(f.fd), b, &done, &o)
269 if e != 0 {
270 return 0, e
271 }
272 return int(done), 0
273 }
274
275
276
277
278
279 func (f *File) seek(offset int64, whence int) (ret int64, err int) {
280 f.l.Lock()
281 defer f.l.Unlock()
282 return syscall.Seek(f.fd, offset, whence)
283 }
284
285
286
287 func Truncate(name string, size int64) Error {
288 f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666)
289 if e != nil {
290 return e
291 }
292 defer f.Close()
293 e1 := f.Truncate(size)
294 if e1 != nil {
295 return e1
296 }
297 return nil
298 }
299
300
301
302 func Pipe() (r *File, w *File, err Error) {
303 var p [2]syscall.Handle
304
305
306 syscall.ForkLock.RLock()
307 e := syscall.Pipe(p[0:])
308 if iserror(e) {
309 syscall.ForkLock.RUnlock()
310 return nil, nil, NewSyscallError("pipe", e)
311 }
312 syscall.CloseOnExec(p[0])
313 syscall.CloseOnExec(p[1])
314 syscall.ForkLock.RUnlock()
315
316 return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
317 }