// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package p9test

import (
	"bytes"
	"fmt"
	"io"
	"math/rand"
	"os"
	"reflect"
	"strings"
	"syscall"
	"testing"
	"time"

	"github.com/golang/mock/gomock"
	"gvisor.dev/gvisor/pkg/fd"
	"gvisor.dev/gvisor/pkg/p9"
	"gvisor.dev/gvisor/pkg/sync"
)

func TestPanic(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	// Create a new root.
	d := h.NewDirectory(nil)(nil)
	defer d.Close() // Needed manually.
	h.Attacher.EXPECT().Attach().Return(d, nil).Do(func() {
		// Panic here, and ensure that we get back EFAULT.
		panic("handler")
	})

	// Attach to the client.
	if _, err := c.Attach("/"); err != syscall.EFAULT {
		t.Fatalf("got attach err %v, want EFAULT", err)
	}
}

func TestAttachNoLeak(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	// Create a new root.
	d := h.NewDirectory(nil)(nil)
	h.Attacher.EXPECT().Attach().Return(d, nil).Times(1)

	// Attach to the client.
	f, err := c.Attach("/")
	if err != nil {
		t.Fatalf("got attach err %v, want nil", err)
	}

	// Don't close the file. This should be closed automatically when the
	// client disconnects. The mock asserts that everything is closed
	// exactly once. This statement just removes the unused variable error.
	_ = f
}

func TestBadAttach(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	// Return an error on attach.
	h.Attacher.EXPECT().Attach().Return(nil, syscall.EINVAL).Times(1)

	// Attach to the client.
	if _, err := c.Attach("/"); err != syscall.EINVAL {
		t.Fatalf("got attach err %v, want syscall.EINVAL", err)
	}
}

func TestWalkAttach(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	// Create a new root.
	d := h.NewDirectory(map[string]Generator{
		"a": h.NewDirectory(map[string]Generator{
			"b": h.NewFile(),
		}),
	})(nil)
	h.Attacher.EXPECT().Attach().Return(d, nil).Times(1)

	// Attach to the client as a non-root, and ensure that the walk above
	// occurs as expected. We should get back b, and all references should
	// be dropped when the file is closed.
	f, err := c.Attach("/a/b")
	if err != nil {
		t.Fatalf("got attach err %v, want nil", err)
	}
	defer f.Close()

	// Check that's a regular file.
	if _, _, attr, err := f.GetAttr(p9.AttrMaskAll()); err != nil {
		t.Errorf("got err %v, want nil", err)
	} else if !attr.Mode.IsRegular() {
		t.Errorf("got mode %v, want regular file", err)
	}
}

// newTypeMap returns a new type map dictionary.
func newTypeMap(h *Harness) map[string]Generator {
	return map[string]Generator{
		"directory":        h.NewDirectory(map[string]Generator{}),
		"file":             h.NewFile(),
		"symlink":          h.NewSymlink(),
		"block-device":     h.NewBlockDevice(),
		"character-device": h.NewCharacterDevice(),
		"named-pipe":       h.NewNamedPipe(),
		"socket":           h.NewSocket(),
	}
}

// newRoot returns a new root filesystem.
//
// This is set up in a deterministic way for testing most operations.
//
// The represented file system looks like:
// - file
// - symlink
// - directory
// ...
// + one
//   - file
//   - symlink
//   - directory
//   ...
//   + two
//     - file
//     - symlink
//     - directory
//     ...
// + three
//   - file
//   - symlink
//   - directory
//   ...
func newRoot(h *Harness, c *p9.Client) (*Mock, p9.File) {
	root := newTypeMap(h)
	one := newTypeMap(h)
	two := newTypeMap(h)
	three := newTypeMap(h)
	one["two"] = h.NewDirectory(two)      // Will be nested in one.
	root["one"] = h.NewDirectory(one)     // Top level.
	root["three"] = h.NewDirectory(three) // Alternate top-level.

	// Create a new root.
	rootBackend := h.NewDirectory(root)(nil)
	h.Attacher.EXPECT().Attach().Return(rootBackend, nil)

	// Attach to the client.
	r, err := c.Attach("/")
	if err != nil {
		h.t.Fatalf("got attach err %v, want nil", err)
	}

	return rootBackend, r
}

func allInvalidNames(from string) []string {
	return []string{
		from + "/other",
		from + "/..",
		from + "/.",
		from + "/",
		"other/" + from,
		"/" + from,
		"./" + from,
		"../" + from,
		".",
		"..",
		"/",
		"",
	}
}

func TestWalkInvalid(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	_, root := newRoot(h, c)
	defer root.Close()

	// Run relevant tests.
	for name := range newTypeMap(h) {
		// These are all the various ways that one might attempt to
		// construct compound paths. They should all be rejected, as
		// any compound that contains a / is not allowed, as well as
		// the singular paths of '.' and '..'.
		if _, _, err := root.Walk([]string{".", name}); err != syscall.EINVAL {
			t.Errorf("Walk through . %s wanted EINVAL, got %v", name, err)
		}
		if _, _, err := root.Walk([]string{"..", name}); err != syscall.EINVAL {
			t.Errorf("Walk through . %s wanted EINVAL, got %v", name, err)
		}
		if _, _, err := root.Walk([]string{name, "."}); err != syscall.EINVAL {
			t.Errorf("Walk through %s . wanted EINVAL, got %v", name, err)
		}
		if _, _, err := root.Walk([]string{name, ".."}); err != syscall.EINVAL {
			t.Errorf("Walk through %s .. wanted EINVAL, got %v", name, err)
		}
		for _, invalidName := range allInvalidNames(name) {
			if _, _, err := root.Walk([]string{invalidName}); err != syscall.EINVAL {
				t.Errorf("Walk through %s wanted EINVAL, got %v", invalidName, err)
			}
		}
		wantErr := syscall.EINVAL
		if name == "directory" {
			// We can attempt a walk through a directory. However,
			// we should never see a file named "other", so we
			// expect this to return ENOENT.
			wantErr = syscall.ENOENT
		}
		if _, _, err := root.Walk([]string{name, "other"}); err != wantErr {
			t.Errorf("Walk through %s/other wanted %v, got %v", name, wantErr, err)
		}

		// Do a successful walk.
		_, f, err := root.Walk([]string{name})
		if err != nil {
			t.Errorf("Walk to %s wanted nil, got %v", name, err)
		}
		defer f.Close()
		local := h.Pop(f)

		// Check that the file matches.
		_, localMask, localAttr, localErr := local.GetAttr(p9.AttrMaskAll())
		if _, mask, attr, err := f.GetAttr(p9.AttrMaskAll()); mask != localMask || attr != localAttr || err != localErr {
			t.Errorf("GetAttr got (%v, %v, %v), wanted (%v, %v, %v)",
				mask, attr, err, localMask, localAttr, localErr)
		}

		// Ensure we can't walk backwards.
		if _, _, err := f.Walk([]string{"."}); err != syscall.EINVAL {
			t.Errorf("Walk through %s/. wanted EINVAL, got %v", name, err)
		}
		if _, _, err := f.Walk([]string{".."}); err != syscall.EINVAL {
			t.Errorf("Walk through %s/.. wanted EINVAL, got %v", name, err)
		}
	}
}

// fileGenerator is a function to generate files via walk or create.
//
// Examples are:
//	- walkHelper
//	- walkAndOpenHelper
//	- createHelper
type fileGenerator func(*Harness, string, p9.File) (*Mock, *Mock, p9.File)

// walkHelper walks to the given file.
//
// The backends of the parent and walked file are returned, as well as the
// walked client file.
func walkHelper(h *Harness, name string, dir p9.File) (parentBackend *Mock, walkedBackend *Mock, walked p9.File) {
	_, parent, err := dir.Walk(nil)
	if err != nil {
		h.t.Fatalf("Walk(nil) got err %v, want nil", err)
	}
	defer parent.Close()
	parentBackend = h.Pop(parent)

	_, walked, err = parent.Walk([]string{name})
	if err != nil {
		h.t.Fatalf("Walk(%s) got err %v, want nil", name, err)
	}
	walkedBackend = h.Pop(walked)

	return parentBackend, walkedBackend, walked
}

// walkAndOpenHelper additionally opens the walked file, if possible.
func walkAndOpenHelper(h *Harness, name string, dir p9.File) (*Mock, *Mock, p9.File) {
	parentBackend, walkedBackend, walked := walkHelper(h, name, dir)
	if p9.CanOpen(walkedBackend.Attr.Mode) {
		// Open for all file types that we can. We stick to a read-only
		// open here because directories may not be opened otherwise.
		walkedBackend.EXPECT().Open(p9.ReadOnly).Times(1)
		if _, _, _, err := walked.Open(p9.ReadOnly); err != nil {
			h.t.Errorf("got open err %v, want nil", err)
		}
	} else {
		// ... or assert an error for others.
		if _, _, _, err := walked.Open(p9.ReadOnly); err != syscall.EINVAL {
			h.t.Errorf("got open err %v, want EINVAL", err)
		}
	}
	return parentBackend, walkedBackend, walked
}

