main
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}