github.com/hanwen/go-fuse@v1.0.0/unionfs/unionfs_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  	"bytes"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"regexp"
    15  	"strconv"
    16  	"strings"
    17  	"syscall"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/hanwen/go-fuse/fuse"
    22  	"github.com/hanwen/go-fuse/fuse/nodefs"
    23  	"github.com/hanwen/go-fuse/fuse/pathfs"
    24  	"github.com/hanwen/go-fuse/internal/testutil"
    25  )
    26  
    27  func TestFilePathHash(t *testing.T) {
    28  	got := filePathHash("xyz/abc")
    29  	want := "34d52a6371ee5c79-abc"
    30  	if got != want {
    31  		t.Errorf("got %q want %q", got, want)
    32  	}
    33  }
    34  
    35  var testOpts = UnionFsOptions{
    36  	DeletionCacheTTL: entryTTL,
    37  	DeletionDirName:  "DELETIONS",
    38  	BranchCacheTTL:   entryTTL,
    39  	HiddenFiles:      []string{"hidden"},
    40  }
    41  
    42  func setRecursiveWritable(t *testing.T, dir string, writable bool) {
    43  	err := filepath.Walk(
    44  		dir,
    45  		func(path string, fi os.FileInfo, err error) error {
    46  			var newMode uint32
    47  			if writable {
    48  				newMode = uint32(fi.Mode().Perm()) | 0200
    49  			} else {
    50  				newMode = uint32(fi.Mode().Perm()) &^ 0222
    51  			}
    52  			if fi.Mode()|os.ModeSymlink != 0 {
    53  				return nil
    54  			}
    55  			return os.Chmod(path, os.FileMode(newMode))
    56  		})
    57  	if err != nil {
    58  		t.Fatalf("Walk: %v", err)
    59  	}
    60  }
    61  
    62  // Creates a temporary dir "wd" with 3 directories:
    63  // mnt ... overlayed (unionfs) mount
    64  // rw .... modifiable data
    65  // ro .... read-only data
    66  func setupUfs(t *testing.T) (wd string, cleanup func()) {
    67  	// Make sure system setting does not affect test.
    68  	syscall.Umask(0)
    69  
    70  	wd = testutil.TempDir()
    71  	err := os.Mkdir(wd+"/mnt", 0700)
    72  	if err != nil {
    73  		t.Fatalf("Mkdir: %v", err)
    74  	}
    75  
    76  	err = os.Mkdir(wd+"/rw", 0700)
    77  	if err != nil {
    78  		t.Fatalf("Mkdir: %v", err)
    79  	}
    80  
    81  	os.Mkdir(wd+"/ro", 0700)
    82  	if err != nil {
    83  		t.Fatalf("Mkdir: %v", err)
    84  	}
    85  
    86  	fses := []pathfs.FileSystem{
    87  		pathfs.NewLoopbackFileSystem(wd + "/rw"),
    88  		NewCachingFileSystem(pathfs.NewLoopbackFileSystem(wd+"/ro"), 0),
    89  	}
    90  	ufs, err := NewUnionFs(fses, testOpts)
    91  	if err != nil {
    92  		t.Fatalf("NewUnionFs: %v", err)
    93  	}
    94  	// We configure timeouts are smaller, so we can check for
    95  	// UnionFs's cache consistency.
    96  	opts := &nodefs.Options{
    97  		EntryTimeout:        entryTTL / 2,
    98  		AttrTimeout:         entryTTL / 2,
    99  		NegativeTimeout:     entryTTL / 2,
   100  		PortableInodes:      true,
   101  		Debug:               testutil.VerboseTest(),
   102  		LookupKnownChildren: true,
   103  	}
   104  
   105  	pathfs := pathfs.NewPathNodeFs(ufs,
   106  		&pathfs.PathNodeFsOptions{ClientInodes: true,
   107  			Debug: opts.Debug,
   108  		})
   109  	state, _, err := nodefs.MountRoot(wd+"/mnt", pathfs.Root(), opts)
   110  	if err != nil {
   111  		t.Fatalf("MountNodeFileSystem: %v", err)
   112  	}
   113  	go state.Serve()
   114  	state.WaitMount()
   115  
   116  	return wd, func() {
   117  		err := state.Unmount()
   118  		if err != nil {
   119  			return
   120  		}
   121  		setRecursiveWritable(t, wd, true)
   122  		os.RemoveAll(wd)
   123  	}
   124  }
   125  
   126  func readFromFile(t *testing.T, path string) string {
   127  	b, err := ioutil.ReadFile(path)
   128  	if err != nil {
   129  		t.Fatalf("ReadFile: %v", err)
   130  	}
   131  	return string(b)
   132  }
   133  
   134  func dirNames(t *testing.T, path string) map[string]bool {
   135  	f, err := os.Open(path)
   136  	if err != nil {
   137  		t.Fatalf("Open: %v", err)
   138  	}
   139  
   140  	result := make(map[string]bool)
   141  	names, err := f.Readdirnames(-1)
   142  	if err != nil {
   143  		t.Fatalf("Readdirnames: %v", err)
   144  	}
   145  	err = f.Close()
   146  	if err != nil {
   147  		t.Fatalf("Close: %v", err)
   148  	}
   149  
   150  	for _, nm := range names {
   151  		result[nm] = true
   152  	}
   153  	return result
   154  }
   155  
   156  func checkMapEq(t *testing.T, m1, m2 map[string]bool) {
   157  	if !mapEq(m1, m2) {
   158  		msg := fmt.Sprintf("mismatch: got %v != expect %v", m1, m2)
   159  		panic(msg)
   160  	}
   161  }
   162  
   163  func mapEq(m1, m2 map[string]bool) bool {
   164  	if len(m1) != len(m2) {
   165  		return false
   166  	}
   167  
   168  	for k, v := range m1 {
   169  		val, ok := m2[k]
   170  		if !ok || val != v {
   171  			return false
   172  		}
   173  	}
   174  	return true
   175  }
   176  
   177  func fileExists(path string) bool {
   178  	f, err := os.Lstat(path)
   179  	return err == nil && f != nil
   180  }
   181  
   182  func TestUnionFsAutocreateDeletionDir(t *testing.T) {
   183  	wd, clean := setupUfs(t)
   184  	defer clean()
   185  
   186  	err := os.Remove(wd + "/rw/DELETIONS")
   187  	if err != nil {
   188  		t.Fatalf("Remove: %v", err)
   189  	}
   190  
   191  	err = os.Mkdir(wd+"/mnt/dir", 0755)
   192  	if err != nil {
   193  		t.Fatalf("Mkdir: %v", err)
   194  	}
   195  
   196  	_, err = ioutil.ReadDir(wd + "/mnt/dir")
   197  	if err != nil {
   198  		t.Fatalf("ReadDir: %v", err)
   199  	}
   200  }
   201  
   202  func TestUnionFsSymlink(t *testing.T) {
   203  	wd, clean := setupUfs(t)
   204  	defer clean()
   205  
   206  	err := os.Symlink("/foobar", wd+"/mnt/link")
   207  	if err != nil {
   208  		t.Fatalf("Symlink: %v", err)
   209  	}
   210  
   211  	val, err := os.Readlink(wd + "/mnt/link")
   212  	if err != nil {
   213  		t.Fatalf("Readlink: %v", err)
   214  	}
   215  
   216  	if val != "/foobar" {
   217  		t.Errorf("symlink mismatch: %v", val)
   218  	}
   219  }
   220  
   221  func TestUnionFsSymlinkPromote(t *testing.T) {
   222  	wd, clean := setupUfs(t)
   223  	defer clean()
   224  
   225  	err := os.Mkdir(wd+"/ro/subdir", 0755)
   226  	if err != nil {
   227  		t.Fatalf("Mkdir: %v", err)
   228  	}
   229  
   230  	err = os.Symlink("/foobar", wd+"/mnt/subdir/link")
   231  	if err != nil {
   232  		t.Fatalf("Symlink: %v", err)
   233  	}
   234  }
   235  
   236  func TestUnionFsChtimes(t *testing.T) {
   237  	wd, clean := setupUfs(t)
   238  	defer clean()
   239  
   240  	WriteFile(t, wd+"/ro/file", "a")
   241  	err := os.Chtimes(wd+"/ro/file", time.Unix(42, 0), time.Unix(43, 0))
   242  	if err != nil {
   243  		t.Fatalf("Chtimes: %v", err)
   244  	}
   245  
   246  	err = os.Chtimes(wd+"/mnt/file", time.Unix(82, 0), time.Unix(83, 0))
   247  	if err != nil {
   248  		t.Fatalf("Chtimes: %v", err)
   249  	}
   250  
   251  	fi, err := os.Lstat(wd + "/mnt/file")
   252  	attr := &fuse.Attr{}
   253  	attr.FromStat(fuse.ToStatT(fi))
   254  	if attr.Atime != 82 || attr.Mtime != 83 {
   255  		t.Error("Incorrect timestamp", fi)
   256  	}
   257  }
   258  
   259  func TestUnionFsChmod(t *testing.T) {
   260  	wd, clean := setupUfs(t)
   261  	defer clean()
   262  
   263  	ro_fn := wd + "/ro/file"
   264  	m_fn := wd + "/mnt/file"
   265  	WriteFile(t, ro_fn, "a")
   266  	err := os.Chmod(m_fn, 00070)
   267  	if err != nil {
   268  		t.Fatalf("Chmod: %v", err)
   269  	}
   270  
   271  	fi, err := os.Lstat(m_fn)
   272  	if err != nil {
   273  		t.Fatalf("Lstat: %v", err)
   274  	}
   275  	if fi.Mode()&07777 != 00270 {
   276  		t.Errorf("Unexpected mode found: %o", uint32(fi.Mode().Perm()))
   277  	}
   278  	_, err = os.Lstat(wd + "/rw/file")
   279  	if err != nil {
   280  		t.Errorf("File not promoted")
   281  	}
   282  }
   283  
   284  func TestUnionFsChown(t *testing.T) {
   285  	wd, clean := setupUfs(t)
   286  	defer clean()
   287  
   288  	ro_fn := wd + "/ro/file"
   289  	m_fn := wd + "/mnt/file"
   290  	WriteFile(t, ro_fn, "a")
   291  
   292  	err := os.Chown(m_fn, 0, 0)
   293  	code := fuse.ToStatus(err)
   294  	if code != fuse.EPERM {
   295  		t.Error("Unexpected error code", code, err)
   296  	}
   297  }
   298  
   299  func TestUnionFsDelete(t *testing.T) {
   300  	wd, clean := setupUfs(t)
   301  	defer clean()
   302  
   303  	WriteFile(t, wd+"/ro/file", "a")
   304  	_, err := os.Lstat(wd + "/mnt/file")
   305  	if err != nil {
   306  		t.Fatalf("Lstat: %v", err)
   307  	}
   308  
   309  	err = os.Remove(wd + "/mnt/file")
   310  	if err != nil {
   311  		t.Fatalf("Remove: %v", err)
   312  	}
   313  
   314  	_, err = os.Lstat(wd + "/mnt/file")
   315  	if err == nil {
   316  		t.Fatal("should have disappeared.")
   317  	}
   318  	delPath := wd + "/rw/" + testOpts.DeletionDirName
   319  	names := dirNames(t, delPath)
   320  	if len(names) != 1 {
   321  		t.Fatal("Should have 1 deletion", names)
   322  	}
   323  
   324  	for k := range names {
   325  		c, err := ioutil.ReadFile(delPath + "/" + k)
   326  		if err != nil {
   327  			t.Fatalf("ReadFile: %v", err)
   328  		}
   329  		if string(c) != "file" {
   330  			t.Fatal("content mismatch", string(c))
   331  		}
   332  	}
   333  }
   334  
   335  func TestUnionFsBasic(t *testing.T) {
   336  	wd, clean := setupUfs(t)
   337  	defer clean()
   338  
   339  	WriteFile(t, wd+"/rw/rw", "a")
   340  	WriteFile(t, wd+"/ro/ro1", "a")
   341  	WriteFile(t, wd+"/ro/ro2", "b")
   342  
   343  	names := dirNames(t, wd+"/mnt")
   344  	expected := map[string]bool{
   345  		"rw": true, "ro1": true, "ro2": true,
   346  	}
   347  	checkMapEq(t, names, expected)
   348  
   349  	WriteFile(t, wd+"/mnt/new", "new contents")
   350  	if !fileExists(wd + "/rw/new") {
   351  		t.Errorf("missing file in rw layer: %s", wd+"/rw/new")
   352  	}
   353  
   354  	contents := readFromFile(t, wd+"/mnt/new")
   355  	if contents != "new contents" {
   356  		t.Errorf("read mismatch: '%v'", contents)
   357  	}
   358  	WriteFile(t, wd+"/mnt/ro1", "promote me")
   359  	if !fileExists(wd + "/rw/ro1") {
   360  		t.Errorf("missing file in rw layer: %s", wd+"/mnt/ro1")
   361  	}
   362  
   363  	err := os.Remove(wd + "/mnt/new")
   364  	if err != nil {
   365  		t.Fatalf("Remove: %v", err)
   366  	}
   367  
   368  	names = dirNames(t, wd+"/mnt")
   369  	checkMapEq(t, names, map[string]bool{
   370  		"rw": true, "ro1": true, "ro2": true,
   371  	})
   372  
   373  	names = dirNames(t, wd+"/rw")
   374  	checkMapEq(t, names, map[string]bool{
   375  		testOpts.DeletionDirName: true,
   376  		"rw": true, "ro1": true,
   377  	})
   378  	names = dirNames(t, wd+"/rw/"+testOpts.DeletionDirName)
   379  	if len(names) != 0 {
   380  		t.Errorf("Expected 0 entry in %v", names)
   381  	}
   382  
   383  	err = os.Remove(wd + "/mnt/ro1")
   384  	if err != nil {
   385  		t.Fatalf("Remove: %v", err)
   386  	}
   387  	names = dirNames(t, wd+"/mnt")
   388  	checkMapEq(t, names, map[string]bool{
   389  		"rw": true, "ro2": true,
   390  	})
   391  
   392  	names = dirNames(t, wd+"/rw")
   393  	checkMapEq(t, names, map[string]bool{
   394  		"rw": true, testOpts.DeletionDirName: true,
   395  	})
   396  
   397  	names = dirNames(t, wd+"/rw/"+testOpts.DeletionDirName)
   398  	if len(names) != 1 {
   399  		t.Errorf("Expected 1 entry in %v", names)
   400  	}
   401  }
   402  
   403  func TestUnionFsPromote(t *testing.T) {
   404  	wd, clean := setupUfs(t)
   405  	defer clean()
   406  
   407  	err := os.Mkdir(wd+"/ro/subdir", 0755)
   408  	if err != nil {
   409  		t.Fatalf("Mkdir: %v", err)
   410  	}
   411  	WriteFile(t, wd+"/ro/subdir/file", "content")
   412  	WriteFile(t, wd+"/mnt/subdir/file", "other-content")
   413  }
   414  
   415  func TestUnionFsCreate(t *testing.T) {
   416  	wd, clean := setupUfs(t)
   417  	defer clean()
   418  
   419  	err := os.MkdirAll(wd+"/ro/subdir/sub2", 0755)
   420  	if err != nil {
   421  		t.Fatalf("MkdirAll: %v", err)
   422  	}
   423  	WriteFile(t, wd+"/mnt/subdir/sub2/file", "other-content")
   424  	_, err = os.Lstat(wd + "/mnt/subdir/sub2/file")
   425  	if err != nil {
   426  		t.Fatalf("Lstat: %v", err)
   427  	}
   428  }
   429  
   430  func TestUnionFsOpenUndeletes(t *testing.T) {
   431  	wd, clean := setupUfs(t)
   432  	defer clean()
   433  
   434  	WriteFile(t, wd+"/ro/file", "X")
   435  	err := os.Remove(wd + "/mnt/file")
   436  	if err != nil {
   437  		t.Fatalf("Remove: %v", err)
   438  	}
   439  	WriteFile(t, wd+"/mnt/file", "X")
   440  	_, err = os.Lstat(wd + "/mnt/file")
   441  	if err != nil {
   442  		t.Fatalf("Lstat: %v", err)
   443  	}
   444  }
   445  
   446  func TestUnionFsMkdir(t *testing.T) {
   447  	wd, clean := setupUfs(t)
   448  	defer clean()
   449  
   450  	dirname := wd + "/mnt/subdir"
   451  	err := os.Mkdir(dirname, 0755)
   452  	if err != nil {
   453  		t.Fatalf("Mkdir: %v", err)
   454  	}
   455  
   456  	err = os.Remove(dirname)
   457  	if err != nil {
   458  		t.Fatalf("Remove: %v", err)
   459  	}
   460  }
   461  
   462  func TestUnionFsMkdirPromote(t *testing.T) {
   463  	wd, clean := setupUfs(t)
   464  	defer clean()
   465  
   466  	dirname := wd + "/ro/subdir/subdir2"
   467  	err := os.MkdirAll(dirname, 0755)
   468  	if err != nil {
   469  		t.Fatalf("MkdirAll: %v", err)
   470  	}
   471  
   472  	err = os.Mkdir(wd+"/mnt/subdir/subdir2/dir3", 0755)
   473  	if err != nil {
   474  		t.Fatalf("Mkdir: %v", err)
   475  	}
   476  	fi, _ := os.Lstat(wd + "/rw/subdir/subdir2/dir3")
   477  	if err != nil {
   478  		t.Fatalf("Lstat: %v", err)
   479  	}
   480  	if fi == nil || !fi.IsDir() {
   481  		t.Error("is not a directory: ", fi)
   482  	}
   483  }
   484  
   485  func TestUnionFsRmdirMkdir(t *testing.T) {
   486  	wd, clean := setupUfs(t)
   487  	defer clean()
   488  
   489  	err := os.Mkdir(wd+"/ro/subdir", 0755)
   490  	if err != nil {
   491  		t.Fatalf("Mkdir: %v", err)
   492  	}
   493  
   494  	dirname := wd + "/mnt/subdir"
   495  	err = os.Remove(dirname)
   496  	if err != nil {
   497  		t.Fatalf("Remove: %v", err)
   498  	}
   499  
   500  	err = os.Mkdir(dirname, 0755)
   501  	if err != nil {
   502  		t.Fatalf("Mkdir: %v", err)
   503  	}
   504  }
   505  
   506  func TestUnionFsRename(t *testing.T) {
   507  	type Config struct {
   508  		f1_ro bool
   509  		f1_rw bool
   510  		f2_ro bool
   511  		f2_rw bool
   512  	}
   513  
   514  	configs := make([]Config, 0)
   515  	for i := 0; i < 16; i++ {
   516  		c := Config{i&0x1 != 0, i&0x2 != 0, i&0x4 != 0, i&0x8 != 0}
   517  		if !(c.f1_ro || c.f1_rw) {
   518  			continue
   519  		}
   520  
   521  		configs = append(configs, c)
   522  	}
   523  
   524  	for i, c := range configs {
   525  		t.Run(fmt.Sprintf("config %d", i), func(t *testing.T) {
   526  			wd, clean := setupUfs(t)
   527  			defer clean()
   528  
   529  			if c.f1_ro {
   530  				WriteFile(t, wd+"/ro/file1", "c1")
   531  			}
   532  			if c.f1_rw {
   533  				WriteFile(t, wd+"/rw/file1", "c2")
   534  			}
   535  			if c.f2_ro {
   536  				WriteFile(t, wd+"/ro/file2", "c3")
   537  			}
   538  			if c.f2_rw {
   539  				WriteFile(t, wd+"/rw/file2", "c4")
   540  			}
   541  
   542  			err := os.Rename(wd+"/mnt/file1", wd+"/mnt/file2")
   543  			if err != nil {
   544  				t.Fatalf("Rename: %v", err)
   545  			}
   546  
   547  			_, err = os.Lstat(wd + "/mnt/file1")
   548  			if err == nil {
   549  				t.Errorf("Should have lost file1")
   550  			}
   551  			_, err = os.Lstat(wd + "/mnt/file2")
   552  			if err != nil {
   553  				t.Errorf("Should have gotten file2: %v", err)
   554  			}
   555  			err = os.Rename(wd+"/mnt/file2", wd+"/mnt/file1")
   556  			if err != nil {
   557  				t.Fatalf("Rename: %v", err)
   558  			}
   559  
   560  			_, err = os.Lstat(wd + "/mnt/file2")
   561  			if err == nil {
   562  				t.Errorf("Should have lost file2")
   563  			}
   564  			_, err = os.Lstat(wd + "/mnt/file1")
   565  			if err != nil {
   566  				t.Errorf("Should have gotten file1: %v", err)
   567  			}
   568  		})
   569  	}
   570  }
   571  
   572  func TestUnionFsRenameDirBasic(t *testing.T) {
   573  	wd, clean := setupUfs(t)
   574  	defer clean()
   575  
   576  	err := os.MkdirAll(wd+"/ro/dir/subdir", 0755)
   577  	if err != nil {
   578  		t.Fatalf("MkdirAll: %v", err)
   579  	}
   580  
   581  	err = os.Rename(wd+"/mnt/dir", wd+"/mnt/renamed")
   582  	if err != nil {
   583  		t.Fatalf("Rename: %v", err)
   584  	}
   585  
   586  	if fi, _ := os.Lstat(wd + "/mnt/dir"); fi != nil {
   587  		t.Fatalf("%s/mnt/dir should have disappeared: %v", wd, fi)
   588  	}
   589  
   590  	if fi, _ := os.Lstat(wd + "/mnt/renamed"); fi == nil || !fi.IsDir() {
   591  		t.Fatalf("%s/mnt/renamed should be directory: %v", wd, fi)
   592  	}
   593  
   594  	entries, err := ioutil.ReadDir(wd + "/mnt/renamed")
   595  	if err != nil || len(entries) != 1 || entries[0].Name() != "subdir" {
   596  		t.Errorf("readdir(%s/mnt/renamed) should have one entry: %v, err %v", wd, entries, err)
   597  	}
   598  
   599  	if err = os.Mkdir(wd+"/mnt/dir", 0755); err != nil {
   600  		t.Errorf("mkdir should succeed %v", err)
   601  	}
   602  }
   603  
   604  func TestUnionFsRenameDirAllSourcesGone(t *testing.T) {
   605  	wd, clean := setupUfs(t)
   606  	defer clean()
   607  
   608  	err := os.MkdirAll(wd+"/ro/dir", 0755)
   609  	if err != nil {
   610  		t.Fatalf("MkdirAll: %v", err)
   611  	}
   612  
   613  	err = ioutil.WriteFile(wd+"/ro/dir/file.txt", []byte{42}, 0644)
   614  	if err != nil {
   615  		t.Fatalf("WriteFile: %v", err)
   616  	}
   617  
   618  	setRecursiveWritable(t, wd+"/ro", false)
   619  	err = os.Rename(wd+"/mnt/dir", wd+"/mnt/renamed")
   620  	if err != nil {
   621  		t.Fatalf("Rename: %v", err)
   622  	}
   623  
   624  	names := dirNames(t, wd+"/rw/"+testOpts.DeletionDirName)
   625  	if len(names) != 2 {
   626  		t.Errorf("Expected 2 entries in %v", names)
   627  	}
   628  }
   629  
   630  func TestUnionFsRenameDirWithDeletions(t *testing.T) {
   631  	wd, clean := setupUfs(t)
   632  	defer clean()
   633  
   634  	err := os.MkdirAll(wd+"/ro/dir/subdir", 0755)
   635  	if err != nil {
   636  		t.Fatalf("MkdirAll: %v", err)
   637  	}
   638  
   639  	err = ioutil.WriteFile(wd+"/ro/dir/file.txt", []byte{42}, 0644)
   640  	if err != nil {
   641  		t.Fatalf("WriteFile: %v", err)
   642  	}
   643  
   644  	err = ioutil.WriteFile(wd+"/ro/dir/subdir/file.txt", []byte{42}, 0644)
   645  	if err != nil {
   646  		t.Fatalf("WriteFile: %v", err)
   647  	}
   648  	setRecursiveWritable(t, wd+"/ro", false)
   649  
   650  	if fi, _ := os.Lstat(wd + "/mnt/dir/subdir/file.txt"); fi == nil || fi.Mode()&os.ModeType != 0 {
   651  		t.Fatalf("%s/mnt/dir/subdir/file.txt should be file: %v", wd, fi)
   652  	}
   653  
   654  	err = os.Remove(wd + "/mnt/dir/file.txt")
   655  	if err != nil {
   656  		t.Fatalf("Remove: %v", err)
   657  	}
   658  
   659  	err = os.Rename(wd+"/mnt/dir", wd+"/mnt/renamed")
   660  	if err != nil {
   661  		t.Fatalf("Rename: %v", err)
   662  	}
   663  
   664  	if fi, _ := os.Lstat(wd + "/mnt/dir/subdir/file.txt"); fi != nil {
   665  		t.Fatalf("%s/mnt/dir/subdir/file.txt should have disappeared: %v", wd, fi)
   666  	}
   667  
   668  	if fi, _ := os.Lstat(wd + "/mnt/dir"); fi != nil {
   669  		t.Fatalf("%s/mnt/dir should have disappeared: %v", wd, fi)
   670  	}
   671  
   672  	if fi, _ := os.Lstat(wd + "/mnt/renamed"); fi == nil || !fi.IsDir() {
   673  		t.Fatalf("%s/mnt/renamed should be directory: %v", wd, fi)
   674  	}
   675  
   676  	if fi, _ := os.Lstat(wd + "/mnt/renamed/file.txt"); fi != nil {
   677  		t.Fatalf("%s/mnt/renamed/file.txt should have disappeared %#v", wd, fi)
   678  	}
   679  
   680  	if err = os.Mkdir(wd+"/mnt/dir", 0755); err != nil {
   681  		t.Errorf("mkdir should succeed %v", err)
   682  	}
   683  
   684  	if fi, _ := os.Lstat(wd + "/mnt/dir/subdir"); fi != nil {
   685  		t.Fatalf("%s/mnt/dir/subdir should have disappeared %#v", wd, fi)
   686  	}
   687  }
   688  
   689  func TestUnionFsRenameSymlink(t *testing.T) {
   690  	wd, clean := setupUfs(t)
   691  	defer clean()
   692  
   693  	err := os.Symlink("linktarget", wd+"/ro/link")
   694  	if err != nil {
   695  		t.Fatalf("Symlink: %v", err)
   696  	}
   697  
   698  	err = os.Rename(wd+"/mnt/link", wd+"/mnt/renamed")
   699  	if err != nil {
   700  		t.Fatalf("Rename: %v", err)
   701  	}
   702  
   703  	if fi, _ := os.Lstat(wd + "/mnt/link"); fi != nil {
   704  		t.Fatalf("%s/mnt/link should have disappeared: %v", wd, fi)
   705  	}
   706  
   707  	if fi, _ := os.Lstat(wd + "/mnt/renamed"); fi == nil || fi.Mode()&os.ModeSymlink == 0 {
   708  		t.Fatalf("%s/mnt/renamed should be link: %v", wd, fi)
   709  	}
   710  
   711  	if link, err := os.Readlink(wd + "/mnt/renamed"); err != nil || link != "linktarget" {
   712  		t.Fatalf("readlink(%s/mnt/renamed) should point to 'linktarget': %v, err %v", wd, link, err)
   713  	}
   714  }
   715  
   716  func TestUnionFsWritableDir(t *testing.T) {
   717  	wd, clean := setupUfs(t)
   718  	defer clean()
   719  
   720  	dirname := wd + "/ro/subdir"
   721  	err := os.Mkdir(dirname, 0555)
   722  	if err != nil {
   723  		t.Fatalf("Mkdir: %v", err)
   724  	}
   725  	setRecursiveWritable(t, wd+"/ro", false)
   726  
   727  	fi, err := os.Lstat(wd + "/mnt/subdir")
   728  	if err != nil {
   729  		t.Fatalf("Lstat: %v", err)
   730  	}
   731  	if fi.Mode().Perm()&0222 == 0 {
   732  		t.Errorf("unexpected permission %o", fi.Mode().Perm())
   733  	}
   734  }
   735  
   736  func TestUnionFsWriteAccess(t *testing.T) {
   737  	wd, clean := setupUfs(t)
   738  	defer clean()
   739  
   740  	fn := wd + "/ro/file"
   741  	// No write perms.
   742  	err := ioutil.WriteFile(fn, []byte("foo"), 0444)
   743  	if err != nil {
   744  		t.Fatalf("WriteFile: %v", err)
   745  	}
   746  	setRecursiveWritable(t, wd+"/ro", false)
   747  
   748  	err = syscall.Access(wd+"/mnt/file", fuse.W_OK)
   749  	if err != nil {
   750  		if err != nil {
   751  			t.Fatalf("Access: %v", err)
   752  		}
   753  	}
   754  }
   755  
   756  func TestUnionFsLink(t *testing.T) {
   757  	wd, clean := setupUfs(t)
   758  	defer clean()
   759  
   760  	content := "blabla"
   761  	fn := wd + "/ro/file"
   762  	err := ioutil.WriteFile(fn, []byte(content), 0666)
   763  	if err != nil {
   764  		t.Fatalf("WriteFile: %v", err)
   765  	}
   766  	setRecursiveWritable(t, wd+"/ro", false)
   767  
   768  	err = os.Link(wd+"/mnt/file", wd+"/mnt/linked")
   769  	if err != nil {
   770  		t.Fatalf("Link: %v", err)
   771  	}
   772  
   773  	fi2, err := os.Lstat(wd + "/mnt/linked")
   774  	if err != nil {
   775  		t.Fatalf("Lstat: %v", err)
   776  	}
   777  
   778  	fi1, err := os.Lstat(wd + "/mnt/file")
   779  	if err != nil {
   780  		t.Fatalf("Lstat: %v", err)
   781  	}
   782  
   783  	s1 := fuse.ToStatT(fi1)
   784  	s2 := fuse.ToStatT(fi2)
   785  	if s1.Ino != s2.Ino {
   786  		t.Errorf("inode numbers should be equal for linked files %v, %v", s1.Ino, s2.Ino)
   787  	}
   788  	c, err := ioutil.ReadFile(wd + "/mnt/linked")
   789  	if string(c) != content {
   790  		t.Errorf("content mismatch got %q want %q", string(c), content)
   791  	}
   792  }
   793  
   794  func TestUnionFsTruncate(t *testing.T) {
   795  	wd, clean := setupUfs(t)
   796  	defer clean()
   797  
   798  	WriteFile(t, wd+"/ro/file", "hello")
   799  	setRecursiveWritable(t, wd+"/ro", false)
   800  
   801  	os.Truncate(wd+"/mnt/file", 2)
   802  	content := readFromFile(t, wd+"/mnt/file")
   803  	if content != "he" {
   804  		t.Errorf("unexpected content %v", content)
   805  	}
   806  	content2 := readFromFile(t, wd+"/rw/file")
   807  	if content2 != content {
   808  		t.Errorf("unexpected rw content %v", content2)
   809  	}
   810  }
   811  
   812  func TestUnionFsCopyChmod(t *testing.T) {
   813  	wd, clean := setupUfs(t)
   814  	defer clean()
   815  
   816  	contents := "hello"
   817  	fn := wd + "/mnt/y"
   818  	err := ioutil.WriteFile(fn, []byte(contents), 0644)
   819  	if err != nil {
   820  		t.Fatalf("WriteFile(%v): %v", fn, err)
   821  	}
   822  
   823  	err = os.Chmod(fn, 0755)
   824  	if err != nil {
   825  		t.Fatalf("Chmod(%v): %v", fn, err)
   826  	}
   827  
   828  	fi, err := os.Lstat(fn)
   829  	if err != nil {
   830  		t.Fatalf("Lstat(%v): %v", fn, err)
   831  	}
   832  	if fi.Mode()&0111 == 0 {
   833  		t.Errorf("Lstat(%v): got mode %o, want some +x bit", fn, fi.Mode())
   834  	}
   835  	time.Sleep(entryTTL)
   836  	fi, err = os.Lstat(fn)
   837  	if err != nil {
   838  		t.Fatalf("Lstat(%v) after sleep: %v", fn, err)
   839  	}
   840  	if fi.Mode()&0111 == 0 {
   841  		t.Errorf("Lstat(%v) after sleep: mode %o", fn, fi.Mode())
   842  	}
   843  }
   844  
   845  func abs(dt int64) int64 {
   846  	if dt >= 0 {
   847  		return dt
   848  	}
   849  	return -dt
   850  }
   851  
   852  func TestUnionFsTruncateTimestamp(t *testing.T) {
   853  	wd, clean := setupUfs(t)
   854  	defer clean()
   855  
   856  	contents := "hello"
   857  	fn := wd + "/mnt/y"
   858  	err := ioutil.WriteFile(fn, []byte(contents), 0644)
   859  	if err != nil {
   860  		t.Fatalf("WriteFile(%v): %v", fn, err)
   861  	}
   862  	time.Sleep(200 * time.Millisecond)
   863  
   864  	truncTs := time.Now()
   865  	err = os.Truncate(fn, 3)
   866  	if err != nil {
   867  		t.Fatalf("Truncate(%v): %v", fn, err)
   868  	}
   869  
   870  	fi, err := os.Lstat(fn)
   871  	if err != nil {
   872  		t.Fatalf("Lstat(%v): %v", fn, err)
   873  	}
   874  
   875  	if truncTs.Sub(fi.ModTime()) > 100*time.Millisecond {
   876  		t.Errorf("after Truncate: got TS %v, want %v", fi.ModTime(), truncTs)
   877  	}
   878  }
   879  
   880  func TestUnionFsRemoveAll(t *testing.T) {
   881  	wd, clean := setupUfs(t)
   882  	defer clean()
   883  
   884  	err := os.MkdirAll(wd+"/ro/dir/subdir", 0755)
   885  	if err != nil {
   886  		t.Fatalf("MkdirAll: %v", err)
   887  	}
   888  
   889  	contents := "hello"
   890  	fn := wd + "/ro/dir/subdir/y"
   891  	err = ioutil.WriteFile(fn, []byte(contents), 0644)
   892  	if err != nil {
   893  		t.Fatalf("WriteFile: %v", err)
   894  	}
   895  	setRecursiveWritable(t, wd+"/ro", false)
   896  
   897  	err = os.RemoveAll(wd + "/mnt/dir")
   898  	if err != nil {
   899  		t.Error("Should delete all")
   900  	}
   901  
   902  	for _, f := range []string{"dir/subdir/y", "dir/subdir", "dir"} {
   903  		if fi, _ := os.Lstat(filepath.Join(wd, "mount", f)); fi != nil {
   904  			t.Errorf("file %s should have disappeared: %v", f, fi)
   905  		}
   906  	}
   907  
   908  	names, err := Readdirnames(wd + "/rw/DELETIONS")
   909  	if err != nil {
   910  		t.Fatalf("Readdirnames: %v", err)
   911  	}
   912  	if len(names) != 3 {
   913  		t.Fatal("unexpected names", names)
   914  	}
   915  }
   916  
   917  func ProgramVersion(bin string) (major, minor int64, err error) {
   918  	cmd := exec.Command(bin, "--version")
   919  	buf := &bytes.Buffer{}
   920  	cmd.Stdout = buf
   921  	if err := cmd.Run(); err != nil {
   922  		return 0, 0, err
   923  	}
   924  	lines := strings.Split(buf.String(), "\n")
   925  	if len(lines) < 1 {
   926  		return 0, 0, fmt.Errorf("no output")
   927  	}
   928  	matches := regexp.MustCompile(".* ([0-9]+)\\.([0-9]+)").FindStringSubmatch(lines[0])
   929  
   930  	if matches == nil {
   931  		return 0, 0, fmt.Errorf("no match for %q", lines[0])
   932  	}
   933  	major, err = strconv.ParseInt(matches[1], 10, 64)
   934  	if err != nil {
   935  		return 0, 0, err
   936  	}
   937  	minor, err = strconv.ParseInt(matches[2], 10, 64)
   938  	if err != nil {
   939  		return 0, 0, err
   940  	}
   941  	return major, minor, nil
   942  }
   943  
   944  func TestUnionFsRmRf(t *testing.T) {
   945  	wd, clean := setupUfs(t)
   946  	defer clean()
   947  
   948  	err := os.MkdirAll(wd+"/ro/dir/subdir", 0755)
   949  	if err != nil {
   950  		t.Fatalf("MkdirAll: %v", err)
   951  	}
   952  
   953  	contents := "hello"
   954  	fn := wd + "/ro/dir/subdir/y"
   955  	err = ioutil.WriteFile(fn, []byte(contents), 0644)
   956  	if err != nil {
   957  		t.Fatalf("WriteFile: %v", err)
   958  	}
   959  	setRecursiveWritable(t, wd+"/ro", false)
   960  
   961  	bin, err := exec.LookPath("rm")
   962  	if err != nil {
   963  		t.Fatalf("LookPath: %v", err)
   964  	}
   965  
   966  	maj, min, err := ProgramVersion(bin)
   967  	if err != nil {
   968  		t.Logf("ProgramVersion: %v", err)
   969  	}
   970  	if maj < 8 { // assuming GNU coreutils.
   971  		t.Skipf("Skipping test; GNU rm %d.%d is not POSIX compliant.", maj, min)
   972  	}
   973  	names, _ := Readdirnames(wd + "/mnt/dir")
   974  	t.Logf("Contents of %s/mnt/dir: %s", wd, strings.Join(names, ", "))
   975  	cmd := exec.Command(bin, "-rf", wd+"/mnt/dir")
   976  	err = cmd.Run()
   977  	if err != nil {
   978  		t.Fatal("rm -rf returned error:", err)
   979  	}
   980  
   981  	for _, f := range []string{"dir/subdir/y", "dir/subdir", "dir"} {
   982  		if fi, _ := os.Lstat(filepath.Join(wd, "mount", f)); fi != nil {
   983  			t.Errorf("file %s should have disappeared: %v", f, fi)
   984  		}
   985  	}
   986  
   987  	names, err = Readdirnames(wd + "/rw/DELETIONS")
   988  	if err != nil {
   989  		t.Fatalf("Readdirnames: %v", err)
   990  	}
   991  	if len(names) != 3 {
   992  		t.Fatal("unexpected names", names)
   993  	}
   994  }
   995  
   996  func Readdirnames(dir string) ([]string, error) {
   997  	f, err := os.Open(dir)
   998  	if err != nil {
   999  		return nil, err
  1000  	}
  1001  
  1002  	defer f.Close()
  1003  	return f.Readdirnames(-1)
  1004  }
  1005  
  1006  func TestUnionFsDropDeletionCache(t *testing.T) {
  1007  	wd, clean := setupUfs(t)
  1008  	defer clean()
  1009  
  1010  	err := ioutil.WriteFile(wd+"/ro/file", []byte("bla"), 0644)
  1011  	if err != nil {
  1012  		t.Fatalf("WriteFile: %v", err)
  1013  	}
  1014  	setRecursiveWritable(t, wd+"/ro", false)
  1015  
  1016  	_, err = os.Lstat(wd + "/mnt/file")
  1017  	if err != nil {
  1018  		t.Fatalf("Lstat: %v", err)
  1019  	}
  1020  	err = os.Remove(wd + "/mnt/file")
  1021  	if err != nil {
  1022  		t.Fatalf("Remove: %v", err)
  1023  	}
  1024  	fi, _ := os.Lstat(wd + "/mnt/file")
  1025  	if fi != nil {
  1026  		t.Fatal("Lstat() should have failed", fi)
  1027  	}
  1028  
  1029  	names, err := Readdirnames(wd + "/rw/DELETIONS")
  1030  	if err != nil {
  1031  		t.Fatalf("Readdirnames: %v", err)
  1032  	}
  1033  	if len(names) != 1 {
  1034  		t.Fatal("unexpected names", names)
  1035  	}
  1036  	os.Remove(wd + "/rw/DELETIONS/" + names[0])
  1037  	fi, _ = os.Lstat(wd + "/mnt/file")
  1038  	if fi != nil {
  1039  		t.Fatal("Lstat() should have failed", fi)
  1040  	}
  1041  
  1042  	// Expire kernel entry.
  1043  	time.Sleep((6 * entryTTL) / 10)
  1044  	err = ioutil.WriteFile(wd+"/mnt/.drop_cache", []byte(""), 0644)
  1045  	if err != nil {
  1046  		t.Fatalf("WriteFile: %v", err)
  1047  	}
  1048  	_, err = os.Lstat(wd + "/mnt/file")
  1049  	if err != nil {
  1050  		t.Fatal("Lstat() should have succeeded", err)
  1051  	}
  1052  }
  1053  
  1054  func TestUnionFsDropCache(t *testing.T) {
  1055  	wd, clean := setupUfs(t)
  1056  	defer clean()
  1057  
  1058  	err := ioutil.WriteFile(wd+"/ro/file", []byte("bla"), 0644)
  1059  	if err != nil {
  1060  		t.Fatalf("WriteFile: %v", err)
  1061  	}
  1062  
  1063  	_, err = os.Lstat(wd + "/mnt/.drop_cache")
  1064  	if err != nil {
  1065  		t.Fatalf("Lstat: %v", err)
  1066  	}
  1067  
  1068  	names, err := Readdirnames(wd + "/mnt")
  1069  	if err != nil {
  1070  		t.Fatalf("Readdirnames: %v", err)
  1071  	}
  1072  	if len(names) != 1 || names[0] != "file" {
  1073  		t.Fatal("unexpected names", names)
  1074  	}
  1075  
  1076  	err = ioutil.WriteFile(wd+"/ro/file2", []byte("blabla"), 0644)
  1077  	names2, err := Readdirnames(wd + "/mnt")
  1078  	if err != nil {
  1079  		t.Fatalf("Readdirnames: %v", err)
  1080  	}
  1081  	if len(names2) != len(names) {
  1082  		t.Fatal("mismatch", names2)
  1083  	}
  1084  
  1085  	err = ioutil.WriteFile(wd+"/mnt/.drop_cache", []byte("does not matter"), 0644)
  1086  	if err != nil {
  1087  		t.Fatalf("WriteFile: %v", err)
  1088  	}
  1089  	names2, err = Readdirnames(wd + "/mnt")
  1090  	if len(names2) != 2 {
  1091  		t.Fatal("mismatch 2", names2)
  1092  	}
  1093  }
  1094  
  1095  type disappearingFS struct {
  1096  	pathfs.FileSystem
  1097  
  1098  	normal      pathfs.FileSystem
  1099  	nop         pathfs.FileSystem
  1100  	visible     bool
  1101  	visibleChan chan bool
  1102  }
  1103  
  1104  func (d *disappearingFS) fs() pathfs.FileSystem {
  1105  	select {
  1106  	case v := <-d.visibleChan:
  1107  		d.visible = v
  1108  		if v {
  1109  			d.FileSystem = d.normal
  1110  		} else {
  1111  			d.FileSystem = d.nop
  1112  		}
  1113  	default:
  1114  	}
  1115  	return d.FileSystem
  1116  }
  1117  
  1118  func (d *disappearingFS) GetAttr(name string, context *fuse.Context) (a *fuse.Attr, s fuse.Status) {
  1119  	return d.fs().GetAttr(name, context)
  1120  }
  1121  
  1122  func (d *disappearingFS) OpenDir(name string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
  1123  	return d.fs().OpenDir(name, context)
  1124  }
  1125  
  1126  func newDisappearingFS(fs, nop pathfs.FileSystem) *disappearingFS {
  1127  	return &disappearingFS{
  1128  		visibleChan: make(chan bool, 1),
  1129  		visible:     true,
  1130  		normal:      fs,
  1131  		nop:         nop,
  1132  		FileSystem:  fs,
  1133  	}
  1134  }
  1135  
  1136  func TestUnionFsDisappearing(t *testing.T) {
  1137  	// This init is like setupUfs, but we want access to the
  1138  	// writable Fs.
  1139  	wd := testutil.TempDir()
  1140  	defer os.RemoveAll(wd)
  1141  	err := os.Mkdir(wd+"/mnt", 0700)
  1142  	if err != nil {
  1143  		t.Fatalf("Mkdir: %v", err)
  1144  	}
  1145  
  1146  	err = os.Mkdir(wd+"/rw", 0700)
  1147  	if err != nil {
  1148  		t.Fatalf("Mkdir: %v", err)
  1149  	}
  1150  
  1151  	os.Mkdir(wd+"/ro", 0700)
  1152  	if err != nil {
  1153  		t.Fatalf("Mkdir: %v", err)
  1154  	}
  1155  
  1156  	wrFs := newDisappearingFS(pathfs.NewLoopbackFileSystem(wd+"/rw"),
  1157  		pathfs.NewLoopbackFileSystem("/dev/null"))
  1158  	var fses []pathfs.FileSystem
  1159  	fses = append(fses, pathfs.NewLockingFileSystem(wrFs))
  1160  	fses = append(fses, pathfs.NewLoopbackFileSystem(wd+"/ro"))
  1161  	ufs, err := NewUnionFs(fses, testOpts)
  1162  	if err != nil {
  1163  		t.Fatalf("NewUnionFs: %v", err)
  1164  	}
  1165  
  1166  	opts := &nodefs.Options{
  1167  		EntryTimeout:        entryTTL,
  1168  		AttrTimeout:         entryTTL,
  1169  		NegativeTimeout:     entryTTL,
  1170  		Debug:               testutil.VerboseTest(),
  1171  		LookupKnownChildren: true,
  1172  	}
  1173  
  1174  	nfs := pathfs.NewPathNodeFs(ufs, nil)
  1175  	state, _, err := nodefs.MountRoot(wd+"/mnt", nfs.Root(), opts)
  1176  	if err != nil {
  1177  		t.Fatalf("MountNodeFileSystem: %v", err)
  1178  	}
  1179  	defer state.Unmount()
  1180  	go state.Serve()
  1181  	state.WaitMount()
  1182  
  1183  	err = ioutil.WriteFile(wd+"/ro/file", []byte("blabla"), 0644)
  1184  	if err != nil {
  1185  		t.Fatalf("WriteFile: %v", err)
  1186  	}
  1187  	setRecursiveWritable(t, wd+"/ro", false)
  1188  
  1189  	err = os.Remove(wd + "/mnt/file")
  1190  	if err != nil {
  1191  		t.Fatalf("Remove: %v", err)
  1192  	}
  1193  
  1194  	wrFs.visibleChan <- false
  1195  	time.Sleep((3 * entryTTL) / 2)
  1196  
  1197  	_, err = ioutil.ReadDir(wd + "/mnt")
  1198  	if err == nil {
  1199  		t.Fatal("Readdir should have failed")
  1200  	}
  1201  
  1202  	err = ioutil.WriteFile(wd+"/mnt/file2", []byte("blabla"), 0644)
  1203  	if err == nil {
  1204  		t.Fatal("write should have failed")
  1205  	}
  1206  
  1207  	// Wait for the caches to purge, and then restore.
  1208  	time.Sleep((3 * entryTTL) / 2)
  1209  	wrFs.visibleChan <- true
  1210  
  1211  	_, err = ioutil.ReadDir(wd + "/mnt")
  1212  	if err != nil {
  1213  		t.Fatal("Readdir should succeed", err)
  1214  	}
  1215  	err = ioutil.WriteFile(wd+"/mnt/file2", []byte("blabla"), 0644)
  1216  	if err != nil {
  1217  		t.Fatal("write should succeed", err)
  1218  	}
  1219  }
  1220  
  1221  func TestUnionFsDeletedGetAttr(t *testing.T) {
  1222  	wd, clean := setupUfs(t)
  1223  	defer clean()
  1224  
  1225  	err := ioutil.WriteFile(wd+"/ro/file", []byte("blabla"), 0644)
  1226  	if err != nil {
  1227  		t.Fatalf("WriteFile: %v", err)
  1228  	}
  1229  	setRecursiveWritable(t, wd+"/ro", false)
  1230  
  1231  	f, err := os.Open(wd + "/mnt/file")
  1232  	if err != nil {
  1233  		t.Fatalf("Open: %v", err)
  1234  	}
  1235  	defer f.Close()
  1236  
  1237  	err = os.Remove(wd + "/mnt/file")
  1238  	if err != nil {
  1239  		t.Fatalf("Remove: %v", err)
  1240  	}
  1241  
  1242  	if fi, err := f.Stat(); err != nil || fi.Mode()&os.ModeType != 0 {
  1243  		t.Fatalf("stat returned error or non-file: %v %v", err, fi)
  1244  	}
  1245  }
  1246  
  1247  func TestUnionFsDoubleOpen(t *testing.T) {
  1248  	wd, clean := setupUfs(t)
  1249  	defer clean()
  1250  	err := ioutil.WriteFile(wd+"/ro/file", []byte("blablabla"), 0644)
  1251  	if err != nil {
  1252  		t.Fatalf("WriteFile: %v", err)
  1253  	}
  1254  	setRecursiveWritable(t, wd+"/ro", false)
  1255  
  1256  	roFile, err := os.Open(wd + "/mnt/file")
  1257  	if err != nil {
  1258  		t.Fatalf("Open: %v", err)
  1259  	}
  1260  	defer roFile.Close()
  1261  	rwFile, err := os.OpenFile(wd+"/mnt/file", os.O_WRONLY|os.O_TRUNC, 0666)
  1262  	if err != nil {
  1263  		t.Fatalf("OpenFile: %v", err)
  1264  	}
  1265  	defer rwFile.Close()
  1266  
  1267  	output, err := ioutil.ReadAll(roFile)
  1268  	if err != nil {
  1269  		t.Fatalf("ReadAll: %v", err)
  1270  	}
  1271  	if len(output) != 0 {
  1272  		t.Errorf("After r/w truncation, r/o file should be empty too: %q", string(output))
  1273  	}
  1274  
  1275  	want := "hello"
  1276  	_, err = rwFile.Write([]byte(want))
  1277  	if err != nil {
  1278  		t.Fatalf("Write: %v", err)
  1279  	}
  1280  
  1281  	b := make([]byte, 100)
  1282  
  1283  	roFile.Seek(0, 0)
  1284  	n, err := roFile.Read(b)
  1285  	if err != nil {
  1286  		t.Fatalf("Read: %v", err)
  1287  	}
  1288  	b = b[:n]
  1289  
  1290  	if string(b) != "hello" {
  1291  		t.Errorf("r/w and r/o file are not synchronized: got %q want %q", string(b), want)
  1292  	}
  1293  }
  1294  
  1295  func TestUnionFsStatFs(t *testing.T) {
  1296  	wd, clean := setupUfs(t)
  1297  	defer clean()
  1298  
  1299  	s1 := syscall.Statfs_t{}
  1300  	err := syscall.Statfs(wd+"/mnt", &s1)
  1301  	if err != nil {
  1302  		t.Fatal("statfs mnt", err)
  1303  	}
  1304  	if s1.Bsize == 0 {
  1305  		t.Fatal("Expect blocksize > 0")
  1306  	}
  1307  }
  1308  
  1309  func TestUnionFsFlushSize(t *testing.T) {
  1310  	wd, clean := setupUfs(t)
  1311  	defer clean()
  1312  
  1313  	fn := wd + "/mnt/file"
  1314  	f, err := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE, 0644)
  1315  	if err != nil {
  1316  		t.Fatalf("OpenFile: %v", err)
  1317  	}
  1318  	fi, err := f.Stat()
  1319  	if err != nil {
  1320  		t.Fatalf("Stat: %v", err)
  1321  	}
  1322  
  1323  	n, err := f.Write([]byte("hello"))
  1324  	if err != nil {
  1325  		t.Fatalf("Write: %v", err)
  1326  	}
  1327  
  1328  	f.Close()
  1329  	fi, err = os.Lstat(fn)
  1330  	if err != nil {
  1331  		t.Fatalf("Lstat: %v", err)
  1332  	}
  1333  	if fi.Size() != int64(n) {
  1334  		t.Errorf("got %d from Stat().Size, want %d", fi.Size(), n)
  1335  	}
  1336  }
  1337  
  1338  func TestUnionFsFlushRename(t *testing.T) {
  1339  	wd, clean := setupUfs(t)
  1340  	defer clean()
  1341  
  1342  	err := ioutil.WriteFile(wd+"/mnt/file", []byte("x"), 0644)
  1343  
  1344  	fn := wd + "/mnt/tmp"
  1345  	f, err := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE, 0644)
  1346  	if err != nil {
  1347  		t.Fatalf("OpenFile: %v", err)
  1348  	}
  1349  	fi, err := f.Stat()
  1350  	if err != nil {
  1351  		t.Fatalf("Stat: %v", err)
  1352  	}
  1353  
  1354  	n, err := f.Write([]byte("hello"))
  1355  	if err != nil {
  1356  		t.Fatalf("Write: %v", err)
  1357  	}
  1358  	f.Close()
  1359  
  1360  	dst := wd + "/mnt/file"
  1361  	err = os.Rename(fn, dst)
  1362  	if err != nil {
  1363  		t.Fatalf("Rename: %v", err)
  1364  	}
  1365  
  1366  	fi, err = os.Lstat(dst)
  1367  	if err != nil {
  1368  		t.Fatalf("Lstat: %v", err)
  1369  	}
  1370  	if fi.Size() != int64(n) {
  1371  		t.Errorf("got %d from Stat().Size, want %d", fi.Size(), n)
  1372  	}
  1373  }
  1374  
  1375  func TestUnionFsTruncGetAttr(t *testing.T) {
  1376  	wd, clean := setupUfs(t)
  1377  	defer clean()
  1378  
  1379  	c := []byte("hello")
  1380  	f, err := os.OpenFile(wd+"/mnt/file", os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
  1381  	if err != nil {
  1382  		t.Fatalf("OpenFile: %v", err)
  1383  	}
  1384  	_, err = f.Write(c)
  1385  	if err != nil {
  1386  		t.Fatalf("Write: %v", err)
  1387  	}
  1388  	err = f.Close()
  1389  	if err != nil {
  1390  		t.Fatalf("Close: %v", err)
  1391  	}
  1392  
  1393  	fi, err := os.Lstat(wd + "/mnt/file")
  1394  	if fi.Size() != int64(len(c)) {
  1395  		t.Fatalf("Length mismatch got %d want %d", fi.Size(), len(c))
  1396  	}
  1397  }
  1398  
  1399  func TestUnionFsPromoteDirTimeStamp(t *testing.T) {
  1400  	wd, clean := setupUfs(t)
  1401  	defer clean()
  1402  
  1403  	err := os.Mkdir(wd+"/ro/subdir", 0750)
  1404  	if err != nil {
  1405  		t.Fatalf("Mkdir: %v", err)
  1406  	}
  1407  	err = ioutil.WriteFile(wd+"/ro/subdir/file", []byte("hello"), 0644)
  1408  	if err != nil {
  1409  		t.Fatalf("WriteFile: %v", err)
  1410  	}
  1411  	setRecursiveWritable(t, wd+"/ro", false)
  1412  
  1413  	err = os.Chmod(wd+"/mnt/subdir/file", 0060)
  1414  	if err != nil {
  1415  		t.Fatalf("Chmod: %v", err)
  1416  	}
  1417  
  1418  	fRo, err := os.Lstat(wd + "/ro/subdir")
  1419  	if err != nil {
  1420  		t.Fatalf("Lstat: %v", err)
  1421  	}
  1422  	fRw, err := os.Lstat(wd + "/rw/subdir")
  1423  	if err != nil {
  1424  		t.Fatalf("Lstat: %v", err)
  1425  	}
  1426  
  1427  	// TODO - need to update timestamps after promoteDirsTo calls,
  1428  	// not during.
  1429  	if false && fRo.ModTime().Equal(fRw.ModTime()) {
  1430  		t.Errorf("Changed timestamps on promoted subdir: ro %v rw %v", fRo.ModTime(), fRw.ModTime())
  1431  	}
  1432  
  1433  	if fRo.Mode().Perm()|0200 != fRw.Mode().Perm() {
  1434  		t.Errorf("Changed mode ro: %v, rw: %v", fRo.Mode(), fRw.Mode())
  1435  	}
  1436  }
  1437  
  1438  func TestUnionFsCheckHiddenFiles(t *testing.T) {
  1439  	wd, clean := setupUfs(t)
  1440  	defer clean()
  1441  
  1442  	err := ioutil.WriteFile(wd+"/ro/hidden", []byte("bla"), 0644)
  1443  	if err != nil {
  1444  		t.Fatalf("WriteFile: %v", err)
  1445  	}
  1446  	err = ioutil.WriteFile(wd+"/ro/not_hidden", []byte("bla"), 0644)
  1447  	if err != nil {
  1448  		t.Fatalf("WriteFile: %v", err)
  1449  	}
  1450  	setRecursiveWritable(t, wd+"/ro", false)
  1451  
  1452  	fi, _ := os.Lstat(wd + "/mnt/hidden")
  1453  	if fi != nil {
  1454  		t.Fatal("Lstat() should have failed", fi)
  1455  	}
  1456  	_, err = os.Lstat(wd + "/mnt/not_hidden")
  1457  	if err != nil {
  1458  		t.Fatalf("Lstat: %v", err)
  1459  	}
  1460  
  1461  	names, err := Readdirnames(wd + "/mnt")
  1462  	if err != nil {
  1463  		t.Fatalf("Readdirnames: %v", err)
  1464  	}
  1465  	if len(names) != 1 || names[0] != "not_hidden" {
  1466  		t.Fatal("unexpected names", names)
  1467  	}
  1468  }
  1469  
  1470  func TestUnionFSBarf(t *testing.T) {
  1471  	wd, clean := setupUfs(t)
  1472  	defer clean()
  1473  
  1474  	if err := os.Mkdir(wd+"/mnt/dir", 0755); err != nil {
  1475  		t.Fatalf("os.Mkdir: %v", err)
  1476  	}
  1477  	if err := os.Mkdir(wd+"/mnt/dir2", 0755); err != nil {
  1478  		t.Fatalf("os.Mkdir: %v", err)
  1479  	}
  1480  	if err := ioutil.WriteFile(wd+"/rw/dir/file", []byte("bla"), 0644); err != nil {
  1481  		t.Fatalf("WriteFile: %v", err)
  1482  	}
  1483  	if _, err := os.Lstat(wd + "/mnt/dir/file"); err != nil {
  1484  		t.Fatalf("Lstat: %v", err)
  1485  	}
  1486  	if err := os.Rename(wd+"/rw/dir/file", wd+"/rw/file"); err != nil {
  1487  		t.Fatalf("os.Rename: %v", err)
  1488  	}
  1489  	if err := os.Rename(wd+"/mnt/file", wd+"/mnt/dir2/file"); err != nil {
  1490  		t.Fatalf("os.Rename: %v", err)
  1491  	}
  1492  }