// createHelper creates the given file and returns the parent directory,
// created file and client file, which must be closed when done.
func createHelper(h *Harness, name string, dir p9.File) (*Mock, *Mock, p9.File) {
	// Clone the directory first, since Create replaces the existing file.
	// We change the type after calling create.
	_, dirThenFile, err := dir.Walk(nil)
	if err != nil {
		h.t.Fatalf("got walk err %v, want nil", err)
	}

	// Create a new server-side file. On the server-side, the a new file is
	// returned from a create call. The client will reuse the same file,
	// but we still expect the normal chain of closes. This complicates
	// things a bit because the "parent" will always chain to the cloned
	// dir above.
	dirBackend := h.Pop(dirThenFile)   // New backend directory.
	newFile := h.NewFile()(dirBackend) // New file with backend parent.
	dirBackend.EXPECT().Create(name, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, newFile, newFile.QID, uint32(0), nil)

	// Create via the client.
	_, dirThenFile, _, _, err = dirThenFile.Create(name, p9.ReadOnly, 0, 0, 0)
	if err != nil {
		h.t.Fatalf("got create err %v, want nil", err)
	}

	// Ensure subsequent walks succeed.
	dirBackend.AddChild(name, h.NewFile())
	return dirBackend, newFile, dirThenFile
}

// deprecatedRemover allows us to access the deprecated Remove operation within
// the p9.File client object.
type deprecatedRemover interface {
	Remove() error
}

// checkDeleted asserts that relevant methods fail for an unlinked file.
//
// This function will close the file at the end.
func checkDeleted(h *Harness, file p9.File) {
	defer file.Close() // See doc.

	if _, _, _, err := file.Open(p9.ReadOnly); err != syscall.EINVAL {
		h.t.Errorf("open while deleted, got %v, want EINVAL", err)
	}
	if _, _, _, _, err := file.Create("created", p9.ReadOnly, 0, 0, 0); err != syscall.EINVAL {
		h.t.Errorf("create while deleted, got %v, want EINVAL", err)
	}
	if _, err := file.Symlink("old", "new", 0, 0); err != syscall.EINVAL {
		h.t.Errorf("symlink while deleted, got %v, want EINVAL", err)
	}
	// N.B. This link is technically invalid, but if a call to link is
	// actually made in the backend then the mock will panic.
	if err := file.Link(file, "new"); err != syscall.EINVAL {
		h.t.Errorf("link while deleted, got %v, want EINVAL", err)
	}
	if err := file.RenameAt("src", file, "dst"); err != syscall.EINVAL {
		h.t.Errorf("renameAt while deleted, got %v, want EINVAL", err)
	}
	if err := file.UnlinkAt("file", 0); err != syscall.EINVAL {
		h.t.Errorf("unlinkAt while deleted, got %v, want EINVAL", err)
	}
	if err := file.Rename(file, "dst"); err != syscall.EINVAL {
		h.t.Errorf("rename while deleted, got %v, want EINVAL", err)
	}
	if _, err := file.Readlink(); err != syscall.EINVAL {
		h.t.Errorf("readlink while deleted, got %v, want EINVAL", err)
	}
	if _, err := file.Mkdir("dir", p9.ModeDirectory, 0, 0); err != syscall.EINVAL {
		h.t.Errorf("mkdir while deleted, got %v, want EINVAL", err)
	}
	if _, err := file.Mknod("dir", p9.ModeDirectory, 0, 0, 0, 0); err != syscall.EINVAL {
		h.t.Errorf("mknod while deleted, got %v, want EINVAL", err)
	}
	if _, err := file.Readdir(0, 1); err != syscall.EINVAL {
		h.t.Errorf("readdir while deleted, got %v, want EINVAL", err)
	}
	if _, err := file.Connect(p9.ConnectFlags(0)); err != syscall.EINVAL {
		h.t.Errorf("connect while deleted, got %v, want EINVAL", err)
	}

	// The remove method is technically deprecated, but we want to ensure
	// that it still checks for deleted appropriately. We must first clone
	// the file because remove is equivalent to close.
	_, newFile, err := file.Walk(nil)
	if err == syscall.EBUSY {
		// We can't walk from here because this reference is open
		// already. Okay, we will also have unopened cases through
		// TestUnlink, just skip the remove operation for now.
		return
	} else if err != nil {
		h.t.Fatalf("clone failed, got %v, want nil", err)
	}
	if err := newFile.(deprecatedRemover).Remove(); err != syscall.EINVAL {
		h.t.Errorf("remove while deleted, got %v, want EINVAL", err)
	}
}

// deleter is a function to remove a file.
type deleter func(parent p9.File, name string) error

// unlinkAt is a deleter.
func unlinkAt(parent p9.File, name string) error {
	// Call unlink. Note that a filesystem may normally impose additional
	// constaints on unlinkat success, such as ensuring that a directory is
	// empty, requiring AT_REMOVEDIR in flags to remove a directory, etc.
	// None of that is required internally (entire trees can be marked
	// deleted when this operation succeeds), so the mock will succeed.
	return parent.UnlinkAt(name, 0)
}

// remove is a deleter.
func remove(parent p9.File, name string) error {
	// See notes above re: remove.
	_, newFile, err := parent.Walk([]string{name})
	if err != nil {
		// Should not be expected.
		return err
	}

	// Do the actual remove.
	if err := newFile.(deprecatedRemover).Remove(); err != nil {
		return err
	}

	// Ensure that the remove closed the file.
	if err := newFile.(deprecatedRemover).Remove(); err != syscall.EBADF {
		return syscall.EBADF // Propagate this code.
	}

	return nil
}

// unlinkHelper unlinks the noted path, and ensures that all relevant
// operations on that path, acquired from multiple paths, start failing.
func unlinkHelper(h *Harness, root p9.File, targetNames []string, targetGen fileGenerator, deleteFn deleter) {
	// name is the file to be unlinked.
	name := targetNames[len(targetNames)-1]

	// Walk to the directory containing the target.
	_, parent, err := root.Walk(targetNames[:len(targetNames)-1])
	if err != nil {
		h.t.Fatalf("got walk err %v, want nil", err)
	}
	defer parent.Close()
	parentBackend := h.Pop(parent)

	// Walk to or generate the target file.
	_, _, target := targetGen(h, name, parent)
	defer checkDeleted(h, target)

	// Walk to a second reference.
	_, second, err := parent.Walk([]string{name})
	if err != nil {
		h.t.Fatalf("got walk err %v, want nil", err)
	}
	defer checkDeleted(h, second)

	// Walk to a third reference, from the start.
	_, third, err := root.Walk(targetNames)
	if err != nil {
		h.t.Fatalf("got walk err %v, want nil", err)
	}
	defer checkDeleted(h, third)

	// This will be translated in the backend to an unlinkat.
	parentBackend.EXPECT().UnlinkAt(name, uint32(0)).Return(nil)

	// Actually perform the deletion.
	if err := deleteFn(parent, name); err != nil {
		h.t.Fatalf("got delete err %v, want nil", err)
	}
}

func unlinkTest(t *testing.T, targetNames []string, targetGen fileGenerator) {
	t.Run(fmt.Sprintf("unlinkAt(%s)", strings.Join(targetNames, "/")), func(t *testing.T) {
		h, c := NewHarness(t)
		defer h.Finish()

		_, root := newRoot(h, c)
		defer root.Close()

		unlinkHelper(h, root, targetNames, targetGen, unlinkAt)
	})
	t.Run(fmt.Sprintf("remove(%s)", strings.Join(targetNames, "/")), func(t *testing.T) {
		h, c := NewHarness(t)
		defer h.Finish()

		_, root := newRoot(h, c)
		defer root.Close()

		unlinkHelper(h, root, targetNames, targetGen, remove)
	})
}

func TestUnlink(t *testing.T) {
	// Unlink all files.
	for name := range newTypeMap(nil) {
		unlinkTest(t, []string{name}, walkHelper)
		unlinkTest(t, []string{name}, walkAndOpenHelper)
		unlinkTest(t, []string{"one", name}, walkHelper)
		unlinkTest(t, []string{"one", name}, walkAndOpenHelper)
		unlinkTest(t, []string{"one", "two", name}, walkHelper)
		unlinkTest(t, []string{"one", "two", name}, walkAndOpenHelper)
	}

	// Unlink a directory.
	unlinkTest(t, []string{"one"}, walkHelper)
	unlinkTest(t, []string{"one"}, walkAndOpenHelper)
	unlinkTest(t, []string{"one", "two"}, walkHelper)
	unlinkTest(t, []string{"one", "two"}, walkAndOpenHelper)

	// Unlink created files.
	unlinkTest(t, []string{"created"}, createHelper)
	unlinkTest(t, []string{"one", "created"}, createHelper)
	unlinkTest(t, []string{"one", "two", "created"}, createHelper)
}

func TestUnlinkAtInvalid(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	_, root := newRoot(h, c)
	defer root.Close()

	for name := range newTypeMap(nil) {
		for _, invalidName := range allInvalidNames(name) {
			if err := root.UnlinkAt(invalidName, 0); err != syscall.EINVAL {
				t.Errorf("got %v for name %q, want EINVAL", err, invalidName)
			}
		}
	}
}

