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