Home | History | Annotate | Download | only in os
      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 }
    389