// expectRenamed asserts an ordered sequence of rename calls, based on all the
// elements in elements being the source, and the first element therein
// changing to dstName, parented at dstParent.
func expectRenamed(file *Mock, elements []string, dstParent *Mock, dstName string) *gomock.Call {
	if len(elements) > 0 {
		// Recurse to the parent, if necessary.
		call := expectRenamed(file.parent, elements[:len(elements)-1], dstParent, dstName)

		// Recursive case: this element is unchanged, but should have
		// it's hook called after the parent.
		return file.EXPECT().Renamed(file.parent, elements[len(elements)-1]).Do(func(p p9.File, _ string) {
			file.parent = p.(*Mock)
		}).After(call)
	}

	// Base case: this is the changed element.
	return file.EXPECT().Renamed(dstParent, dstName).Do(func(p p9.File, name string) {
		file.parent = p.(*Mock)
	})
}

// renamer is a rename function.
type renamer func(h *Harness, srcParent, dstParent p9.File, origName, newName string, selfRename bool) error

// renameAt is a renamer.
func renameAt(_ *Harness, srcParent, dstParent p9.File, srcName, dstName string, selfRename bool) error {
	return srcParent.RenameAt(srcName, dstParent, dstName)
}

// rename is a renamer.
func rename(h *Harness, srcParent, dstParent p9.File, srcName, dstName string, selfRename bool) error {
	_, f, err := srcParent.Walk([]string{srcName})
	if err != nil {
		return err
	}
	defer f.Close()
	if !selfRename {
		backend := h.Pop(f)
		backend.EXPECT().Renamed(gomock.Any(), dstName).Do(func(p p9.File, name string) {
			backend.parent = p.(*Mock) // Required for close ordering.
		})
	}
	return f.Rename(dstParent, dstName)
}

// renameHelper executes a rename, and asserts that all relevant elements
// receive expected notifications. If overwriting a file, this includes
// ensuring that the target has been appropriately marked as unlinked.
func renameHelper(h *Harness, root p9.File, srcNames []string, dstNames []string, target fileGenerator, renameFn renamer) {
	// Walk to the directory containing the target.
	srcQID, targetParent, err := root.Walk(srcNames[:len(srcNames)-1])
	if err != nil {
		h.t.Fatalf("got walk err %v, want nil", err)
	}
	defer targetParent.Close()
	targetParentBackend := h.Pop(targetParent)

	// Walk to or generate the target file.
	_, targetBackend, src := target(h, srcNames[len(srcNames)-1], targetParent)
	defer src.Close()

	// Walk to a second reference.
	_, second, err := targetParent.Walk([]string{srcNames[len(srcNames)-1]})
	if err != nil {
		h.t.Fatalf("got walk err %v, want nil", err)
	}
	defer second.Close()
	secondBackend := h.Pop(second)

	// Walk to a third reference, from the start.
	_, third, err := root.Walk(srcNames)
	if err != nil {
		h.t.Fatalf("got walk err %v, want nil", err)
	}
	defer third.Close()
	thirdBackend := h.Pop(third)

	// Find the common suffix to identify the rename parent.
	var (
		renameDestPath []string
		renameSrcPath  []string
		selfRename     bool
	)
	for i := 1; i <= len(srcNames) && i <= len(dstNames); i++ {
		if srcNames[len(srcNames)-i] != dstNames[len(dstNames)-i] {
			// Take the full prefix of dstNames up until this
			// point, including the first mismatched name. The
			// first mismatch must be the renamed entry.
			renameDestPath = dstNames[:len(dstNames)-i+1]
			renameSrcPath = srcNames[:len(srcNames)-i+1]

			// Does the renameDestPath fully contain the
			// renameSrcPath here? If yes, then this is a mismatch.
			// We can't rename the src to some subpath of itself.
			if len(renameDestPath) > len(renameSrcPath) &&
				reflect.DeepEqual(renameDestPath[:len(renameSrcPath)], renameSrcPath) {
				renameDestPath = nil
				renameSrcPath = nil
				continue
			}
			break
		}
	}
	if len(renameSrcPath) == 0 || len(renameDestPath) == 0 {
		// This must be a rename to self, or a tricky look-alike. This
		// happens iff we fail to find a suitable divergence in the two
		// paths. It's a true self move if the path length is the same.
		renameDestPath = dstNames
		renameSrcPath = srcNames
		selfRename = len(srcNames) == len(dstNames)
	}

	// Walk to the source parent.
	_, srcParent, err := root.Walk(renameSrcPath[:len(renameSrcPath)-1])
	if err != nil {
		h.t.Fatalf("got walk err %v, want nil", err)
	}
	defer srcParent.Close()
	srcParentBackend := h.Pop(srcParent)

	// Walk to the destination parent.
	_, dstParent, err := root.Walk(renameDestPath[:len(renameDestPath)-1])
	if err != nil {
		h.t.Fatalf("got walk err %v, want nil", err)
	}
	defer dstParent.Close()
	dstParentBackend := h.Pop(dstParent)

	// expectedErr is the result of the rename operation.
	var expectedErr error

	// Walk to the target file, if one exists.
	dstQID, dst, err := root.Walk(renameDestPath)
	if err == nil {
		if !selfRename && srcQID[0].Type == dstQID[0].Type {
			// If there is a destination file, and is it of the
			// same type as the source file, then we expect the
			// rename to succeed. We expect the destination file to
			// be deleted, so we run a deletion test on it in this
			// case.
			defer checkDeleted(h, dst)
		} else {
			// If the type is different than the destination, then
			// we expect the rename to fail. We expect that this
			// is returned.
			//
			// If the file being renamed to itself, this is
			// technically allowed and a no-op, but all the
			// triggers will fire.
			if !selfRename {
				expectedErr = syscall.EINVAL
			}
			dst.Close()
		}
	}
	dstName := renameDestPath[len(renameDestPath)-1] // Renamed element.
	srcName := renameSrcPath[len(renameSrcPath)-1]   // Renamed element.
	if expectedErr == nil && !selfRename {
		// Expect all to be renamed appropriately. Note that if this is
		// a final file being renamed, then we expect the file to be
		// called with the new parent. If not, then we expect the
		// rename hook to be called, but the parent will remain
		// unchanged.
		elements := srcNames[len(renameSrcPath):]
		expectRenamed(targetBackend, elements, dstParentBackend, dstName)
		expectRenamed(secondBackend, elements, dstParentBackend, dstName)
		expectRenamed(thirdBackend, elements, dstParentBackend, dstName)

		// The target parent has also been opened, and may be moved
		// directly or indirectly.
		if len(elements) > 1 {
			expectRenamed(targetParentBackend, elements[:len(elements)-1], dstParentBackend, dstName)
		}
	}

	// Expect the rename if it's not the same file. Note that like unlink,
	// renames are always translated to the at variant in the backend.
	if !selfRename {
		srcParentBackend.EXPECT().RenameAt(srcName, dstParentBackend, dstName).Return(expectedErr)
	}

	// Perform the actual rename; everything has been lined up.
	if err := renameFn(h, srcParent, dstParent, srcName, dstName, selfRename); err != expectedErr {
		h.t.Fatalf("got rename err %v, want %v", err, expectedErr)
	}
}

func renameTest(t *testing.T, srcNames []string, dstNames []string, target fileGenerator) {
	t.Run(fmt.Sprintf("renameAt(%s->%s)", strings.Join(srcNames, "/"), strings.Join(dstNames, "/")), func(t *testing.T) {
		h, c := NewHarness(t)
		defer h.Finish()

		_, root := newRoot(h, c)
		defer root.Close()

		renameHelper(h, root, srcNames, dstNames, target, renameAt)
	})
	t.Run(fmt.Sprintf("rename(%s->%s)", strings.Join(srcNames, "/"), strings.Join(dstNames, "/")), func(t *testing.T) {
		h, c := NewHarness(t)
		defer h.Finish()

		_, root := newRoot(h, c)
		defer root.Close()

		renameHelper(h, root, srcNames, dstNames, target, rename)
	})
}

