Skip to content

Commit

Permalink
Merge pull request #3 from nekojarashi/v1.0.0
Browse files Browse the repository at this point in the history
本家から v1.0.0 タグブランチを取り込み。
  • Loading branch information
y-okubo authored May 27, 2019
2 parents e8302b9 + 1cb6dfc commit c4fd84d
Show file tree
Hide file tree
Showing 29 changed files with 925 additions and 317 deletions.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,45 @@ Grep source code for TODO. Major topics:
Like Go, this library is distributed under the new BSD license. See
accompanying LICENSE file.
--------
## Appendix I. Go-FUSE log format
To increase signal/noise ratio Go-FUSE uses abbreviations in its debug log
output. Here is how to read it:
- `iX` means `inode X`;
- `gX` means `generation X`;
- `tA` and `tE` means timeout for attributes and directory entry correspondingly;
- `[<off> +<size>)` means data range from `<off>` inclusive till `<off>+<size>` exclusive;
- `Xb` means `X bytes`.
Every line is prefixed with either `rx <unique>` or `tx <unique>` to denote
whether it was for kernel request, which Go-FUSE received, or reply, which
Go-FUSE sent back to kernel.
Example debug log output:
```
rx 2: LOOKUP i1 [".wcfs"] 6b
tx 2: OK, {i3 g2 tE=1s tA=1s {M040755 SZ=0 L=0 1000:1000 B0*0 i0:3 A 0.000000 M 0.000000 C 0.000000}}
rx 3: LOOKUP i3 ["zurl"] 5b
tx 3: OK, {i4 g3 tE=1s tA=1s {M0100644 SZ=33 L=1 1000:1000 B0*0 i0:4 A 0.000000 M 0.000000 C 0.000000}}
rx 4: OPEN i4 {O_RDONLY,0x8000}
tx 4: 38=function not implemented, {Fh 0 }
rx 5: READ i4 {Fh 0 [0 +4096) L 0 RDONLY,0x8000}
tx 5: OK, 33b data "file:///"...
rx 6: GETATTR i4 {Fh 0}
tx 6: OK, {tA=1s {M0100644 SZ=33 L=1 1000:1000 B0*0 i0:4 A 0.000000 M 0.000000 C 0.000000}}
rx 7: FLUSH i4 {Fh 0}
tx 7: OK
rx 8: LOOKUP i1 ["head"] 5b
tx 8: OK, {i5 g4 tE=1s tA=1s {M040755 SZ=0 L=0 1000:1000 B0*0 i0:5 A 0.000000 M 0.000000 C 0.000000}}
rx 9: LOOKUP i5 ["bigfile"] 8b
tx 9: OK, {i6 g5 tE=1s tA=1s {M040755 SZ=0 L=0 1000:1000 B0*0 i0:6 A 0.000000 M 0.000000 C 0.000000}}
rx 10: FLUSH i4 {Fh 0}
tx 10: OK
rx 11: GETATTR i1 {Fh 0}
tx 11: OK, {tA=1s {M040755 SZ=0 L=1 1000:1000 B0*0 i0:1 A 0.000000 M 0.000000 C 0.000000}}
```
16 changes: 8 additions & 8 deletions fuse/direntry.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ func NewDirEntryList(data []byte, off uint64) *DirEntryList {

// AddDirEntry tries to add an entry, and reports whether it
// succeeded.
func (l *DirEntryList) AddDirEntry(e DirEntry) (bool, uint64) {
func (l *DirEntryList) AddDirEntry(e DirEntry) bool {
return l.Add(0, e.Name, e.Ino, e.Mode)
}

// Add adds a direntry to the DirEntryList, returning whether it
// succeeded.
func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) (bool, uint64) {
func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) bool {
if inode == 0 {
inode = FUSE_UNKNOWN_INO
}
Expand All @@ -69,7 +69,7 @@ func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) (
newLen := delta + oldLen

if newLen > l.size {
return false, l.offset
return false
}
l.buf = l.buf[:newLen]
oldLen += prefix
Expand All @@ -87,20 +87,20 @@ func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) (
}

l.offset = dirent.Off
return true, l.offset
return true
}

