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
Levin Zimmermann
go-fuse
Commits
e5194406
Commit
e5194406
authored
Feb 23, 2019
by
Han-Wen Nienhuys
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
nodefs: Rename2 support, kinda.
parent
89a1a0c7
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
162 additions
and
35 deletions
+162
-35
nodefs/api.go
nodefs/api.go
+1
-1
nodefs/bridge.go
nodefs/bridge.go
+36
-29
nodefs/inode.go
nodefs/inode.go
+64
-3
nodefs/loopback.go
nodefs/loopback.go
+6
-1
nodefs/simple_test.go
nodefs/simple_test.go
+55
-1
No files found.
nodefs/api.go
View file @
e5194406
...
...
@@ -91,7 +91,7 @@ type Node interface {
Mknod
(
ctx
context
.
Context
,
name
string
,
mode
uint32
,
dev
uint32
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
fuse
.
Status
)
Rmdir
(
ctx
context
.
Context
,
name
string
)
fuse
.
Status
Unlink
(
ctx
context
.
Context
,
name
string
)
fuse
.
Status
Rename
(
ctx
context
.
Context
,
name
string
,
newParent
Node
,
newName
string
)
fuse
.
Status
Rename
(
ctx
context
.
Context
,
name
string
,
newParent
Node
,
newName
string
,
flags
uint32
)
fuse
.
Status
Open
(
ctx
context
.
Context
,
flags
uint32
)
(
fh
File
,
fuseFlags
uint32
,
code
fuse
.
Status
)
...
...
nodefs/bridge.go
View file @
e5194406
...
...
@@ -11,6 +11,7 @@ import (
"time"
"github.com/hanwen/go-fuse/fuse"
"golang.org/x/sys/unix"
)
type
mapEntry
struct
{
...
...
@@ -142,7 +143,7 @@ func (b *rawBridge) Lookup(header *fuse.InHeader, name string, out *fuse.EntryOu
return
code
}
b
.
addNewChild
(
parent
,
name
,
child
,
out
)
b
.
addNewChild
(
parent
,
name
,
child
,
nil
,
out
)
b
.
setEntryOutTimeout
(
out
)
return
fuse
.
OK
}
...
...
@@ -178,7 +179,7 @@ func (b *rawBridge) Mkdir(input *fuse.MkdirIn, name string, out *fuse.EntryOut)
log
.
Panicf
(
"Mkdir: mode must be S_IFDIR (%o), got %o"
,
fuse
.
S_IFDIR
,
out
.
Attr
.
Mode
)
}
b
.
addNewChild
(
parent
,
name
,
child
,
out
)
b
.
addNewChild
(
parent
,
name
,
child
,
nil
,
out
)
b
.
setEntryOutTimeout
(
out
)
return
fuse
.
OK
}
...
...
@@ -191,18 +192,26 @@ func (b *rawBridge) Mknod(input *fuse.MknodIn, name string, out *fuse.EntryOut)
return
code
}
b
.
addNewChild
(
parent
,
name
,
child
,
out
)
b
.
addNewChild
(
parent
,
name
,
child
,
nil
,
out
)
b
.
setEntryOutTimeout
(
out
)
return
fuse
.
OK
}
func
(
b
*
rawBridge
)
addNewChild
(
parent
*
Inode
,
name
string
,
child
*
Inode
,
out
*
fuse
.
EntryOut
)
{
// addNewChild inserts the child into the tree. Returns file handle if file != nil.
func
(
b
*
rawBridge
)
addNewChild
(
parent
*
Inode
,
name
string
,
child
*
Inode
,
file
File
,
out
*
fuse
.
EntryOut
)
uint64
{
lockNodes
(
parent
,
child
)
parent
.
setEntry
(
name
,
child
)
b
.
mu
.
Lock
()
child
.
lookupCount
++
if
child
.
nodeID
==
0
{
b
.
registerInode
(
child
)
}
var
fh
uint64
if
file
!=
nil
{
fh
=
b
.
registerFile
(
file
)
}
out
.
NodeId
=
child
.
nodeID
// NOSUBMIT - or should let FS expose Attr.Ino? This makes
// testing semantics hard though, because os.Lstat doesn't
...
...
@@ -211,6 +220,7 @@ func (b *rawBridge) addNewChild(parent *Inode, name string, child *Inode, out *f
out
.
Generation
=
b
.
nodes
[
out
.
NodeId
]
.
generation
b
.
mu
.
Unlock
()
unlockNodes
(
parent
,
child
)
return
fh
}
func
(
b
*
rawBridge
)
setEntryOutTimeout
(
out
*
fuse
.
EntryOut
)
{
...
...
@@ -252,19 +262,7 @@ func (b *rawBridge) Create(input *fuse.CreateIn, name string, out *fuse.CreateOu
return
code
}
lockNode2
(
parent
,
child
)
parent
.
setEntry
(
name
,
child
)
b
.
mu
.
Lock
()
if
child
.
nodeID
==
0
{
b
.
registerInode
(
child
)
}
out
.
Fh
=
b
.
registerFile
(
f
)
out
.
NodeId
=
child
.
nodeID
out
.
Ino
=
child
.
nodeID
out
.
Generation
=
b
.
nodes
[
child
.
nodeID
]
.
generation
b
.
mu
.
Unlock
()
unlockNode2
(
parent
,
child
)
out
.
Fh
=
b
.
addNewChild
(
parent
,
name
,
child
,
f
,
&
out
.
EntryOut
)
b
.
setEntryOutTimeout
(
&
out
.
EntryOut
)
out
.
OpenFlags
=
flags
...
...
@@ -278,10 +276,7 @@ func (b *rawBridge) Create(input *fuse.CreateIn, name string, out *fuse.CreateOu
}
func
(
b
*
rawBridge
)
Forget
(
nodeid
,
nlookup
uint64
)
{
b
.
mu
.
Lock
()
n
:=
b
.
nodes
[
nodeid
]
.
inode
b
.
mu
.
Unlock
()
n
,
_
:=
b
.
inode
(
nodeid
,
0
)
n
.
removeRef
(
nlookup
,
false
)
}
...
...
@@ -308,14 +303,18 @@ func (b *rawBridge) GetAttr(input *fuse.GetAttrIn, out *fuse.AttrOut) (code fuse
out
.
Nlink
=
1
}
if
b
.
options
.
AttrTimeout
!=
nil
{
out
.
SetTimeout
(
*
b
.
options
.
AttrTimeout
)
}
b
.
setAttrTimeout
(
out
)
out
.
Ino
=
input
.
NodeId
return
code
}
func
(
b
*
rawBridge
)
setAttrTimeout
(
out
*
fuse
.
AttrOut
)
{
if
b
.
options
.
AttrTimeout
!=
nil
{
out
.
SetTimeout
(
*
b
.
options
.
AttrTimeout
)
}
}
func
(
b
*
rawBridge
)
SetAttr
(
input
*
fuse
.
SetAttrIn
,
out
*
fuse
.
AttrOut
)
(
code
fuse
.
Status
)
{
ctx
:=
context
.
TODO
()
...
...
@@ -379,18 +378,25 @@ func (b *rawBridge) SetAttr(input *fuse.SetAttrIn, out *fuse.AttrOut) (code fuse
// Must call GetAttr(); the filesystem may override some of
// the changes we effect here.
attr
:=
&
out
.
Attr
code
=
n
.
node
.
GetAttr
(
ctx
,
f
,
attr
)
// TODO - attr timout?
// should take AttrOut
code
=
n
.
node
.
GetAttr
(
ctx
,
f
,
attr
)
b
.
setAttrTimeout
(
out
)
return
code
}
func
(
b
*
rawBridge
)
Rename
(
input
*
fuse
.
RenameIn
,
oldName
string
,
newName
string
)
(
code
fuse
.
Status
)
{
func
(
b
*
rawBridge
)
Rename
(
input
*
fuse
.
RenameIn
,
oldName
string
,
newName
string
)
fuse
.
Status
{
p1
,
_
:=
b
.
inode
(
input
.
NodeId
,
0
)
p2
,
_
:=
b
.
inode
(
input
.
Newdir
,
0
)
if
code
:=
p1
.
node
.
Rename
(
context
.
TODO
(),
oldName
,
p2
.
node
,
newName
);
code
.
Ok
()
{
p1
.
MvChild
(
oldName
,
p2
,
newName
,
true
)
code
:=
p1
.
node
.
Rename
(
context
.
TODO
(),
oldName
,
p2
.
node
,
newName
,
input
.
Flags
)
if
code
.
Ok
()
{
if
input
.
Flags
&
unix
.
RENAME_EXCHANGE
!=
0
{
// XXX - test coverage.
p1
.
ExchangeChild
(
oldName
,
p2
,
newName
)
}
else
{
p1
.
MvChild
(
oldName
,
p2
,
newName
,
true
)
}
}
return
code
}
...
...
@@ -541,5 +547,6 @@ func (b *rawBridge) FsyncDir(input *fuse.FsyncIn) (code fuse.Status) {
func
(
b
*
rawBridge
)
StatFs
(
input
*
fuse
.
InHeader
,
out
*
fuse
.
StatfsOut
)
(
code
fuse
.
Status
)
{
return
}
func
(
b
*
rawBridge
)
Init
(
*
fuse
.
Server
)
{
}
nodefs/inode.go
View file @
e5194406
...
...
@@ -279,6 +279,7 @@ func (n *Inode) removeRef(nlookup uint64, dropPersistence bool) (forgotten bool,
if
nlookup
>
0
&&
dropPersistence
{
log
.
Panic
(
"only one allowed"
)
}
else
if
nlookup
>
0
{
n
.
lookupCount
-=
nlookup
n
.
changeCounter
++
}
else
if
dropPersistence
&&
n
.
persistent
{
...
...
@@ -396,9 +397,8 @@ retry:
return
true
,
true
}
// TODO - RENAME_NOREPLACE, RENAME_EXCHANGE MvChild executes a
// rename. If overwrite is set, a child at the destination will be
// overwritten.
// MvChild executes a rename. If overwrite is set, a child at the
// destination will be overwritten.
func
(
n
*
Inode
)
MvChild
(
old
string
,
newParent
*
Inode
,
newName
string
,
overwrite
bool
)
bool
{
retry
:
for
{
...
...
@@ -453,3 +453,64 @@ retry:
return
true
}
}
func
(
n
*
Inode
)
ExchangeChild
(
oldName
string
,
newParent
*
Inode
,
newName
string
)
{
oldParent
:=
n
retry
:
for
{
lockNode2
(
oldParent
,
newParent
)
counter1
:=
oldParent
.
changeCounter
counter2
:=
newParent
.
changeCounter
oldChild
:=
oldParent
.
children
[
oldName
]
destChild
:=
newParent
.
children
[
newName
]
unlockNode2
(
oldParent
,
newParent
)
if
destChild
==
nil
&&
oldChild
==
nil
{
return
}
if
destChild
==
oldChild
{
return
}
lockNodes
(
oldParent
,
newParent
,
oldChild
,
destChild
)
if
counter2
!=
newParent
.
changeCounter
||
counter1
!=
oldParent
.
changeCounter
{
unlockNodes
(
oldParent
,
newParent
,
oldChild
,
destChild
)
continue
retry
}
// Detach
if
oldChild
!=
nil
{
delete
(
oldParent
.
children
,
oldName
)
delete
(
oldChild
.
parents
,
parentData
{
oldName
,
oldParent
})
oldParent
.
changeCounter
++
oldChild
.
changeCounter
++
}
if
destChild
!=
nil
{
delete
(
newParent
.
children
,
newName
)
delete
(
destChild
.
parents
,
parentData
{
newName
,
newParent
})
destChild
.
changeCounter
++
newParent
.
changeCounter
++
}
// Attach
if
oldChild
!=
nil
{
newParent
.
children
[
newName
]
=
oldChild
newParent
.
changeCounter
++
oldChild
.
parents
[
parentData
{
newName
,
newParent
}]
=
struct
{}{}
oldChild
.
changeCounter
++
}
if
destChild
!=
nil
{
oldParent
.
children
[
oldName
]
=
oldChild
oldParent
.
changeCounter
++
destChild
.
parents
[
parentData
{
oldName
,
oldParent
}]
=
struct
{}{}
destChild
.
changeCounter
++
}
unlockNodes
(
oldParent
,
newParent
,
oldChild
,
destChild
)
return
}
}
nodefs/loopback.go
View file @
e5194406
...
...
@@ -123,7 +123,12 @@ func (n *loopbackNode) Unlink(ctx context.Context, name string) fuse.Status {
return
fuse
.
ToStatus
(
err
)
}
func
(
n
*
loopbackNode
)
Rename
(
ctx
context
.
Context
,
name
string
,
newParent
Node
,
newName
string
)
fuse
.
Status
{
func
(
n
*
loopbackNode
)
Rename
(
ctx
context
.
Context
,
name
string
,
newParent
Node
,
newName
string
,
flags
uint32
)
fuse
.
Status
{
if
flags
!=
0
{
return
fuse
.
ENOSYS
}
p1
:=
filepath
.
Join
(
n
.
path
(),
name
)
var
newParentLoopback
*
loopbackNode
if
r
,
ok
:=
newParent
.
(
*
loopbackRoot
);
ok
{
...
...
nodefs/simple_test.go
View file @
e5194406
...
...
@@ -14,6 +14,8 @@ import (
"testing"
"time"
"golang.org/x/sys/unix"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil"
)
...
...
@@ -249,7 +251,7 @@ func TestMkdir(t *testing.T) {
}
}
func
TestRename
(
t
*
testing
.
T
)
{
func
testRenameOverwrite
(
t
*
testing
.
T
,
destExists
bool
)
{
tc
:=
newTestCase
(
t
)
defer
tc
.
Clean
()
...
...
@@ -260,6 +262,12 @@ func TestRename(t *testing.T) {
t
.
Fatalf
(
"WriteFile: %v"
,
err
)
}
if
destExists
{
if
err
:=
ioutil
.
WriteFile
(
tc
.
origDir
+
"/dir/renamed"
,
[]
byte
(
"xx"
),
0644
);
err
!=
nil
{
t
.
Fatalf
(
"WriteFile: %v"
,
err
)
}
}
st
:=
syscall
.
Stat_t
{}
if
err
:=
syscall
.
Lstat
(
tc
.
mntDir
+
"/file"
,
&
st
);
err
!=
nil
{
t
.
Fatalf
(
"Lstat before: %v"
,
err
)
...
...
@@ -281,3 +289,49 @@ func TestRename(t *testing.T) {
t
.
Errorf
(
"got ino %d, want %d"
,
got
,
beforeIno
)
}
}
func
TestRenameDestExist
(
t
*
testing
.
T
)
{
testRenameOverwrite
(
t
,
true
)
}
func
TestRenameDestNoExist
(
t
*
testing
.
T
)
{
testRenameOverwrite
(
t
,
false
)
}
func
TestRenameNoOverwrite
(
t
*
testing
.
T
)
{
tc
:=
newTestCase
(
t
)
defer
tc
.
Clean
()
if
err
:=
os
.
Mkdir
(
tc
.
origDir
+
"/dir"
,
0755
);
err
!=
nil
{
t
.
Fatalf
(
"Mkdir: %v"
,
err
)
}
if
err
:=
ioutil
.
WriteFile
(
tc
.
origDir
+
"/file"
,
[]
byte
(
"hello"
),
0644
);
err
!=
nil
{
t
.
Fatalf
(
"WriteFile: %v"
,
err
)
}
if
err
:=
ioutil
.
WriteFile
(
tc
.
origDir
+
"/dir/file"
,
[]
byte
(
"x"
),
0644
);
err
!=
nil
{
t
.
Fatalf
(
"WriteFile: %v"
,
err
)
}
f1
,
err
:=
syscall
.
Open
(
tc
.
mntDir
+
"/"
,
syscall
.
O_DIRECTORY
,
0
)
if
err
!=
nil
{
t
.
Fatalf
(
"open 1: %v"
,
err
)
}
defer
syscall
.
Close
(
f1
)
f2
,
err
:=
syscall
.
Open
(
tc
.
mntDir
+
"/dir"
,
syscall
.
O_DIRECTORY
,
0
)
if
err
!=
nil
{
t
.
Fatalf
(
"open 2: %v"
,
err
)
}
defer
syscall
.
Close
(
f2
)
if
err
:=
unix
.
Renameat2
(
f1
,
"file"
,
f2
,
"file"
,
unix
.
RENAME_NOREPLACE
);
err
==
nil
{
t
.
Errorf
(
"rename NOREPLACE succeeded"
)
}
else
if
err
!=
syscall
.
EEXIST
{
t
.
Errorf
(
"got %v (%T) want EEXIST"
,
err
,
err
)
}
if
err
:=
unix
.
Renameat2
(
f1
,
"file"
,
f2
,
"file"
,
unix
.
RENAME_EXCHANGE
);
err
==
nil
{
t
.
Errorf
(
"rename EXCHANGE succeeded"
)
}
else
if
err
!=
syscall
.
EINVAL
{
t
.
Errorf
(
"got %v (%T) want %v (%T)"
,
err
,
err
,
syscall
.
EINVAL
,
syscall
.
EINVAL
)
}
}
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