func TestRename(t *testing.T) {
	// In-directory rename, simple case.
	for name := range newTypeMap(nil) {
		// Within the root.
		renameTest(t, []string{name}, []string{"renamed"}, walkHelper)
		renameTest(t, []string{name}, []string{"renamed"}, walkAndOpenHelper)

		// Within a subdirectory.
		renameTest(t, []string{"one", name}, []string{"one", "renamed"}, walkHelper)
		renameTest(t, []string{"one", name}, []string{"one", "renamed"}, walkAndOpenHelper)
	}

	// ... with created files.
	renameTest(t, []string{"created"}, []string{"renamed"}, createHelper)
	renameTest(t, []string{"one", "created"}, []string{"one", "renamed"}, createHelper)

	// Across directories.
	for name := range newTypeMap(nil) {
		// Down one level.
		renameTest(t, []string{"one", name}, []string{"one", "two", "renamed"}, walkHelper)
		renameTest(t, []string{"one", name}, []string{"one", "two", "renamed"}, walkAndOpenHelper)

		// Up one level.
		renameTest(t, []string{"one", "two", name}, []string{"one", "renamed"}, walkHelper)
		renameTest(t, []string{"one", "two", name}, []string{"one", "renamed"}, walkAndOpenHelper)

		// Across at the same level.
		renameTest(t, []string{"one", name}, []string{"three", "renamed"}, walkHelper)
		renameTest(t, []string{"one", name}, []string{"three", "renamed"}, walkAndOpenHelper)
	}

	// ... with created files.
	renameTest(t, []string{"one", "created"}, []string{"one", "two", "renamed"}, createHelper)
	renameTest(t, []string{"one", "two", "created"}, []string{"one", "renamed"}, createHelper)
	renameTest(t, []string{"one", "created"}, []string{"three", "renamed"}, createHelper)

	// Renaming parents.
	for name := range newTypeMap(nil) {
		// Rename a parent.
		renameTest(t, []string{"one", name}, []string{"renamed", name}, walkHelper)
		renameTest(t, []string{"one", name}, []string{"renamed", name}, walkAndOpenHelper)

		// Rename a super parent.
		renameTest(t, []string{"one", "two", name}, []string{"renamed", name}, walkHelper)
		renameTest(t, []string{"one", "two", name}, []string{"renamed", name}, walkAndOpenHelper)
	}

	// ... with created files.
	renameTest(t, []string{"one", "created"}, []string{"renamed", "created"}, createHelper)
	renameTest(t, []string{"one", "two", "created"}, []string{"renamed", "created"}, createHelper)

	// Over existing files, including itself.
	for name := range newTypeMap(nil) {
		for other := range newTypeMap(nil) {
			// Overwrite the noted file (may be itself).
			renameTest(t, []string{"one", name}, []string{"one", other}, walkHelper)
			renameTest(t, []string{"one", name}, []string{"one", other}, walkAndOpenHelper)

			// Overwrite other files in another directory.
			renameTest(t, []string{"one", name}, []string{"one", "two", other}, walkHelper)
			renameTest(t, []string{"one", name}, []string{"one", "two", other}, walkAndOpenHelper)
		}

		// Overwrite by moving the parent.
		renameTest(t, []string{"three", name}, []string{"one", name}, walkHelper)
		renameTest(t, []string{"three", name}, []string{"one", name}, walkAndOpenHelper)

		// Create over the types.
		renameTest(t, []string{"one", "created"}, []string{"one", name}, createHelper)
		renameTest(t, []string{"one", "created"}, []string{"one", "two", name}, createHelper)
		renameTest(t, []string{"three", "created"}, []string{"one", name}, createHelper)
	}
}

func TestRenameInvalid(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	_, root := newRoot(h, c)
	defer root.Close()

	for name := range newTypeMap(nil) {
		for _, invalidName := range allInvalidNames(name) {
			if err := root.Rename(root, invalidName); err != syscall.EINVAL {
				t.Errorf("got %v for name %q, want EINVAL", err, invalidName)
			}
		}
	}
}

func TestRenameAtInvalid(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	_, root := newRoot(h, c)
	defer root.Close()

	for name := range newTypeMap(nil) {
		for _, invalidName := range allInvalidNames(name) {
			if err := root.RenameAt(invalidName, root, "okay"); err != syscall.EINVAL {
				t.Errorf("got %v for name %q, want EINVAL", err, invalidName)
			}
			if err := root.RenameAt("okay", root, invalidName); err != syscall.EINVAL {
				t.Errorf("got %v for name %q, want EINVAL", err, invalidName)
			}
		}
	}
}

// TestRenameSecondOrder tests that indirect rename targets continue to receive
// Renamed calls after a rename of its renamed parent. i.e.,
//
// 1. Create /one/file
// 2. Create /directory
// 3. Rename /one -> /directory/one
// 4. Rename /directory -> /three/foo
// 5. file from (1) should still receive Renamed.
//
// This is a regression test for b/135219260.
func TestRenameSecondOrder(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	rootBackend, root := newRoot(h, c)
	defer root.Close()

	// Walk to /one.
	_, oneBackend, oneFile := walkHelper(h, "one", root)
	defer oneFile.Close()

	// Walk to and generate /one/file.
	//
	// walkHelper re-walks to oneFile, so we need the second backend,
	// which will also receive Renamed calls.
	oneSecondBackend, fileBackend, fileFile := walkHelper(h, "file", oneFile)
	defer fileFile.Close()

	// Walk to and generate /directory.
	_, directoryBackend, directoryFile := walkHelper(h, "directory", root)
	defer directoryFile.Close()

	// Rename /one to /directory/one.
	rootBackend.EXPECT().RenameAt("one", directoryBackend, "one").Return(nil)
	expectRenamed(oneBackend, []string{}, directoryBackend, "one")
	expectRenamed(oneSecondBackend, []string{}, directoryBackend, "one")
	expectRenamed(fileBackend, []string{}, oneBackend, "file")
	if err := renameAt(h, root, directoryFile, "one", "one", false); err != nil {
		h.t.Fatalf("got rename err %v, want nil", err)
	}

	// Walk to /three.
	_, threeBackend, threeFile := walkHelper(h, "three", root)
	defer threeFile.Close()

	// Rename /directory to /three/foo.
	rootBackend.EXPECT().RenameAt("directory", threeBackend, "foo").Return(nil)
	expectRenamed(directoryBackend, []string{}, threeBackend, "foo")
	expectRenamed(oneBackend, []string{}, directoryBackend, "one")
	expectRenamed(oneSecondBackend, []string{}, directoryBackend, "one")
	expectRenamed(fileBackend, []string{}, oneBackend, "file")
	if err := renameAt(h, root, threeFile, "directory", "foo", false); err != nil {
		h.t.Fatalf("got rename err %v, want nil", err)
	}
}

func TestReadlink(t *testing.T) {
	for name := range newTypeMap(nil) {
		t.Run(name, func(t *testing.T) {
			h, c := NewHarness(t)
			defer h.Finish()

			_, root := newRoot(h, c)
			defer root.Close()

			// Walk to the file normally.
			_, f, err := root.Walk([]string{name})
			if err != nil {
				t.Fatalf("walk failed: got %v, wanted nil", err)
			}
			defer f.Close()
			backend := h.Pop(f)

			const symlinkTarget = "symlink-target"

			if backend.Attr.Mode.IsSymlink() {
				// This should only go through on symlinks.
				backend.EXPECT().Readlink().Return(symlinkTarget, nil)
			}

			// Attempt a Readlink operation.
			target, err := f.Readlink()
			if err != nil && err != syscall.EINVAL {
				t.Errorf("readlink got %v, wanted EINVAL", err)
			} else if err == nil && target != symlinkTarget {
				t.Errorf("readlink got %v, wanted %v", target, symlinkTarget)
			}
		})
	}
}

// fdTest is a wrapper around operations that may send file descriptors. This
// asserts that the file descriptors are working as intended.
func fdTest(t *testing.T, sendFn func(*fd.FD) *fd.FD) {
	// Create a pipe that we can read from.
	r, w, err := os.Pipe()
	if err != nil {
		t.Fatalf("unable to create pipe: %v", err)
	}
	defer r.Close()
	defer w.Close()

	// Attempt to send the write end.
	wFD, err := fd.NewFromFile(w)
	if err != nil {
		t.Fatalf("unable to convert file: %v", err)
	}
	defer wFD.Close() // This is a copy.

	// Send wFD and receive newFD.
	newFD := sendFn(wFD)
	defer newFD.Close()

	// Attempt to write.
	const message = "hello"
	if _, err := newFD.Write([]byte(message)); err != nil {
		t.Fatalf("write got %v, wanted nil", err)
	}

	// Should see the message on our end.
	buffer := []byte(message)
	if _, err := io.ReadFull(r, buffer); err != nil {
		t.Fatalf("read got %v, wanted nil", err)
	}
	if string(buffer) != message {
		t.Errorf("got message %v, wanted %v", string(buffer), message)
	}
}

func TestConnect(t *testing.T) {
	for name := range newTypeMap(nil) {
		t.Run(name, func(t *testing.T) {
			h, c := NewHarness(t)
			defer h.Finish()

			_, root := newRoot(h, c)
			defer root.Close()

			// Walk to the file normally.
			_, backend, f := walkHelper(h, name, root)
			defer f.Close()

			// Catch all the non-socket cases.
			if !backend.Attr.Mode.IsSocket() {
				// This has been set up to fail if Connect is called.
				if _, err := f.Connect(p9.ConnectFlags(0)); err != syscall.EINVAL {
					t.Errorf("connect got %v, wanted EINVAL", err)
				}
				return
			}

			// Ensure the fd exchange works.
			fdTest(t, func(send *fd.FD) *fd.FD {
				backend.EXPECT().Connect(p9.ConnectFlags(0)).Return(send, nil)
				recv, err := backend.Connect(p9.ConnectFlags(0))
				if err != nil {
					t.Fatalf("connect got %v, wanted nil", err)
				}
				return recv
			})
		})
	}
}

