diff --git a/.travis.yml b/.travis.yml index 45bc131..8156c4e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ arch: go: - "1.13" - "1.14" + - "1.15" - tip os: diff --git a/README.md b/README.md index c3e807a..1400bc4 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ AppFs.Open('/tmp/foo') File System Methods Available: ```go Chmod(name string, mode os.FileMode) : error +Chown(name string, uid, gid int) : error Chtimes(name string, atime time.Time, mtime time.Time) : error Create(name string) : File, error Mkdir(name string, perm os.FileMode) : error diff --git a/afero.go b/afero.go index f5b5e12..469ff7d 100644 --- a/afero.go +++ b/afero.go @@ -91,9 +91,12 @@ type Fs interface { // The name of this FileSystem Name() string - //Chmod changes the mode of the named file to mode. + // Chmod changes the mode of the named file to mode. Chmod(name string, mode os.FileMode) error + // Chown changes the uid and gid of the named file. + Chown(name string, uid, gid int) error + //Chtimes changes the access and modification times of the named file Chtimes(name string, atime time.Time, mtime time.Time) error } diff --git a/basepath.go b/basepath.go index 3a14b83..4f98328 100644 --- a/basepath.go +++ b/basepath.go @@ -83,6 +83,13 @@ func (b *BasePathFs) Chmod(name string, mode os.FileMode) (err error) { return b.source.Chmod(name, mode) } +func (b *BasePathFs) Chown(name string, uid, gid int) (err error) { + if name, err = b.RealPath(name); err != nil { + return &os.PathError{Op: "chown", Path: name, Err: err} + } + return b.source.Chown(name, uid, gid) +} + func (b *BasePathFs) Name() string { return "BasePathFs" } @@ -202,5 +209,3 @@ func (b *BasePathFs) ReadlinkIfPossible(name string) (string, error) { } return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink} } - -// vim: ts=4 sw=4 noexpandtab nolist syn=go diff --git a/cacheOnReadFs.go b/cacheOnReadFs.go index 29a26c6..71471aa 100644 --- a/cacheOnReadFs.go +++ b/cacheOnReadFs.go @@ -117,6 +117,27 @@ func (u *CacheOnReadFs) Chmod(name string, mode os.FileMode) error { return u.layer.Chmod(name, mode) } +func (u *CacheOnReadFs) Chown(name string, uid, gid int) error { + st, _, err := u.cacheStatus(name) + if err != nil { + return err + } + switch st { + case cacheLocal: + case cacheHit: + err = u.base.Chown(name, uid, gid) + case cacheStale, cacheMiss: + if err := u.copyToLayer(name); err != nil { + return err + } + err = u.base.Chown(name, uid, gid) + } + if err != nil { + return err + } + return u.layer.Chown(name, uid, gid) +} + func (u *CacheOnReadFs) Stat(name string) (os.FileInfo, error) { st, fi, err := u.cacheStatus(name) if err != nil { diff --git a/copyOnWriteFs.go b/copyOnWriteFs.go index 96b7701..6ff8f30 100644 --- a/copyOnWriteFs.go +++ b/copyOnWriteFs.go @@ -14,7 +14,7 @@ var _ Lstater = (*CopyOnWriteFs)(nil) // a possibly writeable layer on top. Changes to the file system will only // be made in the overlay: Changing an existing file in the base layer which // is not present in the overlay will copy the file to the overlay ("changing" -// includes also calls to e.g. Chtimes() and Chmod()). +// includes also calls to e.g. Chtimes(), Chmod() and Chown()). // // Reading directories is currently only supported via Open(), not OpenFile(). type CopyOnWriteFs struct { @@ -75,6 +75,19 @@ func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error { return u.layer.Chmod(name, mode) } +func (u *CopyOnWriteFs) Chown(name string, uid, gid int) error { + b, err := u.isBaseFile(name) + if err != nil { + return err + } + if b { + if err := u.copyToLayer(name); err != nil { + return err + } + } + return u.layer.Chown(name, uid, gid) +} + func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) { fi, err := u.layer.Stat(name) if err != nil { diff --git a/httpFs.go b/httpFs.go index c421936..2b86e30 100644 --- a/httpFs.go +++ b/httpFs.go @@ -67,6 +67,10 @@ func (h HttpFs) Chmod(name string, mode os.FileMode) error { return h.source.Chmod(name, mode) } +func (h HttpFs) Chown(name string, uid, gid int) error { + return h.source.Chown(name, uid, gid) +} + func (h HttpFs) Chtimes(name string, atime time.Time, mtime time.Time) error { return h.source.Chtimes(name, atime, mtime) } diff --git a/mem/file.go b/mem/file.go index 122d77c..7c08522 100644 --- a/mem/file.go +++ b/mem/file.go @@ -57,6 +57,8 @@ type FileData struct { dir bool mode os.FileMode modtime time.Time + uid int + gid int } func (d *FileData) Name() string { @@ -95,6 +97,18 @@ func setModTime(f *FileData, mtime time.Time) { f.modtime = mtime } +func SetUID(f *FileData, uid int) { + f.Lock() + f.uid = uid + f.Unlock() +} + +func SetGID(f *FileData, gid int) { + f.Lock() + f.uid = gid + f.Unlock() +} + func GetFileInfo(f *FileData) *FileInfo { return &FileInfo{f} } diff --git a/memmap.go b/memmap.go index 0fa9592..5c265f9 100644 --- a/memmap.go +++ b/memmap.go @@ -363,6 +363,22 @@ func (m *MemMapFs) setFileMode(name string, mode os.FileMode) error { return nil } +func (m *MemMapFs) Chown(name string, uid, gid int) error { + name = normalizePath(name) + + m.mu.RLock() + f, ok := m.getData()[name] + m.mu.RUnlock() + if !ok { + return &os.PathError{Op: "chown", Path: name, Err: ErrFileNotFound} + } + + mem.SetUID(f, uid) + mem.SetGID(f, gid) + + return nil +} + func (m *MemMapFs) Chtimes(name string, atime time.Time, mtime time.Time) error { name = normalizePath(name) @@ -386,9 +402,3 @@ func (m *MemMapFs) List() { fmt.Println(x.Name(), y.Size()) } } - -// func debugMemMapList(fs Fs) { -// if x, ok := fs.(*MemMapFs); ok { -// x.List() -// } -// } diff --git a/memmap_test.go b/memmap_test.go index 476909d..627b106 100644 --- a/memmap_test.go +++ b/memmap_test.go @@ -38,6 +38,8 @@ func TestPathErrors(t *testing.T) { path2 := filepath.Join(".", "different", "path") fs := NewMemMapFs() perm := os.FileMode(0755) + uid := 1000 + gid := 1000 // relevant functions: // func (m *MemMapFs) Chmod(name string, mode os.FileMode) error @@ -54,6 +56,9 @@ func TestPathErrors(t *testing.T) { err := fs.Chmod(path, perm) checkPathError(t, err, "Chmod") + err = fs.Chown(path, uid, gid) + checkPathError(t, err, "Chown") + err = fs.Chtimes(path, time.Now(), time.Now()) checkPathError(t, err, "Chtimes") diff --git a/os.go b/os.go index 4761db5..f136632 100644 --- a/os.go +++ b/os.go @@ -91,6 +91,10 @@ func (OsFs) Chmod(name string, mode os.FileMode) error { return os.Chmod(name, mode) } +func (OsFs) Chown(name string, uid, gid int) error { + return os.Chown(name, uid, gid) +} + func (OsFs) Chtimes(name string, atime time.Time, mtime time.Time) error { return os.Chtimes(name, atime, mtime) } diff --git a/readonlyfs.go b/readonlyfs.go index f94b181..bd8f926 100644 --- a/readonlyfs.go +++ b/readonlyfs.go @@ -28,6 +28,10 @@ func (r *ReadOnlyFs) Chmod(n string, m os.FileMode) error { return syscall.EPERM } +func (r *ReadOnlyFs) Chown(n string, uid, gid int) error { + return syscall.EPERM +} + func (r *ReadOnlyFs) Name() string { return "ReadOnlyFilter" } diff --git a/regexpfs.go b/regexpfs.go index c8fc008..ac359c6 100644 --- a/regexpfs.go +++ b/regexpfs.go @@ -60,6 +60,13 @@ func (r *RegexpFs) Chmod(name string, mode os.FileMode) error { return r.source.Chmod(name, mode) } +func (r *RegexpFs) Chown(name string, uid, gid int) error { + if err := r.dirOrMatches(name); err != nil { + return err + } + return r.source.Chown(name, uid, gid) +} + func (r *RegexpFs) Name() string { return "RegexpFs" } diff --git a/sftpfs/sftp.go b/sftpfs/sftp.go index e41df68..c66ab47 100644 --- a/sftpfs/sftp.go +++ b/sftpfs/sftp.go @@ -130,6 +130,10 @@ func (s Fs) Chmod(name string, mode os.FileMode) error { return s.client.Chmod(name, mode) } +func (s Fs) Chown(name string, uid, gid int) error { + return s.client.Chown(name, uid, gid) +} + func (s Fs) Chtimes(name string, atime time.Time, mtime time.Time) error { return s.client.Chtimes(name, atime, mtime) } diff --git a/tarfs/fs.go b/tarfs/fs.go index f8a2ce2..0d764e5 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -134,4 +134,6 @@ func (fs *Fs) Stat(name string) (os.FileInfo, error) { func (fs *Fs) Chmod(name string, mode os.FileMode) error { return syscall.EROFS } +func (fs *Fs) Chown(name string, uid, gid int) error { return syscall.EROFS } + func (fs *Fs) Chtimes(name string, atime time.Time, mtime time.Time) error { return syscall.EROFS } diff --git a/zipfs/fs.go b/zipfs/fs.go index 72887f9..d08b7f5 100644 --- a/zipfs/fs.go +++ b/zipfs/fs.go @@ -108,4 +108,6 @@ func (fs *Fs) Name() string { return "zipfs" } func (fs *Fs) Chmod(name string, mode os.FileMode) error { return syscall.EPERM } +func (fs *Fs) Chown(name string, uid, gid int) error { return syscall.EPERM } + func (fs *Fs) Chtimes(name string, atime time.Time, mtime time.Time) error { return syscall.EPERM }