Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
J
jacobsa-fuse
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
jacobsa-fuse
Commits
79d87d59
Commit
79d87d59
authored
Mar 03, 2015
by
Aaron Jacobs
Browse files
Options
Browse Files
Download
Plain Diff
Added a FileSystem.MkDir method.
Expanded memfs to support it, including tests.
parents
79aa1c32
17799d45
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
523 additions
and
193 deletions
+523
-193
file_system.go
file_system.go
+60
-30
fuseutil/not_implemented_file_system.go
fuseutil/not_implemented_file_system.go
+6
-0
samples/memfs/dir.go
samples/memfs/dir.go
+0
-48
samples/memfs/file.go
samples/memfs/file.go
+0
-36
samples/memfs/fs.go
samples/memfs/fs.go
+187
-44
samples/memfs/inode.go
samples/memfs/inode.go
+152
-12
samples/memfs/memfs_test.go
samples/memfs/memfs_test.go
+68
-2
server.go
server.go
+50
-21
No files found.
file_system.go
View file @
79d87d59
...
...
@@ -52,6 +52,16 @@ type FileSystem interface {
ctx
context
.
Context
,
req
*
ForgetInodeRequest
)
(
*
ForgetInodeResponse
,
error
)
///////////////////////////////////
// Inode creation
///////////////////////////////////
// Create a directory inode as a child of an existing directory inode. The
// kernel sends this in response to a mkdir(2) call.
MkDir
(
ctx
context
.
Context
,
req
*
MkDirRequest
)
(
*
MkDirResponse
,
error
)
///////////////////////////////////
// Directory handles
///////////////////////////////////
...
...
@@ -187,36 +197,10 @@ type HandleID uint64
// ReadDirRequest.Offset for details.
type
DirOffset
uint64
////////////////////////////////////////////////////////////////////////
// Requests and responses
////////////////////////////////////////////////////////////////////////
type
InitRequest
struct
{
// User and group IDs for the process that is mounting the file system.
Uid
uint32
Gid
uint32
}
type
InitResponse
struct
{
}
type
LookUpInodeRequest
struct
{
// The ID of the directory inode to which the child belongs.
Parent
InodeID
// The name of the child of interest, relative to the parent. For example, in
// this directory structure:
//
// foo/
// bar/
// baz
//
// the file system may receive a request to look up the child named "bar" for
// the parent foo/.
Name
string
}
type
LookUpInodeResponse
struct
{
// 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.
type
ChildInodeEntry
struct
{
// The ID of the child inode. The file system must ensure that the returned
// inode ID remains valid until a later call to ForgetInode.
Child
InodeID
...
...
@@ -284,6 +268,39 @@ type LookUpInodeResponse struct {
EntryExpiration
time
.
Time
}
////////////////////////////////////////////////////////////////////////
// Requests and responses
////////////////////////////////////////////////////////////////////////
type
InitRequest
struct
{
// User and group IDs for the process that is mounting the file system.
Uid
uint32
Gid
uint32
}
type
InitResponse
struct
{
}
type
LookUpInodeRequest
struct
{
// The ID of the directory inode to which the child belongs.
Parent
InodeID
// The name of the child of interest, relative to the parent. For example, in
// this directory structure:
//
// foo/
// bar/
// baz
//
// the file system may receive a request to look up the child named "bar" for
// the parent foo/.
Name
string
}
type
LookUpInodeResponse
struct
{
Entry
ChildInodeEntry
}
type
GetInodeAttributesRequest
struct
{
// The inode of interest.
Inode
InodeID
...
...
@@ -306,6 +323,19 @@ type ForgetInodeRequest struct {
type
ForgetInodeResponse
struct
{
}
type
MkDirRequest
struct
{
// The ID of parent directory inode within which to create the child.
Parent
InodeID
// The name of the child to create, and the mode with which to create it.
Name
string
Mode
os
.
FileMode
}
type
MkDirResponse
struct
{
Entry
ChildInodeEntry
}
type
OpenDirRequest
struct
{
// The ID of the inode to be opened.
Inode
InodeID
...
...
fuseutil/not_implemented_file_system.go
View file @
79d87d59
...
...
@@ -40,6 +40,12 @@ func (fs *NotImplementedFileSystem) ForgetInode(
return
nil
,
fuse
.
ENOSYS
}
func
(
fs
*
NotImplementedFileSystem
)
MkDir
(
ctx
context
.
Context
,
req
*
fuse
.
MkDirRequest
)
(
*
fuse
.
MkDirResponse
,
error
)
{
return
nil
,
fuse
.
ENOSYS
}
func
(
fs
*
NotImplementedFileSystem
)
OpenDir
(
ctx
context
.
Context
,
req
*
fuse
.
OpenDirRequest
)
(
*
fuse
.
OpenDirResponse
,
error
)
{
...
...
samples/memfs/dir.go
deleted
100644 → 0
View file @
79aa1c32
// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)
package
memfs
import
(
"fmt"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseutil"
"github.com/jacobsa/gcloud/syncutil"
)
type
memDir
struct
{
/////////////////////////
// Mutable state
/////////////////////////
mu
syncutil
.
InvariantMutex
// The contents of the directory. An entry with inode zero is unused.
//
// This array can never be shortened, nor can its elements be moved, because
// we use its indices for Dirent.Offset, which is exposed to the user who
// might be calling readdir in a loop while concurrently modifying the
// directory. Unused entries can, however, be reused.
//
// TODO(jacobsa): Add good tests exercising concurrent modifications while
// doing readdir, seekdir, etc. calls.
//
// INVARIANT: For each i, entries[i].Offset == i+1
entries
[]
fuseutil
.
Dirent
}
func
newDir
()
(
d
*
memDir
)
{
d
=
&
memDir
{}
d
.
mu
=
syncutil
.
NewInvariantMutex
(
d
.
checkInvariants
)
return
}
func
(
d
*
memDir
)
checkInvariants
()
{
for
i
,
e
:=
range
d
.
entries
{
if
e
.
Offset
!=
fuse
.
DirOffset
(
i
+
1
)
{
panic
(
fmt
.
Sprintf
(
"Unexpected offset in entry: %v"
,
e
))
}
}
}
samples/memfs/file.go
deleted
100644 → 0
View file @
79aa1c32
// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)
package
memfs
import
(
"errors"
"sync"
"github.com/jacobsa/fuse"
)
type
memFile
struct
{
/////////////////////////
// Constant data
/////////////////////////
inode
fuse
.
InodeID
/////////////////////////
// Mutable state
/////////////////////////
mu
sync
.
RWMutex
// The current contents of the file.
contents
[]
byte
// GUARDED_BY(mu)
}
// TODO(jacobsa): Add a test that various WriteAt calls with a real on-disk
// file to verify what the behavior should be here, particularly when starting
// a write well beyond EOF. Leave the test around for documentation purposes.
func
(
f
*
memFile
)
WriteAt
(
p
[]
byte
,
off
int64
)
(
n
int
,
err
error
)
{
err
=
errors
.
New
(
"TODO(jacobsa): Implement memFile.WriteAt."
)
return
}
samples/memfs/fs.go
View file @
79d87d59
...
...
@@ -5,6 +5,8 @@ package memfs
import
(
"fmt"
"os"
"time"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseutil"
...
...
@@ -26,22 +28,23 @@ type memFS struct {
// Mutable state
/////////////////////////
// When acquiring this lock, the caller must hold no inode locks.
mu
syncutil
.
InvariantMutex
// The collection of all inodes that have ever been created, indexed by inode
// ID. Some inodes are not in use if they have been unlinked, and no inode
// with ID less than fuse.RootInodeID is ever used.
// The collection of live inodes, indexed by ID. IDs of free inodes that may
// be re-used have nil entries. No ID less than fuse.RootInodeID is ever used.
//
// INVARIANT: len(inodes) > fuse.RootInodeID
// INVARIANT: For all i < fuse.RootInodeID, inodes[i].impl == nil
// INVARIANT: inodes[fuse.RootInodeID].impl is of type *memDir
inodes
[]
inode
// GUARDED_BY(mu)
// INVARIANT: For all i < fuse.RootInodeID, inodes[i] == nil
// INVARIANT: inodes[fuse.RootInodeID] != nil
// INVARIANT: inodes[fuse.RootInodeID].dir is true
inodes
[]
*
inode
// GUARDED_BY(mu)
// A list of inode IDs within inodes available for reuse, not including the
// reserved IDs less than fuse.RootInodeID.
//
// INVARIANT: This is all and only indices i of
inodes
such that i >
// fuse.RootInodeID and inodes[i]
.impl
== nil
// INVARIANT: This is all and only indices i of
'inodes'
such that i >
// fuse.RootInodeID and inodes[i] == nil
freeInodes
[]
fuse
.
InodeID
// GUARDED_BY(mu)
}
...
...
@@ -51,11 +54,15 @@ func NewMemFS(
// Set up the basic struct.
fs
:=
&
memFS
{
clock
:
clock
,
inodes
:
make
([]
inode
,
fuse
.
RootInodeID
+
1
),
inodes
:
make
([]
*
inode
,
fuse
.
RootInodeID
+
1
),
}
// Set up the root inode.
fs
.
inodes
[
fuse
.
RootInodeID
]
.
impl
=
newDir
()
rootAttrs
:=
fuse
.
InodeAttributes
{
Mode
:
0777
|
os
.
ModeDir
,
}
fs
.
inodes
[
fuse
.
RootInodeID
]
=
newInode
(
rootAttrs
)
// Set up invariant checking.
fs
.
mu
=
syncutil
.
NewInvariantMutex
(
fs
.
checkInvariants
)
...
...
@@ -64,27 +71,23 @@ func NewMemFS(
}
func
(
fs
*
memFS
)
checkInvariants
()
{
// Check general inode invariants.
for
i
:=
range
fs
.
inodes
{
fs
.
inodes
[
i
]
.
checkInvariants
()
}
// Check reserved inodes.
for
i
:=
0
;
i
<
fuse
.
RootInodeID
;
i
++
{
var
inode
*
inode
=
&
fs
.
inodes
[
i
]
if
inode
.
impl
!=
nil
{
panic
(
fmt
.
Sprintf
(
"Non-nil impl for ID: %v"
,
i
))
if
fs
.
inodes
[
i
]
!=
nil
{
panic
(
fmt
.
Sprintf
(
"Non-nil inode for ID: %v"
,
i
))
}
}
// Check the root inode.
_
=
fs
.
inodes
[
fuse
.
RootInodeID
]
.
impl
.
(
*
memDir
)
if
!
fs
.
inodes
[
fuse
.
RootInodeID
]
.
dir
{
panic
(
"Expected root to be a directory."
)
}
//
Check inodes, building our own se
t of free IDs.
//
Build our own lis
t of free IDs.
freeIDsEncountered
:=
make
(
map
[
fuse
.
InodeID
]
struct
{})
for
i
:=
fuse
.
RootInodeID
+
1
;
i
<
len
(
fs
.
inodes
);
i
++
{
var
inode
*
inode
=
&
fs
.
inodes
[
i
]
if
inode
.
impl
==
nil
{
inode
:=
fs
.
inodes
[
i
]
if
inode
==
nil
{
freeIDsEncountered
[
fuse
.
InodeID
(
i
)]
=
struct
{}{}
continue
}
...
...
@@ -113,19 +116,154 @@ func (fs *memFS) Init(
return
}
// Panic if not a live dir.
// Find the given inode and return it with its lock held. Panic if it doesn't
// exist.
//
// SHARED_LOCKS_REQUIRED(fs.mu)
// EXCLUSIVE_LOCK_FUNCTION(inode.mu)
func
(
fs
*
memFS
)
getInodeForModifyingOrDie
(
id
fuse
.
InodeID
)
(
inode
*
inode
)
{
inode
=
fs
.
inodes
[
id
]
if
inode
==
nil
{
panic
(
fmt
.
Sprintf
(
"Unknown inode: %v"
,
id
))
}
inode
.
mu
.
Lock
()
return
}
// Find the given inode and return it with its lock held for reading. Panic if
// it doesn't exist.
//
// LOCKS_EXCLUDED(fs.mu)
func
(
fs
*
memFS
)
getDirOrDie
(
inodeID
fuse
.
InodeID
)
(
d
*
memDir
)
{
// SHARED_LOCKS_REQUIRED(fs.mu)
// SHARED_LOCK_FUNCTION(inode.mu)
func
(
fs
*
memFS
)
getInodeForReadingOrDie
(
id
fuse
.
InodeID
)
(
inode
*
inode
)
{
inode
=
fs
.
inodes
[
id
]
if
inode
==
nil
{
panic
(
fmt
.
Sprintf
(
"Unknown inode: %v"
,
id
))
}
inode
.
mu
.
RLock
()
return
}
// Allocate a new inode, assigning it an ID that is not in use. Return it with
// its lock held.
//
// EXCLUSIVE_LOCKS_REQUIRED(fs.mu)
// EXCLUSIVE_LOCK_FUNCTION(inode.mu)
func
(
fs
*
memFS
)
allocateInode
(
attrs
fuse
.
InodeAttributes
)
(
id
fuse
.
InodeID
,
inode
*
inode
)
{
// Create and lock the inode.
inode
=
newInode
(
attrs
)
inode
.
mu
.
Lock
()
// Re-use a free ID if possible. Otherwise mint a new one.
numFree
:=
len
(
fs
.
freeInodes
)
if
numFree
!=
0
{
id
=
fs
.
freeInodes
[
numFree
-
1
]
fs
.
freeInodes
=
fs
.
freeInodes
[
:
numFree
-
1
]
fs
.
inodes
[
id
]
=
inode
}
else
{
id
=
fuse
.
InodeID
(
len
(
fs
.
inodes
))
fs
.
inodes
=
append
(
fs
.
inodes
,
inode
)
}
return
}
func
(
fs
*
memFS
)
LookUpInode
(
ctx
context
.
Context
,
req
*
fuse
.
LookUpInodeRequest
)
(
resp
*
fuse
.
LookUpInodeResponse
,
err
error
)
{
resp
=
&
fuse
.
LookUpInodeResponse
{}
fs
.
mu
.
RLock
()
defer
fs
.
mu
.
RUnlock
()
if
inodeID
>=
fuse
.
InodeID
(
len
(
fs
.
inodes
))
{
panic
(
fmt
.
Sprintf
(
"Inode out of range: %v vs. %v"
,
inodeID
,
len
(
fs
.
inodes
)))
// Grab the parent directory.
inode
:=
fs
.
getInodeForReadingOrDie
(
req
.
Parent
)
defer
inode
.
mu
.
RUnlock
()
// Does the directory have an entry with the given name?
childID
,
ok
:=
inode
.
LookUpChild
(
req
.
Name
)
if
!
ok
{
err
=
fuse
.
ENOENT
return
}
var
inode
*
inode
=
&
fs
.
inodes
[
inodeID
]
d
=
inode
.
impl
.
(
*
memDir
)
// Grab the child.
child
:=
fs
.
getInodeForReadingOrDie
(
childID
)
defer
child
.
mu
.
RUnlock
()
// Fill in the response.
resp
.
Entry
.
Child
=
childID
resp
.
Entry
.
Attributes
=
child
.
attributes
// We don't spontaneously mutate, so the kernel can cache as long as it wants
// (since it also handles invalidation).
resp
.
Entry
.
AttributesExpiration
=
fs
.
clock
.
Now
()
.
Add
(
365
*
24
*
time
.
Hour
)
resp
.
Entry
.
EntryExpiration
=
resp
.
Entry
.
EntryExpiration
return
}
func
(
fs
*
memFS
)
GetInodeAttributes
(
ctx
context
.
Context
,
req
*
fuse
.
GetInodeAttributesRequest
)
(
resp
*
fuse
.
GetInodeAttributesResponse
,
err
error
)
{
resp
=
&
fuse
.
GetInodeAttributesResponse
{}
fs
.
mu
.
RLock
()
defer
fs
.
mu
.
RUnlock
()
// Grab the inode.
inode
:=
fs
.
getInodeForReadingOrDie
(
req
.
Inode
)
defer
inode
.
mu
.
RUnlock
()
// Fill in the response.
resp
.
Attributes
=
inode
.
attributes
// We don't spontaneously mutate, so the kernel can cache as long as it wants
// (since it also handles invalidation).
resp
.
AttributesExpiration
=
fs
.
clock
.
Now
()
.
Add
(
365
*
24
*
time
.
Hour
)
return
}
func
(
fs
*
memFS
)
MkDir
(
ctx
context
.
Context
,
req
*
fuse
.
MkDirRequest
)
(
resp
*
fuse
.
MkDirResponse
,
err
error
)
{
resp
=
&
fuse
.
MkDirResponse
{}
fs
.
mu
.
Lock
()
defer
fs
.
mu
.
Unlock
()
// Grab the parent, which we will update shortly.
parent
:=
fs
.
getInodeForModifyingOrDie
(
req
.
Parent
)
defer
parent
.
mu
.
Unlock
()
// Allocate a child.
now
:=
fs
.
clock
.
Now
()
childAttrs
:=
fuse
.
InodeAttributes
{
Mode
:
req
.
Mode
,
Atime
:
now
,
Mtime
:
now
,
Crtime
:
now
,
}
childID
,
child
:=
fs
.
allocateInode
(
childAttrs
)
defer
child
.
mu
.
Unlock
()
// Add an entry in the parent.
parent
.
AddChild
(
childID
,
req
.
Name
,
fuseutil
.
DT_Directory
)
// Fill in the response.
resp
.
Entry
.
Child
=
childID
resp
.
Entry
.
Attributes
=
child
.
attributes
// We don't spontaneously mutate, so the kernel can cache as long as it wants
// (since it also handles invalidation).
resp
.
Entry
.
AttributesExpiration
=
fs
.
clock
.
Now
()
.
Add
(
365
*
24
*
time
.
Hour
)
resp
.
Entry
.
EntryExpiration
=
resp
.
Entry
.
EntryExpiration
return
}
...
...
@@ -135,10 +273,18 @@ func (fs *memFS) OpenDir(
req
*
fuse
.
OpenDirRequest
)
(
resp
*
fuse
.
OpenDirResponse
,
err
error
)
{
resp
=
&
fuse
.
OpenDirResponse
{}
fs
.
mu
.
RLock
()
defer
fs
.
mu
.
RUnlock
()
// We don't mutate spontaneosuly, so if the VFS layer has asked for an
// inode that doesn't exist, something screwed up earlier (a lookup, a
// cache invalidation, etc.).
_
=
fs
.
getDirOrDie
(
req
.
Inode
)
inode
:=
fs
.
getInodeForReadingOrDie
(
req
.
Inode
)
defer
inode
.
mu
.
RUnlock
()
if
!
inode
.
dir
{
panic
(
"Found non-dir."
)
}
return
}
...
...
@@ -148,21 +294,18 @@ func (fs *memFS) ReadDir(
req
*
fuse
.
ReadDirRequest
)
(
resp
*
fuse
.
ReadDirResponse
,
err
error
)
{
resp
=
&
fuse
.
ReadDirResponse
{}
// Grab the directory.
d
:=
fs
.
getDirOrDie
(
req
.
Inode
)
d
.
mu
.
RLock
()
defer
d
.
mu
.
RUnlock
()
fs
.
mu
.
RLock
()
defer
fs
.
mu
.
RUnlock
()
//
Return the entries requested
.
for
i
:=
int
(
req
.
Offset
);
i
<
len
(
d
.
entries
);
i
++
{
resp
.
Data
=
fuseutil
.
AppendDirent
(
resp
.
Data
,
d
.
entries
[
i
]
)
//
Grab the directory
.
inode
:=
fs
.
getInodeForReadingOrDie
(
req
.
Inode
)
defer
inode
.
mu
.
RUnlock
(
)
// Trim and stop early if we've exceeded the requested size
.
if
len
(
resp
.
Data
)
>
req
.
Size
{
resp
.
Data
=
resp
.
Data
[
:
req
.
Size
]
break
}
// Serve the request
.
resp
.
Data
,
err
=
inode
.
ReadDir
(
int
(
req
.
Offset
),
req
.
Size
)
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"inode.ReadDir: %v"
,
err
)
return
}
return
...
...
samples/memfs/inode.go
View file @
79d87d59
...
...
@@ -5,29 +5,169 @@ package memfs
import
(
"fmt"
"reflect"
"os"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseutil"
"github.com/jacobsa/gcloud/syncutil"
)
// Common attributes for files and directories.
//
// TODO(jacobsa): Add tests for interacting with a file/directory after it has
// been unlinked, including creating a new file. Make sure we don't screw up
// and reuse
the inode
while it is still in use.
// and reuse
an inode ID
while it is still in use.
type
inode
struct
{
// The *memFile or *memDir for this inode, or nil if the inode is available
// for reuse.
/////////////////////////
// Constant data
/////////////////////////
// Is this a directory? If not, it is a file.
dir
bool
/////////////////////////
// Mutable state
/////////////////////////
mu
syncutil
.
InvariantMutex
// The current attributes of this inode.
//
// INVARIANT: No non-permission mode bits are set besides os.ModeDir
// INVARIANT: If dir, then os.ModeDir is set
// INVARIANT: If !dir, then os.ModeDir is not set
attributes
fuse
.
InodeAttributes
// GUARDED_BY(mu)
// For directories, entries describing the children of the directory.
//
// INVARIANT: impl is nil, or of type *memFile or *memDir
impl
interface
{}
// This array can never be shortened, nor can its elements be moved, because
// we use its indices for Dirent.Offset, which is exposed to the user who
// might be calling readdir in a loop while concurrently modifying the
// directory. Unused entries can, however, be reused.
//
// TODO(jacobsa): Add good tests exercising concurrent modifications while
// doing readdir, seekdir, etc. calls.
//
// INVARIANT: If dir is false, this is nil.
// INVARIANT: For each i, entries[i].Offset == i+1
// INVARIANT: Contains no duplicate names.
entries
[]
fuseutil
.
Dirent
// GUARDED_BY(mu)
// For files, the current contents of the file.
//
// INVARIANT: If dir is true, this is nil.
contents
[]
byte
// GUARDED_BY(mu)
}
func
newInode
(
attrs
fuse
.
InodeAttributes
)
(
in
*
inode
)
{
in
=
&
inode
{
dir
:
(
attrs
.
Mode
&
os
.
ModeDir
!=
0
),
attributes
:
attrs
,
}
in
.
mu
=
syncutil
.
NewInvariantMutex
(
in
.
checkInvariants
)
return
}
func
(
inode
*
inode
)
checkInvariants
()
{
switch
inode
.
impl
.
(
type
)
{
case
nil
:
case
*
memFile
:
case
*
memDir
:
default
:
// No non-permission mode bits should be set besides os.ModeDir.
if
inode
.
attributes
.
Mode
&
^
(
os
.
ModePerm
|
os
.
ModeDir
)
!=
0
{
panic
(
fmt
.
Sprintf
(
"Unexpected mode: %v"
,
inode
.
attributes
.
Mode
))
}
// Check os.ModeDir.
if
inode
.
dir
!=
(
inode
.
attributes
.
Mode
&
os
.
ModeDir
==
os
.
ModeDir
)
{
panic
(
fmt
.
Sprintf
(
"Unexpected inode impl type: %v"
,
reflect
.
TypeOf
(
inode
.
impl
)))
fmt
.
Sprintf
(
"Unexpected mode: %v, dir: %v"
,
inode
.
attributes
.
Mode
,
inode
.
dir
))
}
// Check directory-specific stuff.
if
inode
.
dir
{
if
inode
.
contents
!=
nil
{
panic
(
"Non-nil contents in a directory."
)
}
childNames
:=
make
(
map
[
string
]
struct
{})
for
i
,
e
:=
range
inode
.
entries
{
if
e
.
Offset
!=
fuse
.
DirOffset
(
i
+
1
)
{
panic
(
fmt
.
Sprintf
(
"Unexpected offset: %v"
,
e
.
Offset
))
}
if
_
,
ok
:=
childNames
[
e
.
Name
];
ok
{
panic
(
fmt
.
Sprintf
(
"Duplicate name: %s"
,
e
.
Name
))
}
childNames
[
e
.
Name
]
=
struct
{}{}
}
}
// Check file-specific stuff.
if
!
inode
.
dir
{
if
inode
.
entries
!=
nil
{
panic
(
"Non-nil entries in a file."
)
}
}
}
// Find an entry for the given child name and return its inode ID.
//
// REQUIRES: inode.dir
// SHARED_LOCKS_REQUIRED(inode.mu)
func
(
inode
*
inode
)
LookUpChild
(
name
string
)
(
id
fuse
.
InodeID
,
ok
bool
)
{
if
!
inode
.
dir
{
panic
(
"LookUpChild called on non-directory."
)
}
for
_
,
e
:=
range
inode
.
entries
{
if
e
.
Name
==
name
{
id
=
e
.
Inode
ok
=
true
return
}
}
return
}
// Add an entry for a child.
//
// REQUIRES: inode.dir
// EXCLUSIVE_LOCKS_REQUIRED(inode.mu)
func
(
inode
*
inode
)
AddChild
(
id
fuse
.
InodeID
,
name
string
,
dt
fuseutil
.
DirentType
)
{
e
:=
fuseutil
.
Dirent
{
Offset
:
fuse
.
DirOffset
(
len
(
inode
.
entries
)
+
1
),
Inode
:
id
,
Name
:
name
,
Type
:
dt
,
}
inode
.
entries
=
append
(
inode
.
entries
,
e
)
}
// Serve a ReadDir request.
//
// REQUIRED: inode.dir
// SHARED_LOCKS_REQUIRED(inode.mu)
func
(
inode
*
inode
)
ReadDir
(
offset
int
,
size
int
)
(
data
[]
byte
,
err
error
)
{
if
!
inode
.
dir
{
panic
(
"ReadDir called on non-directory."
)
}
for
i
:=
offset
;
i
<
len
(
inode
.
entries
);
i
++
{
data
=
fuseutil
.
AppendDirent
(
data
,
inode
.
entries
[
i
])
// Trim and stop early if we've exceeded the requested size.
if
len
(
data
)
>
size
{
data
=
data
[
:
size
]
break
}
}
return
}
samples/memfs/memfs_test.go
View file @
79d87d59
...
...
@@ -6,6 +6,8 @@ package memfs_test
import
(
"io/ioutil"
"log"
"os"
"path"
"strings"
"testing"
"time"
...
...
@@ -93,11 +95,75 @@ func (t *MemFSTest) ContentsOfEmptyFileSystem() {
}
func
(
t
*
MemFSTest
)
Mkdir
()
{
AssertTrue
(
false
,
"TODO"
)
var
err
error
var
fi
os
.
FileInfo
dirName
:=
path
.
Join
(
t
.
mfs
.
Dir
(),
"dir"
)
// Create a directory within the root.
createTime
:=
t
.
clock
.
Now
()
err
=
os
.
Mkdir
(
dirName
,
0754
)
AssertEq
(
nil
,
err
)
// Simulate time proceeding.
t
.
clock
.
AdvanceTime
(
time
.
Second
)
// Stat the directory.
fi
,
err
=
os
.
Stat
(
dirName
)
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
())
// Read the directory.
entries
,
err
:=
ioutil
.
ReadDir
(
dirName
)
AssertEq
(
nil
,
err
)
ExpectThat
(
entries
,
ElementsAre
())
}
func
(
t
*
MemFSTest
)
Mkdir_AlreadyExists
()
{
AssertTrue
(
false
,
"TODO"
)
var
err
error
dirName
:=
path
.
Join
(
t
.
mfs
.
Dir
(),
"dir"
)
// Create the directory once.
err
=
os
.
Mkdir
(
dirName
,
0754
)
AssertEq
(
nil
,
err
)
// Attempt to create it again.
err
=
os
.
Mkdir
(
dirName
,
0754
)
AssertNe
(
nil
,
err
)
ExpectThat
(
err
,
Error
(
HasSubstr
(
"exists"
)))
}
func
(
t
*
MemFSTest
)
Mkdir_IntermediateIsFile
()
{
var
err
error
// Create a file.
fileName
:=
path
.
Join
(
t
.
mfs
.
Dir
(),
"foo"
)
err
=
ioutil
.
WriteFile
(
fileName
,
[]
byte
{},
0700
)
AssertEq
(
nil
,
err
)
// Attempt to create a directory within the file.
dirName
:=
path
.
Join
(
fileName
,
"dir"
)
err
=
os
.
Mkdir
(
dirName
,
0754
)
AssertNe
(
nil
,
err
)
ExpectThat
(
err
,
Error
(
HasSubstr
(
"TODO"
)))
}
func
(
t
*
MemFSTest
)
Mkdir_IntermediateIsNonExistent
()
{
var
err
error
// Attempt to create a sub-directory of a non-existent sub-directory.
dirName
:=
path
.
Join
(
t
.
mfs
.
Dir
(),
"foo/dir"
)
err
=
os
.
Mkdir
(
dirName
,
0754
)
AssertNe
(
nil
,
err
)
ExpectThat
(
err
,
Error
(
HasSubstr
(
"no such file or directory"
)))
}
func
(
t
*
MemFSTest
)
CreateNewFile_InRoot
()
{
...
...
server.go
View file @
79d87d59
...
...
@@ -32,6 +32,17 @@ func newServer(fs FileSystem) (s *server, err error) {
return
}
func
convertChildInodeEntry
(
clock
timeutil
.
Clock
,
in
*
ChildInodeEntry
,
out
*
bazilfuse
.
LookupResponse
)
{
out
.
Node
=
bazilfuse
.
NodeID
(
in
.
Child
)
out
.
Generation
=
uint64
(
in
.
Generation
)
out
.
Attr
=
convertAttributes
(
in
.
Child
,
in
.
Attributes
)
out
.
AttrValid
=
in
.
AttributesExpiration
.
Sub
(
clock
.
Now
())
out
.
EntryValid
=
in
.
EntryExpiration
.
Sub
(
clock
.
Now
())
}
// 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.
...
...
@@ -78,14 +89,14 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
// Call the file system.
_
,
err
:=
s
.
fs
.
Init
(
ctx
,
req
)
if
err
!=
nil
{
s
.
logger
.
Print
(
"Responding:"
,
err
)
s
.
logger
.
Print
ln
(
"Responding:"
,
err
)
typed
.
RespondError
(
err
)
return
}
// Convert the response.
fuseResp
:=
&
bazilfuse
.
InitResponse
{}
s
.
logger
.
Print
(
"Responding:"
,
fuseResp
)
s
.
logger
.
Print
ln
(
"Responding:"
,
fuseResp
)
typed
.
Respond
(
fuseResp
)
case
*
bazilfuse
.
StatfsRequest
:
...
...
@@ -106,21 +117,16 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
// Call the file system.
resp
,
err
:=
s
.
fs
.
LookUpInode
(
ctx
,
req
)
if
err
!=
nil
{
s
.
logger
.
Print
(
"Responding:"
,
err
)
s
.
logger
.
Print
ln
(
"Responding:"
,
err
)
typed
.
RespondError
(
err
)
return
}
// Convert the response.
fuseResp
:=
&
bazilfuse
.
LookupResponse
{
Node
:
bazilfuse
.
NodeID
(
resp
.
Child
),
Generation
:
uint64
(
resp
.
Generation
),
Attr
:
convertAttributes
(
resp
.
Child
,
resp
.
Attributes
),
AttrValid
:
resp
.
AttributesExpiration
.
Sub
(
s
.
clock
.
Now
()),
EntryValid
:
resp
.
EntryExpiration
.
Sub
(
s
.
clock
.
Now
()),
}
fuseResp
:=
&
bazilfuse
.
LookupResponse
{}
convertChildInodeEntry
(
s
.
clock
,
&
resp
.
Entry
,
fuseResp
)
s
.
logger
.
Print
(
"Responding:"
,
fuseResp
)
s
.
logger
.
Print
ln
(
"Responding:"
,
fuseResp
)
typed
.
Respond
(
fuseResp
)
case
*
bazilfuse
.
GetattrRequest
:
...
...
@@ -132,7 +138,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
// Call the file system.
resp
,
err
:=
s
.
fs
.
GetInodeAttributes
(
ctx
,
req
)
if
err
!=
nil
{
s
.
logger
.
Print
(
"Responding:"
,
err
)
s
.
logger
.
Print
ln
(
"Responding:"
,
err
)
typed
.
RespondError
(
err
)
return
}
...
...
@@ -143,7 +149,30 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
AttrValid
:
resp
.
AttributesExpiration
.
Sub
(
s
.
clock
.
Now
()),
}
s
.
logger
.
Print
(
"Responding:"
,
fuseResp
)
s
.
logger
.
Println
(
"Responding:"
,
fuseResp
)
typed
.
Respond
(
fuseResp
)
case
*
bazilfuse
.
MkdirRequest
:
// Convert the request.
req
:=
&
MkDirRequest
{
Parent
:
InodeID
(
typed
.
Header
.
Node
),
Name
:
typed
.
Name
,
Mode
:
typed
.
Mode
,
}
// Call the file system.
resp
,
err
:=
s
.
fs
.
MkDir
(
ctx
,
req
)
if
err
!=
nil
{
s
.
logger
.
Println
(
"Responding:"
,
err
)
typed
.
RespondError
(
err
)
return
}
// Convert the response.
fuseResp
:=
&
bazilfuse
.
MkdirResponse
{}
convertChildInodeEntry
(
s
.
clock
,
&
resp
.
Entry
,
&
fuseResp
.
LookupResponse
)
s
.
logger
.
Println
(
"Responding:"
,
fuseResp
)
typed
.
Respond
(
fuseResp
)
case
*
bazilfuse
.
OpenRequest
:
...
...
@@ -158,7 +187,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
// Call the file system.
resp
,
err
:=
s
.
fs
.
OpenDir
(
ctx
,
req
)
if
err
!=
nil
{
s
.
logger
.
Print
(
"Responding:"
,
err
)
s
.
logger
.
Print
ln
(
"Responding:"
,
err
)
typed
.
RespondError
(
err
)
return
}
...
...
@@ -168,7 +197,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
Handle
:
bazilfuse
.
HandleID
(
resp
.
Handle
),
}
s
.
logger
.
Print
(
"Responding:"
,
fuseResp
)
s
.
logger
.
Print
ln
(
"Responding:"
,
fuseResp
)
typed
.
Respond
(
fuseResp
)
}
else
{
// Convert the request.
...
...
@@ -180,7 +209,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
// Call the file system.
resp
,
err
:=
s
.
fs
.
OpenFile
(
ctx
,
req
)
if
err
!=
nil
{
s
.
logger
.
Print
(
"Responding:"
,
err
)
s
.
logger
.
Print
ln
(
"Responding:"
,
err
)
typed
.
RespondError
(
err
)
return
}
...
...
@@ -190,7 +219,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
Handle
:
bazilfuse
.
HandleID
(
resp
.
Handle
),
}
s
.
logger
.
Print
(
"Responding:"
,
fuseResp
)
s
.
logger
.
Print
ln
(
"Responding:"
,
fuseResp
)
typed
.
Respond
(
fuseResp
)
}
...
...
@@ -208,7 +237,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
// Call the file system.
resp
,
err
:=
s
.
fs
.
ReadDir
(
ctx
,
req
)
if
err
!=
nil
{
s
.
logger
.
Print
(
"Responding:"
,
err
)
s
.
logger
.
Print
ln
(
"Responding:"
,
err
)
typed
.
RespondError
(
err
)
return
}
...
...
@@ -218,7 +247,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
Data
:
resp
.
Data
,
}
s
.
logger
.
Print
(
"Responding:"
,
fuseResp
)
s
.
logger
.
Print
ln
(
"Responding:"
,
fuseResp
)
typed
.
Respond
(
fuseResp
)
}
else
{
// Convert the request.
...
...
@@ -232,7 +261,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
// Call the file system.
resp
,
err
:=
s
.
fs
.
ReadFile
(
ctx
,
req
)
if
err
!=
nil
{
s
.
logger
.
Print
(
"Responding:"
,
err
)
s
.
logger
.
Print
ln
(
"Responding:"
,
err
)
typed
.
RespondError
(
err
)
return
}
...
...
@@ -242,7 +271,7 @@ func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
Data
:
resp
.
Data
,
}
s
.
logger
.
Print
(
"Responding:"
,
fuseResp
)
s
.
logger
.
Print
ln
(
"Responding:"
,
fuseResp
)
typed
.
Respond
(
fuseResp
)
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment