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
75dc7589
Commit
75dc7589
authored
May 25, 2011
by
Han-Wen Nienhuys
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move open file administration into mountData.
* simplifies code * deal with GetPath() on deleted files
parent
8379ce53
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
165 additions
and
94 deletions
+165
-94
fuse/loopback_test.go
fuse/loopback_test.go
+46
-2
fuse/pathfilesystem.go
fuse/pathfilesystem.go
+114
-87
fuse/pathops.go
fuse/pathops.go
+5
-5
No files found.
fuse/loopback_test.go
View file @
75dc7589
...
...
@@ -37,6 +37,8 @@ type testCase struct {
connector
*
FileSystemConnector
}
const
testTtl
=
0.1
// Create and mount filesystem.
func
(
me
*
testCase
)
Setup
(
t
*
testing
.
T
)
{
me
.
tester
=
t
...
...
@@ -61,7 +63,12 @@ func (me *testCase) Setup(t *testing.T) {
pfs
=
NewLockingFileSystem
(
pfs
)
var
rfs
RawFileSystem
me
.
connector
=
NewFileSystemConnector
(
pfs
,
nil
)
me
.
connector
=
NewFileSystemConnector
(
pfs
,
&
FileSystemOptions
{
EntryTimeout
:
testTtl
,
AttrTimeout
:
testTtl
,
NegativeTimeout
:
0.0
,
})
rfs
=
me
.
connector
rfs
=
NewTimingRawFileSystem
(
rfs
)
rfs
=
NewLockingRawFileSystem
(
rfs
)
...
...
@@ -676,7 +683,7 @@ func TestRecursiveMount(t *testing.T) {
f
.
Close
()
log
.
Println
(
"Waiting for kernel to flush file-close to fuse..."
)
time
.
Sleep
(
1
e9
)
time
.
Sleep
(
1
.5e9
*
testTtl
)
code
=
ts
.
connector
.
Unmount
(
"/mnt"
)
if
code
!=
OK
{
...
...
@@ -684,3 +691,40 @@ func TestRecursiveMount(t *testing.T) {
}
}
func
TestDeletedUnmount
(
t
*
testing
.
T
)
{
ts
:=
new
(
testCase
)
ts
.
Setup
(
t
)
defer
ts
.
Cleanup
()
submnt
:=
filepath
.
Join
(
ts
.
mountPoint
,
"mnt"
)
err
:=
os
.
Mkdir
(
submnt
,
0777
)
CheckSuccess
(
err
)
pfs2
:=
NewLoopbackFileSystem
(
ts
.
origDir
)
code
:=
ts
.
connector
.
Mount
(
"/mnt"
,
pfs2
,
nil
)
if
!
code
.
Ok
()
{
t
.
Fatal
(
"err"
)
}
f
,
err
:=
os
.
Create
(
filepath
.
Join
(
submnt
,
"hello.txt"
))
CheckSuccess
(
err
)
log
.
Println
(
"Removing"
)
err
=
os
.
Remove
(
filepath
.
Join
(
submnt
,
"hello.txt"
))
CheckSuccess
(
err
)
log
.
Println
(
"Removing"
)
_
,
err
=
f
.
Write
([]
byte
(
"bla"
))
CheckSuccess
(
err
)
code
=
ts
.
connector
.
Unmount
(
"/mnt"
)
if
code
!=
EBUSY
{
t
.
Error
(
"expect EBUSY"
,
code
)
}
f
.
Close
()
time
.
Sleep
(
1.5e9
*
testTtl
)
code
=
ts
.
connector
.
Unmount
(
"/mnt"
)
if
!
code
.
Ok
()
{
t
.
Error
(
"should succeed"
,
code
)
}
}
fuse/pathfilesystem.go
View file @
75dc7589
...
...
@@ -28,6 +28,14 @@ import (
"unsafe"
)
// fileBridge stores either an open dir or an open file.
type
fileBridge
struct
{
*
mountData
*
inode
Flags
uint32
Iface
interface
{}
}
type
mountData
struct
{
// If non-nil the file system mounted here.
fs
FileSystem
...
...
@@ -46,12 +54,20 @@ type mountData struct {
// We could have separate treeLocks per mount; something to
// consider if we can measure significant contention for
// multi-mount filesystems.
options
*
FileSystemOptions
// Protects openFiles
openFilesLock
sync
.
RWMutex
// Open files/directories.
openFiles
map
[
uint64
]
*
fileBridge
}
func
newMount
(
fs
FileSystem
)
*
mountData
{
return
&
mountData
{
fs
:
fs
}
return
&
mountData
{
fs
:
fs
,
openFiles
:
make
(
map
[
uint64
]
*
fileBridge
),
}
}
func
(
me
*
mountData
)
setOwner
(
attr
*
Attr
)
{
...
...
@@ -59,7 +75,40 @@ func (me *mountData) setOwner(attr *Attr) {
attr
.
Owner
=
*
me
.
options
.
Owner
}
}
func
(
me
*
mountData
)
unregisterFile
(
node
*
inode
,
handle
uint64
)
interface
{}
{
me
.
openFilesLock
.
Lock
()
defer
me
.
openFilesLock
.
Unlock
()
b
,
ok
:=
me
.
openFiles
[
handle
]
if
!
ok
{
panic
(
"invalid handle"
)
}
node
.
OpenCount
--
me
.
openFiles
[
handle
]
=
nil
,
false
return
b
.
Iface
}
func
(
me
*
mountData
)
registerFile
(
node
*
inode
,
f
interface
{},
flags
uint32
)
uint64
{
me
.
openFilesLock
.
Lock
()
defer
me
.
openFilesLock
.
Unlock
()
b
:=
&
fileBridge
{
Iface
:
f
,
inode
:
node
,
mountData
:
me
,
Flags
:
flags
,
}
h
:=
uint64
(
uintptr
(
unsafe
.
Pointer
(
b
)))
_
,
ok
:=
me
.
openFiles
[
h
]
if
ok
{
panic
(
"handle counter wrapped"
)
}
node
.
OpenCount
++
me
.
openFiles
[
h
]
=
b
return
h
}
////////////////
// Tests should set to true.
var
paranoia
=
false
...
...
@@ -71,18 +120,29 @@ type inode struct {
NodeId
uint64
Name
string
LookupCount
int
// Protected by openFilesLock.
// TODO - verify() this variable too.
OpenCount
int
// Non-nil if this is a mountpoint.
mountPoint
*
mountData
// The point under which this node is.
// The point under which this node is. Should be non-nil for
// all nodes.
mount
*
mountData
}
// Should be called with treeLock and fileLock held.
func
(
me
*
inode
)
totalOpenCount
()
int
{
o
:=
me
.
OpenCount
o
:=
0
if
me
.
mountPoint
!=
nil
{
me
.
mountPoint
.
openFilesLock
.
RLock
()
defer
me
.
mountPoint
.
openFilesLock
.
RUnlock
()
o
+=
len
(
me
.
mountPoint
.
openFiles
)
}
for
_
,
v
:=
range
me
.
Children
{
o
+=
v
.
totalOpenCount
()
}
...
...
@@ -108,17 +168,19 @@ func (me *inode) IsDir() bool {
const
initDirSize
=
20
func
(
me
*
inode
)
verify
(
cur
*
mountData
)
{
if
!
(
me
.
NodeId
==
FUSE_ROOT_ID
||
me
.
LookupCount
>
0
||
len
(
me
.
Children
)
>
0
)
{
if
!
(
me
.
NodeId
==
FUSE_ROOT_ID
||
me
.
LookupCount
>
0
||
len
(
me
.
Children
)
>
0
||
me
.
mountPoint
!=
nil
)
{
p
,
_
:=
me
.
GetPath
()
panic
(
fmt
.
Sprintf
(
"node %v
should be dead: %v %v"
,
p
,
len
(
me
.
Children
),
me
.
LookupCount
))
panic
(
fmt
.
Sprintf
(
"node %v
%d should be dead: %v %v"
,
p
,
me
.
NodeId
,
len
(
me
.
Children
),
me
.
LookupCount
))
}
if
me
.
mountPoint
=
=
nil
{
if
me
.
mount
!=
cur
{
// panic("me.mount not set correctly", me.mount, cur
)
if
me
.
mountPoint
!
=
nil
{
if
me
.
mount
Point
.
unmountPending
&&
len
(
me
.
mountPoint
.
openFiles
)
>
0
{
panic
(
fmt
.
Sprintf
(
"cannot have open files for pending unmount"
)
)
}
}
else
{
cur
=
me
.
mountPoint
}
if
me
.
mount
!=
cur
{
panic
(
fmt
.
Sprintf
(
"me.mount not set correctly %v %v"
,
me
.
mount
,
cur
))
}
for
n
,
ch
:=
range
me
.
Children
{
if
ch
==
nil
{
...
...
@@ -136,7 +198,29 @@ func (me *inode) verify(cur *mountData) {
}
}
func
(
me
*
inode
)
GetFullPath
()
(
path
string
)
{
rev_components
:=
make
([]
string
,
0
,
10
)
inode
:=
me
for
;
inode
!=
nil
;
inode
=
inode
.
Parent
{
rev_components
=
append
(
rev_components
,
inode
.
Name
)
}
return
ReverseJoin
(
rev_components
,
"/"
)
}
func
ReverseJoin
(
rev_components
[]
string
,
sep
string
)
string
{
components
:=
make
([]
string
,
len
(
rev_components
))
for
i
,
v
:=
range
rev_components
{
components
[
len
(
rev_components
)
-
i
-
1
]
=
v
}
return
strings
.
Join
(
components
,
sep
)
}
func
(
me
*
inode
)
GetPath
()
(
path
string
,
mount
*
mountData
)
{
if
me
.
NodeId
!=
FUSE_ROOT_ID
&&
me
.
Parent
==
nil
{
// Deleted node. Treat as if the filesystem was unmounted.
return
".deleted"
,
nil
}
rev_components
:=
make
([]
string
,
0
,
10
)
inode
:=
me
...
...
@@ -151,27 +235,32 @@ func (me *inode) GetPath() (path string, mount *mountData) {
if
mount
.
unmountPending
{
return
""
,
nil
}
components
:=
make
([]
string
,
len
(
rev_components
))
for
i
,
v
:=
range
rev_components
{
components
[
len
(
rev_components
)
-
i
-
1
]
=
v
}
fullPath
:=
strings
.
Join
(
components
,
"/"
)
return
fullPath
,
mount
return
ReverseJoin
(
rev_components
,
"/"
),
mount
}
// Must be called with treeLock held.
func
(
me
*
inode
)
setParent
(
newParent
*
inode
)
{
if
me
.
Parent
==
newParent
{
oldParent
:=
me
.
Parent
if
oldParent
==
newParent
{
return
}
if
me
.
Parent
!=
nil
{
if
old
Parent
!=
nil
{
if
paranoia
{
ch
:=
me
.
Parent
.
Children
[
me
.
Name
]
ch
:=
old
Parent
.
Children
[
me
.
Name
]
if
ch
==
nil
{
panic
(
fmt
.
Sprintf
(
"parent has no child named %v"
,
me
.
Name
))
}
}
me
.
Parent
.
Children
[
me
.
Name
]
=
nil
,
false
oldParent
.
Children
[
me
.
Name
]
=
nil
,
false
if
oldParent
.
mountPoint
!=
nil
&&
oldParent
.
mountPoint
.
unmountPending
&&
len
(
oldParent
.
Children
)
==
0
{
oldParent
.
mountPoint
=
nil
if
oldParent
.
Parent
!=
nil
{
oldParent
.
mount
=
oldParent
.
Parent
.
mount
}
}
me
.
Parent
=
nil
}
if
newParent
!=
nil
{
...
...
@@ -218,65 +307,16 @@ type FileSystemConnector struct {
// Invariants: see the verify() method.
inodeMap
map
[
uint64
]
*
inode
rootNode
*
inode
// Open files/directories.
openFiles
map
[
uint64
]
*
fileBridge
// Protects openFiles and OpenCount in all of the nodes.
fileLock
sync
.
RWMutex
}
type
fileBridge
struct
{
*
mountData
*
inode
Flags
uint32
Iface
interface
{}
}
func
(
me
*
FileSystemConnector
)
Statistics
()
string
{
me
.
treeLock
.
RLock
()
defer
me
.
treeLock
.
RUnlock
()
me
.
fileLock
.
RLock
()
defer
me
.
fileLock
.
RUnlock
()
root
:=
me
.
rootNode
return
fmt
.
Sprintf
(
"Mounts %20d
\n
Files %20d
\n
Inodes %20d
\n
"
,
root
.
totalMountCount
(),
len
(
me
.
openFiles
),
len
(
me
.
inodeMap
))
}
func
(
me
*
FileSystemConnector
)
unregisterFile
(
node
*
inode
,
handle
uint64
)
interface
{}
{
me
.
fileLock
.
Lock
()
defer
me
.
fileLock
.
Unlock
()
b
,
ok
:=
me
.
openFiles
[
handle
]
if
!
ok
{
panic
(
"invalid handle"
)
}
me
.
openFiles
[
handle
]
=
nil
,
false
node
.
OpenCount
--
return
b
.
Iface
}
func
(
me
*
FileSystemConnector
)
registerFile
(
node
*
inode
,
mount
*
mountData
,
f
interface
{},
flags
uint32
)
uint64
{
me
.
fileLock
.
Lock
()
defer
me
.
fileLock
.
Unlock
()
b
:=
&
fileBridge
{
Iface
:
f
,
inode
:
node
,
mountData
:
mount
,
Flags
:
flags
,
}
h
:=
uint64
(
uintptr
(
unsafe
.
Pointer
(
b
)))
_
,
ok
:=
me
.
openFiles
[
h
]
if
ok
{
panic
(
"handle counter wrapped"
)
}
node
.
OpenCount
++
me
.
openFiles
[
h
]
=
b
return
h
root
.
totalOpenCount
(),
len
(
me
.
inodeMap
))
}
func
(
me
*
FileSystemConnector
)
decodeFileHandle
(
h
uint64
)
*
fileBridge
{
...
...
@@ -305,27 +345,15 @@ func (me *FileSystemConnector) verify() {
}
me
.
treeLock
.
Lock
()
defer
me
.
treeLock
.
Unlock
()
me
.
fileLock
.
Lock
()
defer
me
.
fileLock
.
Unlock
()
hiddenOpen
:=
0
for
k
,
v
:=
range
me
.
inodeMap
{
if
v
.
NodeId
!=
k
{
panic
(
fmt
.
Sprintf
(
"nodeid mismatch %v %v"
,
v
,
k
))
}
if
v
.
Parent
==
nil
&&
v
!=
me
.
rootNode
{
hiddenOpen
+=
v
.
OpenCount
}
}
root
:=
me
.
rootNode
root
.
verify
(
me
.
rootNode
.
mountPoint
)
open
:=
root
.
totalOpenCount
()
openFiles
:=
len
(
me
.
openFiles
)
if
open
+
hiddenOpen
!=
openFiles
{
panic
(
fmt
.
Sprintf
(
"opencount mismatch totalOpen=%v openFiles=%v hiddenOpen=%v"
,
open
,
openFiles
,
hiddenOpen
))
}
}
func
(
me
*
FileSystemConnector
)
newInode
(
root
bool
,
isDir
bool
)
*
inode
{
...
...
@@ -355,6 +383,7 @@ func (me *FileSystemConnector) lookupUpdate(parent *inode, name string, isDir bo
data
=
me
.
newInode
(
false
,
isDir
)
data
.
Name
=
name
data
.
setParent
(
parent
)
data
.
mount
=
parent
.
mount
}
data
.
LookupCount
+=
lookupCount
return
data
...
...
@@ -385,7 +414,7 @@ func (me *FileSystemConnector) considerDropInode(n *inode) {
n
.
OpenCount
<=
0
{
n
.
setParent
(
nil
)
me
.
inodeMap
[
n
.
NodeId
]
=
nil
,
false
}
}
}
func
(
me
*
FileSystemConnector
)
renameUpdate
(
oldParent
*
inode
,
oldName
string
,
newParent
*
inode
,
newName
string
)
{
...
...
@@ -444,7 +473,6 @@ func (me *FileSystemConnector) findInode(fullPath string) *inode {
func
EmptyFileSystemConnector
()
(
out
*
FileSystemConnector
)
{
out
=
new
(
FileSystemConnector
)
out
.
inodeMap
=
make
(
map
[
uint64
]
*
inode
)
out
.
openFiles
=
make
(
map
[
uint64
]
*
fileBridge
)
rootData
:=
out
.
newInode
(
true
,
true
)
rootData
.
Children
=
make
(
map
[
string
]
*
inode
,
initDirSize
)
...
...
@@ -495,6 +523,7 @@ func (me *FileSystemConnector) Mount(mountPoint string, fs FileSystem, opts *Fil
}
node
.
mountPoint
=
newMount
(
fs
)
node
.
mount
=
node
.
mountPoint
if
opts
==
nil
{
opts
=
NewFileSystemOptions
()
}
...
...
@@ -510,7 +539,6 @@ func (me *FileSystemConnector) Unmount(path string) Status {
// Need to lock to look at node.Children
me
.
treeLock
.
RLock
()
me
.
fileLock
.
Lock
()
unmountError
:=
OK
...
...
@@ -529,7 +557,6 @@ func (me *FileSystemConnector) Unmount(path string) Status {
// We settle for eventual consistency.
mount
.
unmountPending
=
true
}
me
.
fileLock
.
Unlock
()
me
.
treeLock
.
RUnlock
()
if
unmountError
.
Ok
()
{
...
...
@@ -551,7 +578,7 @@ func (me *FileSystemConnector) GetPath(nodeid uint64) (path string, mount *mount
p
,
m
:=
n
.
GetPath
()
if
me
.
Debug
{
log
.
Printf
(
"Node %v = '%s'"
,
nodeid
,
p
)
log
.
Printf
(
"Node %v = '%s'"
,
nodeid
,
n
.
GetFullPath
()
)
}
return
p
,
m
,
n
...
...
fuse/pathops.go
View file @
75dc7589
...
...
@@ -130,7 +130,7 @@ func (me *FileSystemConnector) OpenDir(header *InHeader, input *OpenIn) (flags u
de
:=
&
Dir
{
stream
:
stream
,
}
h
:=
m
e
.
registerFile
(
node
,
mount
,
de
,
input
.
Flags
)
h
:=
m
ount
.
registerFile
(
node
,
de
,
input
.
Flags
)
return
0
,
h
,
OK
}
...
...
@@ -155,7 +155,7 @@ func (me *FileSystemConnector) Open(header *InHeader, input *OpenIn) (flags uint
if
err
!=
OK
{
return
0
,
0
,
err
}
h
:=
m
e
.
registerFile
(
node
,
mount
,
f
,
input
.
Flags
)
h
:=
m
ount
.
registerFile
(
node
,
f
,
input
.
Flags
)
return
0
,
h
,
OK
}
...
...
@@ -373,12 +373,12 @@ func (me *FileSystemConnector) Create(header *InHeader, input *CreateIn, name st
}
out
,
code
,
inode
:=
me
.
internalLookupWithNode
(
parent
,
name
,
1
)
return
0
,
m
e
.
registerFile
(
inode
,
mount
,
f
,
input
.
Flags
),
out
,
code
return
0
,
m
ount
.
registerFile
(
inode
,
f
,
input
.
Flags
),
out
,
code
}
func
(
me
*
FileSystemConnector
)
Release
(
header
*
InHeader
,
input
*
ReleaseIn
)
{
node
:=
me
.
getInodeData
(
header
.
NodeId
)
f
:=
me
.
unregisterFile
(
node
,
input
.
Fh
)
.
(
File
)
f
:=
node
.
mount
.
unregisterFile
(
node
,
input
.
Fh
)
.
(
File
)
f
.
Release
()
}
...
...
@@ -406,7 +406,7 @@ func (me *FileSystemConnector) Flush(input *FlushIn) Status {
func
(
me
*
FileSystemConnector
)
ReleaseDir
(
header
*
InHeader
,
input
*
ReleaseIn
)
{
node
:=
me
.
getInodeData
(
header
.
NodeId
)
d
:=
me
.
unregisterFile
(
node
,
input
.
Fh
)
.
(
rawDir
)
d
:=
node
.
mount
.
unregisterFile
(
node
,
input
.
Fh
)
.
(
rawDir
)
d
.
Release
()
me
.
considerDropInode
(
node
)
}
...
...
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