blob: 0b721c6afa2ffe6a1e76bb70cf41dc65112c5d92 [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 (
Russ Cox08a073a2011-11-01 21:49:08 -04008 "io"
Alex Brainmanb07e4d92010-04-13 16:30:11 -07009 "runtime"
Wei Guangjing63b8b9482011-07-01 10:18:07 -040010 "sync"
Alex Brainmanb07e4d92010-04-13 16:30:11 -070011 "syscall"
Alex Brainman0d379982011-11-15 12:48:22 -050012 "unicode/utf16"
Alex Brainmanb07e4d92010-04-13 16:30:11 -070013)
14
Wei Guangjing63b8b9482011-07-01 10:18:07 -040015// File represents an open file descriptor.
16type File struct {
Russ Coxd03611f2011-11-15 12:20:59 -050017 *file
18}
19
20// file is the real representation of *File.
21// The extra level of indirection ensures that no clients of os
22// can overwrite this data, which could cause the finalizer
23// to close the wrong file descriptor.
24type file struct {
Wei Guangjing63b8b9482011-07-01 10:18:07 -040025 fd syscall.Handle
26 name string
27 dirinfo *dirInfo // nil unless directory being read
28 nepipe int // number of consecutive EPIPE in Write
29 l sync.Mutex // used to implement windows pread/pwrite
30}
31
32// Fd returns the Windows handle referencing the open file.
33func (file *File) Fd() syscall.Handle {
34 if file == nil {
35 return syscall.InvalidHandle
36 }
37 return file.fd
38}
39
40// NewFile returns a new File with the given file descriptor and name.
41func NewFile(fd syscall.Handle, name string) *File {
Wei Guangjing4ea5d622012-02-02 10:17:52 +110042 if fd == syscall.InvalidHandle {
Wei Guangjing63b8b9482011-07-01 10:18:07 -040043 return nil
44 }
Russ Coxd03611f2011-11-15 12:20:59 -050045 f := &File{&file{fd: fd, name: name}}
46 runtime.SetFinalizer(f.file, (*file).close)
Wei Guangjing63b8b9482011-07-01 10:18:07 -040047 return f
48}
49
Alex Brainmanb07e4d92010-04-13 16:30:11 -070050// Auxiliary information if the File describes a directory
51type dirInfo struct {
Alex Brainman37f390a2011-09-06 09:59:08 +100052 data syscall.Win32finddata
53 needdata bool
Alex Brainmanb07e4d92010-04-13 16:30:11 -070054}
55
Peter Mundy12befd02010-08-03 13:03:50 -070056const DevNull = "NUL"
57
Russ Cox8fe77012012-01-10 20:26:11 -080058func (f *file) isdir() bool { return f != nil && f.dirinfo != nil }
Alex Brainmanb07e4d92010-04-13 16:30:11 -070059
Brad Fitzpatrick6454a3e2012-01-19 15:45:18 -080060func openFile(name string, flag int, perm FileMode) (file *File, err error) {
61 r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
Russ Coxc017a822011-11-13 22:44:52 -050062 if e != nil {
63 return nil, &PathError{"open", name, e}
Alex Brainmanb07e4d92010-04-13 16:30:11 -070064 }
65
66 // There's a race here with fork/exec, which we are
67 // content to live with. See ../syscall/exec.go
68 if syscall.O_CLOEXEC == 0 { // O_CLOEXEC not supported
69 syscall.CloseOnExec(r)
70 }
71
72 return NewFile(r, name), nil
73}
74
Russ Cox08a073a2011-11-01 21:49:08 -040075func openDir(name string) (file *File, err error) {
Alex Brainmanb07e4d92010-04-13 16:30:11 -070076 d := new(dirInfo)
Alex Brainman37f390a2011-09-06 09:59:08 +100077 r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+`\*`), &d.data)
Russ Coxc017a822011-11-13 22:44:52 -050078 if e != nil {
79 return nil, &PathError{"open", name, e}
Alex Brainmanb07e4d92010-04-13 16:30:11 -070080 }
Wei Guangjing63b8b9482011-07-01 10:18:07 -040081 f := NewFile(r, name)
Alex Brainmanb07e4d92010-04-13 16:30:11 -070082 f.dirinfo = d
83 return f, nil
84}
85
Rob Pike8a90fd32011-04-04 23:42:14 -070086// OpenFile is the generalized open call; most users will use Open
87// or Create instead. It opens the named file with specified flag
88// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
89// methods on the returned File can be used for I/O.
Rob Pikebe0f6fe2012-02-09 16:55:36 +110090// If there is an error, it will be of type *PathError.
Brad Fitzpatrick6454a3e2012-01-19 15:45:18 -080091func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
Alex Brainmane38a1052011-11-26 11:01:49 +110092 if name == "" {
93 return nil, &PathError{"open", name, syscall.ENOENT}
94 }
Alex Brainmanb07e4d92010-04-13 16:30:11 -070095 // TODO(brainman): not sure about my logic of assuming it is dir first, then fall back to file
96 r, e := openDir(name)
97 if e == nil {
Alex Brainman17fe2472010-10-04 17:31:49 +110098 if flag&O_WRONLY != 0 || flag&O_RDWR != 0 {
99 r.Close()
100 return nil, &PathError{"open", name, EISDIR}
101 }
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700102 return r, nil
103 }
Alex Brainman03526592010-04-13 22:30:41 -0700104 r, e = openFile(name, flag, perm)
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700105 if e == nil {
106 return r, nil
107 }
108 return nil, e
109}
110
111// Close closes the File, rendering it unusable for I/O.
Russ Cox08a073a2011-11-01 21:49:08 -0400112// It returns an error, if any.
113func (file *File) Close() error {
Russ Coxd03611f2011-11-15 12:20:59 -0500114 return file.file.close()
115}
116
117func (file *file) close() error {
Wei Guangjing4ea5d622012-02-02 10:17:52 +1100118 if file == nil || file.fd == syscall.InvalidHandle {
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700119 return EINVAL
120 }
Russ Coxc017a822011-11-13 22:44:52 -0500121 var e error
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700122 if file.isdir() {
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400123 e = syscall.FindClose(syscall.Handle(file.fd))
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700124 } else {
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400125 e = syscall.CloseHandle(syscall.Handle(file.fd))
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700126 }
Russ Cox08a073a2011-11-01 21:49:08 -0400127 var err error
Russ Coxc017a822011-11-13 22:44:52 -0500128 if e != nil {
129 err = &PathError{"close", file.name, e}
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700130 }
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400131 file.fd = syscall.InvalidHandle // so it can't be closed again
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700132
133 // no need for a finalizer anymore
134 runtime.SetFinalizer(file, nil)
135 return err
136}
137
Alex Brainman994e0642012-01-17 16:51:54 +1100138func (file *File) readdir(n int) (fi []FileInfo, err error) {
Wei Guangjing4ea5d622012-02-02 10:17:52 +1100139 if file == nil || file.fd == syscall.InvalidHandle {
Peter Mundybfb12762010-09-23 22:06:59 -0400140 return nil, EINVAL
141 }
142 if !file.isdir() {
143 return nil, &PathError{"Readdir", file.name, ENOTDIR}
144 }
Alex Brainman505f0bb2011-05-29 11:59:35 +1000145 wantAll := n <= 0
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700146 size := n
Alex Brainman505f0bb2011-05-29 11:59:35 +1000147 if wantAll {
148 n = -1
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700149 size = 100
150 }
151 fi = make([]FileInfo, 0, size) // Empty with room to grow.
Alex Brainman37f390a2011-09-06 09:59:08 +1000152 d := &file.dirinfo.data
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700153 for n != 0 {
Alex Brainman37f390a2011-09-06 09:59:08 +1000154 if file.dirinfo.needdata {
155 e := syscall.FindNextFile(syscall.Handle(file.fd), d)
Russ Coxc017a822011-11-13 22:44:52 -0500156 if e != nil {
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700157 if e == syscall.ERROR_NO_MORE_FILES {
158 break
159 } else {
Russ Coxc017a822011-11-13 22:44:52 -0500160 err = &PathError{"FindNextFile", file.name, e}
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700161 if !wantAll {
162 fi = nil
163 }
164 return
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700165 }
166 }
167 }
Alex Brainman37f390a2011-09-06 09:59:08 +1000168 file.dirinfo.needdata = true
Russ Cox8dce57e2011-11-30 12:04:16 -0500169 name := string(syscall.UTF16ToString(d.FileName[0:]))
170 if name == "." || name == ".." { // Useless names
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700171 continue
172 }
Russ Cox8dce57e2011-11-30 12:04:16 -0500173 f := toFileInfo(name, d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime)
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700174 n--
Russ Cox69c4e932010-10-27 19:47:23 -0700175 fi = append(fi, f)
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700176 }
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700177 if !wantAll && len(fi) == 0 {
Russ Cox08a073a2011-11-01 21:49:08 -0400178 return fi, io.EOF
Brad Fitzpatrick4da5cd42011-05-16 09:26:16 -0700179 }
Alex Brainmanb07e4d92010-04-13 16:30:11 -0700180 return fi, nil
181}
Alex Brainmanfb6b3912010-04-26 23:17:14 -0700182
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000183// read reads up to len(b) bytes from the File.
184// It returns the number of bytes read and an error, if any.
Russ Coxc017a822011-11-13 22:44:52 -0500185func (f *File) read(b []byte) (n int, err error) {
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000186 f.l.Lock()
187 defer f.l.Unlock()
188 return syscall.Read(f.fd, b)
189}
190
191// pread reads len(b) bytes from the File starting at byte offset off.
192// It returns the number of bytes read and the error, if any.
193// EOF is signaled by a zero count with err set to 0.
Russ Coxc017a822011-11-13 22:44:52 -0500194func (f *File) pread(b []byte, off int64) (n int, err error) {
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000195 f.l.Lock()
196 defer f.l.Unlock()
197 curoffset, e := syscall.Seek(f.fd, 0, 1)
Russ Coxc017a822011-11-13 22:44:52 -0500198 if e != nil {
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000199 return 0, e
200 }
201 defer syscall.Seek(f.fd, curoffset, 0)
202 o := syscall.Overlapped{
203 OffsetHigh: uint32(off >> 32),
204 Offset: uint32(off),
205 }
206 var done uint32
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400207 e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o)
Russ Coxc017a822011-11-13 22:44:52 -0500208 if e != nil {
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000209 return 0, e
210 }
Russ Coxc017a822011-11-13 22:44:52 -0500211 return int(done), nil
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000212}
213
214// write writes len(b) bytes to the File.
215// It returns the number of bytes written and an error, if any.
Russ Coxc017a822011-11-13 22:44:52 -0500216func (f *File) write(b []byte) (n int, err error) {
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000217 f.l.Lock()
218 defer f.l.Unlock()
219 return syscall.Write(f.fd, b)
220}
221
222// pwrite writes len(b) bytes to the File starting at byte offset off.
223// It returns the number of bytes written and an error, if any.
Russ Coxc017a822011-11-13 22:44:52 -0500224func (f *File) pwrite(b []byte, off int64) (n int, err error) {
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000225 f.l.Lock()
226 defer f.l.Unlock()
227 curoffset, e := syscall.Seek(f.fd, 0, 1)
Russ Coxc017a822011-11-13 22:44:52 -0500228 if e != nil {
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000229 return 0, e
230 }
231 defer syscall.Seek(f.fd, curoffset, 0)
232 o := syscall.Overlapped{
233 OffsetHigh: uint32(off >> 32),
234 Offset: uint32(off),
235 }
236 var done uint32
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400237 e = syscall.WriteFile(syscall.Handle(f.fd), b, &done, &o)
Russ Coxc017a822011-11-13 22:44:52 -0500238 if e != nil {
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000239 return 0, e
240 }
Russ Coxc017a822011-11-13 22:44:52 -0500241 return int(done), nil
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000242}
243
244// seek sets the offset for the next Read or Write on file to offset, interpreted
245// according to whence: 0 means relative to the origin of the file, 1 means
246// relative to the current offset, and 2 means relative to the end.
247// It returns the new offset and an error, if any.
Russ Coxc017a822011-11-13 22:44:52 -0500248func (f *File) seek(offset int64, whence int) (ret int64, err error) {
Alex Brainmanb1deb3b2011-04-26 18:09:46 +1000249 f.l.Lock()
250 defer f.l.Unlock()
251 return syscall.Seek(f.fd, offset, whence)
252}
253
Alex Brainmanfb6b3912010-04-26 23:17:14 -0700254// Truncate changes the size of the named file.
255// If the file is a symbolic link, it changes the size of the link's target.
Russ Cox08a073a2011-11-01 21:49:08 -0400256func Truncate(name string, size int64) error {
Rob Pike80c5ef92011-04-04 23:57:08 -0700257 f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666)
Alex Brainmanfb6b3912010-04-26 23:17:14 -0700258 if e != nil {
259 return e
260 }
261 defer f.Close()
262 e1 := f.Truncate(size)
263 if e1 != nil {
264 return e1
265 }
266 return nil
267}
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400268
Alex Brainman796a2c12011-12-20 11:52:20 +1100269// Remove removes the named file or directory.
Rob Pikebe0f6fe2012-02-09 16:55:36 +1100270// If there is an error, it will be of type *PathError.
Alex Brainman796a2c12011-12-20 11:52:20 +1100271func Remove(name string) error {
272 p := &syscall.StringToUTF16(name)[0]
273
274 // Go file interface forces us to know whether
275 // name is a file or directory. Try both.
276 e := syscall.DeleteFile(p)
277 if e == nil {
278 return nil
279 }
280 e1 := syscall.RemoveDirectory(p)
281 if e1 == nil {
282 return nil
283 }
284
285 // Both failed: figure out which error to return.
286 if e1 != e {
287 a, e2 := syscall.GetFileAttributes(p)
288 if e2 != nil {
289 e = e2
290 } else {
291 if a&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
292 e = e1
293 }
294 }
295 }
296 return &PathError{"remove", name, e}
297}
298
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400299// Pipe returns a connected pair of Files; reads from r return bytes written to w.
Russ Cox08a073a2011-11-01 21:49:08 -0400300// It returns the files and an error, if any.
301func Pipe() (r *File, w *File, err error) {
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400302 var p [2]syscall.Handle
303
304 // See ../syscall/exec.go for description of lock.
305 syscall.ForkLock.RLock()
306 e := syscall.Pipe(p[0:])
Russ Coxc017a822011-11-13 22:44:52 -0500307 if e != nil {
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400308 syscall.ForkLock.RUnlock()
309 return nil, nil, NewSyscallError("pipe", e)
310 }
311 syscall.CloseOnExec(p[0])
312 syscall.CloseOnExec(p[1])
313 syscall.ForkLock.RUnlock()
314
315 return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
316}
Russ Cox0acd8792011-11-14 14:06:50 -0500317
318// TempDir returns the default directory to use for temporary files.
319func TempDir() string {
320 const pathSep = '\\'
Alex Brainman0d379982011-11-15 12:48:22 -0500321 dirw := make([]uint16, syscall.MAX_PATH)
Russ Cox0acd8792011-11-14 14:06:50 -0500322 n, _ := syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
323 if n > uint32(len(dirw)) {
324 dirw = make([]uint16, n)
325 n, _ = syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
326 if n > uint32(len(dirw)) {
327 n = 0
328 }
329 }
330 if n > 0 && dirw[n-1] == pathSep {
331 n--
332 }
333 return string(utf16.Decode(dirw[0:n]))
334}