func TestReaddir(t *testing.T) {
	for name := range newTypeMap(nil) {
		t.Run(name, func(t *testing.T) {
			h, c := NewHarness(t)
			defer h.Finish()

			_, root := newRoot(h, c)
			defer root.Close()

			// Walk to the file normally.
			_, backend, f := walkHelper(h, name, root)
			defer f.Close()

			// Catch all the non-directory cases.
			if !backend.Attr.Mode.IsDir() {
				// This has also been set up to fail if Readdir is called.
				if _, err := f.Readdir(0, 1); err != syscall.EINVAL {
					t.Errorf("readdir got %v, wanted EINVAL", err)
				}
				return
			}

			// Ensure that readdir works for directories.
			if _, err := f.Readdir(0, 1); err != syscall.EINVAL {
				t.Errorf("readdir got %v, wanted EINVAL", err)
			}
			if _, _, _, err := f.Open(p9.ReadWrite); err != syscall.EISDIR {
				t.Errorf("readdir got %v, wanted EISDIR", err)
			}
			if _, _, _, err := f.Open(p9.WriteOnly); err != syscall.EISDIR {
				t.Errorf("readdir got %v, wanted EISDIR", err)
			}
			backend.EXPECT().Open(p9.ReadOnly).Times(1)
			if _, _, _, err := f.Open(p9.ReadOnly); err != nil {
				t.Errorf("readdir got %v, wanted nil", err)
			}
			backend.EXPECT().Readdir(uint64(0), uint32(1)).Times(1)
			if _, err := f.Readdir(0, 1); err != nil {
				t.Errorf("readdir got %v, wanted nil", err)
			}
		})
	}
}

func TestOpen(t *testing.T) {
	type openTest struct {
		name  string
		flags p9.OpenFlags
		err   error
		match func(p9.FileMode) bool
	}

	cases := []openTest{
		{
			name:  "not-openable-read-only",
			flags: p9.ReadOnly,
			err:   syscall.EINVAL,
			match: func(mode p9.FileMode) bool { return !p9.CanOpen(mode) },
		},
		{
			name:  "not-openable-write-only",
			flags: p9.WriteOnly,
			err:   syscall.EINVAL,
			match: func(mode p9.FileMode) bool { return !p9.CanOpen(mode) },
		},
		{
			name:  "not-openable-read-write",
			flags: p9.ReadWrite,
			err:   syscall.EINVAL,
			match: func(mode p9.FileMode) bool { return !p9.CanOpen(mode) },
		},
		{
			name:  "directory-read-only",
			flags: p9.ReadOnly,
			err:   nil,
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
		},
		{
			name:  "directory-read-write",
			flags: p9.ReadWrite,
			err:   syscall.EISDIR,
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
		},
		{
			name:  "directory-write-only",
			flags: p9.WriteOnly,
			err:   syscall.EISDIR,
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
		},
		{
			name:  "read-only",
			flags: p9.ReadOnly,
			err:   nil,
			match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) },
		},
		{
			name:  "write-only",
			flags: p9.WriteOnly,
			err:   nil,
			match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) && !mode.IsDir() },
		},
		{
			name:  "read-write",
			flags: p9.ReadWrite,
			err:   nil,
			match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) && !mode.IsDir() },
		},
		{
			name:  "directory-read-only-truncate",
			flags: p9.ReadOnly | p9.OpenTruncate,
			err:   syscall.EISDIR,
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
		},
		{
			name:  "read-only-truncate",
			flags: p9.ReadOnly | p9.OpenTruncate,
			err:   nil,
			match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) && !mode.IsDir() },
		},
		{
			name:  "write-only-truncate",
			flags: p9.WriteOnly | p9.OpenTruncate,
			err:   nil,
			match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) && !mode.IsDir() },
		},
		{
			name:  "read-write-truncate",
			flags: p9.ReadWrite | p9.OpenTruncate,
			err:   nil,
			match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) && !mode.IsDir() },
		},
	}

	// Open(flags OpenFlags) (*fd.FD, QID, uint32, error)
	// - only works on Regular, NamedPipe, BLockDevice, CharacterDevice
	// - returning a file works as expected
	for name := range newTypeMap(nil) {
		for _, tc := range cases {
			t.Run(fmt.Sprintf("%s-%s", tc.name, name), func(t *testing.T) {
				h, c := NewHarness(t)
				defer h.Finish()

				_, root := newRoot(h, c)
				defer root.Close()

				// Walk to the file normally.
				_, backend, f := walkHelper(h, name, root)
				defer f.Close()

				// Does this match the case?
				if !tc.match(backend.Attr.Mode) {
					t.SkipNow()
				}

				// Ensure open-required operations fail.
				if _, err := f.ReadAt([]byte("hello"), 0); err != syscall.EINVAL {
					t.Errorf("readAt got %v, wanted EINVAL", err)
				}
				if _, err := f.WriteAt(make([]byte, 6), 0); err != syscall.EINVAL {
					t.Errorf("writeAt got %v, wanted EINVAL", err)
				}
				if err := f.FSync(); err != syscall.EINVAL {
					t.Errorf("fsync got %v, wanted EINVAL", err)
				}
				if _, err := f.Readdir(0, 1); err != syscall.EINVAL {
					t.Errorf("readdir got %v, wanted EINVAL", err)
				}

				// Attempt the given open.
				if tc.err != nil {
					// We expect an error, just test and return.
					if _, _, _, err := f.Open(tc.flags); err != tc.err {
						t.Fatalf("open with flags %v got %v, want %v", tc.flags, err, tc.err)
					}
					return
				}

				// Run an FD test, since we expect success.
				fdTest(t, func(send *fd.FD) *fd.FD {
					backend.EXPECT().Open(tc.flags).Return(send, p9.QID{}, uint32(0), nil).Times(1)
					recv, _, _, err := f.Open(tc.flags)
					if err != tc.err {
						t.Fatalf("open with flags %v got %v, want %v", tc.flags, err, tc.err)
					}
					return recv
				})

				// If the open was successful, attempt another one.
				if _, _, _, err := f.Open(tc.flags); err != syscall.EINVAL {
					t.Errorf("second open with flags %v got %v, want EINVAL", tc.flags, err)
				}

				// Ensure that all illegal operations fail.
				if _, _, err := f.Walk(nil); err != syscall.EINVAL && err != syscall.EBUSY {
					t.Errorf("walk got %v, wanted EINVAL or EBUSY", err)
				}
				if _, _, _, _, err := f.WalkGetAttr(nil); err != syscall.EINVAL && err != syscall.EBUSY {
					t.Errorf("walkgetattr got %v, wanted EINVAL or EBUSY", err)
				}
			})
		}
	}
}

func TestClose(t *testing.T) {
	type closeTest struct {
		name    string
		closeFn func(backend *Mock, f p9.File) error
	}

	cases := []closeTest{
		{
			name: "close",
			closeFn: func(_ *Mock, f p9.File) error {
				return f.Close()
			},
		},
		{
			name: "remove",
			closeFn: func(backend *Mock, f p9.File) error {
				// Allow the rename call in the parent, automatically translated.
				backend.parent.EXPECT().UnlinkAt(gomock.Any(), gomock.Any()).Times(1)
				return f.(deprecatedRemover).Remove()
			},
		},
		{
			name: "setAttrClose",
			closeFn: func(backend *Mock, f p9.File) error {
				valid := p9.SetAttrMask{ATime: true}
				attr := p9.SetAttr{ATimeSeconds: 1, ATimeNanoSeconds: 2}
				backend.EXPECT().SetAttr(valid, attr).Times(1)
				return f.SetAttrClose(valid, attr)
			},
		},
	}

	for name := range newTypeMap(nil) {
		for _, tc := range cases {
			t.Run(fmt.Sprintf("%s(%s)", tc.name, name), func(t *testing.T) {
				h, c := NewHarness(t)
				defer h.Finish()

				_, root := newRoot(h, c)
				defer root.Close()

				// Walk to the file normally.
				_, backend, f := walkHelper(h, name, root)

				// Close via the prescribed method.
				if err := tc.closeFn(backend, f); err != nil {
					t.Fatalf("closeFn failed: %v", err)
				}

				// Everything should fail with EBADF.
				if _, _, err := f.Walk(nil); err != syscall.EBADF {
					t.Errorf("walk got %v, wanted EBADF", err)
				}
				if _, err := f.StatFS(); err != syscall.EBADF {
					t.Errorf("statfs got %v, wanted EBADF", err)
				}
				if _, _, _, err := f.GetAttr(p9.AttrMaskAll()); err != syscall.EBADF {
					t.Errorf("getattr got %v, wanted EBADF", err)
				}
				if err := f.SetAttr(p9.SetAttrMask{}, p9.SetAttr{}); err != syscall.EBADF {
					t.Errorf("setattrk got %v, wanted EBADF", err)
				}
				if err := f.Rename(root, "new-name"); err != syscall.EBADF {
					t.Errorf("rename got %v, wanted EBADF", err)
				}
				if err := f.Close(); err != syscall.EBADF {
					t.Errorf("close got %v, wanted EBADF", err)
				}
				if _, _, _, err := f.Open(p9.ReadOnly); err != syscall.EBADF {
					t.Errorf("open got %v, wanted EBADF", err)
				}
				if _, err := f.ReadAt([]byte("hello"), 0); err != syscall.EBADF {
					t.Errorf("readAt got %v, wanted EBADF", err)
				}
				if _, err := f.WriteAt(make([]byte, 6), 0); err != syscall.EBADF {
					t.Errorf("writeAt got %v, wanted EBADF", err)
				}
				if err := f.FSync(); err != syscall.EBADF {
					t.Errorf("fsync got %v, wanted EBADF", err)
				}
				if _, _, _, _, err := f.Create("new-file", p9.ReadWrite, 0, 0, 0); err != syscall.EBADF {
					t.Errorf("create got %v, wanted EBADF", err)
				}
				if _, err := f.Mkdir("new-directory", 0, 0, 0); err != syscall.EBADF {
					t.Errorf("mkdir got %v, wanted EBADF", err)
				}
				if _, err := f.Symlink("old-name", "new-name", 0, 0); err != syscall.EBADF {
					t.Errorf("symlink got %v, wanted EBADF", err)
				}
				if err := f.Link(root, "new-name"); err != syscall.EBADF {
					t.Errorf("link got %v, wanted EBADF", err)
				}
				if _, err := f.Mknod("new-block-device", 0, 0, 0, 0, 0); err != syscall.EBADF {
					t.Errorf("mknod got %v, wanted EBADF", err)
				}
				if err := f.RenameAt("old-name", root, "new-name"); err != syscall.EBADF {
					t.Errorf("renameAt got %v, wanted EBADF", err)
				}
				if err := f.UnlinkAt("name", 0); err != syscall.EBADF {
					t.Errorf("unlinkAt got %v, wanted EBADF", err)
				}
				if _, err := f.Readdir(0, 1); err != syscall.EBADF {
					t.Errorf("readdir got %v, wanted EBADF", err)
				}
				if _, err := f.Readlink(); err != syscall.EBADF {
					t.Errorf("readlink got %v, wanted EBADF", err)
				}
				if err := f.Flush(); err != syscall.EBADF {
					t.Errorf("flush got %v, wanted EBADF", err)
				}
				if _, _, _, _, err := f.WalkGetAttr(nil); err != syscall.EBADF {
					t.Errorf("walkgetattr got %v, wanted EBADF", err)
				}
				if _, err := f.Connect(p9.ConnectFlags(0)); err != syscall.EBADF {
					t.Errorf("connect got %v, wanted EBADF", err)
				}
			})
		}
	}
}

