Fixed permissions issues in memfs, making the package support doing so.
commit
19f7ef25c9
|
@ -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).
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
28
server.go
28
server.go
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue