blob: ac37b8e2d3496d88368b3526dc00a4ec579fe4b9 [file] [log] [blame]
Alex Brainmanb07e4d92010-04-13 16:30:11 -07001// Copyright 2009 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
Alex Brainmanb07e4d92010-04-13 16:30:11 -07005package os
6
7import (
8 "runtime"
9 "syscall"
10)
11
12// Auxiliary information if the File describes a directory
13type dirInfo struct {
14 stat syscall.Stat_t
15 usefirststat bool
16}
17
Peter Mundy12befd02010-08-03 13:03:50 -070018const DevNull = "NUL"
19
Alex Brainmanb07e4d92010-04-13 16:30:11 -070020func (file *File) isdir() bool { return file != nil && file.dirinfo != nil }
21
Rob Pikee45b58f2010-08-04 08:34:52 +100022func openFile(name string, flag int, perm uint32) (file *File, err Error) {
Alex Brainmanb07e4d92010-04-13 16:30:11 -070023 r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, perm)
24 if e != 0 {
25 return nil, &PathError{"open", name, Errno(e)}
26 }
27
28 // There's a race here with fork/exec, which we are
29 // content to live with. See ../syscall/exec.go
30 if syscall.O_CLOEXEC == 0 { // O_CLOEXEC not supported
31 syscall.CloseOnExec(r)
32 }
33
34 return NewFile(r, name), nil
35}
36
37func openDir(name string) (file *File, err Error) {
38 d := new(dirInfo)
39 r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+"\\*"), &d.stat.Windata)
40 if e != 0 {
41 return nil, &PathError{"open", name, Errno(e)}
42 }
43 f := NewFile(int(r), name)
44 d.usefirststat = true
45 f.dirinfo = d
46 return f, nil
47}
48
Rob Pike8a90fd32011-04-04 23:42:14 -070049// OpenFile is the generalized open call; most users will use Open
50// or Create instead. It opens the named file with specified flag
51// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
52// methods on the returned File can be used for I/O.
Alex Brainmanb07e4d92010-04-13 16:30:11 -070053// It returns the File and an Error, if any.
Rob Pike8a90fd32011-04-04 23:42:14 -070054func OpenFile(name string, flag int, perm uint32) (file *File, err Error) {
Alex Brainmanb07e4d92010-04-13 16:30:11 -070055 // TODO(brainman): not sure about my logic of assuming it is dir first, then fall back to file
56 r, e := openDir(name)
57 if e == nil {
Alex Brainman17fe2472010-10-04 17:31:49 +110058 if flag&O_WRONLY != 0 || flag&O_RDWR != 0 {
59 r.Close()
60 return nil, &PathError{"open", name, EISDIR}
61 }
Alex Brainmanb07e4d92010-04-13 16:30:11 -070062 return r, nil
63 }
Alex Brainman03526592010-04-13 22:30:41 -070064 r, e = openFile(name, flag, perm)
Alex Brainmanb07e4d92010-04-13 16:30:11 -070065 if e == nil {
66 return r, nil
67 }
Alex Brainman17fe2472010-10-04 17:31:49 +110068 // Imitating Unix behavior by replacing syscall.ERROR_PATH_NOT_FOUND with
69 // os.ENOTDIR. Not sure if we should go into that.
70 if e2, ok := e.(*PathError); ok {
71 if e3, ok := e2.Error.(Errno); ok {
72 if e3 == Errno(syscall.ERROR_PATH_NOT_FOUND) {
73 return nil, &PathError{"open", name, ENOTDIR}
74 }
75 }
76 }
Alex Brainmanb07e4d92010-04-13 16:30:11 -070077 return nil, e
78}
79
80// Close closes the File, rendering it unusable for I/O.
81// It returns an Error, if any.
82func (file *File) Close() Error {
83 if file == nil || file.fd < 0 {
84 return EINVAL
85 }
86 var e int
87 if file.isdir() {
Alex Brainmancf75c862011-02-11 10:15:51 +110088 e = syscall.FindClose(int32(file.fd))
Alex Brainmanb07e4d92010-04-13 16:30:11 -070089 } else {
Alex Brainmancf75c862011-02-11 10:15:51 +110090 e = syscall.CloseHandle(int32(file.fd))
Alex Brainmanb07e4d92010-04-13 16:30:11 -070091 }
92 var err Error
93 if e != 0 {
94 err = &PathError{"close", file.name, Errno(e)}
95 }
96 file.fd = -1 // so it can't be closed again
97
98 // no need for a finalizer anymore
99 runtime.SetFinalizer(file, nil)
100 return err
101}
102
Alex Brainmanfb6b3912010-04-26 23:17:14 -0700103func (file *File) statFile(name string) (fi *FileInfo, err Error) {
104 var stat syscall.ByHandleFileInformation
Alex Brainmancf75c862011-02-11 10:15:51 +1100105 e := syscall.GetFileInformationByHandle(int32(file.fd), &stat)
106 if e != 0 {
Alex Brainmanfb6b3912010-04-26 23:17:14 -0700107 return nil, &PathError{"stat", file.name, Errno(e)}
108 }
109 return fileInfoFromByHandleInfo(new(FileInfo), file.name, &stat), nil
110}
111
112// Stat returns the FileInfo structure describing file.
113// It returns the FileInfo and an error, if any.
114func (file *File) Stat() (fi *FileInfo, err Error) {
115 if file == nil || file.fd < 0 {
116 return nil, EINVAL
117 }
118 if file.isdir() {
119 // I don't know any better way to do that for directory
120 return Stat(file.name)
121 }
122 return file.statFile(file.name)
123}
124
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700125// Readdir reads the contents of the directory associated with file and
126// returns an array of up to count FileInfo structures, as would be returned
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700127// by Lstat, in directory order. Subsequent calls on the same file will yield
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700128// further FileInfos.
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700129//
130// If n > 0, Readdir returns at most n names. In this case, if
131// Readdirnames returns an empty slice, it will return a non-nil error
132// explaining why. At the end of a directory, the error is os.EOF.
133//
134// If n <= 0, Readdir returns all the FileInfo from the directory in
135// a single slice. In this case, if Readdir succeeds (reads all
136// the way to the end of the directory), it returns the slice and a
137// nil os.Error. If it encounters an error before the end of the
138// directory, Readdir returns the FileInfo read until that point
139// and a non-nil error.
140func (file *File) Readdir(n int) (fi []FileInfo, err Error) {
Peter Mundybfb12762010-09-23 22:06:59 -0400141 if file == nil || file.fd < 0 {
142 return nil, EINVAL
143 }
144 if !file.isdir() {
145 return nil, &PathError{"Readdir", file.name, ENOTDIR}
146 }
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700147 di := file.dirinfo
Alex Brainman505f0bb2011-05-29 11:59:35 +1000148 wantAll := n <= 0
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700149 size := n
Alex Brainman505f0bb2011-05-29 11:59:35 +1000150 if wantAll {
151 n = -1
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700152 size = 100
153 }
154 fi = make([]FileInfo, 0, size) // Empty with room to grow.
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700155 for n != 0 {
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700156 if di.usefirststat {
157 di.usefirststat = false
158 } else {
Alex Brainmancf75c862011-02-11 10:15:51 +1100159 e := syscall.FindNextFile(int32(file.fd), &di.stat.Windata)
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700160 if e != 0 {
161 if e == syscall.ERROR_NO_MORE_FILES {
162 break
163 } else {
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700164 err = &PathError{"FindNextFile", file.name, Errno(e)}
165 if !wantAll {
166 fi = nil
167 }
168 return
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700169 }
170 }
171 }
172 var f FileInfo
Alex Brainmanfb6b3912010-04-26 23:17:14 -0700173 fileInfoFromWin32finddata(&f, &di.stat.Windata)
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700174 if f.Name == "." || f.Name == ".." { // Useless names
175 continue
176 }
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700177 n--
Russ Cox69c4e932010-10-27 19:47:23 -0700178 fi = append(fi, f)
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700179 }
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700180 if !wantAll && len(fi) == 0 {
181 return fi, EOF
182 }
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700183 return fi, nil
184}
Alex Brainmanfb6b3912010-04-26 23:17:14 -0700185
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000186// read reads up to len(b) bytes from the File.
187// It returns the number of bytes read and an error, if any.
188func (f *File) read(b []byte) (n int, err int) {
189 f.l.Lock()
190 defer f.l.Unlock()
191 return syscall.Read(f.fd, b)
192}
193
194// pread reads len(b) bytes from the File starting at byte offset off.
195// It returns the number of bytes read and the error, if any.
196// EOF is signaled by a zero count with err set to 0.
197func (f *File) pread(b []byte, off int64) (n int, err int) {
198 f.l.Lock()
199 defer f.l.Unlock()
200 curoffset, e := syscall.Seek(f.fd, 0, 1)
201 if e != 0 {
202 return 0, e
203 }
204 defer syscall.Seek(f.fd, curoffset, 0)
205 o := syscall.Overlapped{
206 OffsetHigh: uint32(off >> 32),
207 Offset: uint32(off),
208 }
209 var done uint32
210 e = syscall.ReadFile(int32(f.fd), b, &done, &o)
211 if e != 0 {
212 return 0, e
213 }
214 return int(done), 0
215}
216
217// write writes len(b) bytes to the File.
218// It returns the number of bytes written and an error, if any.
219func (f *File) write(b []byte) (n int, err int) {
220 f.l.Lock()
221 defer f.l.Unlock()
222 return syscall.Write(f.fd, b)
223}
224
225// pwrite writes len(b) bytes to the File starting at byte offset off.
226// It returns the number of bytes written and an error, if any.
227func (f *File) pwrite(b []byte, off int64) (n int, err int) {
228 f.l.Lock()
229 defer f.l.Unlock()
230 curoffset, e := syscall.Seek(f.fd, 0, 1)
231 if e != 0 {
232 return 0, e
233 }
234 defer syscall.Seek(f.fd, curoffset, 0)
235 o := syscall.Overlapped{
236 OffsetHigh: uint32(off >> 32),
237 Offset: uint32(off),
238 }
239 var done uint32
240 e = syscall.WriteFile(int32(f.fd), b, &done, &o)
241 if e != 0 {
242 return 0, e
243 }
244 return int(done), 0
245}
246
247// seek sets the offset for the next Read or Write on file to offset, interpreted
248// according to whence: 0 means relative to the origin of the file, 1 means
249// relative to the current offset, and 2 means relative to the end.
250// It returns the new offset and an error, if any.
251func (f *File) seek(offset int64, whence int) (ret int64, err int) {
252 f.l.Lock()
253 defer f.l.Unlock()
254 return syscall.Seek(f.fd, offset, whence)
255}
256
Alex Brainmanfb6b3912010-04-26 23:17:14 -0700257// Truncate changes the size of the named file.
258// If the file is a symbolic link, it changes the size of the link's target.
259func Truncate(name string, size int64) Error {
Rob Pike80c5ef92011-04-04 23:57:08 -0700260 f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666)
Alex Brainmanfb6b3912010-04-26 23:17:14 -0700261 if e != nil {
262 return e
263 }
264 defer f.Close()
265 e1 := f.Truncate(size)
266 if e1 != nil {
267 return e1
268 }
269 return nil
270}