Fixed permissions issues in memfs, making the package support doing so.
commit
19f7ef25c9
|
@ -159,12 +159,13 @@ type InodeAttributes struct {
|
||||||
Size uint64
|
Size uint64
|
||||||
Mode os.FileMode
|
Mode os.FileMode
|
||||||
|
|
||||||
// Time information
|
// Time information. See `man 2 stat` for full details.
|
||||||
Atime time.Time
|
Atime time.Time // Time of last access
|
||||||
Mtime time.Time
|
Mtime time.Time // Time of last modification
|
||||||
Crtime time.Time
|
Ctime time.Time // Time of last modification to inode
|
||||||
|
Crtime time.Time // Time of creation (OS X only)
|
||||||
|
|
||||||
// Owner information
|
// Ownership information
|
||||||
Uid uint32
|
Uid uint32
|
||||||
Gid uint32
|
Gid uint32
|
||||||
}
|
}
|
||||||
|
@ -197,6 +198,13 @@ type HandleID uint64
|
||||||
// ReadDirRequest.Offset for details.
|
// ReadDirRequest.Offset for details.
|
||||||
type DirOffset uint64
|
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
|
// Information about a child inode within its parent directory. Shared by the
|
||||||
// responses for LookUpInode, MkDir, etc. Consumed by the kernel in order to
|
// responses for LookUpInode, MkDir, etc. Consumed by the kernel in order to
|
||||||
// set up a dcache entry.
|
// set up a dcache entry.
|
||||||
|
@ -209,7 +217,7 @@ type ChildInodeEntry struct {
|
||||||
// See comments on type GenerationNumber for more.
|
// See comments on type GenerationNumber for more.
|
||||||
Generation GenerationNumber
|
Generation GenerationNumber
|
||||||
|
|
||||||
// Current ttributes for the child inode.
|
// Current attributes for the child inode.
|
||||||
Attributes InodeAttributes
|
Attributes InodeAttributes
|
||||||
|
|
||||||
// The FUSE VFS layer in the kernel maintains a cache of file attributes,
|
// The FUSE VFS layer in the kernel maintains a cache of file attributes,
|
||||||
|
@ -273,15 +281,15 @@ type ChildInodeEntry struct {
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
type InitRequest struct {
|
type InitRequest struct {
|
||||||
// User and group IDs for the process that is mounting the file system.
|
Header RequestHeader
|
||||||
Uid uint32
|
|
||||||
Gid uint32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type InitResponse struct {
|
type InitResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type LookUpInodeRequest struct {
|
type LookUpInodeRequest struct {
|
||||||
|
Header RequestHeader
|
||||||
|
|
||||||
// The ID of the directory inode to which the child belongs.
|
// The ID of the directory inode to which the child belongs.
|
||||||
Parent InodeID
|
Parent InodeID
|
||||||
|
|
||||||
|
@ -302,6 +310,8 @@ type LookUpInodeResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetInodeAttributesRequest struct {
|
type GetInodeAttributesRequest struct {
|
||||||
|
Header RequestHeader
|
||||||
|
|
||||||
// The inode of interest.
|
// The inode of interest.
|
||||||
Inode InodeID
|
Inode InodeID
|
||||||
}
|
}
|
||||||
|
@ -314,6 +324,8 @@ type GetInodeAttributesResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ForgetInodeRequest struct {
|
type ForgetInodeRequest struct {
|
||||||
|
Header RequestHeader
|
||||||
|
|
||||||
// The inode to be forgotten. The kernel guarantees that the node ID will not
|
// 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
|
// be used in further calls to the file system (unless it is reissued by the
|
||||||
// file system).
|
// file system).
|
||||||
|
@ -324,6 +336,8 @@ type ForgetInodeResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MkDirRequest struct {
|
type MkDirRequest struct {
|
||||||
|
Header RequestHeader
|
||||||
|
|
||||||
// The ID of parent directory inode within which to create the child.
|
// The ID of parent directory inode within which to create the child.
|
||||||
Parent InodeID
|
Parent InodeID
|
||||||
|
|
||||||
|
@ -333,10 +347,22 @@ type MkDirRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MkDirResponse 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
|
Entry ChildInodeEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
type OpenDirRequest struct {
|
type OpenDirRequest struct {
|
||||||
|
Header RequestHeader
|
||||||
|
|
||||||
// The ID of the inode to be opened.
|
// The ID of the inode to be opened.
|
||||||
Inode InodeID
|
Inode InodeID
|
||||||
|
|
||||||
|
@ -360,6 +386,8 @@ type OpenDirResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReadDirRequest struct {
|
type ReadDirRequest struct {
|
||||||
|
Header RequestHeader
|
||||||
|
|
||||||
// The directory inode that we are reading, and the handle previously
|
// The directory inode that we are reading, and the handle previously
|
||||||
// returned by OpenDir when opening that inode.
|
// returned by OpenDir when opening that inode.
|
||||||
Inode InodeID
|
Inode InodeID
|
||||||
|
@ -449,6 +477,8 @@ type ReadDirResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReleaseDirHandleRequest struct {
|
type ReleaseDirHandleRequest struct {
|
||||||
|
Header RequestHeader
|
||||||
|
|
||||||
// The handle ID to be released. The kernel guarantees that this ID will not
|
// 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
|
// be used in further calls to the file system (unless it is reissued by the
|
||||||
// file system).
|
// file system).
|
||||||
|
@ -459,6 +489,8 @@ type ReleaseDirHandleResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type OpenFileRequest struct {
|
type OpenFileRequest struct {
|
||||||
|
Header RequestHeader
|
||||||
|
|
||||||
// The ID of the inode to be opened.
|
// The ID of the inode to be opened.
|
||||||
Inode InodeID
|
Inode InodeID
|
||||||
|
|
||||||
|
@ -482,6 +514,8 @@ type OpenFileResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReadFileRequest struct {
|
type ReadFileRequest struct {
|
||||||
|
Header RequestHeader
|
||||||
|
|
||||||
// The file inode that we are reading, and the handle previously returned by
|
// The file inode that we are reading, and the handle previously returned by
|
||||||
// OpenFile when opening that inode.
|
// OpenFile when opening that inode.
|
||||||
Inode InodeID
|
Inode InodeID
|
||||||
|
@ -505,6 +539,8 @@ type ReadFileResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReleaseFileHandleRequest struct {
|
type ReleaseFileHandleRequest struct {
|
||||||
|
Header RequestHeader
|
||||||
|
|
||||||
// The handle ID to be released. The kernel guarantees that this ID will not
|
// 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
|
// be used in further calls to the file system (unless it is reissued by the
|
||||||
// file system).
|
// file system).
|
||||||
|
|
|
@ -135,8 +135,8 @@ func (fs *HelloFS) Init(
|
||||||
req *fuse.InitRequest) (
|
req *fuse.InitRequest) (
|
||||||
resp *fuse.InitResponse, err error) {
|
resp *fuse.InitResponse, err error) {
|
||||||
resp = &fuse.InitResponse{}
|
resp = &fuse.InitResponse{}
|
||||||
fs.Uid = req.Uid
|
fs.Uid = req.Header.Uid
|
||||||
fs.Gid = req.Gid
|
fs.Gid = req.Header.Gid
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,9 +57,10 @@ func NewMemFS(
|
||||||
inodes: make([]*inode, fuse.RootInodeID+1),
|
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{
|
rootAttrs := fuse.InodeAttributes{
|
||||||
Mode: 0777 | os.ModeDir,
|
Mode: 0700 | os.ModeDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.inodes[fuse.RootInodeID] = newInode(rootAttrs)
|
fs.inodes[fuse.RootInodeID] = newInode(rootAttrs)
|
||||||
|
@ -70,6 +71,10 @@ func NewMemFS(
|
||||||
return fs
|
return fs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// Helpers
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
func (fs *memFS) checkInvariants() {
|
func (fs *memFS) checkInvariants() {
|
||||||
// Check reserved inodes.
|
// Check reserved inodes.
|
||||||
for i := 0; i < fuse.RootInodeID; i++ {
|
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
|
// Find the given inode and return it with its lock held. Panic if it doesn't
|
||||||
// exist.
|
// exist.
|
||||||
//
|
//
|
||||||
|
@ -171,6 +169,29 @@ func (fs *memFS) allocateInode(
|
||||||
return
|
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(
|
func (fs *memFS) LookUpInode(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *fuse.LookUpInodeRequest) (resp *fuse.LookUpInodeResponse, err error) {
|
req *fuse.LookUpInodeRequest) (resp *fuse.LookUpInodeResponse, err error) {
|
||||||
|
@ -241,15 +262,20 @@ func (fs *memFS) MkDir(
|
||||||
parent := fs.getInodeForModifyingOrDie(req.Parent)
|
parent := fs.getInodeForModifyingOrDie(req.Parent)
|
||||||
defer parent.mu.Unlock()
|
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()
|
now := fs.clock.Now()
|
||||||
childAttrs := fuse.InodeAttributes{
|
childAttrs := fuse.InodeAttributes{
|
||||||
Mode: req.Mode,
|
Mode: req.Mode,
|
||||||
Atime: now,
|
Atime: now,
|
||||||
Mtime: now,
|
Mtime: now,
|
||||||
|
Ctime: now,
|
||||||
Crtime: now,
|
Crtime: now,
|
||||||
|
Uid: req.Header.Uid,
|
||||||
|
Gid: req.Header.Gid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allocate a child.
|
||||||
childID, child := fs.allocateInode(childAttrs)
|
childID, child := fs.allocateInode(childAttrs)
|
||||||
defer child.mu.Unlock()
|
defer child.mu.Unlock()
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,10 @@ type inode struct {
|
||||||
contents []byte // GUARDED_BY(mu)
|
contents []byte // GUARDED_BY(mu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// Helpers
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
func newInode(attrs fuse.InodeAttributes) (in *inode) {
|
func newInode(attrs fuse.InodeAttributes) (in *inode) {
|
||||||
in = &inode{
|
in = &inode{
|
||||||
dir: (attrs.Mode&os.ModeDir != 0),
|
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.
|
// Find an entry for the given child name and return its inode ID.
|
||||||
//
|
//
|
||||||
// REQUIRES: inode.dir
|
// REQUIRES: inode.dir
|
||||||
|
|
|
@ -7,8 +7,11 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -22,6 +25,42 @@ import (
|
||||||
|
|
||||||
func TestMemFS(t *testing.T) { RunTests(t) }
|
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
|
// Boilerplate
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -94,9 +133,12 @@ func (t *MemFSTest) ContentsOfEmptyFileSystem() {
|
||||||
ExpectThat(entries, ElementsAre())
|
ExpectThat(entries, ElementsAre())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MemFSTest) Mkdir() {
|
func (t *MemFSTest) Mkdir_OneLevel() {
|
||||||
var err error
|
var err error
|
||||||
var fi os.FileInfo
|
var fi os.FileInfo
|
||||||
|
var stat *syscall.Stat_t
|
||||||
|
var entries []os.FileInfo
|
||||||
|
|
||||||
dirName := path.Join(t.mfs.Dir(), "dir")
|
dirName := path.Join(t.mfs.Dir(), "dir")
|
||||||
|
|
||||||
// Create a directory within the root.
|
// Create a directory within the root.
|
||||||
|
@ -104,11 +146,12 @@ func (t *MemFSTest) Mkdir() {
|
||||||
err = os.Mkdir(dirName, 0754)
|
err = os.Mkdir(dirName, 0754)
|
||||||
AssertEq(nil, err)
|
AssertEq(nil, err)
|
||||||
|
|
||||||
// Simulate time proceeding.
|
// Simulate time advancing.
|
||||||
t.clock.AdvanceTime(time.Second)
|
t.clock.AdvanceTime(time.Second)
|
||||||
|
|
||||||
// Stat the directory.
|
// Stat the directory.
|
||||||
fi, err = os.Stat(dirName)
|
fi, err = os.Stat(dirName)
|
||||||
|
stat = fi.Sys().(*syscall.Stat_t)
|
||||||
|
|
||||||
AssertEq(nil, err)
|
AssertEq(nil, err)
|
||||||
ExpectEq("dir", fi.Name())
|
ExpectEq("dir", fi.Name())
|
||||||
|
@ -117,11 +160,83 @@ func (t *MemFSTest) Mkdir() {
|
||||||
ExpectEq(0, fi.ModTime().Sub(createTime))
|
ExpectEq(0, fi.ModTime().Sub(createTime))
|
||||||
ExpectTrue(fi.IsDir())
|
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.
|
// Read the directory.
|
||||||
entries, err := ioutil.ReadDir(dirName)
|
entries, err = ioutil.ReadDir(dirName)
|
||||||
|
|
||||||
AssertEq(nil, err)
|
AssertEq(nil, err)
|
||||||
ExpectThat(entries, ElementsAre())
|
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() {
|
func (t *MemFSTest) Mkdir_AlreadyExists() {
|
||||||
|
@ -166,6 +281,20 @@ func (t *MemFSTest) Mkdir_IntermediateIsNonExistent() {
|
||||||
ExpectThat(err, Error(HasSubstr("no such file or directory")))
|
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() {
|
func (t *MemFSTest) CreateNewFile_InRoot() {
|
||||||
AssertTrue(false, "TODO")
|
AssertTrue(false, "TODO")
|
||||||
}
|
}
|
||||||
|
|
28
server.go
28
server.go
|
@ -43,6 +43,13 @@ func convertChildInodeEntry(
|
||||||
out.EntryValid = in.EntryExpiration.Sub(clock.Now())
|
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
|
// Serve the fuse connection by repeatedly reading requests from the supplied
|
||||||
// FUSE connection, responding as dictated by the file system. Return when the
|
// FUSE connection, responding as dictated by the file system. Return when the
|
||||||
// connection is closed or an unexpected error occurs.
|
// connection is closed or an unexpected error occurs.
|
||||||
|
@ -82,8 +89,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
|
||||||
case *bazilfuse.InitRequest:
|
case *bazilfuse.InitRequest:
|
||||||
// Convert the request.
|
// Convert the request.
|
||||||
req := &InitRequest{
|
req := &InitRequest{
|
||||||
Uid: typed.Header.Uid,
|
Header: convertHeader(typed.Header),
|
||||||
Gid: typed.Header.Gid,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the file system.
|
// Call the file system.
|
||||||
|
@ -110,6 +116,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
|
||||||
case *bazilfuse.LookupRequest:
|
case *bazilfuse.LookupRequest:
|
||||||
// Convert the request.
|
// Convert the request.
|
||||||
req := &LookUpInodeRequest{
|
req := &LookUpInodeRequest{
|
||||||
|
Header: convertHeader(typed.Header),
|
||||||
Parent: InodeID(typed.Header.Node),
|
Parent: InodeID(typed.Header.Node),
|
||||||
Name: typed.Name,
|
Name: typed.Name,
|
||||||
}
|
}
|
||||||
|
@ -132,7 +139,8 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
|
||||||
case *bazilfuse.GetattrRequest:
|
case *bazilfuse.GetattrRequest:
|
||||||
// Convert the request.
|
// Convert the request.
|
||||||
req := &GetInodeAttributesRequest{
|
req := &GetInodeAttributesRequest{
|
||||||
Inode: InodeID(typed.Header.Node),
|
Header: convertHeader(typed.Header),
|
||||||
|
Inode: InodeID(typed.Header.Node),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the file system.
|
// Call the file system.
|
||||||
|
@ -155,6 +163,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
|
||||||
case *bazilfuse.MkdirRequest:
|
case *bazilfuse.MkdirRequest:
|
||||||
// Convert the request.
|
// Convert the request.
|
||||||
req := &MkDirRequest{
|
req := &MkDirRequest{
|
||||||
|
Header: convertHeader(typed.Header),
|
||||||
Parent: InodeID(typed.Header.Node),
|
Parent: InodeID(typed.Header.Node),
|
||||||
Name: typed.Name,
|
Name: typed.Name,
|
||||||
Mode: typed.Mode,
|
Mode: typed.Mode,
|
||||||
|
@ -180,8 +189,9 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
|
||||||
if typed.Dir {
|
if typed.Dir {
|
||||||
// Convert the request.
|
// Convert the request.
|
||||||
req := &OpenDirRequest{
|
req := &OpenDirRequest{
|
||||||
Inode: InodeID(typed.Header.Node),
|
Header: convertHeader(typed.Header),
|
||||||
Flags: typed.Flags,
|
Inode: InodeID(typed.Header.Node),
|
||||||
|
Flags: typed.Flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the file system.
|
// Call the file system.
|
||||||
|
@ -202,8 +212,9 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
|
||||||
} else {
|
} else {
|
||||||
// Convert the request.
|
// Convert the request.
|
||||||
req := &OpenFileRequest{
|
req := &OpenFileRequest{
|
||||||
Inode: InodeID(typed.Header.Node),
|
Header: convertHeader(typed.Header),
|
||||||
Flags: typed.Flags,
|
Inode: InodeID(typed.Header.Node),
|
||||||
|
Flags: typed.Flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the file system.
|
// Call the file system.
|
||||||
|
@ -228,6 +239,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
|
||||||
if typed.Dir {
|
if typed.Dir {
|
||||||
// Convert the request.
|
// Convert the request.
|
||||||
req := &ReadDirRequest{
|
req := &ReadDirRequest{
|
||||||
|
Header: convertHeader(typed.Header),
|
||||||
Inode: InodeID(typed.Header.Node),
|
Inode: InodeID(typed.Header.Node),
|
||||||
Handle: HandleID(typed.Handle),
|
Handle: HandleID(typed.Handle),
|
||||||
Offset: DirOffset(typed.Offset),
|
Offset: DirOffset(typed.Offset),
|
||||||
|
@ -252,6 +264,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
|
||||||
} else {
|
} else {
|
||||||
// Convert the request.
|
// Convert the request.
|
||||||
req := &ReadFileRequest{
|
req := &ReadFileRequest{
|
||||||
|
Header: convertHeader(typed.Header),
|
||||||
Inode: InodeID(typed.Header.Node),
|
Inode: InodeID(typed.Header.Node),
|
||||||
Handle: HandleID(typed.Handle),
|
Handle: HandleID(typed.Handle),
|
||||||
Offset: typed.Offset,
|
Offset: typed.Offset,
|
||||||
|
@ -288,6 +301,7 @@ func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr {
|
||||||
Mode: attr.Mode,
|
Mode: attr.Mode,
|
||||||
Atime: attr.Atime,
|
Atime: attr.Atime,
|
||||||
Mtime: attr.Mtime,
|
Mtime: attr.Mtime,
|
||||||
|
Ctime: attr.Ctime,
|
||||||
Crtime: attr.Crtime,
|
Crtime: attr.Crtime,
|
||||||
Uid: attr.Uid,
|
Uid: attr.Uid,
|
||||||
Gid: attr.Gid,
|
Gid: attr.Gid,
|
||||||
|
|
Loading…
Reference in New Issue