Files
ThothII/tools/tht/internal/safeio/private_root_windows.go
T

1112 lines
34 KiB
Go

//go:build windows
package safeio
import (
"errors"
"io"
"os"
"runtime"
"sort"
"time"
"unsafe"
"golang.org/x/sys/windows"
)
// windowsPrivateDirectory keeps the root's canonical component handles alive, then uses NT
// RootDirectory-relative opens for every descendant. Unlike a lexical child path, an NT relative
// object name is resolved by the already-open directory handle and cannot be redirected by a
// rename, replacement, or reparse point at the original root path.
type windowsPrivateDirectory struct {
anchors *windowsParentHandles
parent windows.Handle
handle windows.Handle
info windows.ByHandleFileInformation
}
type windowsPrivateRegularAt struct {
handle windows.Handle
info windows.ByHandleFileInformation
}
func openPrivateDirectory(path string, ensure bool) (PrivateDirectoryHandle, bool, error) {
anchors, target, err := openCanonicalWindowsParent(path)
if err != nil || anchors == nil || len(anchors.handles) == 0 {
if anchors != nil {
anchors.Close()
}
return nil, false, ErrUnsafeFile
}
parent := anchors.handles[len(anchors.handles)-1]
handle, found, err := openWindowsPrivateDirectoryAt(parent, target, false)
if err != nil {
anchors.Close()
return nil, false, ErrUnsafeFile
}
if !found {
// The final root is absent. The retained canonical parent must prove that the same
// ensure operation could create it; validation itself must remain side-effect free.
// FILE_APPEND_DATA is the Win32 spelling of directory FILE_ADD_SUBDIRECTORY.
anchors.Close()
writableAnchors, writableTarget, probeErr := openCanonicalWindowsParentWithFinalAccess(path, windows.FILE_APPEND_DATA)
if probeErr != nil || writableAnchors == nil || len(writableAnchors.handles) == 0 {
if writableAnchors != nil {
writableAnchors.Close()
}
return nil, false, ErrUnsafeFile
}
if !ensure {
writableAnchors.Close()
return nil, false, nil
}
anchors = writableAnchors
target = writableTarget
parent = anchors.handles[len(anchors.handles)-1]
handle, found, err = openWindowsPrivateDirectoryAt(parent, target, true)
if err != nil || !found {
anchors.Close()
return nil, false, ErrUnsafeFile
}
}
value := &windowsPrivateDirectory{anchors: anchors, handle: handle}
if value.captureAndValidate() != nil {
_ = value.Close()
return nil, false, ErrUnsafeFile
}
return value, true, nil
}
func openWindowsPrivateDirectoryAt(parent windows.Handle, name string, ensure bool) (windows.Handle, bool, error) {
if parent == 0 || !validPrivateLeafName(name) {
return 0, false, ErrUnsafeFile
}
for attempt := 0; attempt < 2; attempt++ {
handle, err := openWindowsRelativeDirectory(parent, name)
if err == nil {
return handle, true, nil
}
if !isWindowsRelativeNotFound(err) {
return 0, false, ErrUnsafeFile
}
if !ensure {
return 0, false, nil
}
handle, err = createWindowsRelativePrivateDirectory(parent, name)
if err == nil {
return handle, true, nil
}
}
return 0, false, ErrUnsafeFile
}
func openWindowsRelativeDirectory(parent windows.Handle, name string) (windows.Handle, error) {
handle, err := openWindowsRelativeObject(
parent,
name,
// The retained directory handle is also the RootDirectory for create, rename,
// hard-link, and delete operations below, so it needs the owner's full private
// directory capability rather than a read-only probe handle.
windows.FILE_GENERIC_READ|windows.FILE_GENERIC_WRITE|windows.DELETE,
windows.FILE_OPEN,
windows.FILE_DIRECTORY_FILE|windows.FILE_SYNCHRONOUS_IO_NONALERT|windows.FILE_OPEN_REPARSE_POINT,
nil,
)
if err != nil {
return 0, err
}
if _, err := privateWindowsDirectoryInfo(handle); err != nil {
_ = windows.CloseHandle(handle)
return 0, ErrUnsafeFile
}
return handle, nil
}
func createWindowsRelativePrivateDirectory(parent windows.Handle, name string) (windows.Handle, error) {
security, err := newOwnerOnlySecurityDescriptor()
if err != nil {
return 0, ErrUnsafeFile
}
defer security.Close()
handle, err := openWindowsRelativeObject(
parent,
name,
windows.FILE_GENERIC_READ|windows.FILE_GENERIC_WRITE|windows.DELETE,
windows.FILE_CREATE,
windows.FILE_DIRECTORY_FILE|windows.FILE_SYNCHRONOUS_IO_NONALERT|windows.FILE_OPEN_REPARSE_POINT,
security,
)
if err != nil {
return 0, err
}
if _, err := privateWindowsDirectoryInfo(handle); err != nil {
_ = markWindowsHandleForDelete(handle)
_ = windows.CloseHandle(handle)
return 0, ErrUnsafeFile
}
return handle, nil
}
func openWindowsRelativeObject(
parent windows.Handle,
name string,
access uint32,
disposition uint32,
options uint32,
security *ownerOnlySecurityDescriptor,
) (windows.Handle, error) {
return openWindowsRelativeObjectWithShareMode(
parent,
name,
access,
disposition,
options,
security,
windowsRetainedHandleShareMode,
)
}
func openWindowsRelativeObjectWithShareMode(
parent windows.Handle,
name string,
access uint32,
disposition uint32,
options uint32,
security *ownerOnlySecurityDescriptor,
shareMode uint32,
) (windows.Handle, error) {
if parent == 0 || !validPrivateLeafName(name) {
return 0, ErrUnsafeFile
}
access = normalizeWindowsNTDesiredAccess(access)
objectName, err := windows.NewNTUnicodeString(name)
if err != nil {
return 0, ErrUnsafeFile
}
attributes := &windows.OBJECT_ATTRIBUTES{
Length: uint32(unsafe.Sizeof(windows.OBJECT_ATTRIBUTES{})),
RootDirectory: parent,
ObjectName: objectName,
Attributes: windows.OBJ_CASE_INSENSITIVE,
SecurityDescriptor: nil,
}
if security != nil {
attributes.SecurityDescriptor = security.descriptor
}
var (
handle windows.Handle
status windows.IO_STATUS_BLOCK
allocationSize int64
)
err = windows.NtCreateFile(
&handle,
access,
attributes,
&status,
&allocationSize,
// NtCreateFile supplies the normal attribute default when this is zero; no
// explicit creation attribute is needed for these retained open/create calls.
0,
shareMode,
disposition,
options,
0,
0,
)
runtime.KeepAlive(objectName)
runtime.KeepAlive(security)
if err != nil {
return 0, err
}
return handle, nil
}
func normalizeWindowsNTDesiredAccess(access uint32) uint32 {
if access&uint32(windows.GENERIC_ALL) != 0 {
const fileSpecificAll = uint32(0x1ff)
access = access&^uint32(windows.GENERIC_ALL) |
uint32(windows.STANDARD_RIGHTS_REQUIRED|windows.SYNCHRONIZE) | fileSpecificAll
}
if access&uint32(windows.GENERIC_READ) != 0 {
access = access&^uint32(windows.GENERIC_READ) | uint32(windows.FILE_GENERIC_READ)
}
if access&uint32(windows.GENERIC_WRITE) != 0 {
access = access&^uint32(windows.GENERIC_WRITE) | uint32(windows.FILE_GENERIC_WRITE)
}
if access&uint32(windows.GENERIC_EXECUTE) != 0 {
access = access&^uint32(windows.GENERIC_EXECUTE) | uint32(windows.FILE_GENERIC_EXECUTE)
}
return access
}
func privateWindowsDirectoryInfo(handle windows.Handle) (windows.ByHandleFileInformation, error) {
var info windows.ByHandleFileInformation
if handle == 0 || windows.GetFileInformationByHandle(handle, &info) != nil ||
info.FileAttributes&windows.FILE_ATTRIBUTE_REPARSE_POINT != 0 ||
info.FileAttributes&windows.FILE_ATTRIBUTE_DIRECTORY == 0 ||
validateOwnerOnlyDACL(handle) != nil {
return windows.ByHandleFileInformation{}, ErrUnsafeFile
}
return info, nil
}
func privateWindowsRegularInfo(handle windows.Handle, allowedLinks ...uint32) (windows.ByHandleFileInformation, error) {
var info windows.ByHandleFileInformation
if handle == 0 || windows.GetFileInformationByHandle(handle, &info) != nil ||
info.FileAttributes&windows.FILE_ATTRIBUTE_REPARSE_POINT != 0 ||
info.FileAttributes&windows.FILE_ATTRIBUTE_DIRECTORY != 0 ||
validateOwnerOnlyDACL(handle) != nil {
return windows.ByHandleFileInformation{}, ErrUnsafeFile
}
for _, links := range allowedLinks {
if info.NumberOfLinks == links {
return info, nil
}
}
return windows.ByHandleFileInformation{}, ErrUnsafeFile
}
func isWindowsRelativeNotFound(err error) bool {
return errors.Is(err, windows.ERROR_FILE_NOT_FOUND) ||
errors.Is(err, windows.ERROR_PATH_NOT_FOUND) ||
errors.Is(err, windows.STATUS_NO_SUCH_FILE) ||
errors.Is(err, windows.STATUS_OBJECT_NAME_NOT_FOUND) ||
errors.Is(err, windows.STATUS_OBJECT_PATH_NOT_FOUND)
}
func isWindowsRelativeCollision(err error) bool {
return errors.Is(err, windows.ERROR_FILE_EXISTS) ||
errors.Is(err, windows.ERROR_ALREADY_EXISTS) ||
errors.Is(err, windows.STATUS_OBJECT_NAME_COLLISION)
}
func (directory *windowsPrivateDirectory) captureAndValidate() error {
if directory == nil || directory.handle == 0 {
return ErrUnsafeFile
}
info, err := privateWindowsDirectoryInfo(directory.handle)
if err != nil {
return ErrUnsafeFile
}
directory.info = info
return nil
}
func (directory *windowsPrivateDirectory) Close() error {
if directory == nil {
return nil
}
var result error
if directory.handle != 0 {
if err := windows.CloseHandle(directory.handle); err != nil {
result = ErrUnsafeFile
}
directory.handle = 0
}
if directory.parent != 0 {
if err := windows.CloseHandle(directory.parent); err != nil {
result = ErrUnsafeFile
}
directory.parent = 0
}
if directory.anchors != nil {
directory.anchors.Close()
directory.anchors = nil
}
return result
}
func sameWindowsPrivateDirectoryIdentity(left, right windows.ByHandleFileInformation) bool {
return left.VolumeSerialNumber == right.VolumeSerialNumber && left.FileIndexHigh == right.FileIndexHigh &&
left.FileIndexLow == right.FileIndexLow && left.FileAttributes == right.FileAttributes
}
func sameWindowsPrivateDirectorySnapshot(left, right windows.ByHandleFileInformation) bool {
return sameWindowsPrivateDirectoryIdentity(left, right) && left.LastWriteTime == right.LastWriteTime
}
func (directory *windowsPrivateDirectory) Validate() error {
if directory == nil || directory.handle == 0 || (directory.anchors == nil && directory.parent == 0) {
return ErrUnsafeFile
}
if directory.anchors != nil && len(directory.anchors.handles) == 0 {
return ErrUnsafeFile
}
if directory.parent != 0 {
if _, err := privateWindowsDirectoryInfo(directory.parent); err != nil {
return ErrUnsafeFile
}
}
current, err := privateWindowsDirectoryInfo(directory.handle)
if err != nil || !sameWindowsPrivateDirectoryIdentity(directory.info, current) {
return ErrUnsafeFile
}
return nil
}
func duplicateWindowsRetainedHandle(handle windows.Handle) (windows.Handle, error) {
if handle == 0 {
return 0, ErrUnsafeFile
}
var duplicate windows.Handle
process := windows.CurrentProcess()
if err := windows.DuplicateHandle(process, handle, process, &duplicate, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
return 0, ErrUnsafeFile
}
return duplicate, nil
}
func (directory *windowsPrivateDirectory) OpenChild(name string, ensure bool) (PrivateDirectoryHandle, bool, error) {
if directory.Validate() != nil || !validPrivateLeafName(name) {
return nil, false, ErrUnsafeFile
}
parent, err := duplicateWindowsRetainedHandle(directory.handle)
if err != nil {
return nil, false, ErrUnsafeFile
}
handle, found, err := openWindowsPrivateDirectoryAt(parent, name, ensure)
if err != nil || !found {
_ = windows.CloseHandle(parent)
if err != nil {
return nil, false, ErrUnsafeFile
}
return nil, false, nil
}
child := &windowsPrivateDirectory{parent: parent, handle: handle}
if child.captureAndValidate() != nil || directory.Validate() != nil {
_ = child.Close()
return nil, false, ErrUnsafeFile
}
return child, true, nil
}
func openWindowsPrivateRegularAt(
parent windows.Handle,
name string,
access uint32,
allowedLinks ...uint32,
) (*windowsPrivateRegularAt, error) {
return openWindowsPrivateRegularAtWithShareMode(
parent,
name,
access,
windowsRetainedHandleShareMode,
allowedLinks...,
)
}
func openWindowsPrivateRegularAtWithShareMode(
parent windows.Handle,
name string,
access uint32,
shareMode uint32,
allowedLinks ...uint32,
) (*windowsPrivateRegularAt, error) {
handle, err := openWindowsRelativeObjectWithShareMode(
parent,
name,
access,
windows.FILE_OPEN,
windows.FILE_NON_DIRECTORY_FILE|windows.FILE_SYNCHRONOUS_IO_NONALERT|windows.FILE_OPEN_REPARSE_POINT,
nil,
shareMode,
)
if err != nil {
return nil, err
}
info, err := privateWindowsRegularInfo(handle, allowedLinks...)
if err != nil {
_ = windows.CloseHandle(handle)
return nil, ErrUnsafeFile
}
return &windowsPrivateRegularAt{handle: handle, info: info}, nil
}
func openWindowsPrivateRegularAtAllowedLinks(
parent windows.Handle,
name string,
access uint32,
allowedLinks ...uint32,
) (*windowsPrivateRegularAt, error) {
var (
lastError error
unsafeFound bool
)
for _, links := range allowedLinks {
value, err := openWindowsPrivateRegularAt(parent, name, access, links)
if err == nil {
return value, nil
}
lastError = err
if !isWindowsRelativeNotFound(err) {
unsafeFound = true
}
}
if unsafeFound {
return nil, ErrUnsafeFile
}
return nil, lastError
}
func createWindowsPrivateRegularAt(parent windows.Handle, name string) (*windowsPrivateRegularAt, error) {
return createWindowsPrivateRegularAtWithAccess(parent, name, windows.FILE_GENERIC_READ|windows.FILE_GENERIC_WRITE|windows.DELETE)
}
// createWindowsPrivateRegularAtWithAccess creates the final leaf beneath an already-retained
// parent with an owner-only DACL in the same NtCreateFile operation. The caller never re-resolves
// an absolute pathname after the parent is pinned.
func createWindowsPrivateRegularAtWithAccess(parent windows.Handle, name string, access uint32) (*windowsPrivateRegularAt, error) {
security, err := newOwnerOnlySecurityDescriptor()
if err != nil {
return nil, ErrUnsafeFile
}
defer security.Close()
// The caller's data authority is intentionally preserved (GENERIC_WRITE is used for
// streaming creates), but privateWindowsRegularInfo must inspect attributes and links
// before returning the handle. FILE_READ_ATTRIBUTES grants that inspection without
// adding read-data authority to a write-only create.
access |= windows.FILE_READ_ATTRIBUTES
handle, err := openWindowsRelativeObject(
parent,
name,
access|windows.DELETE,
windows.FILE_CREATE,
windows.FILE_NON_DIRECTORY_FILE|windows.FILE_SYNCHRONOUS_IO_NONALERT|windows.FILE_OPEN_REPARSE_POINT,
security,
)
if err != nil {
return nil, err
}
info, err := privateWindowsRegularInfo(handle, 1)
if err != nil {
_ = markWindowsHandleForDelete(handle)
_ = windows.CloseHandle(handle)
return nil, ErrUnsafeFile
}
return &windowsPrivateRegularAt{handle: handle, info: info}, nil
}
func (value *windowsPrivateRegularAt) Close() error {
if value == nil || value.handle == 0 {
return nil
}
handle := value.handle
value.handle = 0
if err := windows.CloseHandle(handle); err != nil {
return ErrUnsafeFile
}
return nil
}
func writeWindowsPrivateRegular(value *windowsPrivateRegularAt, contents []byte) error {
if value == nil || value.handle == 0 || len(contents) == 0 {
return ErrUnsafeFile
}
for remaining := contents; len(remaining) > 0; {
var written uint32
if err := windows.WriteFile(value.handle, remaining, &written, nil); err != nil || written == 0 || int(written) > len(remaining) {
return ErrUnsafeFile
}
remaining = remaining[written:]
}
if err := windows.FlushFileBuffers(value.handle); err != nil {
return ErrUnsafeFile
}
info, err := privateWindowsRegularInfo(value.handle, 1)
if err != nil {
return ErrUnsafeFile
}
value.info = info
return nil
}
func readWindowsPrivateRegular(value *windowsPrivateRegularAt, maximum int64, links uint32) ([]byte, error) {
if value == nil || value.handle == 0 || maximum < 0 || maximum == int64(^uint64(0)>>1) {
return nil, ErrUnsafeFile
}
contents := make([]byte, 0, 4096)
buffer := make([]byte, 4096)
for {
var read uint32
err := windows.ReadFile(value.handle, buffer, &read, nil)
if read > 0 {
if int64(len(contents))+int64(read) > maximum {
return nil, ErrUnsafeFile
}
contents = append(contents, buffer[:read]...)
}
if err != nil {
if errors.Is(err, windows.ERROR_HANDLE_EOF) {
break
}
return nil, ErrUnsafeFile
}
if read == 0 {
break
}
}
after, err := privateWindowsRegularInfo(value.handle, links)
if err != nil || !sameWindowsPrivateFile(value.info, after) {
return nil, ErrUnsafeFile
}
value.info = after
return contents, nil
}
func markWindowsHandleForDelete(handle windows.Handle) error {
if handle == 0 {
return ErrUnsafeFile
}
buffer := [1]byte{1}
var status windows.IO_STATUS_BLOCK
if err := windows.NtSetInformationFile(handle, &status, &buffer[0], uint32(len(buffer)), windows.FileDispositionInformation); err != nil {
return ErrUnsafeFile
}
return nil
}
func finishWindowsPrivateRegularCleanup(mark func() error, close func() error) error {
failed := false
if mark == nil || mark() != nil {
failed = true
}
if close == nil || close() != nil {
failed = true
}
if failed {
return ErrUnsafeFile
}
return nil
}
func closeAndDeleteWindowsPrivateRegular(value *windowsPrivateRegularAt) error {
if value == nil || value.handle == 0 {
return ErrUnsafeFile
}
return finishWindowsPrivateRegularCleanup(
func() error { return markWindowsHandleForDelete(value.handle) },
value.Close,
)
}
func (directory *windowsPrivateDirectory) CreateRegular(name string, contents []byte) (bool, error) {
if directory.Validate() != nil || !validPrivateLeafName(name) || len(contents) == 0 {
return false, ErrUnsafeFile
}
value, err := createWindowsPrivateRegularAt(directory.handle, name)
if err != nil {
existing, existingErr := openWindowsPrivateRegularAt(directory.handle, name, windows.FILE_GENERIC_READ, 1)
if existingErr == nil {
closeErr := existing.Close()
validateErr := directory.Validate()
if closeErr != nil || validateErr != nil {
return false, ErrUnsafeFile
}
return false, nil
}
return false, ErrUnsafeFile
}
published := false
defer func() {
if !published {
_ = closeAndDeleteWindowsPrivateRegular(value)
}
}()
if writeWindowsPrivateRegular(value, contents) != nil || directory.Validate() != nil {
return false, ErrUnsafeFile
}
if value.Close() != nil {
return false, ErrUnsafeFile
}
published = true
return true, nil
}
func (directory *windowsPrivateDirectory) CreateRegularFile(name string) (*os.File, bool, error) {
if directory.Validate() != nil || !validPrivateLeafName(name) {
return nil, false, ErrUnsafeFile
}
value, err := createWindowsPrivateRegularAt(directory.handle, name)
if err != nil {
existing, existingErr := openWindowsPrivateRegularAt(directory.handle, name, windows.FILE_GENERIC_READ, 1)
if existingErr == nil {
if existing.Close() != nil || directory.Validate() != nil {
return nil, false, ErrUnsafeFile
}
return nil, false, nil
}
return nil, false, ErrUnsafeFile
}
failed := true
defer func() {
if failed {
_ = closeAndDeleteWindowsPrivateRegular(value)
}
}()
info, err := privateWindowsRegularInfo(value.handle, 1)
if err != nil || directory.Validate() != nil {
return nil, false, ErrUnsafeFile
}
value.info = info
file := os.NewFile(uintptr(value.handle), "tht-safeio-private-root-stream")
if file == nil {
return nil, false, ErrUnsafeFile
}
value.handle = 0
failed = false
return file, true, nil
}
func (directory *windowsPrivateDirectory) ReadRegular(name string, maximum int64) ([]byte, bool, error) {
if directory.Validate() != nil || !validPrivateLeafName(name) || maximum < 0 || maximum == int64(^uint64(0)>>1) {
return nil, false, ErrUnsafeFile
}
value, err := openWindowsPrivateRegularAt(directory.handle, name, windows.FILE_GENERIC_READ, 1)
if isWindowsRelativeNotFound(err) {
return nil, false, nil
}
if err != nil {
return nil, false, ErrUnsafeFile
}
defer value.Close()
contents, err := readWindowsPrivateRegular(value, maximum, 1)
if err != nil || directory.Validate() != nil {
return nil, false, ErrUnsafeFile
}
return contents, true, nil
}
type windowsRelativeNameInformation struct {
Flags uint32
RootDirectory windows.Handle
FileNameLength uint32
FileName [1]uint16
}
func setWindowsRelativeNameInformation(
handle windows.Handle,
parent windows.Handle,
name string,
class uint32,
flags uint32,
) error {
if handle == 0 || parent == 0 || !validPrivateLeafName(name) {
return ErrUnsafeFile
}
encoded, err := windows.UTF16FromString(name)
if err != nil || len(encoded) < 2 {
return ErrUnsafeFile
}
nameBytes := (len(encoded) - 1) * 2
var header windowsRelativeNameInformation
size := int(unsafe.Offsetof(header.FileName)) + nameBytes
buffer := make([]byte, size)
value := (*windowsRelativeNameInformation)(unsafe.Pointer(&buffer[0]))
value.Flags = flags
value.RootDirectory = parent
value.FileNameLength = uint32(nameBytes)
copy(unsafe.Slice(&value.FileName[0], len(encoded)-1), encoded[:len(encoded)-1])
var status windows.IO_STATUS_BLOCK
if err := windows.NtSetInformationFile(handle, &status, &buffer[0], uint32(len(buffer)), class); err != nil {
return ErrUnsafeFile
}
runtime.KeepAlive(encoded)
runtime.KeepAlive(buffer)
return nil
}
func renameWindowsPrivateRegularAt(handle windows.Handle, parent windows.Handle, name string) error {
return setWindowsRelativeNameInformation(handle, parent, name, windows.FileRenameInformation, windows.FILE_RENAME_REPLACE_IF_EXISTS)
}
func linkWindowsPrivateRegularAt(handle windows.Handle, parent windows.Handle, name string) error {
return setWindowsRelativeNameInformation(handle, parent, name, windows.FileLinkInformation, 0)
}
func createWindowsPrivateTemporaryAt(parent windows.Handle, contents []byte) (*windowsPrivateRegularAt, error) {
for attempt := 0; attempt < 16; attempt++ {
name, err := randomTemporaryName()
if err != nil {
return nil, ErrUnsafeFile
}
value, err := createWindowsPrivateRegularAt(parent, name)
if err != nil {
if isWindowsRelativeCollision(err) {
continue
}
return nil, ErrUnsafeFile
}
if writeWindowsPrivateRegular(value, contents) == nil {
return value, nil
}
_ = closeAndDeleteWindowsPrivateRegular(value)
return nil, ErrUnsafeFile
}
return nil, ErrUnsafeFile
}
func (directory *windowsPrivateDirectory) ReplaceRegular(name string, contents []byte) error {
if directory.Validate() != nil || !validPrivateLeafName(name) || len(contents) == 0 {
return ErrUnsafeFile
}
existing, err := openWindowsPrivateRegularAt(directory.handle, name, windows.FILE_GENERIC_READ, 1)
if err != nil {
return ErrUnsafeFile
}
if existing.Close() != nil {
return ErrUnsafeFile
}
temporary, err := createWindowsPrivateTemporaryAt(directory.handle, contents)
if err != nil {
return ErrUnsafeFile
}
renamed := false
defer func() {
if !renamed {
_ = closeAndDeleteWindowsPrivateRegular(temporary)
}
}()
current, err := openWindowsPrivateRegularAt(directory.handle, name, windows.FILE_GENERIC_READ, 1)
if err != nil {
return ErrUnsafeFile
}
currentCloseErr := current.Close()
currentValidateErr := directory.Validate()
if currentCloseErr != nil || currentValidateErr != nil {
return ErrUnsafeFile
}
renameErr := renameWindowsPrivateRegularAt(temporary.handle, directory.handle, name)
temporaryCloseErr := temporary.Close()
if renameErr != nil || temporaryCloseErr != nil {
return ErrUnsafeFile
}
renamed = true
replaced, err := openWindowsPrivateRegularAt(directory.handle, name, windows.FILE_GENERIC_READ, 1)
if err != nil {
return ErrUnsafeFile
}
replacedCloseErr := replaced.Close()
replacedValidateErr := directory.Validate()
if replacedCloseErr != nil || replacedValidateErr != nil {
return ErrUnsafeFile
}
return nil
}
func (directory *windowsPrivateDirectory) RemoveRegular(name string) (bool, error) {
if directory.Validate() != nil || !validPrivateLeafName(name) {
return false, ErrUnsafeFile
}
value, err := openWindowsPrivateRegularAt(directory.handle, name, windows.FILE_GENERIC_READ|windows.DELETE, 1)
if isWindowsRelativeNotFound(err) {
return false, nil
}
if err != nil {
return false, ErrUnsafeFile
}
cleanupErr := closeAndDeleteWindowsPrivateRegular(value)
validateErr := directory.Validate()
if cleanupErr != nil || validateErr != nil {
return false, ErrUnsafeFile
}
return true, nil
}
func (directory *windowsPrivateDirectory) ListPage(
maximumEntries int,
afterName string,
validName func(string) bool,
validLinks func(string, uint64) bool,
) (PrivateDirectoryPage, error) {
if directory.Validate() != nil || maximumEntries < 1 || maximumEntries > 4096 || validName == nil || validLinks == nil ||
(afterName != "" && !validName(afterName)) {
return PrivateDirectoryPage{}, ErrUnsafeFile
}
var before windows.ByHandleFileInformation
if err := windows.GetFileInformationByHandle(directory.handle, &before); err != nil ||
!sameWindowsPrivateDirectoryIdentity(directory.info, before) {
return PrivateDirectoryPage{}, ErrUnsafeFile
}
duplicate, err := duplicateWindowsRetainedHandle(directory.handle)
if err != nil {
return PrivateDirectoryPage{}, ErrUnsafeFile
}
file := os.NewFile(uintptr(duplicate), "tht-safeio-private-root-list")
if file == nil {
_ = windows.CloseHandle(duplicate)
return PrivateDirectoryPage{}, ErrUnsafeFile
}
defer file.Close()
seen := make(map[string]struct{}, maximumEntries+1)
selected := make([]PrivateDirectoryEntry, 0, maximumEntries+1)
scanned := 0
for {
entries, readErr := file.ReadDir(1)
if readErr != nil && !errors.Is(readErr, io.EOF) {
return PrivateDirectoryPage{}, ErrUnsafeFile
}
if len(entries) == 0 {
break
}
if len(entries) != 1 {
return PrivateDirectoryPage{}, ErrUnsafeFile
}
scanned++
if scanned > maximumPrivateDirectoryPageScanEntries {
return PrivateDirectoryPage{}, ErrUnsafeFile
}
name := entries[0].Name()
if !validPrivateLeafName(name) || !validName(name) {
return PrivateDirectoryPage{}, ErrUnsafeFile
}
if _, duplicate := seen[name]; duplicate {
return PrivateDirectoryPage{}, ErrUnsafeFile
}
seen[name] = struct{}{}
value, valueErr := openWindowsPrivateRegularAtAllowedLinks(directory.handle, name, windows.FILE_GENERIC_READ, 1, 2)
if valueErr != nil {
return PrivateDirectoryPage{}, ErrUnsafeFile
}
info := value.info
if value.Close() != nil {
return PrivateDirectoryPage{}, ErrUnsafeFile
}
if !validLinks(name, uint64(info.NumberOfLinks)) {
return PrivateDirectoryPage{}, ErrUnsafeFile
}
if name > afterName {
selected = appendBoundedPrivateDirectoryEntry(selected, PrivateDirectoryEntry{
Name: name, ModifiedUnixMs: time.Unix(0, info.LastWriteTime.Nanoseconds()).UnixMilli(),
}, maximumEntries+1)
}
if errors.Is(readErr, io.EOF) {
break
}
}
var after windows.ByHandleFileInformation
if windows.GetFileInformationByHandle(directory.handle, &after) != nil || directory.Validate() != nil ||
!sameWindowsPrivateDirectorySnapshot(before, after) {
return PrivateDirectoryPage{}, ErrUnsafeFile
}
sort.Slice(selected, func(left, right int) bool { return selected[left].Name < selected[right].Name })
more := len(selected) > maximumEntries
if more {
selected = selected[:maximumEntries]
}
return PrivateDirectoryPage{Entries: selected, More: more}, nil
}
func sameWindowsRelativeClaim(source, claim *windowsPrivateRegularAt) bool {
return source != nil && claim != nil && sameWindowsPrivateFile(source.info, claim.info)
}
func finishWindowsClaimCleanup(closeClaim, deleteSource, deleteClaim, validate func() error) error {
failed := false
for _, operation := range []func() error{closeClaim, deleteSource, deleteClaim, validate} {
if operation == nil || operation() != nil {
failed = true
}
}
if failed {
return ErrUnsafeFile
}
return nil
}
func windowsRelativeClaimPairExists(directory *windowsPrivateDirectory, source, claim string) (bool, error) {
left, err := openWindowsPrivateRegularAt(directory.handle, source, windows.FILE_GENERIC_READ, 2)
if isWindowsRelativeNotFound(err) {
return false, nil
}
if err != nil {
return false, ErrUnsafeFile
}
defer left.Close()
right, err := openWindowsPrivateRegularAt(directory.handle, claim, windows.FILE_GENERIC_READ, 2)
if isWindowsRelativeNotFound(err) {
return false, nil
}
if err != nil {
return false, ErrUnsafeFile
}
defer right.Close()
return sameWindowsRelativeClaim(left, right), nil
}
func windowsRelativeClaimAbsentOrOrphan(directory *windowsPrivateDirectory, source, claim string) (bool, error) {
current, err := openWindowsPrivateRegularAtAllowedLinks(directory.handle, source, windows.FILE_GENERIC_READ, 1, 2)
if err == nil {
_ = current.Close()
return false, nil
}
if !isWindowsRelativeNotFound(err) {
return false, ErrUnsafeFile
}
orphan, err := openWindowsPrivateRegularAt(directory.handle, claim, windows.FILE_GENERIC_READ, 1)
if err == nil {
_ = orphan.Close()
return true, nil
}
if isWindowsRelativeNotFound(err) {
return true, nil
}
return false, ErrUnsafeFile
}
const windowsClaimSharingRetries = 100
func openWindowsPrivateClaimSource(directory windows.Handle, source string) (*windowsPrivateRegularAt, error) {
for attempt := 0; ; attempt++ {
value, err := openWindowsPrivateRegularAt(
directory,
source,
windows.FILE_GENERIC_READ|windows.FILE_GENERIC_WRITE|windows.DELETE,
1,
)
if !errors.Is(err, windows.ERROR_SHARING_VIOLATION) &&
!errors.Is(err, windows.STATUS_SHARING_VIOLATION) {
return value, err
}
if attempt == windowsClaimSharingRetries {
return nil, err
}
time.Sleep(time.Millisecond)
}
}
func (directory *windowsPrivateDirectory) ClaimRegular(source, claim string) (bool, error) {
if directory.Validate() != nil || !validPrivateLeafName(source) || !validPrivateLeafName(claim) {
return false, ErrUnsafeFile
}
// A concurrent winner temporarily holds the source with DELETE access and deliberately
// without FILE_SHARE_DELETE. Wait only for that specific, bounded contention before
// observing the resulting pair or absence below. Persistent sharing remains unsafe.
value, err := openWindowsPrivateClaimSource(directory.handle, source)
if err != nil {
pair, pairErr := windowsRelativeClaimPairExists(directory, source, claim)
if pairErr == nil && pair {
return false, nil
}
orphan, orphanErr := windowsRelativeClaimAbsentOrOrphan(directory, source, claim)
if orphanErr == nil && orphan {
return false, nil
}
return false, ErrUnsafeFile
}
defer value.Close()
if linkWindowsPrivateRegularAt(value.handle, directory.handle, claim) != nil {
existing, existingErr := openWindowsPrivateRegularAtAllowedLinks(directory.handle, claim, windows.FILE_GENERIC_READ, 1, 2)
if existingErr == nil {
_ = existing.Close()
return false, nil
}
return false, ErrUnsafeFile
}
after, err := privateWindowsRegularInfo(value.handle, 2)
if err != nil {
return false, ErrUnsafeFile
}
value.info = after
claimed, err := openWindowsPrivateRegularAt(directory.handle, claim, windows.FILE_GENERIC_READ, 2)
if err != nil {
return false, ErrUnsafeFile
}
defer claimed.Close()
if !sameWindowsRelativeClaim(value, claimed) || directory.Validate() != nil {
return false, ErrUnsafeFile
}
return true, nil
}
func (directory *windowsPrivateDirectory) ReadClaim(source, claim string, maximum int64) ([]byte, bool, error) {
if directory.Validate() != nil || !validPrivateLeafName(source) || !validPrivateLeafName(claim) ||
maximum < 0 || maximum == int64(^uint64(0)>>1) {
return nil, false, ErrUnsafeFile
}
value, err := openWindowsPrivateRegularAt(directory.handle, source, windows.FILE_GENERIC_READ, 2)
if err != nil {
orphan, orphanErr := windowsRelativeClaimAbsentOrOrphan(directory, source, claim)
if orphanErr == nil && orphan {
return nil, false, nil
}
return nil, false, ErrUnsafeFile
}
defer value.Close()
claimed, err := openWindowsPrivateRegularAt(directory.handle, claim, windows.FILE_GENERIC_READ, 2)
if isWindowsRelativeNotFound(err) {
return nil, false, nil
}
if err != nil {
return nil, false, ErrUnsafeFile
}
defer claimed.Close()
if !sameWindowsRelativeClaim(value, claimed) {
return nil, false, ErrUnsafeFile
}
contents, err := readWindowsPrivateRegular(value, maximum, 2)
if err != nil {
return nil, false, ErrUnsafeFile
}
afterClaim, err := privateWindowsRegularInfo(claimed.handle, 2)
if err != nil || !sameWindowsPrivateFile(value.info, afterClaim) || directory.Validate() != nil {
return nil, false, ErrUnsafeFile
}
return contents, true, nil
}
func (directory *windowsPrivateDirectory) RemoveClaim(source, claim string) (bool, error) {
if directory.Validate() != nil || !validPrivateLeafName(source) || !validPrivateLeafName(claim) {
return false, ErrUnsafeFile
}
value, err := openWindowsPrivateRegularAt(directory.handle, source, windows.FILE_GENERIC_READ|windows.DELETE, 2)
if err != nil {
orphan, orphanErr := windowsRelativeClaimAbsentOrOrphan(directory, source, claim)
if orphanErr == nil && orphan {
return false, nil
}
return false, ErrUnsafeFile
}
claimed, err := openWindowsPrivateRegularAtWithShareMode(
directory.handle,
claim,
windows.FILE_GENERIC_READ,
windowsRetainedHandleShareMode|windows.FILE_SHARE_DELETE,
2,
)
if isWindowsRelativeNotFound(err) {
closeErr := value.Close()
validateErr := directory.Validate()
if closeErr != nil || validateErr != nil {
return false, ErrUnsafeFile
}
return false, nil
}
if err != nil || !sameWindowsRelativeClaim(value, claimed) {
valueCloseErr := value.Close()
claimedCloseErr := error(nil)
if claimed != nil {
claimedCloseErr = claimed.Close()
}
if valueCloseErr != nil || claimedCloseErr != nil || directory.Validate() != nil {
return false, ErrUnsafeFile
}
return false, ErrUnsafeFile
}
cleanupErr := finishWindowsClaimCleanup(
claimed.Close,
func() error { return closeAndDeleteWindowsPrivateRegular(value) },
func() error {
remaining, remainingErr := openWindowsPrivateRegularAt(directory.handle, claim, windows.FILE_GENERIC_READ|windows.DELETE, 1)
if remainingErr != nil {
return ErrUnsafeFile
}
return closeAndDeleteWindowsPrivateRegular(remaining)
},
directory.Validate,
)
if cleanupErr != nil {
return false, ErrUnsafeFile
}
return true, nil
}