Skip to content

Commit 7b0fe8b

Browse files
committed
Add support for Chmod on billy.Filesystem
The billy package contains a `Change` interface type, which seems to have gone unused for several years now, presumably due to the difficulty of implementing most of the required methods for all supported platforms. This commit reduces the `Change` interface to a `Chmod` interface, which is supported on all platforms. The interface is added to the main `Filesystem` interface and implemented in all applicable abstractions. Supporting `chmod` in billy would help with issues such as go-git/go-git#588 (which was closed as stale, but is unfortunately still a real issue for some folks). Signed-off-by: Conrad Hoffmann <ch@bitfehler.net>
1 parent 4289a4e commit 7b0fe8b

File tree

8 files changed

+57
-16
lines changed

8 files changed

+57
-16
lines changed

embedfs/embed.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ func (fs *Embed) ReadDir(path string) ([]fs.DirEntry, error) {
9292
return fs.underlying.ReadDir(path)
9393
}
9494

95+
// Chmod is not supported.
96+
//
97+
// Calls will always return billy.ErrNotSupported.
98+
func (fs *Embed) Chmod(path string, mode fs.FileMode) error {
99+
return billy.ErrNotSupported
100+
}
101+
95102
// Chroot is not supported.
96103
//
97104
// Calls will always return billy.ErrNotSupported.

fs.go

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"errors"
55
"io"
66
"io/fs"
7-
"time"
87
)
98

109
var (
@@ -58,6 +57,7 @@ type Filesystem interface {
5857
TempFile
5958
Dir
6059
Symlink
60+
Chmod
6161
Chroot
6262
}
6363

@@ -132,24 +132,12 @@ type Symlink interface {
132132
Readlink(link string) (string, error)
133133
}
134134

135-
// Change abstract the FileInfo change related operations in a storage-agnostic
135+
// Chmod abstract the FileInfo change related operations in a storage-agnostic
136136
// interface as an extension to the Basic interface
137-
type Change interface {
137+
type Chmod interface {
138138
// Chmod changes the mode of the named file to mode. If the file is a
139139
// symbolic link, it changes the mode of the link's target.
140140
Chmod(name string, mode fs.FileMode) error
141-
// Lchown changes the numeric uid and gid of the named file. If the file is
142-
// a symbolic link, it changes the uid and gid of the link itself.
143-
Lchown(name string, uid, gid int) error
144-
// Chown changes the numeric uid and gid of the named file. If the file is a
145-
// symbolic link, it changes the uid and gid of the link's target.
146-
Chown(name string, uid, gid int) error
147-
// Chtimes changes the access and modification times of the named file,
148-
// similar to the Unix utime() or utimes() functions.
149-
//
150-
// The underlying filesystem may truncate or round the values to a less
151-
// precise time unit.
152-
Chtimes(name string, atime time.Time, mtime time.Time) error
153141
}
154142

155143
// Chroot abstract the chroot related operations in a storage-agnostic interface

helper/chroot/chroot.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,15 @@ func (fs *ChrootHelper) Readlink(link string) (string, error) {
201201
return string(os.PathSeparator) + target, nil
202202
}
203203

204+
func (fs *ChrootHelper) Chmod(path string, mode fs.FileMode) error {
205+
fullpath, err := fs.underlyingPath(path)
206+
if err != nil {
207+
return err
208+
}
209+
210+
return fs.underlying.(billy.Chmod).Chmod(fullpath, mode)
211+
}
212+
204213
func (fs *ChrootHelper) Chroot(path string) (billy.Filesystem, error) {
205214
fullpath, err := fs.underlyingPath(path)
206215
if err != nil {

helper/polyfill/polyfill.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type Polyfill struct {
1414
c capabilities
1515
}
1616

17-
type capabilities struct{ tempfile, dir, symlink, chroot bool }
17+
type capabilities struct{ tempfile, dir, symlink, chmod, chroot bool }
1818

1919
// New creates a new filesystem wrapping up 'fs' the intercepts all the calls
2020
// made and errors if fs doesn't implement any of the billy interfaces.
@@ -28,6 +28,7 @@ func New(fs billy.Basic) billy.Filesystem {
2828
_, h.c.tempfile = h.Basic.(billy.TempFile)
2929
_, h.c.dir = h.Basic.(billy.Dir)
3030
_, h.c.symlink = h.Basic.(billy.Symlink)
31+
_, h.c.chmod = h.Basic.(billy.Chmod)
3132
_, h.c.chroot = h.Basic.(billy.Chroot)
3233
return h
3334
}
@@ -80,6 +81,14 @@ func (h *Polyfill) Lstat(path string) (os.FileInfo, error) {
8081
return h.Basic.(billy.Symlink).Lstat(path)
8182
}
8283

84+
func (h *Polyfill) Chmod(path string, mode fs.FileMode) error {
85+
if !h.c.chmod {
86+
return billy.ErrNotSupported
87+
}
88+
89+
return h.Basic.(billy.Chmod).Chmod(path, mode)
90+
}
91+
8392
func (h *Polyfill) Chroot(path string) (billy.Filesystem, error) {
8493
if !h.c.chroot {
8594
return nil, billy.ErrNotSupported

memfs/memory.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ func (fs *Memory) Remove(filename string) error {
177177
return fs.s.Remove(filename)
178178
}
179179

180+
func (fs *Memory) Chmod(path string, mode gofs.FileMode) error {
181+
return fs.s.Chmod(path, mode)
182+
}
183+
180184
// Falls back to Go's filepath.Join, which works differently depending on the
181185
// OS where the code is being executed.
182186
func (fs *Memory) Join(elem ...string) string {

memfs/storage.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,18 @@ func (s *storage) Remove(path string) error {
222222
return nil
223223
}
224224

225+
func (s *storage) Chmod(path string, mode fs.FileMode) error {
226+
path = clean(path)
227+
228+
f, has := s.Get(path)
229+
if !has {
230+
return os.ErrNotExist
231+
}
232+
233+
f.mode = mode
234+
return nil
235+
}
236+
225237
func clean(path string) string {
226238
return filepath.Clean(filepath.FromSlash(path))
227239
}

osfs/os_bound.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,14 @@ func (fs *BoundOS) Readlink(link string) (string, error) {
226226
return os.Readlink(link)
227227
}
228228

229+
func (fs *BoundOS) Chmod(path string, mode fs.FileMode) error {
230+
abspath, err := fs.abs(path)
231+
if err != nil {
232+
return err
233+
}
234+
return os.Chmod(abspath, mode)
235+
}
236+
229237
// Chroot returns a new BoundOS filesystem, with the base dir set to the
230238
// result of joining the provided path with the underlying base dir.
231239
func (fs *BoundOS) Chroot(path string) (billy.Filesystem, error) {

osfs/os_chroot.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ func (fs *ChrootOS) Remove(filename string) error {
8080
return os.Remove(filename)
8181
}
8282

83+
func (fs *ChrootOS) Chmod(path string, mode fs.FileMode) error {
84+
return os.Chmod(path, mode)
85+
}
86+
8387
func (fs *ChrootOS) TempFile(dir, prefix string) (billy.File, error) {
8488
if err := fs.createDir(dir + string(os.PathSeparator)); err != nil {
8589
return nil, err

0 commit comments

Comments
 (0)