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
895b8c41
Commit
895b8c41
authored
Dec 15, 2015
by
Aaron Jacobs
Browse files
Options
Browse Files
Download
Plain Diff
Added support for mknod(2) and related calls to fuse_mknod.
For googlecloudplatform/gcsfuse#137.
parents
1dcc6791
ab9f5b35
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
191 additions
and
17 deletions
+191
-17
conversions.go
conversions.go
+26
-0
fuseops/ops.go
fuseops/ops.go
+27
-0
fuseutil/file_system.go
fuseutil/file_system.go
+4
-0
fuseutil/not_implemented_file_system.go
fuseutil/not_implemented_file_system.go
+7
-0
samples/memfs/memfs.go
samples/memfs/memfs.go
+29
-12
samples/memfs/memfs_test.go
samples/memfs/memfs_test.go
+98
-5
No files found.
conversions.go
View file @
895b8c41
...
...
@@ -134,6 +134,27 @@ func convertInMessage(
Mode
:
convertFileMode
(
in
.
Mode
)
|
os
.
ModeDir
,
}
case
fusekernel
.
OpMknod
:
in
:=
(
*
fusekernel
.
MknodIn
)(
inMsg
.
Consume
(
fusekernel
.
MknodInSize
(
protocol
)))
if
in
==
nil
{
err
=
errors
.
New
(
"Corrupt OpMknod"
)
return
}
name
:=
inMsg
.
ConsumeBytes
(
inMsg
.
Len
())
i
:=
bytes
.
IndexByte
(
name
,
'\x00'
)
if
i
<
0
{
err
=
errors
.
New
(
"Corrupt OpMknod"
)
return
}
name
=
name
[
:
i
]
o
=
&
fuseops
.
MkNodeOp
{
Parent
:
fuseops
.
InodeID
(
inMsg
.
Header
()
.
Nodeid
),
Name
:
string
(
name
),
Mode
:
convertFileMode
(
in
.
Mode
),
}
case
fusekernel
.
OpCreate
:
in
:=
(
*
fusekernel
.
CreateIn
)(
inMsg
.
Consume
(
fusekernel
.
CreateInSize
(
protocol
)))
if
in
==
nil
{
...
...
@@ -491,6 +512,11 @@ func (c *Connection) kernelResponseForOp(
out
:=
(
*
fusekernel
.
EntryOut
)(
m
.
Grow
(
size
))
convertChildInodeEntry
(
&
o
.
Entry
,
out
)
case
*
fuseops
.
MkNodeOp
:
size
:=
fusekernel
.
EntryOutSize
(
c
.
protocol
)
out
:=
(
*
fusekernel
.
EntryOut
)(
m
.
Grow
(
size
))
convertChildInodeEntry
(
&
o
.
Entry
,
out
)
case
*
fuseops
.
CreateFileOp
:
eSize
:=
fusekernel
.
EntryOutSize
(
c
.
protocol
)
...
...
fuseops/ops.go
View file @
895b8c41
...
...
@@ -235,6 +235,33 @@ type MkDirOp struct {
Entry
ChildInodeEntry
}
// Create a file inode as a child of an existing directory inode. The kernel
// sends this in response to a mknod(2) call. It may also send it in special
// cases such as an NFS export (cf. https://goo.gl/HiLfnK). It is more typical
// to see CreateFileOp, which is received for an open(2) that creates a file.
//
// The Linux kernel appears to verify the name doesn't already exist (mknod
// calls sys_mknodat calls user_path_create calls filename_create, which
// verifies: http://goo.gl/FZpLu5). But osxfuse may not guarantee this, as with
// mkdir(2). And if names may be created outside of the kernel's control, it
// doesn't matter what the kernel does anyway.
//
// Therefore the file system should return EEXIST if the name already exists.
type
MkNodeOp
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
// Set by the file system: information about the inode that was created.
//
// The lookup count for the inode is implicitly incremented. See notes on
// ForgetInodeOp for more information.
Entry
ChildInodeEntry
}
// Create a file inode and open it.
//
// The kernel sends this when the user asks to open a file with the O_CREAT
...
...
fuseutil/file_system.go
View file @
895b8c41
...
...
@@ -41,6 +41,7 @@ type FileSystem interface {
SetInodeAttributes
(
context
.
Context
,
*
fuseops
.
SetInodeAttributesOp
)
error
ForgetInode
(
context
.
Context
,
*
fuseops
.
ForgetInodeOp
)
error
MkDir
(
context
.
Context
,
*
fuseops
.
MkDirOp
)
error
MkNode
(
context
.
Context
,
*
fuseops
.
MkNodeOp
)
error
CreateFile
(
context
.
Context
,
*
fuseops
.
CreateFileOp
)
error
CreateSymlink
(
context
.
Context
,
*
fuseops
.
CreateSymlinkOp
)
error
Rename
(
context
.
Context
,
*
fuseops
.
RenameOp
)
error
...
...
@@ -138,6 +139,9 @@ func (s *fileSystemServer) handleOp(
case
*
fuseops
.
MkDirOp
:
err
=
s
.
fs
.
MkDir
(
ctx
,
typed
)
case
*
fuseops
.
MkNodeOp
:
err
=
s
.
fs
.
MkNode
(
ctx
,
typed
)
case
*
fuseops
.
CreateFileOp
:
err
=
s
.
fs
.
CreateFile
(
ctx
,
typed
)
...
...
fuseutil/not_implemented_file_system.go
View file @
895b8c41
...
...
@@ -71,6 +71,13 @@ func (fs *NotImplementedFileSystem) MkDir(
return
}
func
(
fs
*
NotImplementedFileSystem
)
MkNode
(
ctx
context
.
Context
,
op
*
fuseops
.
MkNodeOp
)
(
err
error
)
{
err
=
fuse
.
ENOSYS
return
}
func
(
fs
*
NotImplementedFileSystem
)
CreateFile
(
ctx
context
.
Context
,
op
*
fuseops
.
CreateFileOp
)
(
err
error
)
{
...
...
samples/memfs/memfs.go
View file @
895b8c41
...
...
@@ -305,28 +305,37 @@ func (fs *memFS) MkDir(
return
}
func
(
fs
*
memFS
)
CreateFil
e
(
func
(
fs
*
memFS
)
MkNod
e
(
ctx
context
.
Context
,
op
*
fuseops
.
CreateFil
eOp
)
(
err
error
)
{
op
*
fuseops
.
MkNod
eOp
)
(
err
error
)
{
fs
.
mu
.
Lock
()
defer
fs
.
mu
.
Unlock
()
op
.
Entry
,
err
=
fs
.
createFile
(
op
.
Parent
,
op
.
Name
,
op
.
Mode
)
return
}
// LOCKS_REQUIRED(fs.mu)
func
(
fs
*
memFS
)
createFile
(
parentID
fuseops
.
InodeID
,
name
string
,
mode
os
.
FileMode
)
(
entry
fuseops
.
ChildInodeEntry
,
err
error
)
{
// Grab the parent, which we will update shortly.
parent
:=
fs
.
getInodeOrDie
(
op
.
Parent
)
parent
:=
fs
.
getInodeOrDie
(
parentID
)
// Ensure that the name doesn't already exist, so we don't wind up with a
// duplicate.
_
,
_
,
exists
:=
parent
.
LookUpChild
(
op
.
N
ame
)
_
,
_
,
exists
:=
parent
.
LookUpChild
(
n
ame
)
if
exists
{
err
=
fuse
.
EEXIST
return
}
// Set up attributes f
rom
the child.
// Set up attributes f
or
the child.
now
:=
time
.
Now
()
childAttrs
:=
fuseops
.
InodeAttributes
{
Nlink
:
1
,
Mode
:
op
.
M
ode
,
Mode
:
m
ode
,
Atime
:
now
,
Mtime
:
now
,
Ctime
:
now
,
...
...
@@ -339,19 +348,27 @@ func (fs *memFS) CreateFile(
childID
,
child
:=
fs
.
allocateInode
(
childAttrs
)
// Add an entry in the parent.
parent
.
AddChild
(
childID
,
op
.
N
ame
,
fuseutil
.
DT_File
)
parent
.
AddChild
(
childID
,
n
ame
,
fuseutil
.
DT_File
)
// Fill in the response entry.
op
.
E
ntry
.
Child
=
childID
op
.
E
ntry
.
Attributes
=
child
.
attrs
e
ntry
.
Child
=
childID
e
ntry
.
Attributes
=
child
.
attrs
// We don't spontaneously mutate, so the kernel can cache as long as it wants
// (since it also handles invalidation).
op
.
Entry
.
AttributesExpiration
=
time
.
Now
()
.
Add
(
365
*
24
*
time
.
Hour
)
op
.
Entry
.
EntryExpiration
=
op
.
Entry
.
EntryExpiration
entry
.
AttributesExpiration
=
time
.
Now
()
.
Add
(
365
*
24
*
time
.
Hour
)
entry
.
EntryExpiration
=
entry
.
AttributesExpiration
return
}
// We have nothing interesting to put in the Handle field.
func
(
fs
*
memFS
)
CreateFile
(
ctx
context
.
Context
,
op
*
fuseops
.
CreateFileOp
)
(
err
error
)
{
fs
.
mu
.
Lock
()
defer
fs
.
mu
.
Unlock
()
op
.
Entry
,
err
=
fs
.
createFile
(
op
.
Parent
,
op
.
Name
,
op
.
Mode
)
return
}
...
...
samples/memfs/memfs_test.go
View file @
895b8c41
...
...
@@ -21,6 +21,7 @@ import (
"os"
"os/user"
"path"
"runtime"
"strconv"
"syscall"
"testing"
...
...
@@ -87,21 +88,25 @@ func applyUmask(m os.FileMode) os.FileMode {
// Boilerplate
////////////////////////////////////////////////////////////////////////
type
M
emFSTest
struct
{
type
m
emFSTest
struct
{
samples
.
SampleTest
}
func
init
()
{
RegisterTestSuite
(
&
MemFSTest
{})
}
func
(
t
*
MemFSTest
)
SetUp
(
ti
*
TestInfo
)
{
func
(
t
*
memFSTest
)
SetUp
(
ti
*
TestInfo
)
{
t
.
Server
=
memfs
.
NewMemFS
(
currentUid
(),
currentGid
())
t
.
SampleTest
.
SetUp
(
ti
)
}
////////////////////////////////////////////////////////////////////////
//
Test function
s
//
Basic
s
////////////////////////////////////////////////////////////////////////
type
MemFSTest
struct
{
memFSTest
}
func
init
()
{
RegisterTestSuite
(
&
MemFSTest
{})
}
func
(
t
*
MemFSTest
)
ContentsOfEmptyFileSystem
()
{
entries
,
err
:=
fusetesting
.
ReadDirPicky
(
t
.
Dir
)
...
...
@@ -1614,3 +1619,91 @@ func (t *MemFSTest) RenameNonExistentFile() {
err
=
os
.
Rename
(
path
.
Join
(
t
.
Dir
,
"foo"
),
path
.
Join
(
t
.
Dir
,
"bar"
))
ExpectThat
(
err
,
Error
(
HasSubstr
(
"no such file"
)))
}
////////////////////////////////////////////////////////////////////////
// Mknod
////////////////////////////////////////////////////////////////////////
type
MknodTest
struct
{
memFSTest
}
func
init
()
{
RegisterTestSuite
(
&
MknodTest
{})
}
func
(
t
*
MknodTest
)
File
()
{
// mknod(2) only works for root on OS X.
if
runtime
.
GOOS
==
"darwin"
{
return
}
var
err
error
p
:=
path
.
Join
(
t
.
Dir
,
"foo"
)
// Create
err
=
syscall
.
Mknod
(
p
,
syscall
.
S_IFREG
|
0641
,
0
)
AssertEq
(
nil
,
err
)
// Stat
fi
,
err
:=
os
.
Stat
(
p
)
AssertEq
(
nil
,
err
)
ExpectEq
(
path
.
Base
(
p
),
fi
.
Name
())
ExpectEq
(
0
,
fi
.
Size
())
ExpectEq
(
os
.
FileMode
(
0641
),
fi
.
Mode
())
// Read
contents
,
err
:=
ioutil
.
ReadFile
(
p
)
AssertEq
(
nil
,
err
)
ExpectEq
(
""
,
string
(
contents
))
}
func
(
t
*
MknodTest
)
Directory
()
{
// mknod(2) only works for root on OS X.
if
runtime
.
GOOS
==
"darwin"
{
return
}
var
err
error
p
:=
path
.
Join
(
t
.
Dir
,
"foo"
)
// Quoth `man 2 mknod`: "Under Linux, this call cannot be used to create
// directories."
err
=
syscall
.
Mknod
(
p
,
syscall
.
S_IFDIR
|
0700
,
0
)
ExpectEq
(
syscall
.
EPERM
,
err
)
}
func
(
t
*
MknodTest
)
AlreadyExists
()
{
// mknod(2) only works for root on OS X.
if
runtime
.
GOOS
==
"darwin"
{
return
}
var
err
error
p
:=
path
.
Join
(
t
.
Dir
,
"foo"
)
// Create (first)
err
=
ioutil
.
WriteFile
(
p
,
[]
byte
(
"taco"
),
0600
)
AssertEq
(
nil
,
err
)
// Create (second)
err
=
syscall
.
Mknod
(
p
,
syscall
.
S_IFREG
|
0600
,
0
)
ExpectEq
(
syscall
.
EEXIST
,
err
)
// Read
contents
,
err
:=
ioutil
.
ReadFile
(
p
)
AssertEq
(
nil
,
err
)
ExpectEq
(
"taco"
,
string
(
contents
))
}
func
(
t
*
MknodTest
)
NonExistentParent
()
{
// mknod(2) only works for root on OS X.
if
runtime
.
GOOS
==
"darwin"
{
return
}
var
err
error
p
:=
path
.
Join
(
t
.
Dir
,
"foo/bar"
)
err
=
syscall
.
Mknod
(
p
,
syscall
.
S_IFREG
|
0600
,
0
)
ExpectEq
(
syscall
.
ENOENT
,
err
)
}
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