]> Cypherpunks repositories - gostls13.git/commitdiff
os: use GetFileAttributesEx to implement Stat on windows
authorAlex Brainman <alex.brainman@gmail.com>
Mon, 5 Sep 2011 23:59:08 +0000 (09:59 +1000)
committerAlex Brainman <alex.brainman@gmail.com>
Mon, 5 Sep 2011 23:59:08 +0000 (09:59 +1000)
Fixes #2129.

R=rsc
CC=golang-dev
https://golang.org/cl/4934049

src/pkg/os/file_posix.go
src/pkg/os/file_unix.go
src/pkg/os/file_windows.go
src/pkg/os/stat_windows.go
src/pkg/syscall/syscall_windows.go
src/pkg/syscall/zsyscall_windows_386.go
src/pkg/syscall/zsyscall_windows_amd64.go
src/pkg/syscall/ztypes_windows.go

index 0791a0dc04ba84a946f3d2e611ff1d5282e463fb..14ddd92c4abf59273de159ed54cf130a7f205d5b 100644 (file)
@@ -21,39 +21,6 @@ func epipecheck(file *File, e int) {
        }
 }
 
-// Stat returns a FileInfo structure describing the named file and an error, if any.
-// If name names a valid symbolic link, the returned FileInfo describes
-// the file pointed at by the link and has fi.FollowedSymlink set to true.
-// If name names an invalid symbolic link, the returned FileInfo describes
-// the link itself and has fi.FollowedSymlink set to false.
-func Stat(name string) (fi *FileInfo, err Error) {
-       var lstat, stat syscall.Stat_t
-       e := syscall.Lstat(name, &lstat)
-       if iserror(e) {
-               return nil, &PathError{"stat", name, Errno(e)}
-       }
-       statp := &lstat
-       if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
-               e := syscall.Stat(name, &stat)
-               if !iserror(e) {
-                       statp = &stat
-               }
-       }
-       return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
-}
-
-// Lstat returns the FileInfo structure describing the named file and an
-// error, if any.  If the file is a symbolic link, the returned FileInfo
-// describes the symbolic link.  Lstat makes no attempt to follow the link.
-func Lstat(name string) (fi *FileInfo, err Error) {
-       var stat syscall.Stat_t
-       e := syscall.Lstat(name, &stat)
-       if iserror(e) {
-               return nil, &PathError{"lstat", name, Errno(e)}
-       }
-       return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil
-}
-
 // Remove removes the named file or directory.
 func Remove(name string) Error {
        // System call interface forces us to know
index 301c2f473fb80781238eea0999a0ee24f4118de7..ab32ce98d458cce8aae40e36953d3b12e3582c72 100644 (file)
@@ -94,6 +94,39 @@ func (file *File) Stat() (fi *FileInfo, err Error) {
        return fileInfoFromStat(file.name, new(FileInfo), &stat, &stat), nil
 }
 
+// Stat returns a FileInfo structure describing the named file and an error, if any.
+// If name names a valid symbolic link, the returned FileInfo describes
+// the file pointed at by the link and has fi.FollowedSymlink set to true.
+// If name names an invalid symbolic link, the returned FileInfo describes
+// the link itself and has fi.FollowedSymlink set to false.
+func Stat(name string) (fi *FileInfo, err Error) {
+       var lstat, stat syscall.Stat_t
+       e := syscall.Lstat(name, &lstat)
+       if iserror(e) {
+               return nil, &PathError{"stat", name, Errno(e)}
+       }
+       statp := &lstat
+       if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
+               e := syscall.Stat(name, &stat)
+               if !iserror(e) {
+                       statp = &stat
+               }
+       }
+       return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
+}
+
+// Lstat returns the FileInfo structure describing the named file and an
+// error, if any.  If the file is a symbolic link, the returned FileInfo
+// describes the symbolic link.  Lstat makes no attempt to follow the link.
+func Lstat(name string) (fi *FileInfo, err Error) {
+       var stat syscall.Stat_t
+       e := syscall.Lstat(name, &stat)
+       if iserror(e) {
+               return nil, &PathError{"lstat", name, Errno(e)}
+       }
+       return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil
+}
+
 // Readdir reads the contents of the directory associated with file and
 // returns an array of up to n FileInfo structures, as would be returned
 // by Lstat, in directory order. Subsequent calls on the same file will yield
index 70dd6e24103e9c19d89a9aae1f4b92492cde3ef6..0cdd2fdf65f9a057c48d0d20e102f1a012e2019a 100644 (file)
@@ -39,8 +39,8 @@ func NewFile(fd syscall.Handle, name string) *File {
 
 // Auxiliary information if the File describes a directory
 type dirInfo struct {
-       stat         syscall.Stat_t
-       usefirststat bool
+       data     syscall.Win32finddata
+       needdata bool
 }
 
 const DevNull = "NUL"
@@ -64,12 +64,11 @@ func openFile(name string, flag int, perm uint32) (file *File, err Error) {
 
 func openDir(name string) (file *File, err Error) {
        d := new(dirInfo)
-       r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+"\\*"), &d.stat.Windata)
+       r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+`\*`), &d.data)
        if e != 0 {
                return nil, &PathError{"open", name, Errno(e)}
        }
        f := NewFile(r, name)
-       d.usefirststat = true
        f.dirinfo = d
        return f, nil
 }
@@ -128,28 +127,6 @@ func (file *File) Close() Error {
        return err
 }
 
-func (file *File) statFile(name string) (fi *FileInfo, err Error) {
-       var stat syscall.ByHandleFileInformation
-       e := syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &stat)
-       if e != 0 {
-               return nil, &PathError{"stat", file.name, Errno(e)}
-       }
-       return fileInfoFromByHandleInfo(new(FileInfo), file.name, &stat), nil
-}
-
-// Stat returns the FileInfo structure describing file.
-// It returns the FileInfo and an error, if any.
-func (file *File) Stat() (fi *FileInfo, err Error) {
-       if file == nil || file.fd < 0 {
-               return nil, EINVAL
-       }
-       if file.isdir() {
-               // I don't know any better way to do that for directory
-               return Stat(file.name)
-       }
-       return file.statFile(file.name)
-}
-
 // Readdir reads the contents of the directory associated with file and
 // returns an array of up to n FileInfo structures, as would be returned
 // by Lstat, in directory order. Subsequent calls on the same file will yield
