Commit 8581b7e9 authored by Jakob Unterwurzacher's avatar Jakob Unterwurzacher Committed by Han-Wen Nienhuys

DirEntryList: implement changing of mode after entry was serialized

When a directory is modified while READDIRPLUS is running,
the file type may change between readdir and lookup.

This patch adds the function FixMode() which allows to
overwrite the file type of the last serialized dirent.

Add test

Fixes https://github.com/hanwen/go-fuse/issues/323

Change-Id: Id45a9c4633e9abcb075588dc610ce8341d104825
parent 3a40eb5c
...@@ -902,9 +902,8 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out ...@@ -902,9 +902,8 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out
child.setEntryOut(entryOut) child.setEntryOut(entryOut)
b.setEntryOutTimeout(entryOut) b.setEntryOutTimeout(entryOut)
if (e.Mode &^ 07777) != (child.stableAttr.Mode &^ 07777) { if (e.Mode &^ 07777) != (child.stableAttr.Mode &^ 07777) {
// should go back and change the // The file type has changed behind our back. Use the new value.
// already serialized entry out.FixMode(child.stableAttr.Mode)
log.Panicf("mode mismatch between readdir %o and lookup %o", e.Mode, child.stableAttr.Mode)
} }
entryOut.Mode = child.stableAttr.Mode | (entryOut.Mode & 07777) entryOut.Mode = child.stableAttr.Mode | (entryOut.Mode & 07777)
} }
......
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs
import (
"context"
"math/rand"
"os"
"syscall"
"testing"
"github.com/hanwen/go-fuse/v2/fuse"
)
type randomTypeTest struct {
Inode
}
var _ = (NodeLookuper)((*randomTypeTest)(nil))
var _ = (NodeReaddirer)((*randomTypeTest)(nil))
// Lookup finds a dir.
func (fn *randomTypeTest) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
stable := StableAttr{
Mode: fuse.S_IFDIR,
}
if rand.Intn(2) == 0 {
stable.Mode = fuse.S_IFREG
}
childFN := &randomTypeTest{}
child := fn.NewInode(ctx, childFN, stable)
return child, syscall.F_OK
}
// Readdir will always return one child dir.
func (fn *randomTypeTest) Readdir(ctx context.Context) (DirStream, syscall.Errno) {
var entries []fuse.DirEntry
for i := 0; i < 100; i++ {
entries = append(entries, fuse.DirEntry{
Name: "one",
Mode: fuse.S_IFDIR,
})
}
return NewListDirStream(entries), syscall.F_OK
}
func TestReaddirTypeFixup(t *testing.T) {
root := &randomTypeTest{}
mntDir, _, clean := testMount(t, root, nil)
defer clean()
f, err := os.Open(mntDir)
if err != nil {
t.Fatalf("open: %v", err)
}
defer f.Close()
// No panic.
if _, err := f.Readdir(-1); err != nil {
t.Fatalf("readdir: %v", err)
}
}
...@@ -105,6 +105,14 @@ func (l *DirEntryList) AddDirLookupEntry(e DirEntry) *EntryOut { ...@@ -105,6 +105,14 @@ func (l *DirEntryList) AddDirLookupEntry(e DirEntry) *EntryOut {
return result return result
} }
// FixMode overrides the mode of the last direntry that was added. This can
// be needed when a directory changes while READDIRPLUS is running.
func (l *DirEntryList) FixMode(mode uint32) {
oldLen := len(l.buf) - int(unsafe.Sizeof(_Dirent{}))
dirent := (*_Dirent)(unsafe.Pointer(&l.buf[oldLen]))
dirent.Typ = (mode & 0170000) >> 12
}
func (l *DirEntryList) bytes() []byte { func (l *DirEntryList) bytes() []byte {
return l.buf return l.buf
} }
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