github.com/reiver/go@v0.0.0-20150109200633-1d0c7792f172/src/os/file_windows.go (about)

     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  	"io"
     9  	"runtime"
    10  	"sync"
    11  	"syscall"
    12  	"unicode/utf16"
    13  	"unicode/utf8"
    14  	"unsafe"
    15  )
    16  
    17  // File represents an open file descriptor.
    18  type File struct {
    19  	*file
    20  }
    21  
    22  // file is the real representation of *File.
    23  // The extra level of indirection ensures that no clients of os
    24  // can overwrite this data, which could cause the finalizer
    25  // to close the wrong file descriptor.
    26  type file struct {
    27  	fd      syscall.Handle
    28  	name    string
    29  	dirinfo *dirInfo   // nil unless directory being read
    30  	l       sync.Mutex // used to implement windows pread/pwrite
    31  
    32  	// only for console io
    33  	isConsole bool
    34  	lastbits  []byte // first few bytes of the last incomplete rune in last write
    35  	readbuf   []rune // input console buffer
    36  }
    37  
    38  // Fd returns the Windows handle referencing the open file.
    39  // The handle is valid only until f.Close is called or f is garbage collected.
    40  func (file *File) Fd() uintptr {
    41  	if file == nil {
    42  		return uintptr(syscall.InvalidHandle)
    43  	}
    44  	return uintptr(file.fd)
    45  }
    46  
    47  // newFile returns a new File with the given file handle and name.
    48  // Unlike NewFile, it does not check that h is syscall.InvalidHandle.
    49  func newFile(h syscall.Handle, name string) *File {
    50  	f := &File{&file{fd: h, name: name}}
    51  	var m uint32
    52  	if syscall.GetConsoleMode(f.fd, &m) == nil {
    53  		f.isConsole = true
    54  	}
    55  	runtime.SetFinalizer(f.file, (*file).close)
    56  	return f
    57  }
    58  
    59  // NewFile returns a new File with the given file descriptor and name.
    60  func NewFile(fd uintptr, name string) *File {
    61  	h := syscall.Handle(fd)
    62  	if h == syscall.InvalidHandle {
    63  		return nil
    64  	}
    65  	return newFile(h, name)
    66  }
    67  
    68  // Auxiliary information if the File describes a directory
    69  type dirInfo struct {
    70  	data     syscall.Win32finddata
    71  	needdata bool
    72  	path     string
    73  	isempty  bool // set if FindFirstFile returns ERROR_FILE_NOT_FOUND
    74  }
    75  
    76  func epipecheck(file *File, e error) {
    77  }
    78  
    79  const DevNull = "NUL"
    80  
    81  func (f *file) isdir() bool { return f != nil && f.dirinfo != nil }
    82  
    83  func openFile(name string, flag int, perm FileMode) (file *File, err error) {
    84  	r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
    85  	if e != nil {
    86  		return nil, e
    87  	}
    88  	return NewFile(uintptr(r), name), nil
    89  }
    90  
    91  func openDir(name string) (file *File, err error) {
    92  	maskp, e := syscall.UTF16PtrFromString(name + `\*`)
    93  	if e != nil {
    94  		return nil, e
    95  	}
    96  	d := new(dirInfo)
    97  	r, e := syscall.FindFirstFile(maskp, &d.data)
    98  	if e != nil {
    99  		// FindFirstFile returns ERROR_FILE_NOT_FOUND when
   100  		// no matching files can be found. Then, if directory
   101  		// exists, we should proceed.
   102  		if e != syscall.ERROR_FILE_NOT_FOUND {
   103  			return nil, e
   104  		}
   105  		var fa syscall.Win32FileAttributeData
   106  		namep, e := syscall.UTF16PtrFromString(name)
   107  		if e != nil {
   108  			return nil, e
   109  		}
   110  		e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
   111  		if e != nil {
   112  			return nil, e
   113  		}
   114  		if fa.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 {
   115  			return nil, e
   116  		}
   117  		d.isempty = true
   118  	}
   119  	d.path = name
   120  	if !isAbs(d.path) {
   121  		d.path, e = syscall.FullPath(d.path)
   122  		if e != nil {
   123  			return nil, e
   124  		}
   125  	}
   126  	f := newFile(r, name)
   127  	f.dirinfo = d
   128  	return f, nil
   129  }
   130  
   131  // OpenFile is the generalized open call; most users will use Open
   132  // or Create instead.  It opens the named file with specified flag
   133  // (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
   134  // methods on the returned File can be used for I/O.
   135  // If there is an error, it will be of type *PathError.
   136  func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
   137  	if name == "" {
   138  		return nil, &PathError{"open", name, syscall.ENOENT}
   139  	}
   140  	r, errf := openFile(name, flag, perm)
   141  	if errf == nil {
   142  		return r, nil
   143  	}
   144  	r, errd := openDir(name)
   145  	if errd == nil {
   146  		if flag&O_WRONLY != 0 || flag&O_RDWR != 0 {
   147  			r.Close()
   148  			return nil, &PathError{"open", name, syscall.EISDIR}
   149  		}
   150  		return r, nil
   151  	}
   152  	return nil, &PathError{"open", name, errf}
   153  }
   154  
   155  // Close closes the File, rendering it unusable for I/O.
   156  // It returns an error, if any.
   157  func (file *File) Close() error {
   158  	if file == nil {
   159  		return ErrInvalid
   160  	}
   161  	return file.file.close()
   162  }
   163  
   164  func (file *file) close() error {
   165  	if file == nil {
   166  		return syscall.EINVAL
   167  	}
   168  	if file.isdir() && file.dirinfo.isempty {
   169  		// "special" empty directories
   170  		return nil
   171  	}
   172  	if file.fd == syscall.InvalidHandle {
   173  		return syscall.EINVAL
   174  	}
   175  	var e error
   176  	if file.isdir() {
   177  		e = syscall.FindClose(syscall.Handle(file.fd))
   178  	} else {
   179  		e = syscall.CloseHandle(syscall.Handle(file.fd))
   180  	}
   181  	var err error
   182  	if e != nil {
   183  		err = &PathError{"close", file.name, e}
   184  	}
   185  	file.fd = syscall.InvalidHandle // so it can't be closed again
   186  
   187  	// no need for a finalizer anymore
   188  	runtime.SetFinalizer(file, nil)
   189  	return err
   190  }
   191  
   192  func (file *File) readdir(n int) (fi []FileInfo, err error) {
   193  	if file == nil {
   194  		return nil, syscall.EINVAL
   195  	}
   196  	if !file.isdir() {
   197  		return nil, &PathError{"Readdir", file.name, syscall.ENOTDIR}
   198  	}
   199  	if !file.dirinfo.isempty && file.fd == syscall.InvalidHandle {
   200  		return nil, syscall.EINVAL
   201  	}
   202  	wantAll := n <= 0
   203  	size := n
   204  	if wantAll {
   205  		n = -1
   206  		size = 100
   207  	}
   208  	fi = make([]FileInfo, 0, size) // Empty with room to grow.
   209  	d := &file.dirinfo.data
   210  	for n != 0 && !file.dirinfo.isempty {
   211  		if file.dirinfo.needdata {
   212  			e := syscall.FindNextFile(syscall.Handle(file.fd), d)
   213  			if e != nil {
   214  				if e == syscall.ERROR_NO_MORE_FILES {
   215  					break
   216  				} else {
   217  					err = &PathError{"FindNextFile", file.name, e}
   218  					if !wantAll {
   219  						fi = nil
   220  					}
   221  					return
   222  				}
   223  			}
   224  		}
   225  		file.dirinfo.needdata = true
   226  		name := string(syscall.UTF16ToString(d.FileName[0:]))
   227  		if name == "." || name == ".." { // Useless names
   228  			continue
   229  		}
   230  		f := &fileStat{
   231  			name: name,
   232  			sys: syscall.Win32FileAttributeData{
   233  				FileAttributes: d.FileAttributes,
   234  				CreationTime:   d.CreationTime,
   235  				LastAccessTime: d.LastAccessTime,
   236  				LastWriteTime:  d.LastWriteTime,
   237  				FileSizeHigh:   d.FileSizeHigh,
   238  				FileSizeLow:    d.FileSizeLow,
   239  			},
   240  			path: file.dirinfo.path + `\` + name,
   241  		}
   242  		n--
   243  		fi = append(fi, f)
   244  	}
   245  	if !wantAll && len(fi) == 0 {
   246  		return fi, io.EOF
   247  	}
   248  	return fi, nil
   249  }
   250  
   251  // readConsole reads utf16 characters from console File,
   252  // encodes them into utf8 and stores them in buffer b.
   253  // It returns the number of utf8 bytes read and an error, if any.
   254  func (f *File) readConsole(b []byte) (n int, err error) {
   255  	if len(b) == 0 {
   256  		return 0, nil
   257  	}
   258  	if len(f.readbuf) == 0 {
   259  		// syscall.ReadConsole seems to fail, if given large buffer.
   260  		// So limit the buffer to 16000 characters.
   261  		numBytes := len(b)
   262  		if numBytes > 16000 {
   263  			numBytes = 16000
   264  		}
   265  		// get more input data from os
   266  		wchars := make([]uint16, numBytes)
   267  		var p *uint16
   268  		if len(b) > 0 {
   269  			p = &wchars[0]
   270  		}
   271  		var nw uint32
   272  		err := syscall.ReadConsole(f.fd, p, uint32(len(wchars)), &nw, nil)
   273  		if err != nil {
   274  			return 0, err
   275  		}
   276  		f.readbuf = utf16.Decode(wchars[:nw])
   277  	}
   278  	for i, r := range f.readbuf {
   279  		if utf8.RuneLen(r) > len(b) {
   280  			f.readbuf = f.readbuf[i:]
   281  			return n, nil
   282  		}
   283  		nr := utf8.EncodeRune(b, r)
   284  		b = b[nr:]
   285  		n += nr
   286  	}
   287  	f.readbuf = nil
   288  	return n, nil
   289  }
   290  
   291  // read reads up to len(b) bytes from the File.
   292  // It returns the number of bytes read and an error, if any.
   293  func (f *File) read(b []byte) (n int, err error) {
   294  	f.l.Lock()
   295  	defer f.l.Unlock()
   296  	if f.isConsole {
   297  		return f.readConsole(b)
   298  	}
   299  	return fixCount(syscall.Read(f.fd, b))
   300  }
   301  
   302  // pread reads len(b) bytes from the File starting at byte offset off.
   303  // It returns the number of bytes read and the error, if any.
   304  // EOF is signaled by a zero count with err set to 0.
   305  func (f *File) pread(b []byte, off int64) (n int, err error) {
   306  	f.l.Lock()
   307  	defer f.l.Unlock()
   308  	curoffset, e := syscall.Seek(f.fd, 0, 1)
   309  	if e != nil {
   310  		return 0, e
   311  	}
   312  	defer syscall.Seek(f.fd, curoffset, 0)
   313  	o := syscall.Overlapped{
   314  		OffsetHigh: uint32(off >> 32),
   315  		Offset:     uint32(off),
   316  	}
   317  	var done uint32
   318  	e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o)
   319  	if e != nil {
   320  		if e == syscall.ERROR_HANDLE_EOF {
   321  			// end of file
   322  			return 0, nil
   323  		}
   324  		return 0, e
   325  	}
   326  	return int(done), nil
   327  }
   328  
   329  // writeConsole writes len(b) bytes to the console File.
   330  // It returns the number of bytes written and an error, if any.
   331  func (f *File) writeConsole(b []byte) (n int, err error) {
   332  	n = len(b)
   333  	runes := make([]rune, 0, 256)
   334  	if len(f.lastbits) > 0 {
   335  		b = append(f.lastbits, b...)
   336  		f.lastbits = nil
   337  
   338  	}
   339  	for len(b) >= utf8.UTFMax || utf8.FullRune(b) {
   340  		r, l := utf8.DecodeRune(b)
   341  		runes = append(runes, r)
   342  		b = b[l:]
   343  	}
   344  	if len(b) > 0 {
   345  		f.lastbits = make([]byte, len(b))
   346  		copy(f.lastbits, b)
   347  	}
   348  	// syscall.WriteConsole seems to fail, if given large buffer.
   349  	// So limit the buffer to 16000 characters. This number was
   350  	// discovered by experimenting with syscall.WriteConsole.
   351  	const maxWrite = 16000
   352  	for len(runes) > 0 {
   353  		m := len(runes)
   354  		if m > maxWrite {
   355  			m = maxWrite
   356  		}
   357  		chunk := runes[:m]
   358  		runes = runes[m:]
   359  		uint16s := utf16.Encode(chunk)
   360  		for len(uint16s) > 0 {
   361  			var written uint32
   362  			err = syscall.WriteConsole(f.fd, &uint16s[0], uint32(len(uint16s)), &written, nil)
   363  			if err != nil {
   364  				return 0, nil
   365  			}
   366  			uint16s = uint16s[written:]
   367  		}
   368  	}
   369  	return n, nil
   370  }
   371  
   372  // write writes len(b) bytes to the File.
   373  // It returns the number of bytes written and an error, if any.
   374  func (f *File) write(b []byte) (n int, err error) {
   375  	f.l.Lock()
   376  	defer f.l.Unlock()
   377  	if f.isConsole {
   378  		return f.writeConsole(b)
   379  	}
   380  	return fixCount(syscall.Write(f.fd, b))
   381  }
   382  
   383  // pwrite writes len(b) bytes to the File starting at byte offset off.
   384  // It returns the number of bytes written and an error, if any.
   385  func (f *File) pwrite(b []byte, off int64) (n int, err error) {
   386  	f.l.Lock()
   387  	defer f.l.Unlock()
   388  	curoffset, e := syscall.Seek(f.fd, 0, 1)
   389  	if e != nil {
   390  		return 0, e
   391  	}
   392  	defer syscall.Seek(f.fd, curoffset, 0)
   393  	o := syscall.Overlapped{
   394  		OffsetHigh: uint32(off >> 32),
   395  		Offset:     uint32(off),
   396  	}
   397  	var done uint32
   398  	e = syscall.WriteFile(syscall.Handle(f.fd), b, &done, &o)
   399  	if e != nil {
   400  		return 0, e
   401  	}
   402  	return int(done), nil
   403  }
   404  
   405  // seek sets the offset for the next Read or Write on file to offset, interpreted
   406  // according to whence: 0 means relative to the origin of the file, 1 means
   407  // relative to the current offset, and 2 means relative to the end.
   408  // It returns the new offset and an error, if any.
   409  func (f *File) seek(offset int64, whence int) (ret int64, err error) {
   410  	f.l.Lock()
   411  	defer f.l.Unlock()
   412  	return syscall.Seek(f.fd, offset, whence)
   413  }
   414  
   415  // Truncate changes the size of the named file.
   416  // If the file is a symbolic link, it changes the size of the link's target.
   417  func Truncate(name string, size int64) error {
   418  	f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666)
   419  	if e != nil {
   420  		return e
   421  	}
   422  	defer f.Close()
   423  	e1 := f.Truncate(size)
   424  	if e1 != nil {
   425  		return e1
   426  	}
   427  	return nil
   428  }
   429  
   430  // Remove removes the named file or directory.
   431  // If there is an error, it will be of type *PathError.
   432  func Remove(name string) error {
   433  	p, e := syscall.UTF16PtrFromString(name)
   434  	if e != nil {
   435  		return &PathError{"remove", name, e}
   436  	}
   437  
   438  	// Go file interface forces us to know whether
   439  	// name is a file or directory. Try both.
   440  	e = syscall.DeleteFile(p)
   441  	if e == nil {
   442  		return nil
   443  	}
   444  	e1 := syscall.RemoveDirectory(p)
   445  	if e1 == nil {
   446  		return nil
   447  	}
   448  
   449  	// Both failed: figure out which error to return.
   450  	if e1 != e {
   451  		a, e2 := syscall.GetFileAttributes(p)
   452  		if e2 != nil {
   453  			e = e2
   454  		} else {
   455  			if a&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
   456  				e = e1
   457  			}
   458  		}
   459  	}
   460  	return &PathError{"remove", name, e}
   461  }
   462  
   463  // Pipe returns a connected pair of Files; reads from r return bytes written to w.
   464  // It returns the files and an error, if any.
   465  func Pipe() (r *File, w *File, err error) {
   466  	var p [2]syscall.Handle
   467  
   468  	// See ../syscall/exec.go for description of lock.
   469  	syscall.ForkLock.RLock()
   470  	e := syscall.Pipe(p[0:])
   471  	if e != nil {
   472  		syscall.ForkLock.RUnlock()
   473  		return nil, nil, NewSyscallError("pipe", e)
   474  	}
   475  	syscall.CloseOnExec(p[0])
   476  	syscall.CloseOnExec(p[1])
   477  	syscall.ForkLock.RUnlock()
   478  
   479  	return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
   480  }
   481  
   482  // TempDir returns the default directory to use for temporary files.
   483  func TempDir() string {
   484  	const pathSep = '\\'
   485  	dirw := make([]uint16, syscall.MAX_PATH)
   486  	n, _ := syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
   487  	if n > uint32(len(dirw)) {
   488  		dirw = make([]uint16, n)
   489  		n, _ = syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
   490  		if n > uint32(len(dirw)) {
   491  			n = 0
   492  		}
   493  	}
   494  	if n > 0 && dirw[n-1] == pathSep {
   495  		n--
   496  	}
   497  	return string(utf16.Decode(dirw[0:n]))
   498  }
   499  
   500  // Link creates newname as a hard link to the oldname file.
   501  // If there is an error, it will be of type *LinkError.
   502  func Link(oldname, newname string) error {
   503  	n, err := syscall.UTF16PtrFromString(newname)
   504  	if err != nil {
   505  		return &LinkError{"link", oldname, newname, err}
   506  	}
   507  	o, err := syscall.UTF16PtrFromString(oldname)
   508  	if err != nil {
   509  		return &LinkError{"link", oldname, newname, err}
   510  	}
   511  
   512  	e := syscall.CreateHardLink(n, o, 0)
   513  	if e != nil {
   514  		return &LinkError{"link", oldname, newname, err}
   515  	}
   516  	return nil
   517  }
   518  
   519  // Symlink creates newname as a symbolic link to oldname.
   520  // If there is an error, it will be of type *LinkError.
   521  func Symlink(oldname, newname string) error {
   522  	// CreateSymbolicLink is not supported before Windows Vista
   523  	if syscall.LoadCreateSymbolicLink() != nil {
   524  		return &LinkError{"symlink", oldname, newname, syscall.EWINDOWS}
   525  	}
   526  
   527  	// '/' does not work in link's content
   528  	oldname = fromSlash(oldname)
   529  
   530  	// need the exact location of the oldname when its relative to determine if its a directory
   531  	destpath := oldname
   532  	if !isAbs(oldname) {
   533  		destpath = dirname(newname) + `\` + oldname
   534  	}
   535  
   536  	fi, err := Lstat(destpath)
   537  	isdir := err == nil && fi.IsDir()
   538  
   539  	n, err := syscall.UTF16PtrFromString(newname)
   540  	if err != nil {
   541  		return &LinkError{"symlink", oldname, newname, err}
   542  	}
   543  	o, err := syscall.UTF16PtrFromString(oldname)
   544  	if err != nil {
   545  		return &LinkError{"symlink", oldname, newname, err}
   546  	}
   547  
   548  	var flags uint32
   549  	if isdir {
   550  		flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY
   551  	}
   552  	err = syscall.CreateSymbolicLink(n, o, flags)
   553  	if err != nil {
   554  		return &LinkError{"symlink", oldname, newname, err}
   555  	}
   556  	return nil
   557  }
   558  
   559  func fromSlash(path string) string {
   560  	// Replace each '/' with '\\' if present
   561  	var pathbuf []byte
   562  	var lastSlash int
   563  	for i, b := range path {
   564  		if b == '/' {
   565  			if pathbuf == nil {
   566  				pathbuf = make([]byte, len(path))
   567  			}
   568  			copy(pathbuf[lastSlash:], path[lastSlash:i])
   569  			pathbuf[i] = '\\'
   570  			lastSlash = i + 1
   571  		}
   572  	}
   573  	if pathbuf == nil {
   574  		return path
   575  	}
   576  
   577  	copy(pathbuf[lastSlash:], path[lastSlash:])
   578  	return string(pathbuf)
   579  }
   580  
   581  func dirname(path string) string {
   582  	vol := volumeName(path)
   583  	i := len(path) - 1
   584  	for i >= len(vol) && !IsPathSeparator(path[i]) {
   585  		i--
   586  	}
   587  	dir := path[len(vol) : i+1]
   588  	last := len(dir) - 1
   589  	if last > 0 && IsPathSeparator(dir[last]) {
   590  		dir = dir[:last]
   591  	}
   592  	if dir == "" {
   593  		dir = "."
   594  	}
   595  	return vol + dir
   596  }