// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux package xfs import ( "context" "fmt" "io/fs" "os" "strings" "golang.org/x/sys/unix" ) // UnixRoot represents a filesystem wrapper for Unix-like systems. type UnixRoot struct { Shadow string FS FS mntfd int } // Interface guard. var _ interface { Root } = (*UnixRoot)(nil) // OpenFS opens the underlying filesystem. func (root *UnixRoot) OpenFS() error { var err error if root.mntfd != 0 { return nil } root.mntfd, err = root.FS.Open() if err != nil { return fmt.Errorf("failed to create root filesystem: %w", err) } return nil } // Close closes the underlying filesystem. func (root *UnixRoot) Close() error { if root.mntfd == 0 { return nil } root.mntfd = 0 return root.FS.Close() } // RepairFS repairs the underlying filesystem if necessary. func (root *UnixRoot) RepairFS(ctx context.Context) error { return root.FS.Repair(ctx) } // Fd returns the file descriptor of the mounted root filesystem. // It returns an error if the filesystem is not open or has been closed. // Usage of the returned file descriptior is no longer thread safe. func (root *UnixRoot) Fd() (int, error) { if root.mntfd == 0 { return 0, os.ErrClosed } return root.mntfd, nil } // Mkdir creates a new directory in the root filesystem with the specified name and permissions. func (root *UnixRoot) Mkdir(name string, perm os.FileMode) error { return unix.Mkdirat(root.mntfd, strings.TrimLeft(name, "/"), uint32(perm)) } // Open opens a file in the root filesystem with the specified name in read-only mode. func (root *UnixRoot) Open(name string) (fs.File, error) { return root.OpenFile(strings.TrimLeft(name, "/"), unix.O_RDONLY, 0) } // OpenFile opens a file in the root filesystem with the specified name, flags, and permissions. func (root *UnixRoot) OpenFile(name string, flags int, perm os.FileMode) (File, error) { fd, err := unix.Openat(root.mntfd, strings.TrimLeft(name, "/"), flags, uint32(perm)) if err != nil { return nil, err } return os.NewFile(uintptr(fd), strings.TrimLeft(name, "/")), nil } // Remove removes a file or directory from the root filesystem. func (root *UnixRoot) Remove(name string) error { flags := 0 info, err := root.stat(strings.TrimLeft(name, "/")) if err != nil { return err } if info.IsDir() { flags = unix.AT_REMOVEDIR } return unix.Unlinkat(root.mntfd, strings.TrimLeft(name, "/"), flags) } // Rename renames a file or directory in the root filesystem from old to new. func (root *UnixRoot) Rename(oldname, newname string) error { return unix.Renameat(root.mntfd, strings.TrimLeft(oldname, "/"), root.mntfd, strings.TrimLeft(newname, "/")) } func (root *UnixRoot) stat(name string) (os.FileInfo, error) { f, err := root.Open(strings.TrimLeft(name, "/")) if err != nil { return nil, err } defer f.Close() //nolint:errcheck return f.Stat() } // Source returns the source of the underlying filesystem. func (root *UnixRoot) Source() string { return root.FS.Source() } // FSType returns the type of the underlying filesystem. func (root *UnixRoot) FSType() string { return root.FS.FSType() }