Commit 204b45db authored by Jakob Unterwurzacher's avatar Jakob Unterwurzacher Committed by Han-Wen Nienhuys

fuse, loopback: return actual inode numbers from READDIR

When an app in a FUSE mount calls getdents(2), go-fuse receives
READDIR[PLUS] and calls the filesystem's OpenDir function that
returns []DirEntry.

The data returned from getdents(2) contains an inode number for
each directory entry, "d_ino". Until now, struct DirEntry had no
corresponding field and the value passed to the kernel was always
FUSE_UNKNOWN_INO = 0xffffffff

This broke apps that actually look at the d_ino field, like
"find -inum".

This commit adds the "Ino" filed to struct DirEntry. If the field
is not set by the filesystem, it is set to FUSE_UNKNOWN_INO,
as before. Otherwise it is left alone and passed to the kernel.

loopbackFileSystem's OpenDir function is extended to set the inode
number. A test verifies that the returned inode number is sane.

Fixes https://github.com/hanwen/go-fuse/issues/175
parent 55260d4a
...@@ -24,10 +24,13 @@ type DirEntry struct { ...@@ -24,10 +24,13 @@ type DirEntry struct {
// Name is the basename of the file in the directory. // Name is the basename of the file in the directory.
Name string Name string
// Ino is the inode number.
Ino uint64
} }
func (d DirEntry) String() string { func (d DirEntry) String() string {
return fmt.Sprintf("%o: %q", d.Mode, d.Name) return fmt.Sprintf("%o: %q ino=%d", d.Mode, d.Name, d.Ino)
} }
// DirEntryList holds the return value for READDIR and READDIRPLUS // DirEntryList holds the return value for READDIR and READDIRPLUS
...@@ -51,12 +54,15 @@ func NewDirEntryList(data []byte, off uint64) *DirEntryList { ...@@ -51,12 +54,15 @@ func NewDirEntryList(data []byte, off uint64) *DirEntryList {
// AddDirEntry tries to add an entry, and reports whether it // AddDirEntry tries to add an entry, and reports whether it
// succeeded. // succeeded.
func (l *DirEntryList) AddDirEntry(e DirEntry) (bool, uint64) { func (l *DirEntryList) AddDirEntry(e DirEntry) (bool, uint64) {
return l.Add(0, e.Name, uint64(FUSE_UNKNOWN_INO), e.Mode) return l.Add(0, e.Name, e.Ino, e.Mode)
} }
// Add adds a direntry to the DirEntryList, returning whether it // Add adds a direntry to the DirEntryList, returning whether it
// succeeded. // 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, uint64) {
if inode == 0 {
inode = FUSE_UNKNOWN_INO
}
padding := (8 - len(name)&7) & 7 padding := (8 - len(name)&7) & 7
delta := padding + direntSize + len(name) + prefix delta := padding + direntSize + len(name) + prefix
oldLen := len(l.buf) oldLen := len(l.buf)
...@@ -90,7 +96,7 @@ func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) ( ...@@ -90,7 +96,7 @@ func (l *DirEntryList) Add(prefix int, name string, inode uint64, mode uint32) (
func (l *DirEntryList) AddDirLookupEntry(e DirEntry) (*EntryOut, uint64) { func (l *DirEntryList) AddDirLookupEntry(e DirEntry) (*EntryOut, uint64) {
lastStart := len(l.buf) lastStart := len(l.buf)
ok, off := l.Add(int(unsafe.Sizeof(EntryOut{})), e.Name, ok, off := l.Add(int(unsafe.Sizeof(EntryOut{})), e.Name,
uint64(FUSE_UNKNOWN_INO), e.Mode) e.Ino, e.Mode)
if !ok { if !ok {
return nil, off return nil, off
} }
......
...@@ -89,7 +89,7 @@ func (fs *loopbackFileSystem) OpenDir(name string, context *fuse.Context) (strea ...@@ -89,7 +89,7 @@ func (fs *loopbackFileSystem) OpenDir(name string, context *fuse.Context) (strea
for { for {
infos, err := f.Readdir(want) infos, err := f.Readdir(want)
for i := range infos { for i := range infos {
// workaround forhttps://code.google.com/p/go/issues/detail?id=5960 // workaround for https://code.google.com/p/go/issues/detail?id=5960
if infos[i] == nil { if infos[i] == nil {
continue continue
} }
...@@ -99,6 +99,7 @@ func (fs *loopbackFileSystem) OpenDir(name string, context *fuse.Context) (strea ...@@ -99,6 +99,7 @@ func (fs *loopbackFileSystem) OpenDir(name string, context *fuse.Context) (strea
} }
if s := fuse.ToStatT(infos[i]); s != nil { if s := fuse.ToStatT(infos[i]); s != nil {
d.Mode = uint32(s.Mode) d.Mode = uint32(s.Mode)
d.Ino = s.Ino
} else { } else {
log.Printf("ReadDir entry %q for %q has no stat info", n, name) log.Printf("ReadDir entry %q for %q has no stat info", n, name)
} }
......
...@@ -10,6 +10,8 @@ import ( ...@@ -10,6 +10,8 @@ import (
"syscall" "syscall"
"testing" "testing"
"time" "time"
"github.com/hanwen/go-fuse/fuse"
) )
func TestTouch(t *testing.T) { func TestTouch(t *testing.T) {
...@@ -157,3 +159,44 @@ func TestSpecialEntries(t *testing.T) { ...@@ -157,3 +159,44 @@ func TestSpecialEntries(t *testing.T) {
t.Errorf("directory is empty, entries '.' and '..' are missing") t.Errorf("directory is empty, entries '.' and '..' are missing")
} }
} }
// Check that readdir(3) returns valid inode numbers in the directory entries
func TestReaddirInodes(t *testing.T) {
tc := NewTestCase(t)
defer tc.Cleanup()
// create "hello.txt"
filename := "hello.txt"
path := tc.orig + "/" + filename
err := ioutil.WriteFile(path, []byte("xyz"), 0600)
if err != nil {
t.Fatal(err)
}
// open mountpoint dir
d, err := os.Open(tc.mnt)
if err != nil {
t.Fatalf("Open failed: %v", err)
}
defer d.Close()
buf := make([]byte, 100)
// readdir(3) use getdents64(2) internally which returns linux_dirent64
// structures. We don't have readdir(3) so we call getdents64(2) directly.
n, err := syscall.Getdents(int(d.Fd()), buf)
if n == 0 {
t.Error("empty directory - we need at least one file")
}
buf = buf[:n]
entries := parseDirents(buf)
t.Logf("parseDirents returned %d entries", len(entries))
// Find "hello.txt" and check inode number.
for _, entry := range entries {
if entry.name != filename {
continue
}
if entry.ino != 0 && entry.ino != fuse.FUSE_UNKNOWN_INO {
// Inode number looks good, we are done.
return
}
t.Errorf("got invalid inode number: %d = 0x%x", entry.ino, entry.ino)
}
t.Errorf("%q not found in directory listing", filename)
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment