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
5package os
6
7import (
8	"sync"
9	"syscall"
10	"time"
11)
12
13// A fileStat is the implementation of FileInfo returned by Stat and Lstat.
14type fileStat struct {
15	name string
16	sys  syscall.Win32FileAttributeData
17
18	// used to implement SameFile
19	sync.Mutex
20	path  string
21	vol   uint32
22	idxhi uint32
23	idxlo uint32
24}
25
26func (fs *fileStat) Size() int64 {
27	return int64(fs.sys.FileSizeHigh)<<32 + int64(fs.sys.FileSizeLow)
28}
29
30func (fs *fileStat) Mode() (m FileMode) {
31	if fs == &devNullStat {
32		return ModeDevice | ModeCharDevice | 0666
33	}
34	if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
35		m |= ModeDir | 0111
36	}
37	if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
38		m |= 0444
39	} else {
40		m |= 0666
41	}
42	if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
43		m |= ModeSymlink
44	}
45	return m
46}
47
48func (fs *fileStat) ModTime() time.Time {
49	return time.Unix(0, fs.sys.LastWriteTime.Nanoseconds())
50}
51
52// Sys returns syscall.Win32FileAttributeData for file fs.
53func (fs *fileStat) Sys() interface{} { return &fs.sys }
54
55func (fs *fileStat) loadFileId() error {
56	fs.Lock()
57	defer fs.Unlock()
58	if fs.path == "" {
59		// already done
60		return nil
61	}
62	pathp, err := syscall.UTF16PtrFromString(fs.path)
63	if err != nil {
64		return err
65	}
66	h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
67	if err != nil {
68		return err
69	}
70	defer syscall.CloseHandle(h)
71	var i syscall.ByHandleFileInformation
72	err = syscall.GetFileInformationByHandle(syscall.Handle(h), &i)
73	if err != nil {
74		return err
75	}
76	fs.path = ""
77	fs.vol = i.VolumeSerialNumber
78	fs.idxhi = i.FileIndexHigh
79	fs.idxlo = i.FileIndexLow
80	return nil
81}
82
83// devNullStat is fileStat structure describing DevNull file ("NUL").
84var devNullStat = fileStat{
85	name: DevNull,
86	// hopefully this will work for SameFile
87	vol:   0,
88	idxhi: 0,
89	idxlo: 0,
90}
91
92func sameFile(fs1, fs2 *fileStat) bool {
93	e := fs1.loadFileId()
94	if e != nil {
95		return false
96	}
97	e = fs2.loadFileId()
98	if e != nil {
99		return false
100	}
101	return fs1.vol == fs2.vol && fs1.idxhi == fs2.idxhi && fs1.idxlo == fs2.idxlo
102}
103
104// For testing.
105func atime(fi FileInfo) time.Time {
106	return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
107}
108