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
1002f0a1
Commit
1002f0a1
authored
Mar 06, 2019
by
Han-Wen Nienhuys
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
nodefs: ReadDir/ReadDirPlus
parent
7e917a7b
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
258 additions
and
19 deletions
+258
-19
nodefs/api.go
nodefs/api.go
+42
-0
nodefs/bridge.go
nodefs/bridge.go
+126
-17
nodefs/default.go
nodefs/default.go
+8
-0
nodefs/loopback.go
nodefs/loopback.go
+46
-0
nodefs/simple_test.go
nodefs/simple_test.go
+36
-2
No files found.
nodefs/api.go
View file @
1002f0a1
...
...
@@ -63,6 +63,41 @@ func InodeOf(node Operations) *Inode {
return
node
.
inode
()
}
// DirStream lists directory entries.
type
DirStream
interface
{
// HasNext indicates if there are further entries. HasNext
// might be called on already closed streams.
HasNext
()
bool
// Next retrieves the next entry. It is only called if HasNext
// has previously returned true. The Status may be used to
// indicate I/O errors
Next
()
(
fuse
.
DirEntry
,
fuse
.
Status
)
// Close releases resources related to this directory
// stream. A stream should be resilient against double close.
Close
()
}
// XXX names
type
DirArray
struct
{
Entries
[]
fuse
.
DirEntry
}
func
(
a
*
DirArray
)
HasNext
()
bool
{
return
len
(
a
.
Entries
)
>
0
}
func
(
a
*
DirArray
)
Next
()
(
fuse
.
DirEntry
,
fuse
.
Status
)
{
e
:=
a
.
Entries
[
0
]
a
.
Entries
=
a
.
Entries
[
1
:
]
return
e
,
fuse
.
OK
}
func
(
a
*
DirArray
)
Close
()
{
}
/*
NOSUBMIT: how to structure?
...
...
@@ -110,6 +145,13 @@ type Operations interface {
Readlink
(
ctx
context
.
Context
)
(
string
,
fuse
.
Status
)
Open
(
ctx
context
.
Context
,
flags
uint32
)
(
fh
FileHandle
,
fuseFlags
uint32
,
code
fuse
.
Status
)
// OpenDir is called for sanity/permission checks on opening a
// directory.
OpenDir
(
ctx
context
.
Context
)
fuse
.
Status
// ReadDir opens a stream of directory entries.
ReadDir
(
ctx
context
.
Context
)
(
DirStream
,
fuse
.
Status
)
Read
(
ctx
context
.
Context
,
f
FileHandle
,
dest
[]
byte
,
off
int64
)
(
fuse
.
ReadResult
,
fuse
.
Status
)
Write
(
ctx
context
.
Context
,
f
FileHandle
,
data
[]
byte
,
off
int64
)
(
written
uint32
,
code
fuse
.
Status
)
...
...
nodefs/bridge.go
View file @
1002f0a1
...
...
@@ -17,7 +17,10 @@ import (
type
fileEntry
struct
{
file
FileHandle
// space to hold directory stuff
// Directory
dirStream
DirStream
hasOverflow
bool
overflow
fuse
.
DirEntry
}
type
rawBridge
struct
{
...
...
@@ -31,7 +34,7 @@ type rawBridge struct {
nodes
map
[
uint64
]
*
Inode
automaticIno
uint64
files
[]
fileEntry
files
[]
*
fileEntry
freeFiles
[]
uint64
}
...
...
@@ -110,7 +113,7 @@ func NewNodeFS(root Operations, opts *Options) fuse.RawFileSystem {
}
// Fh 0 means no file handle.
bridge
.
files
=
[]
fileEntry
{{}}
bridge
.
files
=
[]
*
fileEntry
{{}}
return
bridge
}
...
...
@@ -118,16 +121,13 @@ func (b *rawBridge) String() string {
return
"rawBridge"
}
func
(
b
*
rawBridge
)
inode
(
id
uint64
,
fh
uint64
)
(
*
Inode
,
fileEntry
)
{
func
(
b
*
rawBridge
)
inode
(
id
uint64
,
fh
uint64
)
(
*
Inode
,
*
fileEntry
)
{
b
.
mu
.
Lock
()
defer
b
.
mu
.
Unlock
()
n
,
f
:=
b
.
nodes
[
id
],
b
.
files
[
fh
]
if
n
==
nil
{
log
.
Panicf
(
"unknown node %d"
,
id
)
}
if
fh
!=
0
&&
f
.
file
==
nil
{
log
.
Panicf
(
"unknown fh %d"
,
fh
)
}
return
n
,
f
}
...
...
@@ -457,7 +457,7 @@ func (b *rawBridge) registerFile(f FileHandle) uint64 {
b
.
freeFiles
=
b
.
freeFiles
[
:
last
]
}
else
{
fh
=
uint64
(
len
(
b
.
files
))
b
.
files
=
append
(
b
.
files
,
fileEntry
{})
b
.
files
=
append
(
b
.
files
,
&
fileEntry
{})
}
b
.
files
[
fh
]
.
file
=
f
...
...
@@ -488,11 +488,19 @@ func (b *rawBridge) Release(input *fuse.ReleaseIn) {
n
,
f
:=
b
.
inode
(
input
.
NodeId
,
input
.
Fh
)
n
.
node
.
Release
(
context
.
TODO
(),
f
.
file
)
if
input
.
Fh
>
0
{
b
.
releaseFileEntry
(
input
.
Fh
)
}
func
(
b
*
rawBridge
)
ReleaseDir
(
input
*
fuse
.
ReleaseIn
)
{
b
.
releaseFileEntry
(
input
.
Fh
)
}
func
(
b
*
rawBridge
)
releaseFileEntry
(
fh
uint64
)
{
if
fh
>
0
{
b
.
mu
.
Lock
()
defer
b
.
mu
.
Unlock
()
b
.
files
[
input
.
F
h
]
.
file
=
nil
b
.
freeFiles
=
append
(
b
.
freeFiles
,
input
.
F
h
)
b
.
files
[
f
h
]
.
file
=
nil
b
.
freeFiles
=
append
(
b
.
freeFiles
,
f
h
)
}
}
...
...
@@ -517,19 +525,120 @@ func (b *rawBridge) Fallocate(input *fuse.FallocateIn) (code fuse.Status) {
}
func
(
b
*
rawBridge
)
OpenDir
(
input
*
fuse
.
OpenIn
,
out
*
fuse
.
OpenOut
)
(
status
fuse
.
Status
)
{
return
fuse
.
ENOSYS
n
,
_
:=
b
.
inode
(
input
.
NodeId
,
0
)
code
:=
n
.
node
.
OpenDir
(
context
.
TODO
())
if
!
code
.
Ok
()
{
return
code
}
b
.
mu
.
Lock
()
defer
b
.
mu
.
Unlock
()
out
.
Fh
=
b
.
registerFile
(
nil
)
return
fuse
.
OK
}
func
(
b
*
rawBridge
)
getStream
(
input
*
fuse
.
ReadIn
,
inode
*
Inode
,
f
*
fileEntry
)
fuse
.
Status
{
if
f
.
dirStream
==
nil
||
input
.
Offset
==
0
{
if
f
.
dirStream
!=
nil
{
f
.
dirStream
.
Close
()
}
str
,
code
:=
inode
.
node
.
ReadDir
(
context
.
TODO
())
if
!
code
.
Ok
()
{
return
code
}
f
.
hasOverflow
=
false
f
.
dirStream
=
str
}
return
fuse
.
OK
}
func
(
b
*
rawBridge
)
ReadDir
(
input
*
fuse
.
ReadIn
,
out
*
fuse
.
DirEntryList
)
fuse
.
Status
{
return
fuse
.
ENOSYS
n
,
f
:=
b
.
inode
(
input
.
NodeId
,
input
.
Fh
)
if
code
:=
b
.
getStream
(
input
,
n
,
f
);
!
code
.
Ok
()
{
return
code
}
if
f
.
hasOverflow
{
// always succeeds.
out
.
AddDirEntry
(
f
.
overflow
)
f
.
hasOverflow
=
false
}
// TODO - should post '..' and '.' ?
for
f
.
dirStream
.
HasNext
()
{
e
,
code
:=
f
.
dirStream
.
Next
()
if
!
code
.
Ok
()
{
f
.
dirStream
.
Close
()
return
code
}
if
!
out
.
AddDirEntry
(
e
)
{
f
.
overflow
=
e
f
.
hasOverflow
=
true
return
code
}
}
f
.
dirStream
.
Close
()
return
fuse
.
OK
}
func
(
b
*
rawBridge
)
ReadDirPlus
(
input
*
fuse
.
ReadIn
,
out
*
fuse
.
DirEntryList
)
fuse
.
Status
{
return
fuse
.
ENOSYS
}
n
,
f
:=
b
.
inode
(
input
.
NodeId
,
input
.
Fh
)
func
(
b
*
rawBridge
)
ReleaseDir
(
input
*
fuse
.
ReleaseIn
)
{
return
if
code
:=
b
.
getStream
(
input
,
n
,
f
);
!
code
.
Ok
()
{
return
code
}
if
f
.
hasOverflow
{
// always succeeds.
out
.
AddDirEntry
(
f
.
overflow
)
f
.
hasOverflow
=
false
}
for
f
.
dirStream
.
HasNext
()
{
var
e
fuse
.
DirEntry
var
code
fuse
.
Status
if
f
.
hasOverflow
{
e
=
f
.
overflow
f
.
hasOverflow
=
false
}
else
{
e
,
code
=
f
.
dirStream
.
Next
()
}
if
!
code
.
Ok
()
{
f
.
dirStream
.
Close
()
return
code
}
entryOut
:=
out
.
AddDirLookupEntry
(
e
)
if
entryOut
==
nil
{
f
.
overflow
=
e
f
.
hasOverflow
=
true
return
fuse
.
OK
}
child
,
code
:=
n
.
node
.
Lookup
(
context
.
TODO
(),
e
.
Name
,
entryOut
)
if
!
code
.
Ok
()
{
if
b
.
options
.
NegativeTimeout
!=
nil
{
entryOut
.
SetEntryTimeout
(
*
b
.
options
.
NegativeTimeout
)
}
}
else
{
b
.
addNewChild
(
n
,
e
.
Name
,
child
,
nil
,
entryOut
)
b
.
setEntryOutTimeout
(
entryOut
)
if
(
e
.
Mode
&^
07777
)
!=
(
child
.
mode
&^
07777
)
{
// XXX should go back and change the
// already serialized entry
log
.
Panicf
(
"mode mismatch between readdir %o and lookup %o"
,
e
.
Mode
,
child
.
mode
)
}
entryOut
.
Mode
=
child
.
mode
|
(
entryOut
.
Mode
&
07777
)
}
}
f
.
dirStream
.
Close
()
return
fuse
.
OK
}
func
(
b
*
rawBridge
)
FsyncDir
(
input
*
fuse
.
FsyncIn
)
(
code
fuse
.
Status
)
{
...
...
nodefs/default.go
View file @
1002f0a1
...
...
@@ -69,6 +69,14 @@ func (n *DefaultOperations) Unlink(ctx context.Context, name string) fuse.Status
return
fuse
.
ENOSYS
}
func
(
n
*
DefaultOperations
)
OpenDir
(
ctx
context
.
Context
)
fuse
.
Status
{
return
fuse
.
ENOSYS
}
func
(
n
*
DefaultOperations
)
ReadDir
(
ctx
context
.
Context
)
(
DirStream
,
fuse
.
Status
)
{
return
nil
,
fuse
.
ENOSYS
}
func
(
n
*
DefaultOperations
)
Rename
(
ctx
context
.
Context
,
name
string
,
newParent
Operations
,
newName
string
,
flags
uint32
)
fuse
.
Status
{
return
fuse
.
ENOSYS
}
...
...
nodefs/loopback.go
View file @
1002f0a1
...
...
@@ -6,6 +6,7 @@ package nodefs
import
(
"context"
"io"
"log"
"os"
"path/filepath"
...
...
@@ -255,6 +256,51 @@ func (n *loopbackNode) Open(ctx context.Context, flags uint32) (fh FileHandle, f
return
lf
,
0
,
fuse
.
OK
}
func
(
n
*
loopbackNode
)
OpenDir
(
ctx
context
.
Context
)
fuse
.
Status
{
fd
,
err
:=
syscall
.
Open
(
n
.
path
(),
syscall
.
O_DIRECTORY
,
0755
)
if
err
!=
nil
{
return
fuse
.
ToStatus
(
err
)
}
syscall
.
Close
(
fd
)
return
fuse
.
OK
}
func
(
n
*
loopbackNode
)
ReadDir
(
ctx
context
.
Context
)
(
DirStream
,
fuse
.
Status
)
{
// XXX should implement streaming read to make sure the API works.
f
,
err
:=
os
.
Open
(
n
.
path
())
if
err
!=
nil
{
return
nil
,
fuse
.
ToStatus
(
err
)
}
defer
f
.
Close
()
var
entries
[]
fuse
.
DirEntry
for
{
want
:=
100
infos
,
err
:=
f
.
Readdir
(
want
)
for
_
,
info
:=
range
infos
{
s
:=
fuse
.
ToStatT
(
info
)
if
s
==
nil
{
continue
}
entries
=
append
(
entries
,
fuse
.
DirEntry
{
Name
:
info
.
Name
(),
Mode
:
uint32
(
s
.
Mode
),
Ino
:
s
.
Ino
,
})
}
if
len
(
infos
)
<
want
||
err
==
io
.
EOF
{
break
}
if
err
!=
nil
{
return
nil
,
fuse
.
ToStatus
(
err
)
}
}
return
&
DirArray
{
entries
},
fuse
.
OK
}
func
(
n
*
loopbackNode
)
fGetAttr
(
ctx
context
.
Context
,
out
*
fuse
.
AttrOut
)
(
fuse
.
Status
,
bool
)
{
n
.
mu
.
Lock
()
defer
n
.
mu
.
Unlock
()
...
...
nodefs/simple_test.go
View file @
1002f0a1
...
...
@@ -6,6 +6,7 @@ package nodefs
import
(
"bytes"
"fmt"
"io/ioutil"
"log"
"os"
...
...
@@ -492,6 +493,39 @@ func TestNotifyEntry(t *testing.T) {
}
}
// Test Notify() , but requires KEEP_CACHE.
// XXX Test Notify() , but requires KEEP_CACHE ? or could use mmap?
// XXX Test NotifyDelete?
// Test NotifyDelete?
func
TestReadDir
(
t
*
testing
.
T
)
{
tc
:=
newTestCase
(
t
)
defer
tc
.
Clean
()
// XXX what about ".." and "." ?
want
:=
map
[
string
]
bool
{}
for
i
:=
0
;
i
<
2
;
i
++
{
// 40 bytes of filename, so 110 entries overflows a
// 4096 page.
nm
:=
fmt
.
Sprintf
(
"file%036x"
,
i
)
want
[
nm
]
=
true
if
err
:=
ioutil
.
WriteFile
(
tc
.
origDir
+
"/"
+
nm
,
[]
byte
(
"hello"
),
0644
);
err
!=
nil
{
t
.
Fatalf
(
"WriteFile: %v"
,
err
)
}
}
entries
,
err
:=
ioutil
.
ReadDir
(
tc
.
mntDir
)
if
err
!=
nil
{
t
.
Fatalf
(
"ReadDir: %v"
,
err
)
}
got
:=
map
[
string
]
bool
{}
for
_
,
e
:=
range
entries
{
got
[
e
.
Name
()]
=
true
}
if
len
(
got
)
!=
len
(
want
)
{
t
.
Errorf
(
"got %d entries, want %d"
,
len
(
got
),
len
(
want
))
}
for
k
:=
range
got
{
if
!
want
[
k
]
{
t
.
Errorf
(
"got unknown name %q"
,
k
)
}
}
}
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