// onlyWorksOnOpenThings is a helper test method for operations that should
// only work on files that have been explicitly opened.
func onlyWorksOnOpenThings(h *Harness, t *testing.T, name string, root p9.File, mode p9.OpenFlags, expectedErr error, fn func(backend *Mock, f p9.File, shouldSucceed bool) error) {
	// Walk to the file normally.
	_, backend, f := walkHelper(h, name, root)
	defer f.Close()

	// Does it work before opening?
	if err := fn(backend, f, false); err != syscall.EINVAL {
		t.Errorf("operation got %v, wanted EINVAL", err)
	}

	// Is this openable?
	if !p9.CanOpen(backend.Attr.Mode) {
		return // Nothing to do.
	}

	// If this is a directory, we can't handle writing.
	if backend.Attr.Mode.IsDir() && (mode == p9.ReadWrite || mode == p9.WriteOnly) {
		return // Skip.
	}

	// Open the file.
	backend.EXPECT().Open(mode)
	if _, _, _, err := f.Open(mode); err != nil {
		t.Fatalf("open got %v, wanted nil", err)
	}

	// Attempt the operation.
	if err := fn(backend, f, expectedErr == nil); err != expectedErr {
		t.Fatalf("operation got %v, wanted %v", err, expectedErr)
	}
}

func TestRead(t *testing.T) {
	type readTest struct {
		name string
		mode p9.OpenFlags
		err  error
	}

	cases := []readTest{
		{
			name: "read-only",
			mode: p9.ReadOnly,
			err:  nil,
		},
		{
			name: "read-write",
			mode: p9.ReadWrite,
			err:  nil,
		},
		{
			name: "write-only",
			mode: p9.WriteOnly,
			err:  syscall.EPERM,
		},
	}

	for name := range newTypeMap(nil) {
		for _, tc := range cases {
			t.Run(fmt.Sprintf("%s-%s", tc.name, name), func(t *testing.T) {
				h, c := NewHarness(t)
				defer h.Finish()

				_, root := newRoot(h, c)
				defer root.Close()

				const message = "hello"

				onlyWorksOnOpenThings(h, t, name, root, tc.mode, tc.err, func(backend *Mock, f p9.File, shouldSucceed bool) error {
					if !shouldSucceed {
						_, err := f.ReadAt([]byte(message), 0)
						return err
					}

					// Prepare for the call to readAt in the backend.
					backend.EXPECT().ReadAt(gomock.Any(), uint64(0)).Do(func(p []byte, offset uint64) {
						copy(p, message)
					}).Return(len(message), nil)

					// Make the client call.
					p := make([]byte, 2*len(message)) // Double size.
					n, err := f.ReadAt(p, 0)

					// Sanity check result.
					if err != nil {
						return err
					}
					if n != len(message) {
						t.Fatalf("message length incorrect, got %d, want %d", n, len(message))
					}
					if !bytes.Equal(p[:n], []byte(message)) {
						t.Fatalf("message incorrect, got %v, want %v", p, []byte(message))
					}
					return nil // Success.
				})
			})
		}
	}
}

func TestWrite(t *testing.T) {
	type writeTest struct {
		name string
		mode p9.OpenFlags
		err  error
	}

	cases := []writeTest{
		{
			name: "read-only",
			mode: p9.ReadOnly,
			err:  syscall.EPERM,
		},
		{
			name: "read-write",
			mode: p9.ReadWrite,
			err:  nil,
		},
		{
			name: "write-only",
			mode: p9.WriteOnly,
			err:  nil,
		},
	}

	for name := range newTypeMap(nil) {
		for _, tc := range cases {
			t.Run(fmt.Sprintf("%s-%s", tc.name, name), func(t *testing.T) {
				h, c := NewHarness(t)
				defer h.Finish()

				_, root := newRoot(h, c)
				defer root.Close()

				const message = "hello"

				onlyWorksOnOpenThings(h, t, name, root, tc.mode, tc.err, func(backend *Mock, f p9.File, shouldSucceed bool) error {
					if !shouldSucceed {
						_, err := f.WriteAt([]byte(message), 0)
						return err
					}

					// Prepare for the call to readAt in the backend.
					var output []byte // Saved by Do below.
					backend.EXPECT().WriteAt(gomock.Any(), uint64(0)).Do(func(p []byte, offset uint64) {
						output = p
					}).Return(len(message), nil)

					// Make the client call.
					n, err := f.WriteAt([]byte(message), 0)

					// Sanity check result.
					if err != nil {
						return err
					}
					if n != len(message) {
						t.Fatalf("message length incorrect, got %d, want %d", n, len(message))
					}
					if !bytes.Equal(output, []byte(message)) {
						t.Fatalf("message incorrect, got %v, want %v", output, []byte(message))
					}
					return nil // Success.
				})
			})
		}
	}
}

func TestFSync(t *testing.T) {
	for name := range newTypeMap(nil) {
		for _, mode := range []p9.OpenFlags{p9.ReadOnly, p9.WriteOnly, p9.ReadWrite} {
			t.Run(fmt.Sprintf("%s-%s", mode, name), func(t *testing.T) {
				h, c := NewHarness(t)
				defer h.Finish()

				_, root := newRoot(h, c)
				defer root.Close()

				onlyWorksOnOpenThings(h, t, name, root, mode, nil, func(backend *Mock, f p9.File, shouldSucceed bool) error {
					if shouldSucceed {
						backend.EXPECT().FSync().Times(1)
					}
					return f.FSync()
				})
			})
		}
	}
}

func TestFlush(t *testing.T) {
	for name := range newTypeMap(nil) {
		t.Run(name, func(t *testing.T) {
			h, c := NewHarness(t)
			defer h.Finish()

			_, root := newRoot(h, c)
			defer root.Close()

			_, backend, f := walkHelper(h, name, root)
			defer f.Close()

			backend.EXPECT().Flush()
			f.Flush()
		})
	}
}

// onlyWorksOnDirectories is a helper test method for operations that should
// only work on unopened directories, such as create, mkdir and symlink.
func onlyWorksOnDirectories(h *Harness, t *testing.T, name string, root p9.File, fn func(backend *Mock, f p9.File, shouldSucceed bool) error) {
	// Walk to the file normally.
	_, backend, f := walkHelper(h, name, root)
	defer f.Close()

	// Only directories support mknod.
	if !backend.Attr.Mode.IsDir() {
		if err := fn(backend, f, false); err != syscall.EINVAL {
			t.Errorf("operation got %v, wanted EINVAL", err)
		}
		return // Nothing else to do.
	}

	// Should succeed.
	if err := fn(backend, f, true); err != nil {
		t.Fatalf("operation got %v, wanted nil", err)
	}

	// Open the directory.
	backend.EXPECT().Open(p9.ReadOnly).Times(1)
	if _, _, _, err := f.Open(p9.ReadOnly); err != nil {
		t.Fatalf("open got %v, wanted nil", err)
	}

	// Should not work again.
	if err := fn(backend, f, false); err != syscall.EINVAL {
		t.Fatalf("operation got %v, wanted EINVAL", err)
	}
}

