From 13353bc51a9782249d484c044949c2a79f403abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Valls=20Fern=C3=A1ndez?= Date: Sat, 3 Mar 2018 23:38:00 +0100 Subject: [PATCH] Improve Uid & Gid support The purpose of this PR is to allow file systems to: 1. Create inodes with the UID and GID of the calling process 2. Support chown --- .travis.yml | 1 + conversions.go | 20 +++++++++++++++++++- fuseops/ops.go | 18 ++++++++++++++++++ samples/memfs/inode.go | 14 +++++++++++++- samples/memfs/memfs.go | 28 ++++++++++++---------------- samples/memfs/memfs_test.go | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 95 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index a8969a96..1fb7ab87 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,7 @@ before_install: # For linux: install fuse. - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -qq fuse; + sudo echo user_allow_other > /etc/fuse.conf; fi # For macOS: update homebrew and then install osxfuse. diff --git a/conversions.go b/conversions.go index 26c27c12..7fe1ae75 100644 --- a/conversions.go +++ b/conversions.go @@ -41,7 +41,9 @@ func convertInMessage( inMsg *buffer.InMessage, outMsg *buffer.OutMessage, protocol fusekernel.Protocol) (o interface{}, err error) { - switch inMsg.Header().Opcode { + header := inMsg.Header() + + switch header.Opcode { case fusekernel.OpLookup: buf := inMsg.ConsumeBytes(inMsg.Len()) n := len(buf) @@ -93,6 +95,14 @@ func convertInMessage( to.Mtime = &t } + if valid&fusekernel.SetattrUid != 0 { + to.Uid = &in.Uid + } + + if valid&fusekernel.SetattrGid != 0 { + to.Gid = &in.Gid + } + case fusekernel.OpForget: type input fusekernel.ForgetIn in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) @@ -132,6 +142,8 @@ func convertInMessage( // opcode is mkdir. But we want the correct mode to go through, so ensure // that os.ModeDir is set. Mode: convertFileMode(in.Mode) | os.ModeDir, + Uid: header.Uid, + Gid: header.Gid, } case fusekernel.OpMknod: @@ -153,6 +165,8 @@ func convertInMessage( Parent: fuseops.InodeID(inMsg.Header().Nodeid), Name: string(name), Mode: convertFileMode(in.Mode), + Uid: header.Uid, + Gid: header.Gid, } case fusekernel.OpCreate: @@ -174,6 +188,8 @@ func convertInMessage( Parent: fuseops.InodeID(inMsg.Header().Nodeid), Name: string(name), Mode: convertFileMode(in.Mode), + Uid: header.Uid, + Gid: header.Gid, } case fusekernel.OpSymlink: @@ -194,6 +210,8 @@ func convertInMessage( Parent: fuseops.InodeID(inMsg.Header().Nodeid), Name: string(newName), Target: string(target), + Uid: header.Uid, + Gid: header.Gid, } case fusekernel.OpRename: diff --git a/fuseops/ops.go b/fuseops/ops.go index 56fe89c3..13a34f05 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -150,6 +150,8 @@ type SetInodeAttributesOp struct { Mode *os.FileMode Atime *time.Time Mtime *time.Time + Uid *uint32 + Gid *uint32 // Set by the file system: the new attributes for the inode, and the time at // which they should expire. See notes on @@ -228,6 +230,10 @@ type MkDirOp struct { Name string Mode os.FileMode + // The uid and gid of the parent process + Uid uint32 + Gid uint32 + // Set by the file system: information about the inode that was created. // // The lookup count for the inode is implicitly incremented. See notes on @@ -255,6 +261,10 @@ type MkNodeOp struct { Name string Mode os.FileMode + // The uid and gid of the parent process + Uid uint32 + Gid uint32 + // Set by the file system: information about the inode that was created. // // The lookup count for the inode is implicitly incremented. See notes on @@ -280,6 +290,10 @@ type CreateFileOp struct { Name string Mode os.FileMode + // The uid and gid of the parent process + Uid uint32 + Gid uint32 + // Set by the file system: information about the inode that was created. // // The lookup count for the inode is implicitly incremented. See notes on @@ -306,6 +320,10 @@ type CreateSymlinkOp struct { // The name of the symlink to create. Name string + // The uid and gid of the parent process + Uid uint32 + Gid uint32 + // The target of the symlink. Target string diff --git a/samples/memfs/inode.go b/samples/memfs/inode.go index b0471cc3..c6317009 100644 --- a/samples/memfs/inode.go +++ b/samples/memfs/inode.go @@ -352,7 +352,9 @@ func (in *inode) WriteAt(p []byte, off int64) (n int, err error) { func (in *inode) SetAttributes( size *uint64, mode *os.FileMode, - mtime *time.Time) { + mtime *time.Time, + uid *uint32, + gid *uint32) { // Update the modification time. in.attrs.Mtime = time.Now() @@ -381,4 +383,14 @@ func (in *inode) SetAttributes( if mtime != nil { in.attrs.Mtime = *mtime } + + // Change uid? + if uid != nil { + in.attrs.Uid = *uid + } + + // Change gid? + if gid != nil { + in.attrs.Gid = *gid + } } diff --git a/samples/memfs/memfs.go b/samples/memfs/memfs.go index 909b9e0a..c40bde11 100644 --- a/samples/memfs/memfs.go +++ b/samples/memfs/memfs.go @@ -31,10 +31,6 @@ import ( type memFS struct { fuseutil.NotImplementedFileSystem - // The UID and GID that every inode receives. - uid uint32 - gid uint32 - ///////////////////////// // Mutable state ///////////////////////// @@ -73,8 +69,6 @@ func NewMemFS( // Set up the basic struct. fs := &memFS{ inodes: make([]*inode, fuseops.RootInodeID+1), - uid: uid, - gid: gid, } // Set up the root inode. @@ -250,7 +244,7 @@ func (fs *memFS) SetInodeAttributes( inode := fs.getInodeOrDie(op.Inode) // Handle the request. - inode.SetAttributes(op.Size, op.Mode, op.Mtime) + inode.SetAttributes(op.Size, op.Mode, op.Mtime, op.Uid, op.Gid) // Fill in the response. op.Attributes = inode.attrs @@ -283,8 +277,8 @@ func (fs *memFS) MkDir( childAttrs := fuseops.InodeAttributes{ Nlink: 1, Mode: op.Mode, - Uid: fs.uid, - Gid: fs.gid, + Uid: op.Uid, + Gid: op.Gid, } // Allocate a child. @@ -311,7 +305,7 @@ func (fs *memFS) MkNode( fs.mu.Lock() defer fs.mu.Unlock() - op.Entry, err = fs.createFile(op.Parent, op.Name, op.Mode) + op.Entry, err = fs.createFile(op.Parent, op.Name, op.Mode, op.Uid, op.Gid) return } @@ -319,7 +313,9 @@ func (fs *memFS) MkNode( func (fs *memFS) createFile( parentID fuseops.InodeID, name string, - mode os.FileMode) (entry fuseops.ChildInodeEntry, err error) { + mode os.FileMode, + uid uint32, + gid uint32) (entry fuseops.ChildInodeEntry, err error) { // Grab the parent, which we will update shortly. parent := fs.getInodeOrDie(parentID) @@ -340,8 +336,8 @@ func (fs *memFS) createFile( Mtime: now, Ctime: now, Crtime: now, - Uid: fs.uid, - Gid: fs.gid, + Uid: uid, + Gid: gid, } // Allocate a child. @@ -368,7 +364,7 @@ func (fs *memFS) CreateFile( fs.mu.Lock() defer fs.mu.Unlock() - op.Entry, err = fs.createFile(op.Parent, op.Name, op.Mode) + op.Entry, err = fs.createFile(op.Parent, op.Name, op.Mode, op.Uid, op.Gid) return } @@ -398,8 +394,8 @@ func (fs *memFS) CreateSymlink( Mtime: now, Ctime: now, Crtime: now, - Uid: fs.uid, - Gid: fs.gid, + Uid: op.Uid, + Gid: op.Gid, } // Allocate a child. diff --git a/samples/memfs/memfs_test.go b/samples/memfs/memfs_test.go index 9abe9232..1568a0c7 100644 --- a/samples/memfs/memfs_test.go +++ b/samples/memfs/memfs_test.go @@ -19,6 +19,7 @@ import ( "io" "io/ioutil" "os" + "os/exec" "os/user" "path" "reflect" @@ -97,6 +98,14 @@ type memFSTest struct { func (t *memFSTest) SetUp(ti *TestInfo) { t.Server = memfs.NewMemFS(currentUid(), currentGid()) + + // If the OS is linux, we need to specify allow_other for + // sudo to work + if os.Getenv("TRAVIS_OS_NAME") == "linux" { + t.SampleTest.MountConfig.Options = make(map[string]string) + t.SampleTest.MountConfig.Options["allow_other"] = "" + } + t.SampleTest.SetUp(ti) } @@ -1007,6 +1016,29 @@ func (t *MemFSTest) Chmod() { ExpectEq(0754, fi.Mode()) } +func (t *MemFSTest) Chown() { + var err error + fileName := path.Join(t.Dir, "foo") + + // Create a file. + err = ioutil.WriteFile(fileName, []byte(""), 0600) + AssertEq(nil, err) + + // Chown it. + err = exec.Command("sudo", "chown", "50:51", fileName).Run() + AssertEq(nil, err) + + // Stat it. + fi, err := os.Stat(fileName) + AssertEq(nil, err) + stat, ok := fi.Sys().(*syscall.Stat_t) + + if ok { + ExpectEq(50, stat.Uid) + ExpectEq(51, stat.Gid) + } +} + func (t *MemFSTest) Chtimes() { var err error fileName := path.Join(t.Dir, "foo")