diff --git a/afero.go b/afero.go index f5b5e12..5702d89 100644 --- a/afero.go +++ b/afero.go @@ -96,6 +96,9 @@ type Fs interface { //Chtimes changes the access and modification times of the named file Chtimes(name string, atime time.Time, mtime time.Time) error + + // Chown changes uid and gid of the named file. + Chown(name string, uid, gid int) error } var ( diff --git a/basepath.go b/basepath.go index 616ff8f..404188e 100644 --- a/basepath.go +++ b/basepath.go @@ -177,4 +177,11 @@ func (b *BasePathFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { return fi, false, err } + +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) +} // vim: ts=4 sw=4 noexpandtab nolist syn=go diff --git a/cacheOnReadFs.go b/cacheOnReadFs.go index 29a26c6..d0db6c5 100644 --- a/cacheOnReadFs.go +++ b/cacheOnReadFs.go @@ -288,3 +288,24 @@ func (u *CacheOnReadFs) Create(name string) (File, error) { } return &UnionFile{Base: bfh, Layer: lfh}, nil } + +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) +} diff --git a/copyOnWriteFs.go b/copyOnWriteFs.go index e8108a8..3b76268 100644 --- a/copyOnWriteFs.go +++ b/copyOnWriteFs.go @@ -291,3 +291,16 @@ func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error { func (u *CopyOnWriteFs) Create(name string) (File, error) { return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666) } + +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) +} diff --git a/mem/file.go b/mem/file.go index 7af2fb5..c0acbd7 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 09498e7..7f57436 100644 --- a/memmap.go +++ b/memmap.go @@ -358,6 +358,21 @@ func (m *MemMapFs) List() { } } +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 debugMemMapList(fs Fs) { // if x, ok := fs.(*MemMapFs); ok { // x.List() diff --git a/os.go b/os.go index 13cc1b8..916ad38 100644 --- a/os.go +++ b/os.go @@ -99,3 +99,7 @@ func (OsFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { fi, err := os.Lstat(name) return fi, true, err } + +func (OsFs) Chown(name string, uid, gid int) error { + return os.Chown(name, uid, gid) +} diff --git a/readonlyfs.go b/readonlyfs.go index c6376ec..7783386 100644 --- a/readonlyfs.go +++ b/readonlyfs.go @@ -78,3 +78,7 @@ func (r *ReadOnlyFs) MkdirAll(n string, p os.FileMode) error { func (r *ReadOnlyFs) Create(n string) (File, error) { return nil, syscall.EPERM } + +func (r *ReadOnlyFs) Chown(n string, uid, gid int) error { + return syscall.EPERM +} diff --git a/regexpfs.go b/regexpfs.go index 9d92dbc..44e50c5 100644 --- a/regexpfs.go +++ b/regexpfs.go @@ -212,3 +212,10 @@ func (f *RegexpFile) Truncate(s int64) error { func (f *RegexpFile) WriteString(s string) (int, error) { return f.f.WriteString(s) } + +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) +} diff --git a/sftpfs/sftp.go b/sftpfs/sftp.go index 28721da..6ad0287 100644 --- a/sftpfs/sftp.go +++ b/sftpfs/sftp.go @@ -127,3 +127,7 @@ func (s Fs) Chmod(name string, mode os.FileMode) error { func (s Fs) Chtimes(name string, atime time.Time, mtime time.Time) error { return s.client.Chtimes(name, atime, mtime) } + +func (s Fs) Chown(name string, uid, gid int) error { + return s.client.Chown(name, uid, gid) +}