Fixed permissions issues in memfs, making the package support doing so.

geesefs-0-30-9
Aaron Jacobs 2015-03-03 11:08:34 +11:00
commit 19f7ef25c9
6 changed files with 244 additions and 31 deletions

View File

@ -159,12 +159,13 @@ type InodeAttributes struct {
Size uint64
Mode os.FileMode
// Time information
Atime time.Time
Mtime time.Time
Crtime time.Time
// Time information. See `man 2 stat` for full details.
Atime time.Time // Time of last access
Mtime time.Time // Time of last modification
Ctime time.Time // Time of last modification to inode
Crtime time.Time // Time of creation (OS X only)
// Owner information
// Ownership information
Uid uint32
Gid uint32
}
@ -197,6 +198,13 @@ type HandleID uint64
// ReadDirRequest.Offset for details.
type DirOffset uint64
// A header that is included with every request.
type RequestHeader struct {
// Credentials information for the process making the request.
Uid uint32
Gid uint32
}
// Information about a child inode within its parent directory. Shared by the
// responses for LookUpInode, MkDir, etc. Consumed by the kernel in order to
// set up a dcache entry.
@ -209,7 +217,7 @@ type ChildInodeEntry struct {
// See comments on type GenerationNumber for more.
Generation GenerationNumber
// Current ttributes for the child inode.
// Current attributes for the child inode.
Attributes InodeAttributes
// The FUSE VFS layer in the kernel maintains a cache of file attributes,
@ -273,15 +281,15 @@ type ChildInodeEntry struct {
////////////////////////////////////////////////////////////////////////
type InitRequest struct {
// User and group IDs for the process that is mounting the file system.
Uid uint32
Gid uint32
Header RequestHeader
}
type InitResponse struct {
}
type LookUpInodeRequest struct {
Header RequestHeader
// The ID of the directory inode to which the child belongs.
Parent InodeID
@ -302,6 +310,8 @@ type LookUpInodeResponse struct {
}
type GetInodeAttributesRequest struct {
Header RequestHeader
// The inode of interest.
Inode InodeID
}
@ -314,6 +324,8 @@ type GetInodeAttributesResponse struct {
}
type ForgetInodeRequest struct {
Header RequestHeader
// The inode to be forgotten. The kernel guarantees that the node ID will not
// be used in further calls to the file system (unless it is reissued by the
// file system).
@ -324,6 +336,8 @@ type ForgetInodeResponse struct {
}
type MkDirRequest struct {
Header RequestHeader
// The ID of parent directory inode within which to create the child.
Parent InodeID
@ -333,10 +347,22 @@ type MkDirRequest struct {
}
type MkDirResponse struct {
// Information about the inode that was created.
//
// The file system is responsible for initializing and recording (where
// supported) attributes like time information, ownership information, etc.
//
// Ownership information in particular must be set to something reasonable or
// by default root will own everything and unprivileged users won't be able
// to do anything useful. In traditional file systems in the kernel, the
// function inode_init_owner (http://goo.gl/5qavg8) contains the
// standards-compliant logic for this.
Entry ChildInodeEntry
}
type OpenDirRequest struct {
Header RequestHeader
// The ID of the inode to be opened.
Inode InodeID
@ -360,6 +386,8 @@ type OpenDirResponse struct {
}
type ReadDirRequest struct {
Header RequestHeader
// The directory inode that we are reading, and the handle previously
// returned by OpenDir when opening that inode.
Inode InodeID
@ -449,6 +477,8 @@ type ReadDirResponse struct {
}
type ReleaseDirHandleRequest struct {
Header RequestHeader
// The handle ID to be released. The kernel guarantees that this ID will not
// be used in further calls to the file system (unless it is reissued by the
// file system).
@ -459,6 +489,8 @@ type ReleaseDirHandleResponse struct {
}
type OpenFileRequest struct {
Header RequestHeader
// The ID of the inode to be opened.
Inode InodeID
@ -482,6 +514,8 @@ type OpenFileResponse struct {
}
type ReadFileRequest struct {
Header RequestHeader
// The file inode that we are reading, and the handle previously returned by
// OpenFile when opening that inode.
Inode InodeID
@ -505,6 +539,8 @@ type ReadFileResponse struct {
}
type ReleaseFileHandleRequest struct {
Header RequestHeader
// The handle ID to be released. The kernel guarantees that this ID will not
// be used in further calls to the file system (unless it is reissued by the
// file system).

View File

@ -135,8 +135,8 @@ func (fs *HelloFS) Init(
req *fuse.InitRequest) (
resp *fuse.InitResponse, err error) {
resp = &fuse.InitResponse{}
fs.Uid = req.Uid
fs.Gid = req.Gid
fs.Uid = req.Header.Uid
fs.Gid = req.Header.Gid
return
}

View File

@ -57,9 +57,10 @@ func NewMemFS(
inodes: make([]*inode, fuse.RootInodeID+1),
}
// Set up the root inode.
// Set up the root inode. Its ownership information will later be modified in
// Init.
rootAttrs := fuse.InodeAttributes{
Mode: 0777 | os.ModeDir,
Mode: 0700 | os.ModeDir,
}
fs.inodes[fuse.RootInodeID] = newInode(rootAttrs)
@ -70,6 +71,10 @@ func NewMemFS(
return fs
}
////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////
func (fs *memFS) checkInvariants() {
// Check reserved inodes.
for i := 0; i < fuse.RootInodeID; i++ {
@ -109,13 +114,6 @@ func (fs *memFS) checkInvariants() {
}
}
func (fs *memFS) Init(
ctx context.Context,
req *fuse.InitRequest) (resp *fuse.InitResponse, err error) {
resp = &fuse.InitResponse{}
return
}
// Find the given inode and return it with its lock held. Panic if it doesn't
// exist.
//
@ -171,6 +169,29 @@ func (fs *memFS) allocateInode(
return
}
////////////////////////////////////////////////////////////////////////
// FileSystem methods
////////////////////////////////////////////////////////////////////////
func (fs *memFS) Init(
ctx context.Context,
req *fuse.InitRequest) (resp *fuse.InitResponse, err error) {
resp = &fuse.InitResponse{}
fs.mu.RLock()
defer fs.mu.RUnlock()
// Update the root inode's ownership information to match the credentials of
// the mounting process.
root := fs.getInodeForModifyingOrDie(fuse.RootInodeID)
defer root.mu.Unlock()
root.attributes.Uid = req.Header.Uid
root.attributes.Gid = req.Header.Gid
return
}
func (fs *memFS) LookUpInode(
ctx context.Context,
req *fuse.LookUpInodeRequest) (resp *fuse.LookUpInodeResponse, err error) {
@ -241,15 +262,20 @@ func (fs *memFS) MkDir(
parent := fs.getInodeForModifyingOrDie(req.Parent)
defer parent.mu.Unlock()
// Allocate a child.
// Set up attributes from the child, using the credientials of the calling
// process as owner (matching inode_init_owner, cf. http://goo.gl/5qavg8).
now := fs.clock.Now()
childAttrs := fuse.InodeAttributes{
Mode: req.Mode,
Atime: now,
Mtime: now,
Ctime: now,
Crtime: now,
Uid: req.Header.Uid,
Gid: req.Header.Gid,
}
// Allocate a child.
childID, child := fs.allocateInode(childAttrs)
defer child.mu.Unlock()

View File

@ -59,6 +59,10 @@ type inode struct {
contents []byte // GUARDED_BY(mu)
}
////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////
func newInode(attrs fuse.InodeAttributes) (in *inode) {
in = &inode{
dir: (attrs.Mode&os.ModeDir != 0),
@ -112,6 +116,10 @@ func (inode *inode) checkInvariants() {
}
}
////////////////////////////////////////////////////////////////////////
// Public methods
////////////////////////////////////////////////////////////////////////
// Find an entry for the given child name and return its inode ID.
//
// REQUIRES: inode.dir

View File

@ -7,8 +7,11 @@ import (
"io/ioutil"
"log"
"os"
"os/user"
"path"
"strconv"
"strings"
"syscall"
"testing"
"time"
@ -22,6 +25,42 @@ import (
func TestMemFS(t *testing.T) { RunTests(t) }
////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////
func currentUid() uint32 {
user, err := user.Current()
if err != nil {
panic(err)
}
uid, err := strconv.ParseUint(user.Uid, 10, 32)
if err != nil {
panic(err)
}
return uint32(uid)
}
func currentGid() uint32 {
user, err := user.Current()
if err != nil {
panic(err)
}
gid, err := strconv.ParseUint(user.Gid, 10, 32)
if err != nil {
panic(err)
}
return uint32(gid)
}
func timespecToTime(ts syscall.Timespec) time.Time {
return time.Unix(ts.Sec, ts.Nsec)
}
////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////
@ -94,9 +133,12 @@ func (t *MemFSTest) ContentsOfEmptyFileSystem() {
ExpectThat(entries, ElementsAre())
}
func (t *MemFSTest) Mkdir() {
func (t *MemFSTest) Mkdir_OneLevel() {
var err error
var fi os.FileInfo
var stat *syscall.Stat_t
var entries []os.FileInfo
dirName := path.Join(t.mfs.Dir(), "dir")
// Create a directory within the root.
@ -104,11 +146,12 @@ func (t *MemFSTest) Mkdir() {
err = os.Mkdir(dirName, 0754)
AssertEq(nil, err)
// Simulate time proceeding.
// Simulate time advancing.
t.clock.AdvanceTime(time.Second)
// Stat the directory.
fi, err = os.Stat(dirName)
stat = fi.Sys().(*syscall.Stat_t)
AssertEq(nil, err)
ExpectEq("dir", fi.Name())
@ -117,11 +160,83 @@ func (t *MemFSTest) Mkdir() {
ExpectEq(0, fi.ModTime().Sub(createTime))
ExpectTrue(fi.IsDir())
ExpectEq(1, stat.Nlink)
ExpectEq(currentUid(), stat.Uid)
ExpectEq(currentGid(), stat.Gid)
ExpectEq(0, stat.Size)
ExpectEq(0, timespecToTime(stat.Atimespec).Sub(createTime))
ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(createTime))
ExpectEq(0, timespecToTime(stat.Ctimespec).Sub(createTime))
// Read the directory.
entries, err := ioutil.ReadDir(dirName)
entries, err = ioutil.ReadDir(dirName)
AssertEq(nil, err)
ExpectThat(entries, ElementsAre())
// Read the root.
entries, err = ioutil.ReadDir(t.mfs.Dir())
AssertEq(nil, err)
AssertEq(1, len(entries))
fi = entries[0]
ExpectEq("dir", fi.Name())
ExpectEq(os.ModeDir|0754, fi.Mode())
}
func (t *MemFSTest) Mkdir_TwoLevels() {
var err error
var fi os.FileInfo
var stat *syscall.Stat_t
var entries []os.FileInfo
// Create a directory within the root.
err = os.Mkdir(path.Join(t.mfs.Dir(), "parent"), 0700)
AssertEq(nil, err)
// Create a child of that directory.
createTime := t.clock.Now()
err = os.Mkdir(path.Join(t.mfs.Dir(), "parent/dir"), 0754)
AssertEq(nil, err)
// Simulate time advancing.
t.clock.AdvanceTime(time.Second)
// Stat the directory.
fi, err = os.Stat(path.Join(t.mfs.Dir(), "parent/dir"))
stat = fi.Sys().(*syscall.Stat_t)
AssertEq(nil, err)
ExpectEq("dir", fi.Name())
ExpectEq(0, fi.Size())
ExpectEq(os.ModeDir|0754, fi.Mode())
ExpectEq(0, fi.ModTime().Sub(createTime))
ExpectTrue(fi.IsDir())
ExpectEq(1, stat.Nlink)
ExpectEq(currentUid(), stat.Uid)
ExpectEq(currentGid(), stat.Gid)
ExpectEq(0, stat.Size)
ExpectEq(0, timespecToTime(stat.Atimespec).Sub(createTime))
ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(createTime))
ExpectEq(0, timespecToTime(stat.Ctimespec).Sub(createTime))
// Read the directory.
entries, err = ioutil.ReadDir(path.Join(t.mfs.Dir(), "parent/dir"))
AssertEq(nil, err)
ExpectThat(entries, ElementsAre())
// Read the parent.
entries, err = ioutil.ReadDir(path.Join(t.mfs.Dir(), "parent"))
AssertEq(nil, err)
AssertEq(1, len(entries))
fi = entries[0]
ExpectEq("dir", fi.Name())
ExpectEq(os.ModeDir|0754, fi.Mode())
}
func (t *MemFSTest) Mkdir_AlreadyExists() {
@ -166,6 +281,20 @@ func (t *MemFSTest) Mkdir_IntermediateIsNonExistent() {
ExpectThat(err, Error(HasSubstr("no such file or directory")))
}
func (t *MemFSTest) Mkdir_PermissionDenied() {
var err error
// Create a directory within the root without write permissions.
err = os.Mkdir(path.Join(t.mfs.Dir(), "parent"), 0500)
AssertEq(nil, err)
// Attempt to create a child of that directory.
err = os.Mkdir(path.Join(t.mfs.Dir(), "parent/dir"), 0754)
AssertNe(nil, err)
ExpectThat(err, Error(HasSubstr("permission denied")))
}
func (t *MemFSTest) CreateNewFile_InRoot() {
AssertTrue(false, "TODO")
}

View File

@ -43,6 +43,13 @@ func convertChildInodeEntry(
out.EntryValid = in.EntryExpiration.Sub(clock.Now())
}
func convertHeader(
in bazilfuse.Header) (out RequestHeader) {
out.Uid = in.Uid
out.Gid = in.Gid
return
}
// Serve the fuse connection by repeatedly reading requests from the supplied
// FUSE connection, responding as dictated by the file system. Return when the
// connection is closed or an unexpected error occurs.
@ -82,8 +89,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
case *bazilfuse.InitRequest:
// Convert the request.
req := &InitRequest{
Uid: typed.Header.Uid,
Gid: typed.Header.Gid,
Header: convertHeader(typed.Header),
}
// Call the file system.
@ -110,6 +116,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
case *bazilfuse.LookupRequest:
// Convert the request.
req := &LookUpInodeRequest{
Header: convertHeader(typed.Header),
Parent: InodeID(typed.Header.Node),
Name: typed.Name,
}
@ -132,7 +139,8 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
case *bazilfuse.GetattrRequest:
// Convert the request.
req := &GetInodeAttributesRequest{
Inode: InodeID(typed.Header.Node),
Header: convertHeader(typed.Header),
Inode: InodeID(typed.Header.Node),
}
// Call the file system.
@ -155,6 +163,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
case *bazilfuse.MkdirRequest:
// Convert the request.
req := &MkDirRequest{
Header: convertHeader(typed.Header),
Parent: InodeID(typed.Header.Node),
Name: typed.Name,
Mode: typed.Mode,
@ -180,8 +189,9 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
if typed.Dir {
// Convert the request.
req := &OpenDirRequest{
Inode: InodeID(typed.Header.Node),
Flags: typed.Flags,
Header: convertHeader(typed.Header),
Inode: InodeID(typed.Header.Node),
Flags: typed.Flags,
}
// Call the file system.
@ -202,8 +212,9 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
} else {
// Convert the request.
req := &OpenFileRequest{
Inode: InodeID(typed.Header.Node),
Flags: typed.Flags,
Header: convertHeader(typed.Header),
Inode: InodeID(typed.Header.Node),
Flags: typed.Flags,
}
// Call the file system.
@ -228,6 +239,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
if typed.Dir {
// Convert the request.
req := &ReadDirRequest{
Header: convertHeader(typed.Header),
Inode: InodeID(typed.Header.Node),
Handle: HandleID(typed.Handle),
Offset: DirOffset(typed.Offset),
@ -252,6 +264,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
} else {
// Convert the request.
req := &ReadFileRequest{
Header: convertHeader(typed.Header),
Inode: InodeID(typed.Header.Node),
Handle: HandleID(typed.Handle),
Offset: typed.Offset,
@ -288,6 +301,7 @@ func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr {
Mode: attr.Mode,
Atime: attr.Atime,
Mtime: attr.Mtime,
Ctime: attr.Ctime,
Crtime: attr.Crtime,
Uid: attr.Uid,
Gid: attr.Gid,