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