github.com/c12o16h1/go/src@v0.0.0-20200114212001-5a151c0f00ed/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  	"github.com/c12o16h1/go/src/internal/poll"
    10  	"github.com/c12o16h1/go/src/internal/syscall/windows"
    11  	"runtime"
    12  	"syscall"
    13  	"unicode/utf16"
    14  	"unsafe"
    15  )
    16  
    17  // file is the real representation of *File.
    18  // The extra level of indirection ensures that no clients of os
    19  // can overwrite this data, which could cause the finalizer
    20  // to close the wrong file descriptor.
    21  type file struct {
    22  	pfd        poll.FD
    23  	name       string
    24  	dirinfo    *dirInfo // nil unless directory being read
    25  	appendMode bool     // whether file is opened for appending
    26  }
    27  
    28  // Fd returns the Windows handle referencing the open file.
    29  // The handle is valid only until f.Close is called or f is garbage collected.
    30  // On Unix systems this will cause the SetDeadline methods to stop working.
    31  func (file *File) Fd() uintptr {
    32  	if file == nil {
    33  		return uintptr(syscall.InvalidHandle)
    34  	}
    35  	return uintptr(file.pfd.Sysfd)
    36  }
    37  
    38  // newFile returns a new File with the given file handle and name.
    39  // Unlike NewFile, it does not check that h is syscall.InvalidHandle.
    40  func newFile(h syscall.Handle, name string, kind string) *File {
    41  	if kind == "file" {
    42  		var m uint32
    43  		if syscall.GetConsoleMode(h, &m) == nil {
    44  			kind = "console"
    45  		}
    46  		if t, err := syscall.GetFileType(h); err == nil && t == syscall.FILE_TYPE_PIPE {
    47  			kind = "pipe"
    48  		}
    49  	}
    50  
    51  	f := &File{&file{
    52  		pfd: poll.FD{
    53  			Sysfd:         h,
    54  			IsStream:      true,
    55  			ZeroReadIsEOF: true,
    56  		},
    57  		name: name,
    58  	}}
    59  	runtime.SetFinalizer(f.file, (*file).close)
    60  
    61  	// Ignore initialization errors.
    62  	// Assume any problems will show up in later I/O.
    63  	f.pfd.Init(kind, false)
    64  
    65  	return f
    66  }
    67  
    68  // newConsoleFile creates new File that will be used as console.
    69  func newConsoleFile(h syscall.Handle, name string) *File {
    70  	return newFile(h, name, "console")
    71  }
    72  
    73  // NewFile returns a new File with the given file descriptor and
    74  // name. The returned value will be nil if fd is not a valid file
    75  // descriptor.
    76  func NewFile(fd uintptr, name string) *File {
    77  	h := syscall.Handle(fd)
    78  	if h == syscall.InvalidHandle {
    79  		return nil
    80  	}
    81  	return newFile(h, name, "file")
    82  }
    83  
    84  // Auxiliary information if the File describes a directory
    85  type dirInfo struct {
    86  	data     syscall.Win32finddata
    87  	needdata bool
    88  	path     string
    89  	isempty  bool // set if FindFirstFile returns ERROR_FILE_NOT_FOUND
    90  }
    91  
    92  func epipecheck(file *File, e error) {
    93  }
    94  
    95  // DevNull is the name of the operating system's ``null device.''
    96  // On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
    97  const DevNull = "NUL"
    98  
    99  func (f *file) isdir() bool { return f != nil && f.dirinfo != nil }
   100  
   101  func openFile(name string, flag int, perm FileMode) (file *File, err error) {
   102  	r, e := syscall.Open(fixLongPath(name), flag|syscall.O_CLOEXEC, syscallMode(perm))
   103  	if e != nil {
   104  		return nil, e
   105  	}
   106  	return newFile(r, name, "file"), nil
   107  }
   108  
   109  func openDir(name string) (file *File, err error) {
   110  	var mask string
   111  
   112  	path := fixLongPath(name)
   113  
   114  	if len(path) == 2 && path[1] == ':' { // it is a drive letter, like C:
   115  		mask = path + `*`
   116  	} else if len(path) > 0 {
   117  		lc := path[len(path)-1]
   118  		if lc == '/' || lc == '\\' {
   119  			mask = path + `*`
   120  		} else {
   121  			mask = path + `\*`
   122  		}
   123  	} else {
   124  		mask = `\*`
   125  	}
   126  	maskp, e := syscall.UTF16PtrFromString(mask)
   127  	if e != nil {
   128  		return nil, e
   129  	}
   130  	d := new(dirInfo)
   131  	r, e := syscall.FindFirstFile(maskp, &d.data)
   132  	if e != nil {
   133  		// FindFirstFile returns ERROR_FILE_NOT_FOUND when
   134  		// no matching files can be found. Then, if directory
   135  		// exists, we should proceed.
   136  		if e != syscall.ERROR_FILE_NOT_FOUND {
   137  			return nil, e
   138  		}
   139  		var fa syscall.Win32FileAttributeData
   140  		pathp, e := syscall.UTF16PtrFromString(path)
   141  		if e != nil {
   142  			return nil, e
   143  		}
   144  		e = syscall.GetFileAttributesEx(pathp, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
   145  		if e != nil {
   146  			return nil, e
   147  		}
   148  		if fa.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 {
   149  			return nil, e
   150  		}
   151  		d.isempty = true
   152  	}
   153  	d.path = path
   154  	if !isAbs(d.path) {
   155  		d.path, e = syscall.FullPath(d.path)
   156  		if e != nil {
   157  			return nil, e
   158  		}
   159  	}
   160  	f := newFile(r, name, "dir")
   161  	f.dirinfo = d
   162  	return f, nil
   163  }
   164  
   165  // openFileNolog is the Windows implementation of OpenFile.
   166  func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
   167  	if name == "" {
   168  		return nil, &PathError{"open", name, syscall.ENOENT}
   169  	}
   170  	r, errf := openFile(name, flag, perm)
   171  	if errf == nil {
   172  		return r, nil
   173  	}
   174  	r, errd := openDir(name)
   175  	if errd == nil {
   176  		if flag&O_WRONLY != 0 || flag&O_RDWR != 0 {
   177  			r.Close()
   178  			return nil, &PathError{"open", name, syscall.EISDIR}
   179  		}
   180  		return r, nil
   181  	}
   182  	return nil, &PathError{"open", name, errf}
   183  }
   184  
   185  // Close closes the File, rendering it unusable for I/O.
   186  // On files that support SetDeadline, any pending I/O operations will
   187  // be canceled and return immediately with an error.
   188  // Close will return an error if it has already been called.
   189  func (file *File) Close() error {
   190  	if file == nil {
   191  		return ErrInvalid
   192  	}
   193  	return file.file.close()
   194  }
   195  
   196  func (file *file) close() error {
   197  	if file == nil {
   198  		return syscall.EINVAL
   199  	}
   200  	if file.isdir() && file.dirinfo.isempty {
   201  		// "special" empty directories
   202  		return nil
   203  	}
   204  	var err error
   205  	if e := file.pfd.Close(); e != nil {
   206  		if e == poll.ErrFileClosing {
   207  			e = ErrClosed
   208  		}
   209  		err = &PathError{"close", file.name, e}
   210  	}
   211  
   212  	// no need for a finalizer anymore
   213  	runtime.SetFinalizer(file, nil)
   214  	return err
   215  }
   216  
   217  // read reads up to len(b) bytes from the File.
   218  // It returns the number of bytes read and an error, if any.
   219  func (f *File) read(b []byte) (n int, err error) {
   220  	n, err = f.pfd.Read(b)
   221  	runtime.KeepAlive(f)
   222  	return n, err
   223  }
   224  
   225  // pread reads len(b) bytes from the File starting at byte offset off.
   226  // It returns the number of bytes read and the error, if any.
   227  // EOF is signaled by a zero count with err set to 0.
   228  func (f *File) pread(b []byte, off int64) (n int, err error) {
   229  	n, err = f.pfd.Pread(b, off)
   230  	runtime.KeepAlive(f)
   231  	return n, err
   232  }
   233  
   234  // write writes len(b) bytes to the File.
   235  // It returns the number of bytes written and an error, if any.
   236  func (f *File) write(b []byte) (n int, err error) {
   237  	n, err = f.pfd.Write(b)
   238  	runtime.KeepAlive(f)
   239  	return n, err
   240  }
   241  
   242  // pwrite writes len(b) bytes to the File starting at byte offset off.
   243  // It returns the number of bytes written and an error, if any.
   244  func (f *File) pwrite(b []byte, off int64) (n int, err error) {
   245  	n, err = f.pfd.Pwrite(b, off)
   246  	runtime.KeepAlive(f)
   247  	return n, err
   248  }
   249  
   250  // seek sets the offset for the next Read or Write on file to offset, interpreted
   251  // according to whence: 0 means relative to the origin of the file, 1 means
   252  // relative to the current offset, and 2 means relative to the end.
   253  // It returns the new offset and an error, if any.
   254  func (f *File) seek(offset int64, whence int) (ret int64, err error) {
   255  	ret, err = f.pfd.Seek(offset, whence)
   256  	runtime.KeepAlive(f)
   257  	return ret, err
   258  }
   259  
   260  // Truncate changes the size of the named file.
   261  // If the file is a symbolic link, it changes the size of the link's target.
   262  func Truncate(name string, size int64) error {
   263  	f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666)
   264  	if e != nil {
   265  		return e
   266  	}
   267  	defer f.Close()
   268  	e1 := f.Truncate(size)
   269  	if e1 != nil {
   270  		return e1
   271  	}
   272  	return nil
   273  }
   274  
   275  // Remove removes the named file or directory.
   276  // If there is an error, it will be of type *PathError.
   277  func Remove(name string) error {
   278  	p, e := syscall.UTF16PtrFromString(fixLongPath(name))
   279  	if e != nil {
   280  		return &PathError{"remove", name, e}
   281  	}
   282  
   283  	// Go file interface forces us to know whether
   284  	// name is a file or directory. Try both.
   285  	e = syscall.DeleteFile(p)
   286  	if e == nil {
   287  		return nil
   288  	}
   289  	e1 := syscall.RemoveDirectory(p)
   290  	if e1 == nil {
   291  		return nil
   292  	}
   293  
   294  	// Both failed: figure out which error to return.
   295  	if e1 != e {
   296  		a, e2 := syscall.GetFileAttributes(p)
   297  		if e2 != nil {
   298  			e = e2
   299  		} else {
   300  			if a&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
   301  				e = e1
   302  			} else if a&syscall.FILE_ATTRIBUTE_READONLY != 0 {
   303  				if e1 = syscall.SetFileAttributes(p, a&^syscall.FILE_ATTRIBUTE_READONLY); e1 == nil {
   304  					if e = syscall.DeleteFile(p); e == nil {
   305  						return nil
   306  					}
   307  				}
   308  			}
   309  		}
   310  	}
   311  	return &PathError{"remove", name, e}
   312  }
   313  
   314  func rename(oldname, newname string) error {
   315  	e := windows.Rename(fixLongPath(oldname), fixLongPath(newname))
   316  	if e != nil {
   317  		return &LinkError{"rename", oldname, newname, e}
   318  	}
   319  	return nil
   320  }
   321  
   322  // Pipe returns a connected pair of Files; reads from r return bytes written to w.
   323  // It returns the files and an error, if any.
   324  func Pipe() (r *File, w *File, err error) {
   325  	var p [2]syscall.Handle
   326  	e := syscall.CreatePipe(&p[0], &p[1], nil, 0)
   327  	if e != nil {
   328  		return nil, nil, NewSyscallError("pipe", e)
   329  	}
   330  	return newFile(p[0], "|0", "pipe"), newFile(p[1], "|1", "pipe"), nil
   331  }
   332  
   333  func tempDir() string {
   334  	n := uint32(syscall.MAX_PATH)
   335  	for {
   336  		b := make([]uint16, n)
   337  		n, _ = syscall.GetTempPath(uint32(len(b)), &b[0])
   338  		if n > uint32(len(b)) {
   339  			continue
   340  		}
   341  		if n == 3 && b[1] == ':' && b[2] == '\\' {
   342  			// Do nothing for path, like C:\.
   343  		} else if n > 0 && b[n-1] == '\\' {
   344  			// Otherwise remove terminating \.
   345  			n--
   346  		}
   347  		return string(utf16.Decode(b[:n]))
   348  	}
   349  }
   350  
   351  // Link creates newname as a hard link to the oldname file.
   352  // If there is an error, it will be of type *LinkError.
   353  func Link(oldname, newname string) error {
   354  	n, err := syscall.UTF16PtrFromString(fixLongPath(newname))
   355  	if err != nil {
   356  		return &LinkError{"link", oldname, newname, err}
   357  	}
   358  	o, err := syscall.UTF16PtrFromString(fixLongPath(oldname))
   359  	if err != nil {
   360  		return &LinkError{"link", oldname, newname, err}
   361  	}
   362  	err = syscall.CreateHardLink(n, o, 0)
   363  	if err != nil {
   364  		return &LinkError{"link", oldname, newname, err}
   365  	}
   366  	return nil
   367  }
   368  
   369  // Symlink creates newname as a symbolic link to oldname.
   370  // If there is an error, it will be of type *LinkError.
   371  func Symlink(oldname, newname string) error {
   372  	// '/' does not work in link's content
   373  	oldname = fromSlash(oldname)
   374  
   375  	// need the exact location of the oldname when it's relative to determine if it's a directory
   376  	destpath := oldname
   377  	if !isAbs(oldname) {
   378  		destpath = dirname(newname) + `\` + oldname
   379  	}
   380  
   381  	fi, err := Stat(destpath)
   382  	isdir := err == nil && fi.IsDir()
   383  
   384  	n, err := syscall.UTF16PtrFromString(fixLongPath(newname))
   385  	if err != nil {
   386  		return &LinkError{"symlink", oldname, newname, err}
   387  	}
   388  	o, err := syscall.UTF16PtrFromString(fixLongPath(oldname))
   389  	if err != nil {
   390  		return &LinkError{"symlink", oldname, newname, err}
   391  	}
   392  
   393  	var flags uint32 = windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
   394  	if isdir {
   395  		flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY
   396  	}
   397  	err = syscall.CreateSymbolicLink(n, o, flags)
   398  
   399  	if err != nil {
   400  		// the unprivileged create flag is unsupported
   401  		// below Windows 10 (1703, v10.0.14972). retry without it.
   402  		flags &^= windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
   403  
   404  		err = syscall.CreateSymbolicLink(n, o, flags)
   405  	}
   406  
   407  	if err != nil {
   408  		return &LinkError{"symlink", oldname, newname, err}
   409  	}
   410  	return nil
   411  }
   412  
   413  // openSymlink calls CreateFile Windows API with FILE_FLAG_OPEN_REPARSE_POINT
   414  // parameter, so that Windows does not follow symlink, if path is a symlink.
   415  // openSymlink returns opened file handle.
   416  func openSymlink(path string) (syscall.Handle, error) {
   417  	p, err := syscall.UTF16PtrFromString(path)
   418  	if err != nil {
   419  		return 0, err
   420  	}
   421  	attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
   422  	// Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink.
   423  	// See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted
   424  	attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT
   425  	h, err := syscall.CreateFile(p, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0)
   426  	if err != nil {
   427  		return 0, err
   428  	}
   429  	return h, nil
   430  }
   431  
   432  // normaliseLinkPath converts absolute paths returned by
   433  // DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, ...)
   434  // into paths acceptable by all Windows APIs.
   435  // For example, it coverts
   436  //  \??\C:\foo\bar into C:\foo\bar
   437  //  \??\UNC\foo\bar into \\foo\bar
   438  //  \??\Volume{abc}\ into C:\
   439  func normaliseLinkPath(path string) (string, error) {
   440  	if len(path) < 4 || path[:4] != `\??\` {
   441  		// unexpected path, return it as is
   442  		return path, nil
   443  	}
   444  	// we have path that start with \??\
   445  	s := path[4:]
   446  	switch {
   447  	case len(s) >= 2 && s[1] == ':': // \??\C:\foo\bar
   448  		return s, nil
   449  	case len(s) >= 4 && s[:4] == `UNC\`: // \??\UNC\foo\bar
   450  		return `\\` + s[4:], nil
   451  	}
   452  
   453  	// handle paths, like \??\Volume{abc}\...
   454  
   455  	err := windows.LoadGetFinalPathNameByHandle()
   456  	if err != nil {
   457  		// we must be using old version of Windows
   458  		return "", err
   459  	}
   460  
   461  	h, err := openSymlink(path)
   462  	if err != nil {
   463  		return "", err
   464  	}
   465  	defer syscall.CloseHandle(h)
   466  
   467  	buf := make([]uint16, 100)
   468  	for {
   469  		n, err := windows.GetFinalPathNameByHandle(h, &buf[0], uint32(len(buf)), windows.VOLUME_NAME_DOS)
   470  		if err != nil {
   471  			return "", err
   472  		}
   473  		if n < uint32(len(buf)) {
   474  			break
   475  		}
   476  		buf = make([]uint16, n)
   477  	}
   478  	s = syscall.UTF16ToString(buf)
   479  	if len(s) > 4 && s[:4] == `\\?\` {
   480  		s = s[4:]
   481  		if len(s) > 3 && s[:3] == `UNC` {
   482  			// return path like \\server\share\...
   483  			return `\` + s[3:], nil
   484  		}
   485  		return s, nil
   486  	}
   487  	return "", errors.New("GetFinalPathNameByHandle returned unexpected path: " + s)
   488  }
   489  
   490  func readlink(path string) (string, error) {
   491  	h, err := openSymlink(path)
   492  	if err != nil {
   493  		return "", err
   494  	}
   495  	defer syscall.CloseHandle(h)
   496  
   497  	rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
   498  	var bytesReturned uint32
   499  	err = syscall.DeviceIoControl(h, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
   500  	if err != nil {
   501  		return "", err
   502  	}
   503  
   504  	rdb := (*windows.REPARSE_DATA_BUFFER)(unsafe.Pointer(&rdbbuf[0]))
   505  	switch rdb.ReparseTag {
   506  	case syscall.IO_REPARSE_TAG_SYMLINK:
   507  		rb := (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.DUMMYUNIONNAME))
   508  		s := rb.Path()
   509  		if rb.Flags&windows.SYMLINK_FLAG_RELATIVE != 0 {
   510  			return s, nil
   511  		}
   512  		return normaliseLinkPath(s)
   513  	case windows.IO_REPARSE_TAG_MOUNT_POINT:
   514  		return normaliseLinkPath((*windows.MountPointReparseBuffer)(unsafe.Pointer(&rdb.DUMMYUNIONNAME)).Path())
   515  	default:
   516  		// the path is not a symlink or junction but another type of reparse
   517  		// point
   518  		return "", syscall.ENOENT
   519  	}
   520  }
   521  
   522  // Readlink returns the destination of the named symbolic link.
   523  // If there is an error, it will be of type *PathError.
   524  func Readlink(name string) (string, error) {
   525  	s, err := readlink(fixLongPath(name))
   526  	if err != nil {
   527  		return "", &PathError{"readlink", name, err}
   528  	}
   529  	return s, nil
   530  }