From 605a52de92a89eb5208ec5b9b8722143e9798f8a Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 3 Mar 2015 10:37:09 +1100 Subject: [PATCH 01/12] Added better tests for statting new dirs. --- samples/memfs/memfs_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/samples/memfs/memfs_test.go b/samples/memfs/memfs_test.go index 9e2dfd8..2700eb6 100644 --- a/samples/memfs/memfs_test.go +++ b/samples/memfs/memfs_test.go @@ -9,6 +9,7 @@ import ( "os" "path" "strings" + "syscall" "testing" "time" @@ -22,6 +23,16 @@ import ( func TestMemFS(t *testing.T) { RunTests(t) } +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +func currentUid() uint32 + +func currentGid() uint32 + +func timespecToTime(ts syscall.Timespec) time.Time + //////////////////////////////////////////////////////////////////////// // Boilerplate //////////////////////////////////////////////////////////////////////// @@ -97,6 +108,8 @@ func (t *MemFSTest) ContentsOfEmptyFileSystem() { func (t *MemFSTest) Mkdir() { var err error var fi os.FileInfo + var stat *syscall.Stat_t + dirName := path.Join(t.mfs.Dir(), "dir") // Create a directory within the root. @@ -109,6 +122,7 @@ func (t *MemFSTest) Mkdir() { // Stat the directory. fi, err = os.Stat(dirName) + stat = fi.Sys().(*syscall.Stat_t) AssertEq(nil, err) ExpectEq("dir", fi.Name()) @@ -117,6 +131,14 @@ 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(createTime, timespecToTime(stat.Atimespec)) + ExpectEq(createTime, timespecToTime(stat.Mtimespec)) + ExpectEq(createTime, timespecToTime(stat.Ctimespec)) + // Read the directory. entries, err := ioutil.ReadDir(dirName) From 6b81f4ce5801489db3afe0b533d1cd5f040b993c Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 3 Mar 2015 10:37:29 +1100 Subject: [PATCH 02/12] Declared a two-level mkdir test. --- samples/memfs/memfs_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/samples/memfs/memfs_test.go b/samples/memfs/memfs_test.go index 2700eb6..2cb9e4c 100644 --- a/samples/memfs/memfs_test.go +++ b/samples/memfs/memfs_test.go @@ -105,7 +105,7 @@ 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 @@ -146,6 +146,10 @@ func (t *MemFSTest) Mkdir() { ExpectThat(entries, ElementsAre()) } +func (t *MemFSTest) Mkdir_TwoLevels() { + AssertTrue(false, "TODO") +} + func (t *MemFSTest) Mkdir_AlreadyExists() { var err error dirName := path.Join(t.mfs.Dir(), "dir") From fa4c2fb864c1f9cbcf305631c075e0d69af24cb4 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 3 Mar 2015 10:40:04 +1100 Subject: [PATCH 03/12] Implemented two missing functions. --- samples/memfs/memfs_test.go | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/samples/memfs/memfs_test.go b/samples/memfs/memfs_test.go index 2cb9e4c..ea2756c 100644 --- a/samples/memfs/memfs_test.go +++ b/samples/memfs/memfs_test.go @@ -7,7 +7,9 @@ import ( "io/ioutil" "log" "os" + "os/user" "path" + "strconv" "strings" "syscall" "testing" @@ -27,9 +29,33 @@ func TestMemFS(t *testing.T) { RunTests(t) } // Helpers //////////////////////////////////////////////////////////////////////// -func currentUid() uint32 +func currentUid() uint32 { + user, err := user.Current() + if err != nil { + panic(err) + } -func currentGid() uint32 + 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 From 01878af39a3dbe81fb2808fd68c1865319925ab5 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 3 Mar 2015 10:40:52 +1100 Subject: [PATCH 04/12] Implemented timespecToTime. --- samples/memfs/memfs_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/samples/memfs/memfs_test.go b/samples/memfs/memfs_test.go index ea2756c..682a50d 100644 --- a/samples/memfs/memfs_test.go +++ b/samples/memfs/memfs_test.go @@ -57,7 +57,9 @@ func currentGid() uint32 { return uint32(gid) } -func timespecToTime(ts syscall.Timespec) time.Time +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(ts.Sec, ts.Nsec) +} //////////////////////////////////////////////////////////////////////// // Boilerplate From f9e2e55ac8226938e2e2f38d3115b51d3616b9b9 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 3 Mar 2015 10:41:30 +1100 Subject: [PATCH 05/12] Fixed some test bugs. --- samples/memfs/memfs_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/memfs/memfs_test.go b/samples/memfs/memfs_test.go index 682a50d..f3a689d 100644 --- a/samples/memfs/memfs_test.go +++ b/samples/memfs/memfs_test.go @@ -163,9 +163,9 @@ func (t *MemFSTest) Mkdir_OneLevel() { ExpectEq(currentUid(), stat.Uid) ExpectEq(currentGid(), stat.Gid) ExpectEq(0, stat.Size) - ExpectEq(createTime, timespecToTime(stat.Atimespec)) - ExpectEq(createTime, timespecToTime(stat.Mtimespec)) - ExpectEq(createTime, timespecToTime(stat.Ctimespec)) + 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) From 420fcddfacaf900ac643b05889e85a5517abc0f3 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 3 Mar 2015 10:48:26 +1100 Subject: [PATCH 06/12] Fixed time-related metadata. --- file_system.go | 9 +++++---- samples/memfs/fs.go | 1 + server.go | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/file_system.go b/file_system.go index 083902c..6ba0eef 100644 --- a/file_system.go +++ b/file_system.go @@ -159,10 +159,11 @@ 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 Uid uint32 diff --git a/samples/memfs/fs.go b/samples/memfs/fs.go index ec927b8..30a33e7 100644 --- a/samples/memfs/fs.go +++ b/samples/memfs/fs.go @@ -247,6 +247,7 @@ func (fs *memFS) MkDir( Mode: req.Mode, Atime: now, Mtime: now, + Ctime: now, Crtime: now, } diff --git a/server.go b/server.go index 242c3e2..58ee522 100644 --- a/server.go +++ b/server.go @@ -288,6 +288,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, From 8d99b454a892038342a4d0a24bec2406455e5146 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 3 Mar 2015 10:54:09 +1100 Subject: [PATCH 07/12] Added documentation callout to inode_init_owner. --- file_system.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/file_system.go b/file_system.go index 6ba0eef..7cf1e71 100644 --- a/file_system.go +++ b/file_system.go @@ -165,7 +165,7 @@ type InodeAttributes struct { 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 } @@ -210,7 +210,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, @@ -334,6 +334,16 @@ 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 } From 44901edc6b0c4847234825a87ec901ec87ce79fa Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 3 Mar 2015 10:58:01 +1100 Subject: [PATCH 08/12] Added a header with credentials to each request. --- file_system.go | 31 ++++++++++++++++++++++++++++--- samples/hellofs/hello_fs.go | 4 ++-- server.go | 27 ++++++++++++++++++++------- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/file_system.go b/file_system.go index 7cf1e71..cf6276c 100644 --- a/file_system.go +++ b/file_system.go @@ -198,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. @@ -274,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 @@ -303,6 +310,8 @@ type LookUpInodeResponse struct { } type GetInodeAttributesRequest struct { + Header RequestHeader + // The inode of interest. Inode InodeID } @@ -315,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). @@ -325,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 @@ -348,6 +361,8 @@ type MkDirResponse struct { } type OpenDirRequest struct { + Header RequestHeader + // The ID of the inode to be opened. Inode InodeID @@ -371,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 @@ -460,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). @@ -470,6 +489,8 @@ type ReleaseDirHandleResponse struct { } type OpenFileRequest struct { + Header RequestHeader + // The ID of the inode to be opened. Inode InodeID @@ -493,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 @@ -516,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). diff --git a/samples/hellofs/hello_fs.go b/samples/hellofs/hello_fs.go index b7802d3..20500a1 100644 --- a/samples/hellofs/hello_fs.go +++ b/samples/hellofs/hello_fs.go @@ -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 } diff --git a/server.go b/server.go index 58ee522..762795e 100644 --- a/server.go +++ b/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, From c81ae6d4b3ebed7f2a797ed632c6077726ca805c Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 3 Mar 2015 11:03:03 +1100 Subject: [PATCH 09/12] Fixed permissions for the root. --- samples/memfs/fs.go | 39 ++++++++++++++++++++++++++++++--------- samples/memfs/inode.go | 8 ++++++++ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/samples/memfs/fs.go b/samples/memfs/fs.go index 30a33e7..23923f1 100644 --- a/samples/memfs/fs.go +++ b/samples/memfs/fs.go @@ -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) { diff --git a/samples/memfs/inode.go b/samples/memfs/inode.go index a22bdc7..f17306e 100644 --- a/samples/memfs/inode.go +++ b/samples/memfs/inode.go @@ -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 From a87e1b33cf93d1e461bfed8ebbef9204e40969da Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 3 Mar 2015 11:04:17 +1100 Subject: [PATCH 10/12] Fixed permissions in MkDir. --- samples/memfs/fs.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/samples/memfs/fs.go b/samples/memfs/fs.go index 23923f1..27906ac 100644 --- a/samples/memfs/fs.go +++ b/samples/memfs/fs.go @@ -262,7 +262,8 @@ 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, @@ -270,8 +271,11 @@ func (fs *memFS) MkDir( 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() From e46341ae0073739f0e9f0002d191f96bb31a82e8 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 3 Mar 2015 11:07:24 +1100 Subject: [PATCH 11/12] MemFSTest.Mkdir_TwoLevels --- samples/memfs/memfs_test.go | 67 +++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/samples/memfs/memfs_test.go b/samples/memfs/memfs_test.go index f3a689d..8c03081 100644 --- a/samples/memfs/memfs_test.go +++ b/samples/memfs/memfs_test.go @@ -137,6 +137,7 @@ 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") @@ -145,7 +146,7 @@ func (t *MemFSTest) Mkdir_OneLevel() { err = os.Mkdir(dirName, 0754) AssertEq(nil, err) - // Simulate time proceeding. + // Simulate time advancing. t.clock.AdvanceTime(time.Second) // Stat the directory. @@ -168,14 +169,74 @@ func (t *MemFSTest) Mkdir_OneLevel() { 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() { - AssertTrue(false, "TODO") + 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() { From 832e800f3b204106ea41f92c5004ff9b9ce8daec Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 3 Mar 2015 11:08:23 +1100 Subject: [PATCH 12/12] MemFSTest.Mkdir_PermissionDenied --- samples/memfs/memfs_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/samples/memfs/memfs_test.go b/samples/memfs/memfs_test.go index 8c03081..683413f 100644 --- a/samples/memfs/memfs_test.go +++ b/samples/memfs/memfs_test.go @@ -281,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") }