github.com/StingNevermore/go@v0.0.0-20180120041312-3810f5bfed72/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/poll"
     9  	"internal/syscall/windows"
    10  	"runtime"
    11  	"syscall"
    12  	"unicode/utf16"
    13  	"unsafe"
    14  )
    15  
    16  // file is the real representation of *File.
    17  // The extra level of indirection ensures that no clients of os
    18  // can overwrite this data, which could cause the finalizer
    19  // to close the wrong file descriptor.
    20  type file struct {
    21  	pfd     poll.FD
    22  	name    string
    23  	dirinfo *dirInfo // nil unless directory being read
    24  }
    25  
    26  // Fd returns the Windows handle referencing the open file.
    27  // The handle is valid only until f.Close is called or f is garbage collected.
    28  // On Unix systems this will cause the SetDeadline methods to stop working.
    29  func (file *File) Fd() uintptr {
    30  	if file == nil {
    31  		return uintptr(syscall.InvalidHandle)
    32  	}
    33  	return uintptr(file.pfd.Sysfd)
    34  }
    35  
    36  // newFile returns a new File with the given file handle and name.
    37  // Unlike NewFile, it does not check that h is syscall.InvalidHandle.
    38  func newFile(h syscall.Handle, name string, kind string) *File {
    39  	if kind == "file" {
    40  		var m uint32
    41  		if syscall.GetConsoleMode(h, &m) == nil {
    42  			kind = "console"
    43  		}
    44  	}
    45  
    46  	f := &File{&file{
    47  		pfd: poll.FD{
    48  			Sysfd:         h,
    49  			IsStream:      true,
    50  			ZeroReadIsEOF: true,
    51  		},
    52  		name: name,
    53  	}}
    54  	runtime.SetFinalizer(f.file, (*file).close)
    55  
    56  	// Ignore initialization errors.
    57  	// Assume any problems will show up in later I/O.
    58  	f.pfd.Init(kind, false)
    59  
    60  	return f
    61  }
    62  
    63  // newConsoleFile creates new File that will be used as console.
    64  func newConsoleFile(h syscall.Handle, name string) *File {
    65  	return newFile(h, name, "console")
    66  }
    67  
    68  // NewFile returns a new File with the given file descriptor and
    69  // name. The returned value will be nil if fd is not a valid file
    70  // descriptor.
    71  func NewFile(fd uintptr, name string) *File {
    72  	h := syscall.Handle(fd)
    73  	if h == syscall.InvalidHandle {
    74  		return nil
    75  	}
    76  	return newFile(h, name, "file")
    77  }
    78  
    79  // Auxiliary information if the File describes a directory
    80  type dirInfo struct {
    81  	data     syscall.Win32finddata
    82  	needdata bool
    83  	path     string
    84  	isempty  bool // set if FindFirstFile returns ERROR_FILE_NOT_FOUND
    85  }
    86  
    87  func epipecheck(file *File, e error) {
    88  }
    89  
    90  const DevNull = "NUL"
    91  
    92  func (f *file) isdir() bool { return f != nil && f.dirinfo != nil }
    93  
    94  func openFile(name string, flag int, perm FileMode) (file *File, err error) {
    95  	r, e := syscall.Open(fixLongPath(name), flag|syscall.O_CLOEXEC, syscallMode(perm))
    96  	if e != nil {
    97  		return nil, e
    98  	}
    99  	return newFile(r, name, "file"), nil
   100  }
   101  
   102  func openDir(name string) (file *File, err error) {
   103  	var mask string
   104  
   105  	path := fixLongPath(name)
   106  
   107  	if len(path) == 2 && path[1] == ':' || (len(path) > 0 && path[len(path)-1] == '\\') { // it is a drive letter, like C:
   108  		mask = path + `*`
   109  	} else {
   110  		mask = path + `\*`
   111  	}
   112  	maskp, e := syscall.UTF16PtrFromString(mask)
   113  	if e != nil {
   114  		return nil, e
   115  	}
   116  	d := new(dirInfo)
   117  	r, e := syscall.FindFirstFile(maskp, &d.data)
   118  	if e != nil {
   119  		// FindFirstFile returns ERROR_FILE_NOT_FOUND when
   120  		// no matching files can be found. Then, if directory
   121  		// exists, we should proceed.
   122  		if e != syscall.ERROR_FILE_NOT_FOUND {
   123  			return nil, e
   124  		}
   125  		var fa syscall.Win32FileAttributeData
   126  		pathp, e := syscall.UTF16PtrFromString(path)
   127  		if e != nil {
   128  			return nil, e
   129  		}
   130  		e = syscall.GetFileAttributesEx(pathp, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
   131  		if e != nil {
   132  			return nil, e
   133  		}
   134  		if fa.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 {
   135  			return nil, e
   136  		}
   137  		d.isempty = true
   138  	}
   139  	d.path = path
   140  	if !isAbs(d.path) {
   141  		d.path, e = syscall.FullPath(d.path)
   142  		if e != nil {
   143  			return nil, e
   144  		}
   145  	}
   146  	f := newFile(r, name, "dir")
   147  	f.dirinfo = d
   148  	return f, nil
   149  }
   150  
   151  // openFileNolog is the Windows implementation of OpenFile.
   152  func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
   153  	if name == "" {
   154  		return nil, &PathError{"open", name, syscall.ENOENT}
   155  	}
   156  	r, errf := openFile(name, flag, perm)
   157  	if errf == nil {
   158  		return r, nil
   159  	}
   160  	r, errd := openDir(name)
   161  	if errd == nil {
   162  		if flag&O_WRONLY != 0 || flag&O_RDWR != 0 {
   163  			r.Close()
   164  			return nil, &PathError{"open", name, syscall.EISDIR}
   165  		}
   166  		return r, nil
   167  	}
   168  	return nil, &PathError{"open", name, errf}
   169  }
   170  
   171  // Close closes the File, rendering it unusable for I/O.
   172  // It returns an error, if any.
   173  func (file *File) Close() error {
   174  	if file == nil {
   175  		return ErrInvalid
   176  	}
   177  	return file.file.close()
   178  }
   179  
   180  func (file *file) close() error {
   181  	if file == nil {
   182  		return syscall.EINVAL
   183  	}
   184  	if file.isdir() && file.dirinfo.isempty {
   185  		// "special" empty directories
   186  		return nil
   187  	}
   188  	var err error
   189  	if e := file.pfd.Close(); e != nil {
   190  		if e == poll.ErrFileClosing {
   191  			e = ErrClosed
   192  		}
   193  		err = &PathError{"close", file.name, e}
   194  	}
   195  
   196  	// no need for a finalizer anymore
   197  	runtime.SetFinalizer(file, nil)
   198  	return err
   199  }
   200  
   201  // read reads up to len(b) bytes from the File.
   202  // It returns the number of bytes read and an error, if any.
   203  func (f *File) read(b []byte) (n int, err error) {
   204  	n, err = f.pfd.Read(b)
   205  	runtime.KeepAlive(f)
   206  	return n, err
   207  }
   208  
   209  // pread reads len(b) bytes from the File starting at byte offset off.
   210  // It returns the number of bytes read and the error, if any.
   211  // EOF is signaled by a zero count with err set to 0.
   212  func (f *File) pread(b []byte, off int64) (n int, err error) {
   213  	n, err = f.pfd.Pread(b, off)
   214  	runtime.KeepAlive(f)
   215  	return n, err
   216  }
   217  
   218  // write writes len(b) bytes to the File.
   219  // It returns the number of bytes written and an error, if any.
   220  func (f *File) write(b []byte) (n int, err error) {
   221  	n, err = f.pfd.Write(b)
   222  	runtime.KeepAlive(f)
   223  	return n, err
   224  }
   225  
   226  // pwrite writes len(b) bytes to the File starting at byte offset off.
   227  // It returns the number of bytes written and an error, if any.
   228  func (f *File) pwrite(b []byte, off int64) (n int, err error) {
   229  	n, err = f.pfd.Pwrite(b, off)
   230  	runtime.KeepAlive(f)
   231  	return n, err
   232  }
   233  
   234  // seek sets the offset for the next Read or Write on file to offset, interpreted
   235  // according to whence: 0 means relative to the origin of the file, 1 means
   236  // relative to the current offset, and 2 means relative to the end.
   237  // It returns the new offset and an error, if any.
   238  func (f *File) seek(offset int64, whence int) (ret int64, err error) {
   239  	ret, err = f.pfd.Seek(offset, whence)
   240  	runtime.KeepAlive(f)
   241  	return ret, err
   242  }
   243  
   244  // Truncate changes the size of the named file.
   245  // If the file is a symbolic link, it changes the size of the link's target.
   246  func Truncate(name string, size int64) error {
   247  	f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666)
   248  	if e != nil {
   249  		return e
   250  	}
   251  	defer f.Close()
   252  	e1 := f.Truncate(size)
   253  	if e1 != nil {
   254  		return e1
   255  	}
   256  	return nil
   257  }
   258  
   259  // Remove removes the named file or directory.
   260  // If there is an error, it will be of type *PathError.
   261  func Remove(name string) error {
   262  	p, e := syscall.UTF16PtrFromString(fixLongPath(name))
   263  	if e != nil {
   264  		return &PathError{"remove", name, e}
   265  	}
   266  
   267  	// Go file interface forces us to know whether
   268  	// name is a file or directory. Try both.
   269  	e = syscall.DeleteFile(p)
   270  	if e == nil {
   271  		return nil
   272  	}
   273  	e1 := syscall.RemoveDirectory(p)
   274  	if e1 == nil {
   275  		return nil
   276  	}
   277  
   278  	// Both failed: figure out which error to return.
   279  	if e1 != e {
   280  		a, e2 := syscall.GetFileAttributes(p)
   281  		if e2 != nil {
   282  			e = e2
   283  		} else {
   284  			if a&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
   285  				e = e1
   286  			} else if a&syscall.FILE_ATTRIBUTE_READONLY != 0 {
   287  				if e1 = syscall.SetFileAttributes(p, a&^syscall.FILE_ATTRIBUTE_READONLY); e1 == nil {
   288  					if e = syscall.DeleteFile(p); e == nil {
   289  						return nil
   290  					}
   291  				}
   292  			}
   293  		}
   294  	}
   295  	return &PathError{"remove", name, e}
   296  }
   297  
   298  func rename(oldname, newname string) error {
   299  	e := windows.Rename(fixLongPath(oldname), fixLongPath(newname))
   300  	if e != nil {
   301  		return &LinkError{"rename", oldname, newname, e}
   302  	}
   303  	return nil
   304  }
   305  
   306  // Pipe returns a connected pair of Files; reads from r return bytes written to w.
   307  // It returns the files and an error, if any.
   308  func Pipe() (r *File, w *File, err error) {
   309  	var p [2]syscall.Handle
   310  	e := syscall.CreatePipe(&p[0], &p[1], nil, 0)
   311  	if e != nil {
   312  		return nil, nil, NewSyscallError("pipe", e)
   313  	}
   314  	return newFile(p[0], "|0", "file"), newFile(p[1], "|1", "file"), nil
   315  }
   316  
   317  func tempDir() string {
   318  	n := uint32(syscall.MAX_PATH)
   319  	for {
   320  		b := make([]uint16, n)
   321  		n, _ = syscall.GetTempPath(uint32(len(b)), &b[0])
   322  		if n > uint32(len(b)) {
   323  			continue
   324  		}
   325  		if n > 0 && b[n-1] == '\\' {
   326  			n--
   327  		}
   328  		return string(utf16.Decode(b[:n]))
   329  	}
   330  }
   331  
   332  // Link creates newname as a hard link to the oldname file.
   333  // If there is an error, it will be of type *LinkError.
   334  func Link(oldname, newname string) error {
   335  	n, err := syscall.UTF16PtrFromString(fixLongPath(newname))
   336  	if err != nil {
   337  		return &LinkError{"link", oldname, newname, err}
   338  	}
   339  	o, err := syscall.UTF16PtrFromString(fixLongPath(oldname))
   340  	if err != nil {
   341  		return &LinkError{"link", oldname, newname, err}
   342  	}
   343  	err = syscall.CreateHardLink(n, o, 0)
   344  	if err != nil {
   345  		return &LinkError{"link", oldname, newname, err}
   346  	}
   347  	return nil
   348  }
   349  
   350  // Symlink creates newname as a symbolic link to oldname.
   351  // If there is an error, it will be of type *LinkError.
   352  func Symlink(oldname, newname string) error {
   353  	// CreateSymbolicLink is not supported before Windows Vista
   354  	if syscall.LoadCreateSymbolicLink() != nil {
   355  		return &LinkError{"symlink", oldname, newname, syscall.EWINDOWS}
   356  	}
   357  
   358  	// '/' does not work in link's content
   359  	oldname = fromSlash(oldname)
   360  
   361  	// need the exact location of the oldname when its relative to determine if its a directory
   362  	destpath := oldname
   363  	if !isAbs(oldname) {
   364  		destpath = dirname(newname) + `\` + oldname
   365  	}
   366  
   367  	fi, err := Lstat(destpath)
   368  	isdir := err == nil && fi.IsDir()
   369  
   370  	n, err := syscall.UTF16PtrFromString(fixLongPath(newname))
   371  	if err != nil {
   372  		return &LinkError{"symlink", oldname, newname, err}
   373  	}
   374  	o, err := syscall.UTF16PtrFromString(fixLongPath(oldname))
   375  	if err != nil {
   376  		return &LinkError{"symlink", oldname, newname, err}
   377  	}
   378  
   379  	var flags uint32
   380  	if isdir {
   381  		flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY
   382  	}
   383  	err = syscall.CreateSymbolicLink(n, o, flags)
   384  	if err != nil {
   385  		return &LinkError{"symlink", oldname, newname, err}
   386  	}
   387  	return nil
   388  }