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