package fileops import ( "crypto/rand" "encoding/hex" "errors" "fmt" "io" "os" "path/filepath" "strings" ) // openConfinedParent opens dst's parent without following a symbolic-link // component. It starts from the filesystem root, so the resulting handle stays // valid even if a pathname ancestor is later renamed or replaced. func openConfinedParent(dst string, create bool, mode os.FileMode) (*os.Root, string, error) { if strings.TrimSpace(dst) == "" { return nil, "", fmt.Errorf("destination path is required") } abs, err := filepath.Abs(dst) if err != nil { return nil, "", fmt.Errorf("resolve destination path: %w", err) } parts, err := absolutePathParts(abs) if err != nil { return nil, "", err } if len(parts) == 0 { return nil, "", fmt.Errorf("destination file name is required") } rootPath := filesystemRoot(abs) root, err := os.OpenRoot(rootPath) if err != nil { return nil, "", fmt.Errorf("open filesystem root %q: %w", rootPath, err) } for _, part := range parts[:len(parts)-1] { child, err := openConfinedChild(root, part, create, mode) if err != nil { _ = root.Close() return nil, "", err } _ = root.Close() root = child } return root, parts[len(parts)-1], nil } func ensureConfinedDirectory(directory string, mode os.FileMode) error { abs, err := filepath.Abs(directory) if err != nil { return fmt.Errorf("resolve workspace directory: %w", err) } parts, err := absolutePathParts(abs) if err != nil { return err } rootPath := filesystemRoot(abs) root, err := os.OpenRoot(rootPath) if err != nil { return fmt.Errorf("open filesystem root %q: %w", rootPath, err) } defer func() { _ = root.Close() }() for _, part := range parts { child, err := openConfinedChild(root, part, true, mode) if err != nil { return err } _ = root.Close() root = child } return setOpenedDirectoryMode(root, mode) } func absolutePathParts(path string) ([]string, error) { root := filesystemRoot(path) relative, err := filepath.Rel(root, path) if err != nil { return nil, fmt.Errorf("resolve path below filesystem root: %w", err) } if relative == "." { return nil, nil } parts := strings.FieldsFunc(relative, func(r rune) bool { return r == filepath.Separator || r == '/' || r == '\\' }) for _, part := range parts { if part == "" || part == "." || part == ".." { return nil, fmt.Errorf("unsafe filesystem path component %q", part) } } return parts, nil } func filesystemRoot(path string) string { volume := filepath.VolumeName(path) if volume == "" { return string(filepath.Separator) } return volume + string(filepath.Separator) } func openConfinedChild(parent *os.Root, name string, create bool, mode os.FileMode) (*os.Root, error) { info, err := parent.Lstat(name) created := false if errors.Is(err, os.ErrNotExist) { if !create { return nil, fmt.Errorf("destination ancestor %q does not exist", name) } if err := parent.Mkdir(name, mode.Perm()); err != nil && !errors.Is(err, os.ErrExist) { return nil, fmt.Errorf("create destination ancestor %q: %w", name, err) } created = true info, err = parent.Lstat(name) } if err != nil { return nil, fmt.Errorf("inspect destination ancestor %q: %w", name, err) } if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() { return nil, fmt.Errorf("destination ancestor %q is not a regular directory", name) } child, err := parent.OpenRoot(name) if err != nil { return nil, fmt.Errorf("open destination ancestor %q: %w", name, err) } opened, err := child.Stat(".") if err != nil { _ = child.Close() return nil, fmt.Errorf("inspect opened destination ancestor %q: %w", name, err) } current, err := parent.Lstat(name) if err != nil || current.Mode()&os.ModeSymlink != 0 || !current.IsDir() || !os.SameFile(opened, current) { _ = child.Close() if err != nil { return nil, fmt.Errorf("reinspect destination ancestor %q: %w", name, err) } return nil, fmt.Errorf("destination ancestor %q changed while being opened", name) } if created { if err := setOpenedDirectoryMode(child, mode); err != nil { _ = child.Close() return nil, fmt.Errorf("set destination ancestor permissions %q: %w", name, err) } } return child, nil } func setOpenedDirectoryMode(root *os.Root, mode os.FileMode) error { directory, err := root.Open(".") if err != nil { return err } defer func() { _ = directory.Close() }() return directory.Chmod(mode) } func createSiblingTemp(parent *os.Root, base string) (*os.File, string, error) { for attempt := 0; attempt < 100; attempt++ { var randomBytes [16]byte if _, err := rand.Read(randomBytes[:]); err != nil { return nil, "", fmt.Errorf("generate temporary file name: %w", err) } name := "." + base + ".tmp-" + hex.EncodeToString(randomBytes[:]) file, err := parent.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o600) if errors.Is(err, os.ErrExist) { continue } if err != nil { return nil, "", err } return file, name, nil } return nil, "", fmt.Errorf("create unique sibling temporary file") } func syncOpenedDirectory(parent *os.Root) error { directory, err := parent.Open(".") if err != nil { return err } defer func() { _ = directory.Close() }() return syncDirectoryFile(directory, parent.Name()) } // OpenFileConfined opens a file after verifying its parent hierarchy without // following symbolic links. Existing symbolic-link leaves are rejected. func OpenFileConfined(path string, flags int, mode os.FileMode) (*os.File, error) { parent, name, err := openConfinedParent(path, false, 0) if err != nil { return nil, err } defer func() { _ = parent.Close() }() if info, err := parent.Lstat(name); err == nil && info.Mode()&os.ModeSymlink != 0 { return nil, fmt.Errorf("destination file %q is a symbolic link", name) } else if err != nil && !errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("inspect destination file %q: %w", name, err) } file, err := parent.OpenFile(name, flags, mode) if err != nil { return nil, err } opened, err := file.Stat() if err != nil { _ = file.Close() return nil, fmt.Errorf("inspect opened destination file %q: %w", name, err) } current, err := parent.Lstat(name) if err != nil || current.Mode()&os.ModeSymlink != 0 || !os.SameFile(opened, current) { _ = file.Close() if err != nil { return nil, fmt.Errorf("reinspect destination file %q: %w", name, err) } return nil, fmt.Errorf("destination file %q changed while being opened", name) } return file, nil } // DownloadedTempFile is a completed, destination-confined temporary file. It // keeps its parent directory open until the caller installs or cleans it up. type DownloadedTempFile struct { parent *os.Root name string } // DownloadToSiblingTemp writes a remote object into a sibling temporary file. // The caller supplies transport behavior through download and retains object // identity and validation policy. The destination parent must already exist. func DownloadToSiblingTemp(destination string, download func(io.Writer) error) (*DownloadedTempFile, error) { if download == nil { return nil, fmt.Errorf("download function is required") } parent, name, err := openConfinedParent(destination, false, 0) if err != nil { return nil, err } file, tempName, err := createSiblingTemp(parent, name) if err != nil { _ = parent.Close() return nil, fmt.Errorf("create sibling temporary file: %w", err) } if err := download(file); err != nil { _ = file.Close() _ = parent.Remove(tempName) _ = parent.Close() return nil, err } if err := file.Sync(); err != nil { _ = file.Close() _ = parent.Remove(tempName) _ = parent.Close() return nil, fmt.Errorf("sync downloaded temporary file: %w", err) } if err := file.Close(); err != nil { _ = parent.Remove(tempName) _ = parent.Close() return nil, fmt.Errorf("close downloaded temporary file: %w", err) } return &DownloadedTempFile{parent: parent, name: tempName}, nil } // Open returns a read handle for validation before installation. func (f *DownloadedTempFile) Open() (*os.File, error) { if f == nil || f.parent == nil || f.name == "" { return nil, fmt.Errorf("downloaded temporary file is unavailable") } return f.parent.Open(f.name) } // Install atomically replaces destinationName in the opened parent directory. func (f *DownloadedTempFile) Install(destinationName string, mode os.FileMode) error { if f == nil || f.parent == nil || f.name == "" { return fmt.Errorf("downloaded temporary file is unavailable") } if filepath.Base(destinationName) != destinationName || destinationName == "." || destinationName == "" { return fmt.Errorf("destination file name is required") } file, err := f.parent.Open(f.name) if err != nil { return fmt.Errorf("open downloaded temporary file: %w", err) } if err := file.Chmod(mode); err != nil { _ = file.Close() return fmt.Errorf("set downloaded temporary file permissions: %w", err) } if err := file.Sync(); err != nil { _ = file.Close() return fmt.Errorf("sync downloaded temporary file: %w", err) } if err := file.Close(); err != nil { return fmt.Errorf("close downloaded temporary file: %w", err) } if err := f.parent.Rename(f.name, destinationName); err != nil { return fmt.Errorf("install downloaded temporary file: %w", err) } f.name = "" if err := syncOpenedDirectory(f.parent); err != nil { return fmt.Errorf("sync destination directory after downloaded-file replacement: %w", err) } return f.closeParent() } // Cleanup removes an uninstalled temporary file. It is safe to call repeatedly. func (f *DownloadedTempFile) Cleanup() error { if f == nil || f.parent == nil { return nil } var err error if f.name != "" { err = f.parent.Remove(f.name) if errors.Is(err, os.ErrNotExist) { err = nil } f.name = "" } closeErr := f.closeParent() if err != nil && closeErr != nil { return errors.Join(err, closeErr) } if err != nil { return err } return closeErr } func (f *DownloadedTempFile) closeParent() error { if f.parent == nil { return nil } err := f.parent.Close() f.parent = nil return err } // DownloadAndInstall obtains remote bytes through a confined sibling temporary // file and installs them with the caller-selected final mode. func DownloadAndInstall(destination string, mode os.FileMode, download func(io.Writer) error) error { temporary, err := DownloadToSiblingTemp(destination, download) if err != nil { return err } defer func() { _ = temporary.Cleanup() }() return temporary.Install(filepath.Base(destination), mode) } func replaceFileFromReaderConfined(dst string, source io.Reader, options ReplaceFileOptions) (resultErr error) { parent, name, err := openConfinedParent(dst, false, 0) if err != nil { return err } defer func() { _ = parent.Close() }() temporary, tempName, err := createSiblingTemp(parent, name) if err != nil { return fmt.Errorf("create temp file: %w", err) } removeTemp := true defer func() { if removeTemp { if err := parent.Remove(tempName); err != nil && !errors.Is(err, os.ErrNotExist) { resultErr = errors.Join(resultErr, fmt.Errorf("remove temporary file: %w", err)) } } }() if _, err := io.Copy(temporary, source); err != nil { return closeTemporaryFileAfterError(temporary, fmt.Errorf("write temp file: %w", err)) } if err := temporary.Chmod(options.Mode); err != nil { return closeTemporaryFileAfterError(temporary, fmt.Errorf("set temp file permissions: %w", err)) } if err := temporary.Sync(); err != nil { return closeTemporaryFileAfterError(temporary, fmt.Errorf("sync temp file: %w", err)) } if err := temporary.Close(); err != nil { return fmt.Errorf("close temp file: %w", err) } if options.BeforeRename != nil { if err := options.BeforeRename(); err != nil { return fmt.Errorf("check before file replacement: %w", err) } } if err := parent.Rename(tempName, name); err != nil { return fmt.Errorf("install temp file: %w", err) } removeTemp = false if err := syncOpenedDirectory(parent); err != nil { return fmt.Errorf("sync destination directory after replacement: %w", err) } return nil } func installDownloadedTempFileConfined(tmpPath, dst string, mode os.FileMode) error { if filepath.Clean(filepath.Dir(tmpPath)) != filepath.Clean(filepath.Dir(dst)) { source, err := os.Open(tmpPath) if err != nil { return fmt.Errorf("open downloaded temporary file: %w", err) } err = replaceFileFromReaderConfined(dst, source, ReplaceFileOptions{Mode: mode}) closeErr := source.Close() if err != nil { return fmt.Errorf("install downloaded file: %w", err) } if closeErr != nil { return fmt.Errorf("close downloaded temporary file: %w", closeErr) } if err := os.Remove(tmpPath); err != nil { return fmt.Errorf("remove installed temporary file: %w", err) } return nil } parent, destinationName, err := openConfinedParent(dst, false, 0) if err != nil { return err } defer func() { _ = parent.Close() }() temporaryName := filepath.Base(tmpPath) info, err := parent.Lstat(temporaryName) if err != nil { return fmt.Errorf("inspect downloaded temporary file: %w", err) } if info.Mode()&os.ModeSymlink != 0 || !info.Mode().IsRegular() { return fmt.Errorf("downloaded temporary file is not a regular file") } file, err := parent.Open(temporaryName) if err != nil { return fmt.Errorf("open downloaded temporary file: %w", err) } if err := file.Chmod(mode); err != nil { _ = file.Close() return fmt.Errorf("set temp file permissions: %w", err) } if err := file.Sync(); err != nil { _ = file.Close() return fmt.Errorf("sync downloaded temp file: %w", err) } if err := file.Close(); err != nil { return fmt.Errorf("close downloaded temp file: %w", err) } if err := parent.Rename(temporaryName, destinationName); err != nil { return fmt.Errorf("install downloaded file: %w", err) } if err := syncOpenedDirectory(parent); err != nil { return fmt.Errorf("sync destination directory after downloaded-file replacement: %w", err) } return nil }