func TestCreate(t *testing.T) {
	for name := range newTypeMap(nil) {
		t.Run(name, func(t *testing.T) {
			h, c := NewHarness(t)
			defer h.Finish()

			_, root := newRoot(h, c)
			defer root.Close()

			onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error {
				if !shouldSucceed {
					_, _, _, _, err := f.Create("new-file", p9.ReadWrite, 0, 1, 2)
					return err
				}

				// If the create is going to succeed, then we
				// need to create a new backend file, and we
				// clone to ensure that we don't close the
				// original.
				_, newF, err := f.Walk(nil)
				if err != nil {
					t.Fatalf("clone got %v, wanted nil", err)
				}
				defer newF.Close()
				newBackend := h.Pop(newF)

				// Run a regular FD test to validate that path.
				fdTest(t, func(send *fd.FD) *fd.FD {
					// Return the send FD on success.
					newFile := h.NewFile()(backend) // New file with the parent backend.
					newBackend.EXPECT().Create("new-file", p9.ReadWrite, p9.FileMode(0), p9.UID(1), p9.GID(2)).Return(send, newFile, p9.QID{}, uint32(0), nil)

					// Receive the fd back.
					recv, _, _, _, err := newF.Create("new-file", p9.ReadWrite, 0, 1, 2)
					if err != nil {
						t.Fatalf("create got %v, wanted nil", err)
					}
					return recv
				})

				// The above will fail via normal test flow, so
				// we can assume that it passed.
				return nil
			})
		})
	}
}

func TestCreateInvalid(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	_, root := newRoot(h, c)
	defer root.Close()

	for name := range newTypeMap(nil) {
		for _, invalidName := range allInvalidNames(name) {
			if _, _, _, _, err := root.Create(invalidName, p9.ReadWrite, 0, 0, 0); err != syscall.EINVAL {
				t.Errorf("got %v for name %q, want EINVAL", err, invalidName)
			}
		}
	}
}

func TestMkdir(t *testing.T) {
	for name := range newTypeMap(nil) {
		t.Run(name, func(t *testing.T) {
			h, c := NewHarness(t)
			defer h.Finish()

			_, root := newRoot(h, c)
			defer root.Close()

			onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error {
				if shouldSucceed {
					backend.EXPECT().Mkdir("new-directory", p9.FileMode(0), p9.UID(1), p9.GID(2))
				}
				_, err := f.Mkdir("new-directory", 0, 1, 2)
				return err
			})
		})
	}
}

func TestMkdirInvalid(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	_, root := newRoot(h, c)
	defer root.Close()

	for name := range newTypeMap(nil) {
		for _, invalidName := range allInvalidNames(name) {
			if _, err := root.Mkdir(invalidName, 0, 0, 0); err != syscall.EINVAL {
				t.Errorf("got %v for name %q, want EINVAL", err, invalidName)
			}
		}
	}
}

func TestSymlink(t *testing.T) {
	for name := range newTypeMap(nil) {
		t.Run(name, func(t *testing.T) {
			h, c := NewHarness(t)
			defer h.Finish()

			_, root := newRoot(h, c)
			defer root.Close()

			onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error {
				if shouldSucceed {
					backend.EXPECT().Symlink("old-name", "new-name", p9.UID(1), p9.GID(2))
				}
				_, err := f.Symlink("old-name", "new-name", 1, 2)
				return err
			})
		})
	}
}

func TestSyminkInvalid(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	_, root := newRoot(h, c)
	defer root.Close()

	for name := range newTypeMap(nil) {
		for _, invalidName := range allInvalidNames(name) {
			// We need only test for invalid names in the new name,
			// the target can be an arbitrary string and we don't
			// need to sanity check it.
			if _, err := root.Symlink("old-name", invalidName, 0, 0); err != syscall.EINVAL {
				t.Errorf("got %v for name %q, want EINVAL", err, invalidName)
			}
		}
	}
}

func TestLink(t *testing.T) {
	for name := range newTypeMap(nil) {
		t.Run(name, func(t *testing.T) {
			h, c := NewHarness(t)
			defer h.Finish()

			_, root := newRoot(h, c)
			defer root.Close()

			onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error {
				if shouldSucceed {
					backend.EXPECT().Link(gomock.Any(), "new-link")
				}
				return f.Link(f, "new-link")
			})
		})
	}
}

func TestLinkInvalid(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	_, root := newRoot(h, c)
	defer root.Close()

	for name := range newTypeMap(nil) {
		for _, invalidName := range allInvalidNames(name) {
			if err := root.Link(root, invalidName); err != syscall.EINVAL {
				t.Errorf("got %v for name %q, want EINVAL", err, invalidName)
			}
		}
	}
}

func TestMknod(t *testing.T) {
	for name := range newTypeMap(nil) {
		t.Run(name, func(t *testing.T) {
			h, c := NewHarness(t)
			defer h.Finish()

			_, root := newRoot(h, c)
			defer root.Close()

			onlyWorksOnDirectories(h, t, name, root, func(backend *Mock, f p9.File, shouldSucceed bool) error {
				if shouldSucceed {
					backend.EXPECT().Mknod("new-block-device", p9.FileMode(0), uint32(1), uint32(2), p9.UID(3), p9.GID(4)).Times(1)
				}
				_, err := f.Mknod("new-block-device", 0, 1, 2, 3, 4)
				return err
			})
		})
	}
}

// concurrentFn is a specification of a concurrent operation. This is used to
// drive the concurrency tests below.
type concurrentFn struct {
	name  string
	match func(p9.FileMode) bool
	op    func(h *Harness, backend *Mock, f p9.File, callback func())
}

func concurrentTest(t *testing.T, name string, fn1, fn2 concurrentFn, sameDir, expectedOkay bool) {
	var (
		names1 []string
		names2 []string
	)
	if sameDir {
		// Use the same file one directory up.
		names1, names2 = []string{"one", name}, []string{"one", name}
	} else {
		// For different directories, just use siblings.
		names1, names2 = []string{"one", name}, []string{"three", name}
	}

	t.Run(fmt.Sprintf("%s(%v)+%s(%v)", fn1.name, names1, fn2.name, names2), func(t *testing.T) {
		h, c := NewHarness(t)
		defer h.Finish()

		_, root := newRoot(h, c)
		defer root.Close()

		// Walk to both files as given.
		_, f1, err := root.Walk(names1)
		if err != nil {
			t.Fatalf("error walking, got %v, want nil", err)
		}
		defer f1.Close()
		b1 := h.Pop(f1)
		_, f2, err := root.Walk(names2)
		if err != nil {
			t.Fatalf("error walking, got %v, want nil", err)
		}
		defer f2.Close()
		b2 := h.Pop(f2)

		// Are these a good match for the current test case?
		if !fn1.match(b1.Attr.Mode) {
			t.SkipNow()
		}
		if !fn2.match(b2.Attr.Mode) {
			t.SkipNow()
		}

		// Construct our "concurrency creator".
		in1 := make(chan struct{}, 1)
		in2 := make(chan struct{}, 1)
		var top sync.WaitGroup
		var fns sync.WaitGroup
		defer top.Wait()
		top.Add(2) // Accounting for below.
		defer fns.Done()
		fns.Add(1) // See line above; released before top.Wait.
		go func() {
			defer top.Done()
			fn1.op(h, b1, f1, func() {
				in1 <- struct{}{}
				fns.Wait()
			})
		}()
		go func() {
			defer top.Done()
			fn2.op(h, b2, f2, func() {
				in2 <- struct{}{}
				fns.Wait()
			})
		}()

		// Compute a reasonable timeout. If we expect the operation to hang,
		// give it 10 milliseconds before we assert that it's fine. After all,
		// there will be a lot of these tests. If we don't expect it to hang,
		// give it a full minute, since the machine could be slow.
		timeout := 10 * time.Millisecond
		if expectedOkay {
			timeout = 1 * time.Minute
		}

		// Read the first channel.
		var second chan struct{}
		select {
		case <-in1:
			second = in2
		case <-in2:
			second = in1
		}

		// Catch concurrency.
		select {
		case <-second:
			// We finished successful. Is this good? Depends on the
			// expected result.
			if !expectedOkay {
				t.Errorf("%q and %q proceeded concurrently!", fn1.name, fn2.name)
			}
		case <-time.After(timeout):
			// Great, things did not proceed concurrently. Is that what we
			// expected?
			if expectedOkay {
				t.Errorf("%q and %q hung concurrently!", fn1.name, fn2.name)
			}
		}
	})
}

func randomFileName() string {
	return fmt.Sprintf("%x", rand.Int63())
}

