Commit 9abf7b8e authored by Kirill Smelkov's avatar Kirill Smelkov

Merge remote-tracking branch 'origin/master' into t

* origin/master:
  nodefs: add Inode.IsRoot
  newunionfs: hide DELETIONS
  nodefs: fix style in doReadDir
  newunionfs: implement Readdir
  nodefs: suppress verbose output for some tests
  fuse: add FOPEN_CACHE_DIR to print registry
  newunionfs: disable umask
  nodefs: disable umask in tests
  nodefs: more renaming
parents 55370734 467f4e06
......@@ -37,6 +37,31 @@ type unionFSNode struct {
const delDir = "DELETIONS"
var delDirHash = filePathHash(delDir)
func (r *unionFSRoot) allMarkers(result map[string]struct{}) syscall.Errno {
dir := filepath.Join(r.roots[0], delDir)
ds, errno := nodefs.NewLoopbackDirStream(dir)
if errno != 0 {
return errno
}
defer ds.Close()
for ds.HasNext() {
e, errno := ds.Next()
if errno != 0 {
return errno
}
if e.Mode != syscall.S_IFREG {
continue
}
result[e.Name] = struct{}{}
}
return 0
}
func (r *unionFSRoot) rmMarker(name string) syscall.Errno {
err := syscall.Unlink(r.markerPath(name))
if err != nil {
......@@ -161,6 +186,10 @@ func (n *unionFSNode) Setattr(ctx context.Context, fh nodefs.FileHandle, in *fus
var _ = (nodefs.Creater)((*unionFSNode)(nil))
func (n *unionFSNode) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (*nodefs.Inode, nodefs.FileHandle, uint32, syscall.Errno) {
if n.IsRoot() && name == delDir {
return nil, nil, 0, syscall.EPERM
}
var st syscall.Stat_t
dirName, idx := n.getBranch(&st)
if idx > 0 {
......@@ -232,6 +261,10 @@ func (n *unionFSNode) Getattr(ctx context.Context, fh nodefs.FileHandle, out *fu
var _ = (nodefs.Lookuper)((*unionFSNode)(nil))
func (n *unionFSNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*nodefs.Inode, syscall.Errno) {
if n.IsRoot() && name == delDir {
return nil, syscall.ENOENT
}
var st syscall.Stat_t
p := filepath.Join(n.Path(nil), name)
......@@ -297,6 +330,53 @@ func (n *unionFSNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
return buf[:count], 0
}
var _ = (nodefs.Readdirer)((*unionFSNode)(nil))
func (n *unionFSNode) Readdir(ctx context.Context) (nodefs.DirStream, syscall.Errno) {
root := n.root()
markers := map[string]struct{}{delDirHash: struct{}{}}
// ignore error: assume no markers
root.allMarkers(markers)
dir := n.Path(nil)
names := map[string]uint32{}
for i := range root.roots {
// deepest root first.
readRoot(root.roots[len(root.roots)-i-1], dir, names)
}
result := make([]fuse.DirEntry, 0, len(names))
for nm, mode := range names {
marker := filePathHash(filepath.Join(dir, nm))
if _, ok := markers[marker]; ok {
continue
}
result = append(result, fuse.DirEntry{
Name: nm,
Mode: mode,
})
}
return nodefs.NewListDirStream(result), 0
}
func readRoot(root string, dir string, result map[string]uint32) {
ds, errno := nodefs.NewLoopbackDirStream(filepath.Join(root, dir))
if errno != 0 {
return
}
defer ds.Close()
for ds.HasNext() {
e, errno := ds.Next()
if errno != 0 {
return
}
result[e.Name] = e.Mode
}
}
// getBranch returns the root where we can find the given file. It
// will check the deletion markers in roots[0].
func (n *unionFSNode) getBranch(st *syscall.Stat_t) (string, int) {
......
......@@ -9,6 +9,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"syscall"
"testing"
......@@ -131,6 +132,15 @@ func TestDeleteMarker(t *testing.T) {
}
}
func TestCreateDeletions(t *testing.T) {
tc := newTestCase(t, true)
defer tc.Clean()
if _, err := syscall.Creat(filepath.Join(tc.mnt, delDir), 0644); err != syscall.EPERM {
t.Fatalf("got err %v, want EPERM", err)
}
}
func TestCreate(t *testing.T) {
tc := newTestCase(t, true)
defer tc.Clean()
......@@ -196,6 +206,70 @@ func TestDeleteRevert(t *testing.T) {
}
}
func TestReaddirRoot(t *testing.T) {
tc := newTestCase(t, true)
defer tc.Clean()
if err := os.Remove(tc.mnt + "/dir/ro-file"); err != nil {
t.Fatalf("Remove: %v", err)
}
f, err := os.Open(tc.mnt)
if err != nil {
t.Fatalf("Open: %v", err)
}
defer f.Close()
names, err := f.Readdirnames(-1)
if err != nil {
t.Fatalf("Readdirnames: %v", err)
}
got := map[string]bool{}
want := map[string]bool{"dir": true}
for _, nm := range names {
got[nm] = true
}
if !reflect.DeepEqual(want, got) {
t.Errorf("got %v want %v", got, want)
}
}
func TestReaddir(t *testing.T) {
tc := newTestCase(t, true)
defer tc.Clean()
if err := ioutil.WriteFile(tc.ro+"/dir/file2", nil, 0644); err != nil {
t.Fatalf("WriteFile: %v", err)
}
if err := os.Mkdir(tc.rw+"/dir", 0755); err != nil {
t.Fatalf("Mkdir: %v", err)
}
if err := ioutil.WriteFile(tc.rw+"/dir/file3", nil, 0644); err != nil {
t.Fatalf("WriteFile: %v", err)
}
if err := os.Remove(tc.mnt + "/dir/ro-file"); err != nil {
t.Fatalf("Remove: %v", err)
}
res, err := ioutil.ReadDir(tc.mnt + "/dir")
if err != nil {
t.Fatalf("ReadDir: %v", err)
}
got := map[string]bool{}
want := map[string]bool{
"file2": true,
"file3": true,
}
for _, fi := range res {
got[fi.Name()] = true
}
if !reflect.DeepEqual(want, got) {
t.Errorf("got %v want %v", got, want)
}
}
func TestPosix(t *testing.T) {
cases := []string{
"SymlinkReadlink",
......@@ -207,7 +281,7 @@ func TestPosix(t *testing.T) {
// "NlinkZero",
"ParallelFileOpen",
// "Link",
// "ReadDir",
"ReadDir",
}
for _, nm := range cases {
......@@ -220,3 +294,7 @@ func TestPosix(t *testing.T) {
})
}
}
func init() {
syscall.Umask(0)
}
......@@ -126,9 +126,9 @@ func (b *rawBridge) addNewChild(parent *Inode, name string, child *Inode, file F
fh = b.registerFile(child, file, fileFlags)
}
out.NodeId = child.nodeAttr.Ino
out.Generation = child.nodeAttr.Gen
out.Attr.Ino = child.nodeAttr.Ino
out.NodeId = child.stableAttr.Ino
out.Generation = child.stableAttr.Gen
out.Attr.Ino = child.stableAttr.Ino
b.mu.Unlock()
unlockNodes(parent, child)
......@@ -406,8 +406,8 @@ func (b *rawBridge) getattr(ctx context.Context, n *Inode, f FileHandle, out *fu
}
if errno == 0 {
out.Ino = n.nodeAttr.Ino
out.Mode = (out.Attr.Mode & 07777) | n.nodeAttr.Mode
out.Ino = n.stableAttr.Ino
out.Mode = (out.Attr.Mode & 07777) | n.stableAttr.Mode
b.setAttr(&out.Attr)
b.setAttrTimeout(out)
}
......@@ -786,18 +786,17 @@ func (b *rawBridge) setStream(cancel <-chan struct{}, input *fuse.ReadIn, inode
}
func (b *rawBridge) getStream(ctx context.Context, inode *Inode) (DirStream, syscall.Errno) {
rd, ok := inode.ops.(Readdirer)
if !ok {
r := []fuse.DirEntry{}
for k, ch := range inode.Children() {
r = append(r, fuse.DirEntry{Mode: ch.Mode(),
Name: k,
Ino: ch.StableAttr().Ino})
}
return NewListDirStream(r), 0
if rd, ok := inode.ops.(Readdirer); ok {
return rd.Readdir(ctx)
}
return rd.Readdir(ctx)
r := []fuse.DirEntry{}
for k, ch := range inode.Children() {
r = append(r, fuse.DirEntry{Mode: ch.Mode(),
Name: k,
Ino: ch.StableAttr().Ino})
}
return NewListDirStream(r), 0
}
func (b *rawBridge) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status {
......@@ -872,12 +871,12 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out
b.addNewChild(n, e.Name, child, nil, 0, entryOut)
child.setEntryOut(entryOut)
b.setEntryOutTimeout(entryOut)
if (e.Mode &^ 07777) != (child.nodeAttr.Mode &^ 07777) {
if (e.Mode &^ 07777) != (child.stableAttr.Mode &^ 07777) {
// should go back and change the
// already serialized entry
log.Panicf("mode mismatch between readdir %o and lookup %o", e.Mode, child.nodeAttr.Mode)
log.Panicf("mode mismatch between readdir %o and lookup %o", e.Mode, child.stableAttr.Mode)
}
entryOut.Mode = child.nodeAttr.Mode | (entryOut.Mode & 07777)
entryOut.Mode = child.stableAttr.Mode | (entryOut.Mode & 07777)
}
}
......
......@@ -57,7 +57,7 @@ func (i *StableAttr) Reserved() bool {
// copied. Inodes should be obtained by calling Inode.NewInode() or
// Inode.NewPersistentInode().
type Inode struct {
nodeAttr StableAttr
stableAttr StableAttr
ops InodeEmbedder
bridge *rawBridge
......@@ -97,7 +97,7 @@ type Inode struct {
}
func (n *Inode) IsDir() bool {
return n.nodeAttr.Mode&syscall.S_IFDIR != 0
return n.stableAttr.Mode&syscall.S_IFDIR != 0
}
func (n *Inode) embed() *Inode {
......@@ -110,7 +110,7 @@ func (n *Inode) EmbeddedInode() *Inode {
func initInode(n *Inode, ops InodeEmbedder, attr StableAttr, bridge *rawBridge, persistent bool) {
n.ops = ops
n.nodeAttr = attr
n.stableAttr = attr
n.bridge = bridge
n.persistent = persistent
n.parents = make(map[parentData]struct{})
......@@ -121,19 +121,19 @@ func initInode(n *Inode, ops InodeEmbedder, attr StableAttr, bridge *rawBridge,
// Set node ID and mode in EntryOut
func (n *Inode) setEntryOut(out *fuse.EntryOut) {
out.NodeId = n.nodeAttr.Ino
out.Ino = n.nodeAttr.Ino
out.Mode = (out.Attr.Mode & 07777) | n.nodeAttr.Mode
out.NodeId = n.stableAttr.Ino
out.Ino = n.stableAttr.Ino
out.Mode = (out.Attr.Mode & 07777) | n.stableAttr.Mode
}
// StableAttr returns the (Ino, Gen) tuple for this node.
func (n *Inode) StableAttr() StableAttr {
return n.nodeAttr
return n.stableAttr
}
// Mode returns the filetype
func (n *Inode) Mode() uint32 {
return n.nodeAttr.Mode
return n.stableAttr.Mode
}
// Returns the root of the tree
......@@ -141,6 +141,11 @@ func (n *Inode) Root() *Inode {
return n.bridge.root
}
// Returns whether this is the root of the tree
func (n *Inode) IsRoot() bool {
return n.bridge.root == n
}
func modeStr(m uint32) string {
return map[uint32]string{
syscall.S_IFREG: "reg",
......@@ -159,10 +164,10 @@ func (n *Inode) String() string {
defer n.mu.Unlock()
var ss []string
for nm, ch := range n.children {
ss = append(ss, fmt.Sprintf("%q=%d[%s]", nm, ch.nodeAttr.Ino, modeStr(ch.nodeAttr.Mode)))
ss = append(ss, fmt.Sprintf("%q=%d[%s]", nm, ch.stableAttr.Ino, modeStr(ch.stableAttr.Mode)))
}
return fmt.Sprintf("%d[%s]: %s", n.nodeAttr.Ino, modeStr(n.nodeAttr.Mode), strings.Join(ss, ","))
return fmt.Sprintf("%d[%s]: %s", n.stableAttr.Ino, modeStr(n.stableAttr.Mode), strings.Join(ss, ","))
}
// sortNodes rearranges inode group in consistent order.
......@@ -391,7 +396,7 @@ retry:
}
n.bridge.mu.Lock()
delete(n.bridge.nodes, n.nodeAttr.Ino)
delete(n.bridge.nodes, n.stableAttr.Ino)
n.bridge.mu.Unlock()
unlockNodes(lockme...)
......@@ -686,7 +691,7 @@ retry:
// tuple should be invalidated. On next access, a LOOKUP operation
// will be started.
func (n *Inode) NotifyEntry(name string) syscall.Errno {
status := n.bridge.server.EntryNotify(n.nodeAttr.Ino, name)
status := n.bridge.server.EntryNotify(n.stableAttr.Ino, name)
return syscall.Errno(status)
}
......@@ -695,7 +700,7 @@ func (n *Inode) NotifyEntry(name string) syscall.Errno {
// to NotifyEntry, but also sends an event to inotify watchers.
func (n *Inode) NotifyDelete(name string, child *Inode) syscall.Errno {
// XXX arg ordering?
return syscall.Errno(n.bridge.server.DeleteNotify(n.nodeAttr.Ino, child.nodeAttr.Ino, name))
return syscall.Errno(n.bridge.server.DeleteNotify(n.stableAttr.Ino, child.stableAttr.Ino, name))
}
......@@ -703,16 +708,16 @@ func (n *Inode) NotifyDelete(name string, child *Inode) syscall.Errno {
// inode should be flushed from buffers.
func (n *Inode) NotifyContent(off, sz int64) syscall.Errno {
// XXX how does this work for directories?
return syscall.Errno(n.bridge.server.InodeNotify(n.nodeAttr.Ino, off, sz))
return syscall.Errno(n.bridge.server.InodeNotify(n.stableAttr.Ino, off, sz))
}
// WriteCache stores data in the kernel cache.
func (n *Inode) WriteCache(offset int64, data []byte) syscall.Errno {
return syscall.Errno(n.bridge.server.InodeNotifyStoreCache(n.nodeAttr.Ino, offset, data))
return syscall.Errno(n.bridge.server.InodeNotifyStoreCache(n.stableAttr.Ino, offset, data))
}
// ReadCache reads data from the kernel cache.
func (n *Inode) ReadCache(offset int64, dest []byte) (count int, errno syscall.Errno) {
c, s := n.bridge.server.InodeRetrieveCache(n.nodeAttr.Ino, offset, dest)
c, s := n.bridge.server.InodeRetrieveCache(n.stableAttr.Ino, offset, dest)
return c, syscall.Errno(s)
}
......@@ -17,7 +17,7 @@ import (
)
func TestRenameExchange(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
if err := os.Mkdir(tc.origDir+"/dir", 0755); err != nil {
......@@ -74,7 +74,7 @@ func TestRenameExchange(t *testing.T) {
}
func TestRenameNoOverwrite(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
if err := os.Mkdir(tc.origDir+"/dir", 0755); err != nil {
......@@ -102,7 +102,7 @@ func TestRenameNoOverwrite(t *testing.T) {
}
func TestXAttr(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
tc.writeOrig("file", "", 0644)
......@@ -137,7 +137,7 @@ func TestXAttr(t *testing.T) {
}
func TestCopyFileRange(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
if !tc.server.KernelSettings().SupportsVersion(7, 28) {
......
......@@ -47,7 +47,16 @@ func (tc *testCase) Clean() {
}
}
func newTestCase(t *testing.T, entryCache bool, attrCache bool) *testCase {
type testOptions struct {
entryCache bool
attrCache bool
suppressDebug bool
}
func newTestCase(t *testing.T, opts *testOptions) *testCase {
if opts == nil {
opts = &testOptions{}
}
tc := &testCase{
dir: testutil.TempDir(),
T: t,
......@@ -70,11 +79,11 @@ func newTestCase(t *testing.T, entryCache bool, attrCache bool) *testCase {
oneSec := time.Second
attrDT := &oneSec
if !attrCache {
if !opts.attrCache {
attrDT = nil
}
entryDT := &oneSec
if !entryCache {
if !opts.entryCache {
entryDT = nil
}
tc.rawFS = NewNodeFS(tc.loopback, &Options{
......@@ -82,10 +91,11 @@ func newTestCase(t *testing.T, entryCache bool, attrCache bool) *testCase {
AttrTimeout: attrDT,
})
tc.server, err = fuse.NewServer(tc.rawFS, tc.mntDir,
&fuse.MountOptions{
Debug: testutil.VerboseTest(),
})
mOpts := &fuse.MountOptions{}
if !opts.suppressDebug {
mOpts.Debug = testutil.VerboseTest()
}
tc.server, err = fuse.NewServer(tc.rawFS, tc.mntDir, mOpts)
if err != nil {
t.Fatal(err)
}
......@@ -98,7 +108,7 @@ func newTestCase(t *testing.T, entryCache bool, attrCache bool) *testCase {
}
func TestBasic(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
tc.writeOrig("file", "hello", 0644)
......@@ -128,21 +138,25 @@ func TestBasic(t *testing.T) {
}
func TestFileBasic(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
posixtest.FileBasic(t, tc.mntDir)
}
func TestFileTruncate(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
posixtest.TruncateFile(t, tc.mntDir)
}
func TestFileFdLeak(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{
suppressDebug: true,
attrCache: true,
entryCache: true,
})
defer func() {
if tc != nil {
tc.Clean()
......@@ -161,14 +175,14 @@ func TestFileFdLeak(t *testing.T) {
}
func TestMkdir(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
posixtest.MkdirRmdir(t, tc.mntDir)
}
func testRenameOverwrite(t *testing.T, destExists bool) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
posixtest.RenameOverwrite(t, tc.mntDir, destExists)
}
......@@ -183,35 +197,35 @@ func TestRenameDestNoExist(t *testing.T) {
func TestNlinkZero(t *testing.T) {
// xfstest generic/035.
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
posixtest.NlinkZero(t, tc.mntDir)
}
func TestParallelFileOpen(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{suppressDebug: true, attrCache: true, entryCache: true})
defer tc.Clean()
posixtest.ParallelFileOpen(t, tc.mntDir)
}
func TestSymlink(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
posixtest.SymlinkReadlink(t, tc.mntDir)
}
func TestLink(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
posixtest.Link(t, tc.mntDir)
}
func TestNotifyEntry(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
orig := tc.origDir + "/file"
......@@ -244,14 +258,18 @@ func TestNotifyEntry(t *testing.T) {
}
func TestReadDir(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{
suppressDebug: true,
attrCache: true,
entryCache: true,
})
defer tc.Clean()
posixtest.ReadDir(t, tc.mntDir)
}
func TestReadDirStress(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{suppressDebug: true, attrCache: true, entryCache: true})
defer tc.Clean()
// (ab)use posixtest.ReadDir to create 110 test files
posixtest.ReadDir(t, tc.mntDir)
......@@ -286,7 +304,7 @@ func TestReadDirStress(t *testing.T) {
// This test is racy. If an external process consumes space while this
// runs, we may see spurious differences between the two statfs() calls.
func TestStatFs(t *testing.T) {
tc := newTestCase(t, true, true)
tc := newTestCase(t, &testOptions{attrCache: true, entryCache: true})
defer tc.Clean()
empty := syscall.Statfs_t{}
......@@ -314,7 +332,7 @@ func TestGetAttrParallel(t *testing.T) {
// can be handled correctly. Here, test that closing and
// (f)stat in parallel don't lead to fstat on closed files.
// We can only test that if we switch off caching
tc := newTestCase(t, false, false)
tc := newTestCase(t, &testOptions{suppressDebug: true})
defer tc.Clean()
N := 100
......@@ -355,7 +373,7 @@ func TestGetAttrParallel(t *testing.T) {
}
func TestMknod(t *testing.T) {
tc := newTestCase(t, false, false)
tc := newTestCase(t, &testOptions{})
defer tc.Clean()
modes := map[string]uint32{
......@@ -390,8 +408,12 @@ func TestMknod(t *testing.T) {
}
func TestTruncate(t *testing.T) {
tc := newTestCase(t, false, false)
tc := newTestCase(t, &testOptions{})
defer tc.Clean()
posixtest.TruncateNoFile(t, tc.mntDir)
}
func init() {
syscall.Umask(0)
}
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