The Go Programming Language

Source file src/pkg/os/file_windows.go

     1	// 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	
     5	package os
     6	
     7	import (
     8		"runtime"
     9		"sync"
    10		"syscall"
    11	)
    12	
    13	// File represents an open file descriptor.
    14	type File struct {
    15		fd      syscall.Handle
    16		name    string
    17		dirinfo *dirInfo   // nil unless directory being read
    18		nepipe  int        // number of consecutive EPIPE in Write
    19		l       sync.Mutex // used to implement windows pread/pwrite
    20	}
    21	
    22	// Fd returns the Windows handle referencing the open file.
    23	func (file *File) Fd() syscall.Handle {
    24		if file == nil {
    25			return syscall.InvalidHandle
    26		}
    27		return file.fd
    28	}
    29	
    30	// NewFile returns a new File with the given file descriptor and name.
    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	// Auxiliary information if the File describes a directory
    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		// There's a race here with fork/exec, which we are
    57		// content to live with.  See ../syscall/exec.go
    58		if syscall.O_CLOEXEC == 0 { // O_CLOEXEC not supported
    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	// OpenFile is the generalized open call; most users will use Open
    78	// or Create instead.  It opens the named file with specified flag
    79	// (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
    80	// methods on the returned File can be used for I/O.
    81	// It returns the File and an Error, if any.
    82	func OpenFile(name string, flag int, perm uint32) (file *File, err Error) {
    83		// TODO(brainman): not sure about my logic of assuming it is dir first, then fall back to file
    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		// Imitating Unix behavior by replacing syscall.ERROR_PATH_NOT_FOUND with
    97		// os.ENOTDIR. Not sure if we should go into that.
    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	// Close closes the File, rendering it unusable for I/O.
   109	// It returns an Error, if any.
   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 // so it can't be closed again
   125	
   126		// no need for a finalizer anymore
   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	// Stat returns the FileInfo structure describing file.
   141	// It returns the FileInfo and an error, if any.
   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			// I don't know any better way to do that for directory
   148			return Stat(file.name)
   149		}
   150		return file.statFile(file.name)
   151	}
   152	
   153	// Readdir reads the contents of the directory associated with file and
   154	// returns an array of up to n FileInfo structures, as would be returned
   155	// by Lstat, in directory order. Subsequent calls on the same file will yield
   156	// further FileInfos.
   157	//
   158	// If n > 0, Readdir returns at most n FileInfo structures. In this case, if
   159	// Readdir returns an empty slice, it will return a non-nil error
   160	// explaining why. At the end of a directory, the error is os.EOF.
   161	//
   162	// If n <= 0, Readdir returns all the FileInfo from the directory in
   163	// a single slice. In this case, if Readdir succeeds (reads all
   164	// the way to the end of the directory), it returns the slice and a
   165	// nil os.Error. If it encounters an error before the end of the
   166	// directory, Readdir returns the FileInfo read until that point
   167	// and a non-nil error.
   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) // Empty with room to grow.
   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 == ".." { // Useless names
   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	// read reads up to len(b) bytes from the File.
   215	// It returns the number of bytes read and an error, if any.
   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	// pread reads len(b) bytes from the File starting at byte offset off.
   223	// It returns the number of bytes read and the error, if any.
   224	// EOF is signaled by a zero count with err set to 0.
   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	// write writes len(b) bytes to the File.
   246	// It returns the number of bytes written and an error, if any.
   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	// pwrite writes len(b) bytes to the File starting at byte offset off.
   254	// It returns the number of bytes written and an error, if any.
   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	// seek sets the offset for the next Read or Write on file to offset, interpreted
   276	// according to whence: 0 means relative to the origin of the file, 1 means
   277	// relative to the current offset, and 2 means relative to the end.
   278	// It returns the new offset and an error, if any.
   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	// Truncate changes the size of the named file.
   286	// If the file is a symbolic link, it changes the size of the link's target.
   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	// Pipe returns a connected pair of Files; reads from r return bytes written to w.
   301	// It returns the files and an Error, if any.
   302	func Pipe() (r *File, w *File, err Error) {
   303		var p [2]syscall.Handle
   304	
   305		// See ../syscall/exec.go for description of lock.
   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	}

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.