2024-10-16 10:54:40 +00:00
|
|
|
//go:build !js
|
2022-07-07 20:11:50 +00:00
|
|
|
// +build !js
|
|
|
|
|
|
|
|
// Package osfs provides a billy filesystem for the OS.
|
2024-10-16 10:54:40 +00:00
|
|
|
package osfs
|
2022-07-07 20:11:50 +00:00
|
|
|
|
|
|
|
import (
|
2024-10-16 10:54:40 +00:00
|
|
|
"fmt"
|
|
|
|
"io/fs"
|
2022-07-07 20:11:50 +00:00
|
|
|
"os"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/go-git/go-billy/v5"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2024-10-16 10:54:40 +00:00
|
|
|
defaultDirectoryMode = 0o755
|
|
|
|
defaultCreateMode = 0o666
|
2022-07-07 20:11:50 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Default Filesystem representing the root of the os filesystem.
|
2024-10-16 10:54:40 +00:00
|
|
|
var Default = &ChrootOS{}
|
2022-07-07 20:11:50 +00:00
|
|
|
|
|
|
|
// New returns a new OS filesystem.
|
2024-10-16 10:54:40 +00:00
|
|
|
// By default paths are deduplicated, but still enforced
|
|
|
|
// under baseDir. For more info refer to WithDeduplicatePath.
|
|
|
|
func New(baseDir string, opts ...Option) billy.Filesystem {
|
|
|
|
o := &options{
|
|
|
|
deduplicatePath: true,
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
2024-10-16 10:54:40 +00:00
|
|
|
for _, opt := range opts {
|
|
|
|
opt(o)
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
|
2024-10-16 10:54:40 +00:00
|
|
|
if o.Type == BoundOSFS {
|
|
|
|
return newBoundOS(baseDir, o.deduplicatePath)
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
|
2024-10-16 10:54:40 +00:00
|
|
|
return newChrootOS(baseDir)
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
|
2024-10-16 10:54:40 +00:00
|
|
|
// WithBoundOS returns the option of using a Bound filesystem OS.
|
|
|
|
func WithBoundOS() Option {
|
|
|
|
return func(o *options) {
|
|
|
|
o.Type = BoundOSFS
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-16 10:54:40 +00:00
|
|
|
// WithChrootOS returns the option of using a Chroot filesystem OS.
|
|
|
|
func WithChrootOS() Option {
|
|
|
|
return func(o *options) {
|
|
|
|
o.Type = ChrootOSFS
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-16 10:54:40 +00:00
|
|
|
// WithDeduplicatePath toggles the deduplication of the base dir in the path.
|
|
|
|
// This occurs when absolute links are being used.
|
|
|
|
// Assuming base dir /base/dir and an absolute symlink /base/dir/target:
|
|
|
|
//
|
|
|
|
// With DeduplicatePath (default): /base/dir/target
|
|
|
|
// Without DeduplicatePath: /base/dir/base/dir/target
|
|
|
|
//
|
|
|
|
// This option is only used by the BoundOS OS type.
|
|
|
|
func WithDeduplicatePath(enabled bool) Option {
|
|
|
|
return func(o *options) {
|
|
|
|
o.deduplicatePath = enabled
|
|
|
|
}
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
|
2024-10-16 10:54:40 +00:00
|
|
|
type options struct {
|
|
|
|
Type
|
|
|
|
deduplicatePath bool
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
|
2024-10-16 10:54:40 +00:00
|
|
|
type Type int
|
2022-07-07 20:11:50 +00:00
|
|
|
|
2024-10-16 10:54:40 +00:00
|
|
|
const (
|
|
|
|
ChrootOSFS Type = iota
|
|
|
|
BoundOSFS
|
|
|
|
)
|
2022-07-07 20:11:50 +00:00
|
|
|
|
2024-10-16 10:54:40 +00:00
|
|
|
func readDir(dir string) ([]os.FileInfo, error) {
|
|
|
|
entries, err := os.ReadDir(dir)
|
|
|
|
if err != nil {
|
2022-07-07 20:11:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2024-10-16 10:54:40 +00:00
|
|
|
infos := make([]fs.FileInfo, 0, len(entries))
|
|
|
|
for _, entry := range entries {
|
|
|
|
fi, err := entry.Info()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
infos = append(infos, fi)
|
|
|
|
}
|
|
|
|
return infos, nil
|
|
|
|
}
|
2022-07-07 20:11:50 +00:00
|
|
|
|
2024-10-16 10:54:40 +00:00
|
|
|
func tempFile(dir, prefix string) (billy.File, error) {
|
|
|
|
f, err := os.CreateTemp(dir, prefix)
|
2022-07-07 20:11:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &file{File: f}, nil
|
|
|
|
}
|
|
|
|
|
2024-10-16 10:54:40 +00:00
|
|
|
func openFile(fn string, flag int, perm os.FileMode, createDir func(string) error) (billy.File, error) {
|
|
|
|
if flag&os.O_CREATE != 0 {
|
|
|
|
if createDir == nil {
|
|
|
|
return nil, fmt.Errorf("createDir func cannot be nil if file needs to be opened in create mode")
|
|
|
|
}
|
|
|
|
if err := createDir(fn); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
|
2024-10-16 10:54:40 +00:00
|
|
|
f, err := os.OpenFile(fn, flag, perm)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &file{File: f}, err
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// file is a wrapper for an os.File which adds support for file locking.
|
|
|
|
type file struct {
|
|
|
|
*os.File
|
|
|
|
m sync.Mutex
|
|
|
|
}
|