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.
27//
28// New returns ChrootOS by default for v5 compatibility. Users should prefer
29// New with WithBoundOS.
30func New(baseDir string, opts ...Option) billy.Filesystem {
31 o := &options{
32 deduplicatePath: true,
33 }
34 for _, opt := range opts {
35 opt(o)
36 }
37
38 if o.Type == BoundOSFS {
39 return newBoundOS(baseDir, o.deduplicatePath)
40 }
41
42 return newChrootOS(baseDir)
43}
44
45// WithBoundOS returns the option of using a Bound filesystem OS.
46func WithBoundOS() Option {
47 return func(o *options) {
48 o.Type = BoundOSFS
49 }
50}
51
52// WithChrootOS returns the option of using a Chroot filesystem OS.
53//
54// Deprecated: use WithBoundOS instead.
55func WithChrootOS() Option {
56 return func(o *options) {
57 o.Type = ChrootOSFS
58 }
59}
60
61// WithDeduplicatePath toggles the deduplication of the base dir in the path.
62// This occurs when absolute links are being used.
63// Assuming base dir /base/dir and an absolute symlink /base/dir/target:
64//
65// With DeduplicatePath (default): /base/dir/target
66// Without DeduplicatePath: /base/dir/base/dir/target
67//
68// This option is only used by the BoundOS OS type.
69func WithDeduplicatePath(enabled bool) Option {
70 return func(o *options) {
71 o.deduplicatePath = enabled
72 }
73}
74
75type options struct {
76 Type
77 deduplicatePath bool
78}
79
80type Type int
81
82const (
83 ChrootOSFS Type = iota
84 BoundOSFS
85)
86
87func readDir(dir string) ([]os.FileInfo, error) {
88 entries, err := os.ReadDir(dir)
89 if err != nil {
90 return nil, err
91 }
92 infos := make([]fs.FileInfo, 0, len(entries))
93 for _, entry := range entries {
94 fi, err := entry.Info()
95 if err != nil {
96 return nil, err
97 }
98 infos = append(infos, fi)
99 }
100 return infos, nil
101}
102
103func tempFile(dir, prefix string) (billy.File, error) {
104 f, err := os.CreateTemp(dir, prefix)
105 if err != nil {
106 return nil, err
107 }
108 return &file{File: f}, nil
109}
110
111func openFile(fn string, flag int, perm os.FileMode, createDir func(string) error) (billy.File, error) {
112 if flag&os.O_CREATE != 0 {
113 if createDir == nil {
114 return nil, fmt.Errorf("createDir func cannot be nil if file needs to be opened in create mode")
115 }
116 if err := createDir(fn); err != nil {
117 return nil, err
118 }
119 }
120
121 f, err := os.OpenFile(fn, flag, perm)
122 if err != nil {
123 return nil, err
124 }
125 return &file{File: f}, err
126}
127
128// file is a wrapper for an os.File which adds support for file locking.
129type file struct {
130 *os.File
131 m sync.Mutex
132}