@@ -172,7 +149,6 @@ func (file *File) Readdir(n int) (fi []FileInfo, err Error) {
        if !file.isdir() {
                return nil, &PathError{"Readdir", file.name, ENOTDIR}
        }
-       di := file.dirinfo
        wantAll := n <= 0
        size := n
        if wantAll {
@@ -180,11 +156,10 @@ func (file *File) Readdir(n int) (fi []FileInfo, err Error) {
                size = 100
        }
        fi = make([]FileInfo, 0, size) // Empty with room to grow.
+       d := &file.dirinfo.data
        for n != 0 {
-               if di.usefirststat {
-                       di.usefirststat = false
-               } else {
-                       e := syscall.FindNextFile(syscall.Handle(file.fd), &di.stat.Windata)
+               if file.dirinfo.needdata {
+                       e := syscall.FindNextFile(syscall.Handle(file.fd), d)
                        if e != 0 {
                                if e == syscall.ERROR_NO_MORE_FILES {
                                        break
@@ -198,7 +173,8 @@ func (file *File) Readdir(n int) (fi []FileInfo, err Error) {
                        }
                }
                var f FileInfo
-               fileInfoFromWin32finddata(&f, &di.stat.Windata)
+               setFileInfo(&f, string(syscall.UTF16ToString(d.FileName[0:])), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime)
+               file.dirinfo.needdata = true
                if f.Name == "." || f.Name == ".." { // Useless names
                        continue
                }
index 11088436a3d12a3b12adc488d6cd73a1e579b9d8..2009d1f1b5423c984276dcd1237722eaf894e335 100644 (file)
@@ -4,24 +4,76 @@
 
 package os
 
-import "syscall"
+import (
+       "unsafe"
+       "syscall"
+)
 
-func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *FileInfo {
-       return fileInfoFromWin32finddata(fi, &stat.Windata)
+// Stat returns the FileInfo structure describing file.
+// It returns the FileInfo and an error, if any.
+func (file *File) Stat() (fi *FileInfo, err Error) {
+       if file == nil || file.fd < 0 {
+               return nil, EINVAL
+       }
+       if file.isdir() {
+               // I don't know any better way to do that for directory
+               return Stat(file.name)
+       }
+       var d syscall.ByHandleFileInformation
+       e := syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &d)
+       if e != 0 {
+               return nil, &PathError{"GetFileInformationByHandle", file.name, Errno(e)}
+       }
+       return setFileInfo(new(FileInfo), basename(file.name), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime), nil
 }
 
-func fileInfoFromWin32finddata(fi *FileInfo, d *syscall.Win32finddata) *FileInfo {
-       return setFileInfo(fi, string(syscall.UTF16ToString(d.FileName[0:])), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime)
+// Stat returns a FileInfo structure describing the named file and an error, if any.
+// If name names a valid symbolic link, the returned FileInfo describes
+// the file pointed at by the link and has fi.FollowedSymlink set to true.
+// If name names an invalid symbolic link, the returned FileInfo describes
+// the link itself and has fi.FollowedSymlink set to false.
+func Stat(name string) (fi *FileInfo, err Error) {
+       if len(name) == 0 {
+               return nil, &PathError{"Stat", name, Errno(syscall.ERROR_PATH_NOT_FOUND)}
+       }
+       var d syscall.Win32FileAttributeData
+       e := syscall.GetFileAttributesEx(syscall.StringToUTF16Ptr(name), syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&d)))
+       if e != 0 {
+               return nil, &PathError{"GetFileAttributesEx", name, Errno(e)}
+       }
+       return setFileInfo(new(FileInfo), basename(name), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime), nil
 }
 
-func fileInfoFromByHandleInfo(fi *FileInfo, name string, d *syscall.ByHandleFileInformation) *FileInfo {
-       for i := len(name) - 1; i >= 0; i-- {
+// Lstat returns the FileInfo structure describing the named file and an
+// error, if any.  If the file is a symbolic link, the returned FileInfo
+// describes the symbolic link.  Lstat makes no attempt to follow the link.
+func Lstat(name string) (fi *FileInfo, err Error) {
+       // No links on Windows
+       return Stat(name)
+}
+
+// basename removes trailing slashes and the leading
+// directory name and drive letter from path name.
+func basename(name string) string {
+       // Remove drive letter
+       if len(name) == 2 && name[1] == ':' {
+               name = "."
+       } else if len(name) > 2 && name[1] == ':' {
+               name = name[2:]
+       }
+       i := len(name) - 1
+       // Remove trailing slashes
+       for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i-- {
+               name = name[:i]
+       }
+       // Remove leading directory name
+       for i--; i >= 0; i-- {
                if name[i] == '/' || name[i] == '\\' {
                        name = name[i+1:]
                        break
                }
        }
-       return setFileInfo(fi, name, d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime)
+       return name
 }
 
 func setFileInfo(fi *FileInfo, name string, fa, sizehi, sizelo uint32, ctime, atime, wtime syscall.Filetime) *FileInfo {
index 30df718491f42d703178caafd5ba457e5cd1e7d5..7bc26d3cf4d5d923f36799cce432bb5db558c656 100644 (file)
@@ -207,6 +207,7 @@ func NewCallback(fn interface{}) uintptr
 //sys  SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int)
 //sys  GetFileAttributes(name *uint16) (attrs uint32, errno int) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW
 //sys  SetFileAttributes(name *uint16, attrs uint32) (errno int) = kernel32.SetFileAttributesW
+//sys  GetFileAttributesEx(name *uint16, level uint32, info *byte) (errno int) = kernel32.GetFileAttributesExW
 //sys  GetCommandLine() (cmd *uint16) = kernel32.GetCommandLineW
 //sys  CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, errno int) [failretval==nil] = shell32.CommandLineToArgvW
 //sys  LocalFree(hmem Handle) (handle Handle, errno int) [failretval!=0]
@@ -354,39 +355,6 @@ func getStdHandle(h int) (fd Handle) {
        return r
 }
 
-func Stat(path string, stat *Stat_t) (errno int) {
-       if len(path) == 0 {
-               return ERROR_PATH_NOT_FOUND
-       }
-       // Remove trailing slash.
-       if path[len(path)-1] == '/' || path[len(path)-1] == '\\' {
-               // Check if we're given root directory ("\" or "c:\").
-               if len(path) == 1 || (len(path) == 3 && path[1] == ':') {
-                       // TODO(brainman): Perhaps should fetch other fields, not just FileAttributes.
-                       stat.Windata = Win32finddata{}
-                       a, e := GetFileAttributes(StringToUTF16Ptr(path))
-                       if e != 0 {
-                               return e
-                       }
-                       stat.Windata.FileAttributes = a
-                       return 0
-               }
-               path = path[:len(path)-1]
-       }
-       h, e := FindFirstFile(StringToUTF16Ptr(path), &stat.Windata)
-       if e != 0 {
-               return e
-       }
-       defer FindClose(h)
-       stat.Mode = 0
-       return 0
-}
-
-func Lstat(path string, stat *Stat_t) (errno int) {
-       // no links on windows, just call Stat
-       return Stat(path, stat)
-}
-
 const ImplementsGetwd = true
 
 func Getwd() (wd string, errno int) {
index b7e9f606c3e30c3ee1fab2db8e1f520e605a89f6..24c82a479268d929f329f1e71421450fca2f0b71 100644 (file)
@@ -66,6 +66,7 @@ var (
        procSetFileTime                = modkernel32.NewProc("SetFileTime")
        procGetFileAttributesW         = modkernel32.NewProc("GetFileAttributesW")
        procSetFileAttributesW         = modkernel32.NewProc("SetFileAttributesW")
+       procGetFileAttributesExW       = modkernel32.NewProc("GetFileAttributesExW")
        procGetCommandLineW            = modkernel32.NewProc("GetCommandLineW")
        procCommandLineToArgvW         = modshell32.NewProc("CommandLineToArgvW")
        procLocalFree                  = modkernel32.NewProc("LocalFree")
@@ -142,7 +143,8 @@ func FreeLibrary(handle Handle) (errno int) {
 }
 
 func GetProcAddress(module Handle, procname string) (proc uintptr, errno int) {
-       proc, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0)
+       r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0)
+       proc = uintptr(r0)
        if proc == 0 {
                if e1 != 0 {
                        errno = int(e1)
@@ -847,6 +849,20 @@ func SetFileAttributes(name *uint16, attrs uint32) (errno int) {
        return
 }
 
+func GetFileAttributesEx(name *uint16, level uint32, info *byte) (errno int) {
+       r1, _, e1 := Syscall(procGetFileAttributesExW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(level), uintptr(unsafe.Pointer(info)))
+       if int(r1) == 0 {
+               if e1 != 0 {
+                       errno = int(e1)
+               } else {
+                       errno = EINVAL
+               }
+       } else {
+               errno = 0
+       }
+       return
+}
+
 func GetCommandLine() (cmd *uint16) {
        r0, _, _ := Syscall(procGetCommandLineW.Addr(), 0, 0, 0, 0)
        cmd = (*uint16)(unsafe.Pointer(r0))
index a791130e0a5ae507ad640c89888ef284d17b3322..06bb114babb5f8519a69c75d572e0ae202b04e60 100644 (file)
@@ -66,6 +66,7 @@ var (
        procSetFileTime                = modkernel32.NewProc("SetFileTime")
        procGetFileAttributesW         = modkernel32.NewProc("GetFileAttributesW")
        procSetFileAttributesW         = modkernel32.NewProc("SetFileAttributesW")
+       procGetFileAttributesExW       = modkernel32.NewProc("GetFileAttributesExW")
        procGetCommandLineW            = modkernel32.NewProc("GetCommandLineW")
        procCommandLineToArgvW         = modshell32.NewProc("CommandLineToArgvW")
        procLocalFree                  = modkernel32.NewProc("LocalFree")
@@ -142,7 +143,8 @@ func FreeLibrary(handle Handle) (errno int) {
 }
 
 func GetProcAddress(module Handle, procname string) (proc uintptr, errno int) {
-       proc, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0)
+       r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0)
+       proc = uintptr(r0)
        if proc == 0 {
                if e1 != 0 {
                        errno = int(e1)
@@ -847,6 +849,20 @@ func SetFileAttributes(name *uint16, attrs uint32) (errno int) {
        return
 }
 
+func GetFileAttributesEx(name *uint16, level uint32, info *byte) (errno int) {
+       r1, _, e1 := Syscall(procGetFileAttributesExW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(level), uintptr(unsafe.Pointer(info)))
+       if int(r1) == 0 {
+               if e1 != 0 {
+                       errno = int(e1)
+               } else {
+                       errno = EINVAL
+               }
+       } else {
+               errno = 0
+       }
+       return
+}
+
 func GetCommandLine() (cmd *uint16) {
        r0, _, _ := Syscall(procGetCommandLineW.Addr(), 0, 0, 0, 0)
        cmd = (*uint16)(unsafe.Pointer(r0))
index 01984473a81fe4d23a314958fd4466553719a8e0..5b8c33a21ad852c6c58e073937c3a2700f758e9d 100644 (file)
@@ -244,6 +244,20 @@ type ByHandleFileInformation struct {
        FileIndexLow       uint32
 }
 
+const (
+       GetFileExInfoStandard = 0
+       GetFileExMaxInfoLevel = 1
+)
+
+type Win32FileAttributeData struct {
+       FileAttributes uint32
+       CreationTime   Filetime
+       LastAccessTime Filetime
+       LastWriteTime  Filetime
+       FileSizeHigh   uint32
+       FileSizeLow    uint32
+}
+
 // ShowWindow constants
 const (
        // winuser.h
@@ -291,12 +305,6 @@ type ProcessInformation struct {
        ThreadId  uint32
 }
 
-// Invented values to support what package os expects.
-type Stat_t struct {
-       Windata Win32finddata
-       Mode    uint32
-}
-
 type Systemtime struct {
        Year         uint16
        Month        uint16