func TestConcurrency(t *testing.T) {
	readExclusive := []concurrentFn{
		{
			// N.B. We can't explicitly check WalkGetAttr behavior,
			// but we rely on the fact that the internal code paths
			// are the same.
			name:  "walk",
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				// See the documentation of WalkCallback.
				// Because walk is actually implemented by the
				// mock, we need a special place for this
				// callback.
				//
				// Note that a clone actually locks the parent
				// node. So we walk from this node to test
				// concurrent operations appropriately.
				backend.WalkCallback = func() error {
					callback()
					return nil
				}
				f.Walk([]string{randomFileName()}) // Won't exist.
			},
		},
		{
			name:  "fsync",
			match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().Open(gomock.Any())
				backend.EXPECT().FSync().Do(func() {
					callback()
				})
				f.Open(p9.ReadOnly) // Required.
				f.FSync()
			},
		},
		{
			name:  "readdir",
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().Open(gomock.Any())
				backend.EXPECT().Readdir(gomock.Any(), gomock.Any()).Do(func(uint64, uint32) {
					callback()
				})
				f.Open(p9.ReadOnly) // Required.
				f.Readdir(0, 1)
			},
		},
		{
			name:  "readlink",
			match: func(mode p9.FileMode) bool { return mode.IsSymlink() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().Readlink().Do(func() {
					callback()
				})
				f.Readlink()
			},
		},
		{
			name:  "connect",
			match: func(mode p9.FileMode) bool { return mode.IsSocket() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().Connect(gomock.Any()).Do(func(p9.ConnectFlags) {
					callback()
				})
				f.Connect(0)
			},
		},
		{
			name:  "open",
			match: func(mode p9.FileMode) bool { return p9.CanOpen(mode) },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().Open(gomock.Any()).Do(func(p9.OpenFlags) {
					callback()
				})
				f.Open(p9.ReadOnly)
			},
		},
		{
			name:  "flush",
			match: func(mode p9.FileMode) bool { return true },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().Flush().Do(func() {
					callback()
				})
				f.Flush()
			},
		},
	}
	writeExclusive := []concurrentFn{
		{
			// N.B. We can't really check getattr. But this is an
			// extremely low-risk function, it seems likely that
			// this check is paranoid anyways.
			name:  "setattr",
			match: func(mode p9.FileMode) bool { return true },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().SetAttr(gomock.Any(), gomock.Any()).Do(func(p9.SetAttrMask, p9.SetAttr) {
					callback()
				})
				f.SetAttr(p9.SetAttrMask{}, p9.SetAttr{})
			},
		},
		{
			name:  "unlinkAt",
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().UnlinkAt(gomock.Any(), gomock.Any()).Do(func(string, uint32) {
					callback()
				})
				f.UnlinkAt(randomFileName(), 0)
			},
		},
		{
			name:  "mknod",
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().Mknod(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, p9.FileMode, uint32, uint32, p9.UID, p9.GID) {
					callback()
				})
				f.Mknod(randomFileName(), 0, 0, 0, 0, 0)
			},
		},
		{
			name:  "link",
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().Link(gomock.Any(), gomock.Any()).Do(func(p9.File, string) {
					callback()
				})
				f.Link(f, randomFileName())
			},
		},
		{
			name:  "symlink",
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().Symlink(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, string, p9.UID, p9.GID) {
					callback()
				})
				f.Symlink(randomFileName(), randomFileName(), 0, 0)
			},
		},
		{
			name:  "mkdir",
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().Mkdir(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, p9.FileMode, p9.UID, p9.GID) {
					callback()
				})
				f.Mkdir(randomFileName(), 0, 0, 0)
			},
		},
		{
			name:  "create",
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				// Return an error for the creation operation, as this is the simplest.
				backend.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, p9.QID{}, uint32(0), syscall.EINVAL).Do(func(string, p9.OpenFlags, p9.FileMode, p9.UID, p9.GID) {
					callback()
				})
				f.Create(randomFileName(), p9.ReadOnly, 0, 0, 0)
			},
		},
	}
	globalExclusive := []concurrentFn{
		{
			name:  "remove",
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				// Remove operates on a locked parent. So we
				// add a child, walk to it and call remove.
				// Note that because this operation can operate
				// concurrently with itself, we need to
				// generate a random file name.
				randomFile := randomFileName()
				backend.AddChild(randomFile, h.NewFile())
				defer backend.RemoveChild(randomFile)
				_, file, err := f.Walk([]string{randomFile})
				if err != nil {
					h.t.Fatalf("walk got %v, want nil", err)
				}

				// Remove is automatically translated to the parent.
				backend.EXPECT().UnlinkAt(gomock.Any(), gomock.Any()).Do(func(string, uint32) {
					callback()
				})

				// Remove is also a close.
				file.(deprecatedRemover).Remove()
			},
		},
		{
			name:  "rename",
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				// Similarly to remove, because we need to
				// operate on a child, we allow a walk.
				randomFile := randomFileName()
				backend.AddChild(randomFile, h.NewFile())
				defer backend.RemoveChild(randomFile)
				_, file, err := f.Walk([]string{randomFile})
				if err != nil {
					h.t.Fatalf("walk got %v, want nil", err)
				}
				defer file.Close()
				fileBackend := h.Pop(file)

				// Rename is automatically translated to the parent.
				backend.EXPECT().RenameAt(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, p9.File, string) {
					callback()
				})

				// Attempt the rename.
				fileBackend.EXPECT().Renamed(gomock.Any(), gomock.Any())
				file.Rename(f, randomFileName())
			},
		},
		{
			name:  "renameAt",
			match: func(mode p9.FileMode) bool { return mode.IsDir() },
			op: func(h *Harness, backend *Mock, f p9.File, callback func()) {
				backend.EXPECT().RenameAt(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(string, p9.File, string) {
					callback()
				})

				// Attempt the rename. There are no active fids
				// with this name, so we don't need to expect
				// Renamed hooks on anything.
				f.RenameAt(randomFileName(), f, randomFileName())
			},
		},
	}

	for _, fn1 := range readExclusive {
		for _, fn2 := range readExclusive {
			for name := range newTypeMap(nil) {
				// Everything should be able to proceed in parallel.
				concurrentTest(t, name, fn1, fn2, true, true)
				concurrentTest(t, name, fn1, fn2, false, true)
			}
		}
	}

	for _, fn1 := range append(readExclusive, writeExclusive...) {
		for _, fn2 := range writeExclusive {
			for name := range newTypeMap(nil) {
				// Only cross-directory functions should proceed in parallel.
				concurrentTest(t, name, fn1, fn2, true, false)
				concurrentTest(t, name, fn1, fn2, false, true)
			}
		}
	}

	for _, fn1 := range append(append(readExclusive, writeExclusive...), globalExclusive...) {
		for _, fn2 := range globalExclusive {
			for name := range newTypeMap(nil) {
				// Nothing should be able to run in parallel.
				concurrentTest(t, name, fn1, fn2, true, false)
				concurrentTest(t, name, fn1, fn2, false, false)
			}
		}
	}
}

func TestReadWriteConcurrent(t *testing.T) {
	h, c := NewHarness(t)
	defer h.Finish()

	_, root := newRoot(h, c)
	defer root.Close()

	const (
		instances  = 10
		iterations = 10000
		dataSize   = 1024
	)
	var (
		dataSets [instances][dataSize]byte
		backends [instances]*Mock
		files    [instances]p9.File
	)

	// Walk to the file normally.
	for i := 0; i < instances; i++ {
		_, backends[i], files[i] = walkHelper(h, "file", root)
		defer files[i].Close()
	}

	// Open the files.
	for i := 0; i < instances; i++ {
		backends[i].EXPECT().Open(p9.ReadWrite)
		if _, _, _, err := files[i].Open(p9.ReadWrite); err != nil {
			t.Fatalf("open got %v, wanted nil", err)
		}
	}

	// Initialize random data for each instance.
	for i := 0; i < instances; i++ {
		if _, err := rand.Read(dataSets[i][:]); err != nil {
			t.Fatalf("error initializing dataSet#%d, got %v", i, err)
		}
	}

	// Define our random read/write mechanism.
	randRead := func(h *Harness, backend *Mock, f p9.File, data, test []byte) {
		// Prepare the backend.
		backend.EXPECT().ReadAt(gomock.Any(), uint64(0)).Do(func(p []byte, offset uint64) {
			if n := copy(p, data); n != len(data) {
				// Note that we have to assert the result here, as the Return statement
				// below cannot be dynamic: it will be bound before this call is made.
				h.t.Errorf("wanted length %d, got %d", len(data), n)
			}
		}).Return(len(data), nil)

		// Execute the read.
		if n, err := f.ReadAt(test, 0); n != len(test) || err != nil {
			t.Errorf("failed read: wanted (%d, nil), got (%d, %v)", len(test), n, err)
			return // No sense doing check below.
		}
		if !bytes.Equal(test, data) {
			t.Errorf("data integrity failed during read") // Not as expected.
		}
	}
	randWrite := func(h *Harness, backend *Mock, f p9.File, data []byte) {
		// Prepare the backend.
		backend.EXPECT().WriteAt(gomock.Any(), uint64(0)).Do(func(p []byte, offset uint64) {
			if !bytes.Equal(p, data) {
				h.t.Errorf("data integrity failed during write") // Not as expected.
			}
		}).Return(len(data), nil)

		// Execute the write.
		if n, err := f.WriteAt(data, 0); n != len(data) || err != nil {
			t.Errorf("failed read: wanted (%d, nil), got (%d, %v)", len(data), n, err)
		}
	}
	randReadWrite := func(n int, h *Harness, backend *Mock, f p9.File, data []byte) {
		test := make([]byte, len(data))
		for i := 0; i < n; i++ {
			if rand.Intn(2) == 0 {
				randRead(h, backend, f, data, test)
			} else {
				randWrite(h, backend, f, data)
			}
		}
	}

	// Start reading and writing.
	var wg sync.WaitGroup
	for i := 0; i < instances; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			randReadWrite(iterations, h, backends[i], files[i], dataSets[i][:])
		}(i)
	}
	wg.Wait()
}