Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
go-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
go-fuse
Commits
45eb49de
Commit
45eb49de
authored
Mar 02, 2019
by
Han-Wen Nienhuys
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
nodefs: support Link
parent
34f928bf
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
84 additions
and
13 deletions
+84
-13
nodefs/README.md
nodefs/README.md
+7
-1
nodefs/api.go
nodefs/api.go
+2
-1
nodefs/bridge.go
nodefs/bridge.go
+13
-4
nodefs/default.go
nodefs/default.go
+4
-1
nodefs/loopback.go
nodefs/loopback.go
+28
-6
nodefs/simple_test.go
nodefs/simple_test.go
+30
-0
No files found.
nodefs/README.md
View file @
45eb49de
...
...
@@ -9,7 +9,8 @@ Decisions
=========
*
Nodes contain references to their children. This is useful
because most filesystems will need to construct tree-like structures.
because most filesystems will need to construct tree-like
structures.
*
Nodes can be "persistent", meaning their lifetime is not under
control of the kernel. This is useful for constructing FS trees
...
...
@@ -24,6 +25,11 @@ Decisions
creation. These are {NodeID, Mode}. Files cannot change type
during their lifetime. It also prevents the common error of
forgetting to return the filetype in Lookup/GetAttr.
*
Support for hard links. libfuse doesn't support this in the
high-level API. Extra care for race conditions is needed when
looking up the same file different paths.
To decide
=========
...
...
nodefs/api.go
View file @
45eb49de
...
...
@@ -82,7 +82,7 @@ type Operations interface {
//
// See InodeOf for public API to retrieve an inode from Node.
inode
()
*
Inode
setInode
(
*
Inode
)
setInode
(
*
Inode
)
bool
// File locking
GetLk
(
ctx
context
.
Context
,
f
FileHandle
,
owner
uint64
,
lk
*
fuse
.
FileLock
,
flags
uint32
,
out
*
fuse
.
FileLock
)
(
code
fuse
.
Status
)
...
...
@@ -105,6 +105,7 @@ type Operations interface {
Unlink
(
ctx
context
.
Context
,
name
string
)
fuse
.
Status
Rename
(
ctx
context
.
Context
,
name
string
,
newParent
Operations
,
newName
string
,
flags
uint32
)
fuse
.
Status
Create
(
ctx
context
.
Context
,
name
string
,
flags
uint32
,
mode
uint32
)
(
node
*
Inode
,
fh
FileHandle
,
fuseFlags
uint32
,
code
fuse
.
Status
)
Link
(
ctx
context
.
Context
,
target
Operations
,
name
string
,
out
*
fuse
.
EntryOut
)
(
node
*
Inode
,
code
fuse
.
Status
)
Symlink
(
ctx
context
.
Context
,
target
,
name
string
,
out
*
fuse
.
EntryOut
)
(
node
*
Inode
,
code
fuse
.
Status
)
Readlink
(
ctx
context
.
Context
)
(
string
,
fuse
.
Status
)
Open
(
ctx
context
.
Context
,
flags
uint32
)
(
fh
FileHandle
,
fuseFlags
uint32
,
code
fuse
.
Status
)
...
...
nodefs/bridge.go
View file @
45eb49de
...
...
@@ -80,7 +80,7 @@ func (b *rawBridge) newInode(node Operations, mode uint32, id FileID, persistent
b
.
nodes
[
id
.
Ino
]
=
inode
node
.
setInode
(
inode
)
return
inode
return
node
.
inode
()
}
func
NewNodeFS
(
root
Operations
,
opts
*
Options
)
fuse
.
RawFileSystem
{
...
...
@@ -370,12 +370,21 @@ func (b *rawBridge) Rename(input *fuse.RenameIn, oldName string, newName string)
return
code
}
func
(
b
*
rawBridge
)
Link
(
input
*
fuse
.
LinkIn
,
filename
string
,
out
*
fuse
.
EntryOut
)
(
code
fuse
.
Status
)
{
return
fuse
.
ENOSYS
func
(
b
*
rawBridge
)
Link
(
input
*
fuse
.
LinkIn
,
name
string
,
out
*
fuse
.
EntryOut
)
(
code
fuse
.
Status
)
{
parent
,
_
:=
b
.
inode
(
input
.
NodeId
,
0
)
target
,
_
:=
b
.
inode
(
input
.
Oldnodeid
,
0
)
child
,
code
:=
parent
.
node
.
Link
(
context
.
TODO
(),
target
.
node
,
name
,
out
)
if
!
code
.
Ok
()
{
return
code
}
b
.
addNewChild
(
parent
,
name
,
child
,
nil
,
out
)
b
.
setEntryOutTimeout
(
out
)
return
fuse
.
OK
}
func
(
b
*
rawBridge
)
Symlink
(
header
*
fuse
.
InHeader
,
target
string
,
name
string
,
out
*
fuse
.
EntryOut
)
(
code
fuse
.
Status
)
{
log
.
Println
(
"symlink1"
)
parent
,
_
:=
b
.
inode
(
header
.
NodeId
,
0
)
child
,
code
:=
parent
.
node
.
Symlink
(
context
.
TODO
(),
target
,
name
,
out
)
if
!
code
.
Ok
()
{
...
...
nodefs/default.go
View file @
45eb49de
...
...
@@ -41,7 +41,7 @@ var _ Operations = &DefaultOperations{}
//
// To read node.inode atomic.LoadPointer is used, however it is not expensive
// since it translates to regular MOVQ on amd64.
func
(
dn
*
DefaultOperations
)
setInode
(
inode
*
Inode
)
*
Inode
{
func
(
dn
*
DefaultOperations
)
setInode
(
inode
*
Inode
)
bool
{
return
atomic
.
CompareAndSwapPointer
(
(
*
unsafe
.
Pointer
)(
unsafe
.
Pointer
(
&
dn
.
inode_
)),
nil
,
unsafe
.
Pointer
(
inode
))
...
...
@@ -196,6 +196,9 @@ func (n *DefaultOperations) Open(ctx context.Context, flags uint32) (fh FileHand
func
(
n
*
DefaultOperations
)
Create
(
ctx
context
.
Context
,
name
string
,
flags
uint32
,
mode
uint32
)
(
node
*
Inode
,
fh
FileHandle
,
fuseFlags
uint32
,
code
fuse
.
Status
)
{
return
nil
,
nil
,
0
,
fuse
.
ENOSYS
}
func
(
n
*
DefaultOperations
)
Link
(
ctx
context
.
Context
,
target
Operations
,
name
string
,
out
*
fuse
.
EntryOut
)
(
node
*
Inode
,
code
fuse
.
Status
)
{
return
nil
,
fuse
.
ENOSYS
}
type
DefaultFile
struct
{
}
...
...
nodefs/loopback.go
View file @
45eb49de
...
...
@@ -135,6 +135,13 @@ func (n *loopbackNode) Unlink(ctx context.Context, name string) fuse.Status {
return
fuse
.
ToStatus
(
err
)
}
func
toLoopbackNode
(
op
Operations
)
*
loopbackNode
{
if
r
,
ok
:=
op
.
(
*
loopbackRoot
);
ok
{
return
&
r
.
loopbackNode
}
return
op
.
(
*
loopbackNode
)
}
func
(
n
*
loopbackNode
)
Rename
(
ctx
context
.
Context
,
name
string
,
newParent
Operations
,
newName
string
,
flags
uint32
)
fuse
.
Status
{
if
flags
!=
0
{
...
...
@@ -142,12 +149,7 @@ func (n *loopbackNode) Rename(ctx context.Context, name string, newParent Operat
}
p1
:=
filepath
.
Join
(
n
.
path
(),
name
)
var
newParentLoopback
*
loopbackNode
if
r
,
ok
:=
newParent
.
(
*
loopbackRoot
);
ok
{
newParentLoopback
=
&
r
.
loopbackNode
}
else
{
newParentLoopback
=
newParent
.
(
*
loopbackNode
)
}
newParentLoopback
:=
toLoopbackNode
(
newParent
)
p2
:=
filepath
.
Join
(
newParentLoopback
.
path
(),
newName
)
err
:=
os
.
Rename
(
p1
,
p2
)
...
...
@@ -204,6 +206,26 @@ func (n *loopbackNode) Symlink(ctx context.Context, target, name string, out *fu
return
ch
,
fuse
.
OK
}
func
(
n
*
loopbackNode
)
Link
(
ctx
context
.
Context
,
target
Operations
,
name
string
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
fuse
.
Status
)
{
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
targetNode
:=
toLoopbackNode
(
target
)
err
:=
syscall
.
Link
(
targetNode
.
path
(),
p
)
if
err
!=
nil
{
return
nil
,
fuse
.
ToStatus
(
err
)
}
st
:=
syscall
.
Stat_t
{}
if
syscall
.
Lstat
(
p
,
&
st
);
err
!=
nil
{
syscall
.
Unlink
(
p
)
return
nil
,
fuse
.
ToStatus
(
err
)
}
node
:=
n
.
rootNode
.
newLoopbackNode
()
ch
:=
n
.
inode
()
.
NewInode
(
node
,
st
.
Mode
,
idFromStat
(
&
st
))
out
.
Attr
.
FromStat
(
&
st
)
return
ch
,
fuse
.
OK
}
func
(
n
*
loopbackNode
)
Readlink
(
ctx
context
.
Context
)
(
string
,
fuse
.
Status
)
{
p
:=
n
.
path
()
...
...
nodefs/simple_test.go
View file @
45eb49de
...
...
@@ -424,3 +424,33 @@ func TestSymlink(t *testing.T) {
t
.
Errorf
(
"Readlink: got %q, want %q"
,
got
,
target
)
}
}
func
TestLink
(
t
*
testing
.
T
)
{
tc
:=
newTestCase
(
t
)
defer
tc
.
Clean
()
link
:=
tc
.
mntDir
+
"/link"
target
:=
tc
.
mntDir
+
"/target"
if
err
:=
ioutil
.
WriteFile
(
target
,
[]
byte
(
"hello"
),
0644
);
err
!=
nil
{
t
.
Fatalf
(
"WriteFile: %v"
,
err
)
}
st
:=
syscall
.
Stat_t
{}
if
err
:=
syscall
.
Lstat
(
target
,
&
st
);
err
!=
nil
{
t
.
Fatalf
(
"Lstat before: %v"
,
err
)
}
beforeIno
:=
st
.
Ino
if
err
:=
os
.
Link
(
target
,
link
);
err
!=
nil
{
t
.
Errorf
(
"Link: %v"
,
err
)
}
if
err
:=
syscall
.
Lstat
(
link
,
&
st
);
err
!=
nil
{
t
.
Fatalf
(
"Lstat after: %v"
,
err
)
}
if
st
.
Ino
!=
beforeIno
{
t
.
Errorf
(
"Lstat after: got %d, want %d"
,
st
.
Ino
,
beforeIno
)
}
}
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