// AddDirLookupEntry is used for ReadDirPlus. It serializes a DirEntry
// and returns the space for entry. If no space is left, returns a nil
// pointer.
func (l *DirEntryList) AddDirLookupEntry(e DirEntry) (*EntryOut, uint64) {
func (l *DirEntryList) AddDirLookupEntry(e DirEntry) *EntryOut {
lastStart := len(l.buf)
ok, off := l.Add(int(unsafe.Sizeof(EntryOut{})), e.Name,
ok := l.Add(int(unsafe.Sizeof(EntryOut{})), e.Name,
e.Ino, e.Mode)
if !ok {
return nil, off
return nil
}
return (*EntryOut)(unsafe.Pointer(&l.buf[lastStart])), off
return (*EntryOut)(unsafe.Pointer(&l.buf[lastStart]))
}

func (l *DirEntryList) bytes() []byte {
Expand Down
4 changes: 3 additions & 1 deletion fuse/nodefs/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ type Node interface {
// FileSystem.Create. Include the NewDefaultFile return value into
// the struct to inherit a null implementation.
type File interface {
// Called upon registering the filehandle in the inode.
// Called upon registering the filehandle in the inode. This
// is useful in that PathFS API, where Create/Open have no
// access to the Inode at hand.
SetInode(*Inode)

// The String method is for debug printing.
Expand Down
36 changes: 15 additions & 21 deletions fuse/nodefs/dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,30 @@ import (

type connectorDir struct {
node Node
inode *Inode
rawFS fuse.RawFileSystem

// Protect stream and lastOffset. These are written in case
// there is a seek on the directory.
mu sync.Mutex
stream []fuse.DirEntry

// lastOffset stores the last offset for a readdir. This lets
// readdir pick up changes to the directory made after opening
// it.
lastOffset uint64
}

func (d *connectorDir) ReadDir(input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) {
d.mu.Lock()
defer d.mu.Unlock()

if d.stream == nil {
return fuse.OK
}
// rewinddir() should be as if reopening directory.
// TODO - test this.
if d.lastOffset > 0 && input.Offset == 0 {
d.stream, code = d.node.OpenDir((*fuse.Context)(&input.Context))
if d.stream == nil || input.Offset == 0 {
d.stream, code = d.node.OpenDir(&input.Context)
if !code.Ok() {
return code
}
d.stream = append(d.stream, d.inode.getMountDirEntries()...)
d.stream = append(d.stream,
fuse.DirEntry{Mode: fuse.S_IFDIR, Name: "."},
fuse.DirEntry{Mode: fuse.S_IFDIR, Name: ".."})
}

if input.Offset > uint64(len(d.stream)) {
Expand All @@ -53,8 +50,7 @@ func (d *connectorDir) ReadDir(input *fuse.ReadIn, out *fuse.DirEntryList) (code
log.Printf("got empty directory entry, mode %o.", e.Mode)
continue
}
ok, off := out.AddDirEntry(e)
d.lastOffset = off
ok := out.AddDirEntry(e)
if !ok {
break
}
Expand All @@ -66,16 +62,16 @@ func (d *connectorDir) ReadDirPlus(input *fuse.ReadIn, out *fuse.DirEntryList) (
d.mu.Lock()
defer d.mu.Unlock()

if d.stream == nil {
return fuse.OK
}

// rewinddir() should be as if reopening directory.
if d.lastOffset > 0 && input.Offset == 0 {
d.stream, code = d.node.OpenDir((*fuse.Context)(&input.Context))
if d.stream == nil || input.Offset == 0 {
d.stream, code = d.node.OpenDir(&input.Context)
if !code.Ok() {
return code
}
d.stream = append(d.stream, d.inode.getMountDirEntries()...)
d.stream = append(d.stream,
fuse.DirEntry{Mode: fuse.S_IFDIR, Name: "."},
fuse.DirEntry{Mode: fuse.S_IFDIR, Name: ".."})
}

if input.Offset > uint64(len(d.stream)) {
Expand All @@ -91,7 +87,7 @@ func (d *connectorDir) ReadDirPlus(input *fuse.ReadIn, out *fuse.DirEntryList) (

// we have to be sure entry will fit if we try to add
// it, or we'll mess up the lookup counts.
entryDest, off := out.AddDirLookupEntry(e)
entryDest := out.AddDirLookupEntry(e)
if entryDest == nil {
break
}
Expand All @@ -106,10 +102,8 @@ func (d *connectorDir) ReadDirPlus(input *fuse.ReadIn, out *fuse.DirEntryList) (
*entryDest = fuse.EntryOut{}

d.rawFS.Lookup(&input.InHeader, e.Name, entryDest)
d.lastOffset = off
}
return fuse.OK

}

type rawDir interface {
Expand Down
12 changes: 10 additions & 2 deletions fuse/nodefs/fsconnector.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"log"
"path/filepath"
"strings"
"sync"
"time"
"unsafe"

Expand All @@ -35,6 +36,13 @@ type FileSystemConnector struct {

// The root of the FUSE file system.
rootNode *Inode

// This lock prevents Lookup() and Forget() from running concurrently.
// Locking at this level is a big hammer, but makes sure we don't return
// forgotten nodes to the kernel. Problems solved by this lock:
// https://github.com/hanwen/go-fuse/issues/168
// https://github.com/rfjakob/gocryptfs/issues/322
lookupLock sync.Mutex
}

// NewOptions generates FUSE options that correspond to libfuse's
Expand Down Expand Up @@ -92,7 +100,7 @@ func (c *FileSystemConnector) verify() {

// childLookup fills entry information for a newly created child inode
func (c *rawBridge) childLookup(out *fuse.EntryOut, n *Inode, context *fuse.Context) {
n.Node().GetAttr((*fuse.Attr)(&out.Attr), nil, context)
n.Node().GetAttr(&out.Attr, nil, context)
n.mount.fillEntry(out)
out.NodeId, out.Generation = c.fsConn().lookupUpdate(n)
if out.Ino == 0 {
Expand Down Expand Up @@ -276,7 +284,7 @@ func (c *FileSystemConnector) lockMount(parent *Inode, name string, root Node, o

node.mountPoint.parentInode = parent
if c.debug {
log.Printf("Mount %T on subdir %s, parent %d", node,
log.Printf("Mount %T on subdir %s, parent i%d", node,
name, c.inodeMap.Handle(&parent.handled))
}
return node, fuse.OK
Expand Down
44 changes: 36 additions & 8 deletions fuse/nodefs/fsmount.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,21 @@ func (m *fileSystemMount) mountName() string {

func (m *fileSystemMount) setOwner(attr *fuse.Attr) {
if m.options.Owner != nil {
attr.Owner = *(*fuse.Owner)(m.options.Owner)
attr.Owner = *m.options.Owner
}
}

func (m *fileSystemMount) fillEntry(out *fuse.EntryOut) {
splitDuration(m.options.EntryTimeout, &out.EntryValid, &out.EntryValidNsec)
splitDuration(m.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec)
out.SetEntryTimeout(m.options.EntryTimeout)
out.SetAttrTimeout(m.options.AttrTimeout)
m.setOwner(&out.Attr)
if out.Mode&fuse.S_IFDIR == 0 && out.Nlink == 0 {
out.Nlink = 1
}
}

func (m *fileSystemMount) fillAttr(out *fuse.AttrOut, nodeId uint64) {
splitDuration(m.options.AttrTimeout, &out.AttrValid, &out.AttrValidNsec)
out.SetTimeout(m.options.AttrTimeout)
m.setOwner(&out.Attr)
if out.Ino == 0 {
out.Ino = nodeId
Expand Down Expand Up @@ -116,8 +116,24 @@ func (m *fileSystemMount) unregisterFileHandle(handle uint64, node *Inode) *open
return opened
}

func (m *fileSystemMount) registerFileHandle(node *Inode, dir *connectorDir, f File, flags uint32) (uint64, *openedFile) {
node.openFilesMutex.Lock()
// registerFileHandle registers f or dir to have a handle.
//
// The handle is then used as file-handle in communications with kernel.
//
// If dir != nil the handle is registered for OpenDir and the inner file (see
// below) must be nil. If dir = nil the handle is registered for regular open &
// friends.
//
// f can be nil, or a WithFlags that leads to File=nil. For !OpenDir, if that
// is the case, returned handle will be 0 to indicate a handleless open, and
// the filesystem operations on the opened file will be routed to be served by
// the node.
//
// other arguments:
//
// node - Inode for which f or dir were opened,
// flags - file open flags, like O_RDWR.
func (m *fileSystemMount) registerFileHandle(node *Inode, dir *connectorDir, f File, flags uint32) (handle uint64, opened *openedFile) {
b := &openedFile{
dir: dir,
WithFlags: WithFlags{
Expand All @@ -138,11 +154,23 @@ func (m *fileSystemMount) registerFileHandle(node *Inode, dir *connectorDir, f F
f = withFlags.File
}

// don't allow both dir and file
if dir != nil && b.WithFlags.File != nil {
panic("registerFileHandle: both dir and file are set.")
}

if b.WithFlags.File == nil && dir == nil {
// it was just WithFlags{...}, but the file itself is nil
return 0, b
}

if b.WithFlags.File != nil {
b.WithFlags.File.SetInode(node)
}

node.openFilesMutex.Lock()
node.openFiles = append(node.openFiles, b)
handle, _ := m.openFiles.Register(&b.handled)
handle, _ = m.openFiles.Register(&b.handled)
node.openFilesMutex.Unlock()
return handle, b
}
Expand All @@ -151,7 +179,7 @@ func (m *fileSystemMount) registerFileHandle(node *Inode, dir *connectorDir, f F
func (m *fileSystemMount) negativeEntry(out *fuse.EntryOut) bool {
if m.options.NegativeTimeout > 0.0 {
out.NodeId = 0
splitDuration(m.options.NegativeTimeout, &out.EntryValid, &out.EntryValidNsec)
out.SetEntryTimeout(m.options.NegativeTimeout)
return true
}
return false
Expand Down
Loading

0 comments on commit c4fd84d

Please sign in to comment.