package safeio import ( "strings" "sync" ) // PrivateDirectoryHandle pins one private directory for the duration of a storage operation. // Implementations use descriptor-relative operations on POSIX and retained no-delete handles on // Windows. Callers must close every returned child before releasing its parent. type PrivateDirectoryHandle interface { Close() error Validate() error OpenChild(name string, ensure bool) (PrivateDirectoryHandle, bool, error) CreateRegular(name string, contents []byte) (bool, error) ReadRegular(name string, maximum int64) ([]byte, bool, error) ReplaceRegular(name string, contents []byte) error RemoveRegular(name string) (bool, error) ListPage(maximumEntries int, afterName string, validName func(string) bool, validLinks func(string, uint64) bool) (PrivateDirectoryPage, error) ClaimRegular(source, claim string) (bool, error) ReadClaim(source, claim string, maximum int64) ([]byte, bool, error) RemoveClaim(source, claim string) (bool, error) } // OpenPrivateDirectory validates or creates the final private directory while retaining the // opened canonical directory handle. With ensure=false, found=false means the final component is // absent but its already-opened parent proves the same operation could safely create it. func OpenPrivateDirectory(path string, ensure bool) (PrivateDirectoryHandle, bool, error) { if err := ValidateCanonicalPath(path); err != nil { return nil, false, ErrUnsafeFile } return openPrivateDirectory(path, ensure) } func validPrivateLeafName(name string) bool { return name != "" && name != "." && name != ".." && !strings.ContainsAny(name, "\\/:\x00") } var privateDirectoryTestHook struct { sync.RWMutex hook func(string) } // SetPrivateDirectoryTestHookForTest installs a deterministic race hook for internal package // tests. It has no production effect unless a test explicitly installs one. func SetPrivateDirectoryTestHookForTest(hook func(string)) func() { privateDirectoryTestHook.Lock() previous := privateDirectoryTestHook.hook privateDirectoryTestHook.hook = hook privateDirectoryTestHook.Unlock() return func() { privateDirectoryTestHook.Lock() privateDirectoryTestHook.hook = previous privateDirectoryTestHook.Unlock() } } // NotifyPrivateDirectoryTestHookForTest marks an internal retained-handle boundary. It is called // only by storage code and lets tests install deterministic directory replacement races. func NotifyPrivateDirectoryTestHookForTest(stage string) { privateDirectoryTestHook.RLock() hook := privateDirectoryTestHook.hook privateDirectoryTestHook.RUnlock() if hook != nil { hook(stage) } }