111 lines
3.7 KiB
Go
111 lines
3.7 KiB
Go
//go:build windows
|
|
|
|
package safeio
|
|
|
|
import (
|
|
"os"
|
|
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
const windowsRetainedHandleShareMode uint32 = windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE
|
|
|
|
// ReadCanonicalRegular resolves every component under the already-opened parent with NT
|
|
// RootDirectory-relative opens. Each retained handle rejects reparse points and denies delete
|
|
// sharing, so a later ancestor replacement cannot redirect the final regular-file open.
|
|
func ReadCanonicalRegular(path string, maximum int64) ([]byte, error) {
|
|
if err := ValidateCanonicalPath(path); err != nil {
|
|
return nil, err
|
|
}
|
|
parents, target, err := openCanonicalWindowsParent(path)
|
|
if err != nil || parents == nil || len(parents.handles) == 0 {
|
|
if parents != nil {
|
|
parents.Close()
|
|
}
|
|
return nil, ErrUnsafeFile
|
|
}
|
|
defer parents.Close()
|
|
handle, err := openWindowsRelativeComponent(parents.handles[len(parents.handles)-1], target, false, windows.GENERIC_READ)
|
|
if err != nil {
|
|
return nil, ErrUnsafeFile
|
|
}
|
|
file := os.NewFile(uintptr(handle), "tht-safeio")
|
|
if file == nil {
|
|
windows.CloseHandle(handle)
|
|
return nil, ErrUnsafeFile
|
|
}
|
|
defer file.Close()
|
|
return readBoundedRegularFile(path, file, maximum)
|
|
}
|
|
|
|
func openWindowsComponent(path string, directory bool) (windows.Handle, error) {
|
|
return openWindowsComponentWithAccess(path, directory, windows.GENERIC_READ)
|
|
}
|
|
|
|
func openWindowsComponentWithAccess(path string, directory bool, access uint32) (windows.Handle, error) {
|
|
flags := uint32(windows.FILE_FLAG_OPEN_REPARSE_POINT)
|
|
if directory {
|
|
flags |= windows.FILE_FLAG_BACKUP_SEMANTICS
|
|
} else {
|
|
flags |= windows.FILE_ATTRIBUTE_NORMAL
|
|
}
|
|
handle, err := windows.CreateFile(
|
|
windows.StringToUTF16Ptr(path),
|
|
access,
|
|
windowsRetainedHandleShareMode,
|
|
nil,
|
|
windows.OPEN_EXISTING,
|
|
flags,
|
|
0,
|
|
)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
var information windows.ByHandleFileInformation
|
|
if err := windows.GetFileInformationByHandle(handle, &information); err != nil {
|
|
windows.CloseHandle(handle)
|
|
return 0, err
|
|
}
|
|
if information.FileAttributes&windows.FILE_ATTRIBUTE_REPARSE_POINT != 0 ||
|
|
(directory && information.FileAttributes&windows.FILE_ATTRIBUTE_DIRECTORY == 0) ||
|
|
(!directory && (information.FileAttributes&windows.FILE_ATTRIBUTE_DIRECTORY != 0 || information.NumberOfLinks != 1)) {
|
|
windows.CloseHandle(handle)
|
|
return 0, ErrUnsafeFile
|
|
}
|
|
return handle, nil
|
|
}
|
|
|
|
// openWindowsRelativeComponent is the generic NT equivalent of openat for one canonical leaf.
|
|
// Its root handle has already pinned all lexical ancestors; FILE_OPEN_REPARSE_POINT makes a
|
|
// reparse point observable so it can be rejected rather than followed.
|
|
func openWindowsRelativeComponent(parent windows.Handle, name string, directory bool, access uint32) (windows.Handle, error) {
|
|
options := uint32(windows.FILE_SYNCHRONOUS_IO_NONALERT | windows.FILE_OPEN_REPARSE_POINT)
|
|
if directory {
|
|
options |= windows.FILE_DIRECTORY_FILE
|
|
} else {
|
|
options |= windows.FILE_NON_DIRECTORY_FILE
|
|
}
|
|
handle, err := openWindowsRelativeObject(parent, name, access, windows.FILE_OPEN, options, nil)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
var information windows.ByHandleFileInformation
|
|
if err := windows.GetFileInformationByHandle(handle, &information); err != nil {
|
|
_ = windows.CloseHandle(handle)
|
|
return 0, err
|
|
}
|
|
if information.FileAttributes&windows.FILE_ATTRIBUTE_REPARSE_POINT != 0 ||
|
|
(directory && information.FileAttributes&windows.FILE_ATTRIBUTE_DIRECTORY == 0) ||
|
|
(!directory && (information.FileAttributes&windows.FILE_ATTRIBUTE_DIRECTORY != 0 || information.NumberOfLinks != 1)) {
|
|
_ = windows.CloseHandle(handle)
|
|
return 0, ErrUnsafeFile
|
|
}
|
|
return handle, nil
|
|
}
|
|
|
|
func closeWindowsHandles(handles []windows.Handle) {
|
|
for _, handle := range handles {
|
|
windows.CloseHandle(handle)
|
|
}
|
|
}
|