github.com/hanwen/go-fuse@v1.0.0/unionfs/unionfs_xattr_test.go (about)

     1  // Copyright 2016 the Go-FUSE Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package unionfs
     6  
     7  import (
     8  	"os"
     9  	"sync/atomic"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/hanwen/go-fuse/fuse"
    14  	"github.com/hanwen/go-fuse/fuse/nodefs"
    15  	"github.com/hanwen/go-fuse/fuse/pathfs"
    16  	"github.com/hanwen/go-fuse/internal/testutil"
    17  )
    18  
    19  type TestFS struct {
    20  	pathfs.FileSystem
    21  	xattrRead int64
    22  }
    23  
    24  func (fs *TestFS) GetAttr(path string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
    25  	switch path {
    26  	case "":
    27  		return &fuse.Attr{Mode: fuse.S_IFDIR | 0755}, fuse.OK
    28  	case "file":
    29  		return &fuse.Attr{Mode: fuse.S_IFREG | 0755}, fuse.OK
    30  	}
    31  	return nil, fuse.ENOENT
    32  }
    33  
    34  func (fs *TestFS) GetXAttr(path string, name string, context *fuse.Context) ([]byte, fuse.Status) {
    35  	if path == "file" && name == "user.attr" {
    36  		atomic.AddInt64(&fs.xattrRead, 1)
    37  		return []byte{42}, fuse.OK
    38  	}
    39  	return nil, fuse.ENOATTR
    40  }
    41  
    42  func TestXAttrCaching(t *testing.T) {
    43  	wd := testutil.TempDir()
    44  	defer os.RemoveAll(wd)
    45  	os.Mkdir(wd+"/mnt", 0700)
    46  	err := os.Mkdir(wd+"/rw", 0700)
    47  	if err != nil {
    48  		t.Fatalf("Mkdir failed: %v", err)
    49  	}
    50  
    51  	rwFS := pathfs.NewLoopbackFileSystem(wd + "/rw")
    52  	roFS := &TestFS{
    53  		FileSystem: pathfs.NewDefaultFileSystem(),
    54  	}
    55  
    56  	ufs, err := NewUnionFs([]pathfs.FileSystem{rwFS,
    57  		NewCachingFileSystem(roFS, entryTTL)}, testOpts)
    58  	if err != nil {
    59  		t.Fatalf("NewUnionFs: %v", err)
    60  	}
    61  
    62  	opts := &nodefs.Options{
    63  		EntryTimeout:        entryTTL / 2,
    64  		AttrTimeout:         entryTTL / 2,
    65  		NegativeTimeout:     entryTTL / 2,
    66  		Debug:               testutil.VerboseTest(),
    67  		LookupKnownChildren: true,
    68  	}
    69  
    70  	pathfs := pathfs.NewPathNodeFs(ufs,
    71  		&pathfs.PathNodeFsOptions{ClientInodes: true,
    72  			Debug: testutil.VerboseTest()})
    73  
    74  	server, _, err := nodefs.MountRoot(wd+"/mnt", pathfs.Root(), opts)
    75  	if err != nil {
    76  		t.Fatalf("MountNodeFileSystem failed: %v", err)
    77  	}
    78  	defer server.Unmount()
    79  	go server.Serve()
    80  	server.WaitMount()
    81  
    82  	start := time.Now()
    83  	if fi, err := os.Lstat(wd + "/mnt"); err != nil || !fi.IsDir() {
    84  		t.Fatalf("root not readable: %v, %v", err, fi)
    85  	}
    86  
    87  	buf := make([]byte, 1024)
    88  	n, err := Getxattr(wd+"/mnt/file", "user.attr", buf)
    89  	if err != nil {
    90  		t.Fatalf("Getxattr: %v", err)
    91  	}
    92  	want := "\x2a"
    93  	got := string(buf[:n])
    94  	if got != want {
    95  		t.Fatalf("Got %q want %q", got, err)
    96  	}
    97  
    98  	time.Sleep(entryTTL / 3)
    99  
   100  	n, err = Getxattr(wd+"/mnt/file", "user.attr", buf)
   101  	if err != nil {
   102  		t.Fatalf("Getxattr: %v", err)
   103  	}
   104  	got = string(buf[:n])
   105  	if got != want {
   106  		t.Fatalf("Got %q want %q", got, err)
   107  	}
   108  
   109  	time.Sleep(entryTTL / 3)
   110  
   111  	// Make sure that an interceding Getxattr() to a filesystem that doesn't implement GetXAttr() doesn't affect future calls.
   112  	Getxattr(wd, "whatever", buf)
   113  
   114  	n, err = Getxattr(wd+"/mnt/file", "user.attr", buf)
   115  	if err != nil {
   116  		t.Fatalf("Getxattr: %v", err)
   117  	}
   118  	got = string(buf[:n])
   119  	if got != want {
   120  		t.Fatalf("Got %q want %q", got, err)
   121  	}
   122  
   123  	if time.Now().Sub(start) >= entryTTL {
   124  		// If we run really slowly, this test will spuriously
   125  		// fail.
   126  		t.Skip("test took too long.")
   127  	}
   128  
   129  	actual := atomic.LoadInt64(&roFS.xattrRead)
   130  	if actual != 1 {
   131  		t.Errorf("got xattrRead=%d, want 1", actual)
   132  	}
   133  }