main
Raw Download raw file
  1//go:build !js
  2// +build !js
  3
  4// Package osfs provides a billy filesystem for the OS.
  5package osfs
  6
  7import (
  8	"fmt"
  9	"io/fs"
 10	"os"
 11	"sync"
 12
 13	"github.com/go-git/go-billy/v5"
 14)
 15
 16const (
 17	defaultDirectoryMode = 0o755
 18	defaultCreateMode    = 0o666
 19)
 20
 21// Default Filesystem representing the root of the os filesystem.
 22var Default = &ChrootOS{}
 23
 24// New returns a new OS filesystem.
 25// By default paths are deduplicated, but still enforced
 26// under baseDir. For more info refer to WithDeduplicatePath.
 27func New(baseDir string, opts ...Option) billy.Filesystem {
 28	o := &options{
 29		deduplicatePath: true,
 30	}
 31	for _, opt := range opts {
 32		opt(o)
 33	}
 34
 35	if o.Type == BoundOSFS {
 36		return newBoundOS(baseDir, o.deduplicatePath)
 37	}
 38
 39	return newChrootOS(baseDir)
 40}
 41
 42// WithBoundOS returns the option of using a Bound filesystem OS.
 43func WithBoundOS() Option {
 44	return func(o *options) {
 45		o.Type = BoundOSFS
 46	}
 47}
 48
 49// WithChrootOS returns the option of using a Chroot filesystem OS.
 50func WithChrootOS() Option {
 51	return func(o *options) {
 52		o.Type = ChrootOSFS
 53	}
 54}
 55
 56// WithDeduplicatePath toggles the deduplication of the base dir in the path.
 57// This occurs when absolute links are being used.
 58// Assuming base dir /base/dir and an absolute symlink /base/dir/target:
 59//
 60// With DeduplicatePath (default): /base/dir/target
 61// Without DeduplicatePath: /base/dir/base/dir/target
 62//
 63// This option is only used by the BoundOS OS type.
 64func WithDeduplicatePath(enabled bool) Option {
 65	return func(o *options) {
 66		o.deduplicatePath = enabled
 67	}
 68}
 69
 70type options struct {
 71	Type
 72	deduplicatePath bool
 73}
 74
 75type Type int
 76
 77const (
 78	ChrootOSFS Type = iota
 79	BoundOSFS
 80)
 81
 82func readDir(dir string) ([]os.FileInfo, error) {
 83	entries, err := os.ReadDir(dir)
 84	if err != nil {
 85		return nil, err
 86	}
 87	infos := make([]fs.FileInfo, 0, len(entries))
 88	for _, entry := range entries {
 89		fi, err := entry.Info()
 90		if err != nil {
 91			return nil, err
 92		}
 93		infos = append(infos, fi)
 94	}
 95	return infos, nil
 96}
 97
 98func tempFile(dir, prefix string) (billy.File, error) {
 99	f, err := os.CreateTemp(dir, prefix)
100	if err != nil {
101		return nil, err
102	}
103	return &file{File: f}, nil
104}
105
106func openFile(fn string, flag int, perm os.FileMode, createDir func(string) error) (billy.File, error) {
107	if flag&os.O_CREATE != 0 {
108		if createDir == nil {
109			return nil, fmt.Errorf("createDir func cannot be nil if file needs to be opened in create mode")
110		}
111		if err := createDir(fn); err != nil {
112			return nil, err
113		}
114	}
115
116	f, err := os.OpenFile(fn, flag, perm)
117	if err != nil {
118		return nil, err
119	}
120	return &file{File: f}, err
121}
122
123// file is a wrapper for an os.File which adds support for file locking.
124type file struct {
125	*os.File
126	m sync.Mutex
127}