cmd/zc_traverser_local_windows.go (47 lines of code) (raw):
//go:build windows
// +build windows
package cmd
import (
"os"
"syscall"
"time"
"unsafe"
"github.com/hillu/go-ntdll"
"golang.org/x/sys/windows"
)
// Override the modtime from the OS stat
type folderStatWrapper struct {
os.FileInfo
changeTime time.Time
}
func (f *folderStatWrapper) ModTime() time.Time {
return f.changeTime
}
func WrapFolder(fullpath string, stat os.FileInfo) (os.FileInfo, error) {
srcPtr, err := syscall.UTF16PtrFromString(fullpath)
if err != nil {
return nil, err
}
// custom open call, because must specify FILE_FLAG_BACKUP_SEMANTICS to make --backup mode work properly (i.e. our use of SeBackupPrivilege)
fd, err := windows.CreateFile(srcPtr,
windows.GENERIC_READ, windows.FILE_SHARE_READ, nil,
windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
if err != nil {
return nil, err
}
defer windows.Close(fd)
buf := make([]byte, 1024)
var rLen = uint32(unsafe.Sizeof(ntdll.FileBasicInformationT{}))
if st := ntdll.CallWithExpandingBuffer(func() ntdll.NtStatus {
var stat ntdll.IoStatusBlock
return ntdll.NtQueryInformationFile(ntdll.Handle(fd), &stat, &buf[0], uint32(len(buf)), ntdll.FileBasicInformation)
}, &buf, &rLen); st.IsSuccess() {
ntdllTime := time.Date(1601, time.January, 1, 0, 0, 0, 0, time.UTC)
// do a nasty unsafe thing and tell go that our []byte is actually a FileStandardInformationT.
fi := (*ntdll.FileBasicInformationT)(unsafe.Pointer(&buf[0]))
// ntdll returns times that are incremented by 100-nanosecond "instants" past the beginning of 1601.
// time.Duration is a 64 bit integer, starting at nanoseconds.
// It cannot hold more than 290 years. You can't add any kind of arbitrary precision number of nanoseconds either.
// However, time.Time can handle such things on it's own.
// So, we just add our changetime 100x. It's a little cheesy, but it does work.
for i := 0; i < 100; i++ {
ntdllTime = ntdllTime.Add(time.Duration(fi.ChangeTime))
}
return &folderStatWrapper{
stat,
ntdllTime,
}, nil
} else {
return nil, st.Error()
}
}