github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libfs/fs_test.go (about)

     1  // Copyright 2017 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libfs
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"os"
    11  	"path"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/keybase/client/go/kbfs/data"
    16  	"github.com/keybase/client/go/kbfs/ioutil"
    17  	"github.com/keybase/client/go/kbfs/kbfsmd"
    18  	"github.com/keybase/client/go/kbfs/libcontext"
    19  	"github.com/keybase/client/go/kbfs/libkbfs"
    20  	"github.com/keybase/client/go/kbfs/test/clocktest"
    21  	"github.com/keybase/client/go/kbfs/tlf"
    22  	"github.com/keybase/client/go/kbfs/tlfhandle"
    23  	"github.com/keybase/client/go/protocol/keybase1"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  	billy "gopkg.in/src-d/go-billy.v4"
    27  )
    28  
    29  func makeFSWithBranch(t *testing.T, branch data.BranchName, subdir string) (
    30  	context.Context, *tlfhandle.Handle, *FS) {
    31  	ctx := libcontext.BackgroundContextWithCancellationDelayer()
    32  	config := libkbfs.MakeTestConfigOrBust(t, "user1", "user2")
    33  	h, err := tlfhandle.ParseHandle(
    34  		ctx, config.KBPKI(), config.MDOps(), nil, "user1", tlf.Private)
    35  	require.NoError(t, err)
    36  	fs, err := NewFS(
    37  		ctx, config, h, branch, subdir, "", keybase1.MDPriorityNormal)
    38  	require.NoError(t, err)
    39  	return ctx, h, fs
    40  }
    41  
    42  func makeFS(t *testing.T, subdir string) (
    43  	context.Context, *tlfhandle.Handle, *FS) {
    44  	return makeFSWithBranch(t, data.MasterBranch, subdir)
    45  }
    46  
    47  func makeFSWithJournal(t *testing.T, subdir string) (
    48  	context.Context, *tlfhandle.Handle, *FS, func()) {
    49  	ctx := libcontext.BackgroundContextWithCancellationDelayer()
    50  	config := libkbfs.MakeTestConfigOrBustLoggedInWithMode(
    51  		t, 0, libkbfs.InitSingleOp, "user1")
    52  
    53  	tempdir, err := os.MkdirTemp(os.TempDir(), "journal_server")
    54  	require.NoError(t, err)
    55  	defer func() {
    56  		if err != nil {
    57  			os.RemoveAll(tempdir)
    58  		}
    59  	}()
    60  	err = config.EnableDiskLimiter(tempdir)
    61  	require.NoError(t, err)
    62  	err = config.EnableJournaling(
    63  		ctx, tempdir, libkbfs.TLFJournalSingleOpBackgroundWorkEnabled)
    64  	require.NoError(t, err)
    65  	shutdown := func() {
    66  		libkbfs.CheckConfigAndShutdown(ctx, t, config)
    67  		err := ioutil.RemoveAll(tempdir)
    68  		assert.NoError(t, err)
    69  	}
    70  
    71  	h, err := tlfhandle.ParseHandle(
    72  		ctx, config.KBPKI(), config.MDOps(), nil, "user1", tlf.Private)
    73  	require.NoError(t, err)
    74  	fs, err := NewFS(
    75  		ctx, config, h, data.MasterBranch, subdir, "",
    76  		keybase1.MDPriorityNormal)
    77  	require.NoError(t, err)
    78  
    79  	return ctx, h, fs, shutdown
    80  }
    81  
    82  func testCreateFile(
    83  	ctx context.Context, t *testing.T, fs *FS, file string,
    84  	parent libkbfs.Node) {
    85  	f, err := fs.Create(file)
    86  	require.NoError(t, err)
    87  	require.Equal(t, file, f.Name())
    88  
    89  	children, err := fs.config.KBFSOps().GetDirChildren(ctx, parent)
    90  	require.NoError(t, err)
    91  	require.Contains(t, children, testPPS(path.Base(file)))
    92  
    93  	// Write to the file.
    94  	data := []byte{1}
    95  	n, err := f.Write(data)
    96  	require.NoError(t, err)
    97  	require.Equal(t, 1, n)
    98  
    99  	err = f.Close()
   100  	require.NoError(t, err)
   101  
   102  	// Re-open and read the file.
   103  	f, err = fs.Open(file)
   104  	require.NoError(t, err)
   105  	gotData := make([]byte, len(data))
   106  	n, err = f.Read(gotData)
   107  	require.NoError(t, err)
   108  	require.Equal(t, len(data), n)
   109  	require.True(t, bytes.Equal(data, gotData))
   110  
   111  	// Shouldn't be able to write to a read-only file.
   112  	_, err = f.Write(gotData)
   113  	require.NotNil(t, err)
   114  
   115  	err = f.Close()
   116  	require.NoError(t, err)
   117  
   118  	err = fs.SyncAll()
   119  	require.NoError(t, err)
   120  }
   121  
   122  func TestCreateFileInRoot(t *testing.T) {
   123  	ctx, h, fs := makeFS(t, "")
   124  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   125  
   126  	rootNode, _, err := fs.config.KBFSOps().GetRootNode(
   127  		ctx, h, data.MasterBranch)
   128  	require.NoError(t, err)
   129  
   130  	testCreateFile(ctx, t, fs, "foo", rootNode)
   131  	testCreateFile(ctx, t, fs, "/bar", rootNode)
   132  }
   133  
   134  func testPPS(s string) data.PathPartString {
   135  	return data.NewPathPartString(s, nil)
   136  }
   137  
   138  func TestCreateFileInSubdir(t *testing.T) {
   139  	ctx, h, fs := makeFS(t, "")
   140  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   141  
   142  	rootNode, _, err := fs.config.KBFSOps().GetRootNode(
   143  		ctx, h, data.MasterBranch)
   144  	require.NoError(t, err)
   145  	aNode, _, err := fs.config.KBFSOps().CreateDir(ctx, rootNode, testPPS("a"))
   146  	require.NoError(t, err)
   147  	bNode, _, err := fs.config.KBFSOps().CreateDir(ctx, aNode, testPPS("b"))
   148  	require.NoError(t, err)
   149  
   150  	testCreateFile(ctx, t, fs, "a/b/foo", bNode)
   151  }
   152  
   153  func TestCreateFileInMissingSubdir(t *testing.T) {
   154  	ctx, _, fs := makeFS(t, "")
   155  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   156  
   157  	f, err := fs.Create("a/b/foo")
   158  	require.NoError(t, err)
   159  	require.Equal(t, "a/b/foo", f.Name())
   160  
   161  	_, err = fs.Lstat("a")
   162  	require.NoError(t, err)
   163  	_, err = fs.Lstat("a/b")
   164  	require.NoError(t, err)
   165  }
   166  
   167  func TestAppendFile(t *testing.T) {
   168  	ctx, h, fs := makeFS(t, "")
   169  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   170  
   171  	rootNode, _, err := fs.config.KBFSOps().GetRootNode(
   172  		ctx, h, data.MasterBranch)
   173  	require.NoError(t, err)
   174  
   175  	testCreateFile(ctx, t, fs, "foo", rootNode)
   176  	f, err := fs.OpenFile("foo", os.O_APPEND, 0600)
   177  	require.NoError(t, err)
   178  
   179  	// Append one byte to the file.
   180  	data := []byte{2}
   181  	n, err := f.Write(data)
   182  	require.NoError(t, err)
   183  	require.Equal(t, 1, n)
   184  
   185  	err = f.Close()
   186  	require.NoError(t, err)
   187  
   188  	// Re-open and read the file.
   189  	f, err = fs.Open("foo")
   190  	require.NoError(t, err)
   191  	gotData := make([]byte, 2)
   192  	n, err = f.Read(gotData)
   193  	require.NoError(t, err)
   194  	require.Equal(t, len(gotData), n)
   195  
   196  	err = f.Close()
   197  	require.NoError(t, err)
   198  
   199  	err = fs.SyncAll()
   200  	require.NoError(t, err)
   201  }
   202  
   203  func TestRecreateAndExcl(t *testing.T) {
   204  	ctx, h, fs := makeFS(t, "")
   205  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   206  
   207  	rootNode, _, err := fs.config.KBFSOps().GetRootNode(
   208  		ctx, h, data.MasterBranch)
   209  	require.NoError(t, err)
   210  
   211  	testCreateFile(ctx, t, fs, "foo", rootNode)
   212  
   213  	// Re-create the same file.
   214  	f, err := fs.Create("foo")
   215  	require.NoError(t, err)
   216  	err = f.Close()
   217  	require.NoError(t, err)
   218  
   219  	// Try to create it with EXCL, and fail.
   220  	_, err = fs.OpenFile("foo", os.O_CREATE|os.O_EXCL, 0600)
   221  	require.NotNil(t, err)
   222  
   223  	// Creating a different file exclusively should work though.
   224  	f, err = fs.OpenFile("foo2", os.O_CREATE|os.O_EXCL, 0600)
   225  	require.NoError(t, err)
   226  	err = f.Close()
   227  	require.NoError(t, err)
   228  	err = fs.SyncAll()
   229  	require.NoError(t, err)
   230  }
   231  
   232  func TestStat(t *testing.T) {
   233  	ctx, h, fs := makeFS(t, "")
   234  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   235  
   236  	clock := &clocktest.TestClock{}
   237  	clock.Set(time.Now())
   238  	fs.config.SetClock(clock)
   239  
   240  	rootNode, _, err := fs.config.KBFSOps().GetRootNode(
   241  		ctx, h, data.MasterBranch)
   242  	require.NoError(t, err)
   243  	aNode, _, err := fs.config.KBFSOps().CreateDir(ctx, rootNode, testPPS("a"))
   244  	require.NoError(t, err)
   245  	testCreateFile(ctx, t, fs, "a/foo", aNode)
   246  
   247  	// Check the dir
   248  	fi, err := fs.Stat("a")
   249  	require.NoError(t, err)
   250  	checkDir := func(fi os.FileInfo, isWriter bool) {
   251  		require.Equal(t, "a", fi.Name())
   252  		// Not sure exactly what the dir size should be.
   253  		require.True(t, fi.Size() > 0)
   254  		expectedMode := os.FileMode(0500) | os.ModeDir
   255  		if isWriter {
   256  			expectedMode |= 0200
   257  		}
   258  		require.Equal(t, expectedMode, fi.Mode())
   259  		require.True(t, clock.Now().Equal(fi.ModTime()))
   260  		require.True(t, fi.IsDir())
   261  	}
   262  	checkDir(fi, true)
   263  
   264  	// Check the file
   265  	fi, err = fs.Stat("a/foo")
   266  	require.NoError(t, err)
   267  	checkFile := func(fi os.FileInfo, isWriter bool) {
   268  		require.Equal(t, "foo", fi.Name())
   269  		require.Equal(t, int64(1), fi.Size())
   270  		expectedMode := os.FileMode(0400)
   271  		if isWriter {
   272  			expectedMode |= 0200
   273  		}
   274  		require.Equal(t, expectedMode, fi.Mode())
   275  		require.True(t, clock.Now().Equal(fi.ModTime()))
   276  		require.False(t, fi.IsDir())
   277  	}
   278  	checkFile(fi, true)
   279  
   280  	// Try a read-only file.
   281  	config2 := libkbfs.ConfigAsUser(fs.config.(*libkbfs.ConfigLocal), "user2")
   282  	defer libkbfs.CheckConfigAndShutdown(ctx, t, config2)
   283  	config2.SetClock(clock)
   284  
   285  	h2, err := tlfhandle.ParseHandle(
   286  		ctx, config2.KBPKI(), config2.MDOps(), nil, "user2#user1", tlf.Private)
   287  	require.NoError(t, err)
   288  	fs2U2, err := NewFS(
   289  		ctx, config2, h2, data.MasterBranch, "", "",
   290  		keybase1.MDPriorityNormal)
   291  	require.NoError(t, err)
   292  	rootNode2, _, err := fs2U2.config.KBFSOps().GetRootNode(
   293  		ctx, h2, data.MasterBranch)
   294  	require.NoError(t, err)
   295  	aNode2, _, err := fs2U2.config.KBFSOps().CreateDir(
   296  		ctx, rootNode2, testPPS("a"))
   297  	require.NoError(t, err)
   298  	testCreateFile(ctx, t, fs2U2, "a/foo", aNode2)
   299  
   300  	// Read as the reader.
   301  	fs2U1, err := NewFS(
   302  		ctx, fs.config, h2, data.MasterBranch, "", "",
   303  		keybase1.MDPriorityNormal)
   304  	require.NoError(t, err)
   305  
   306  	fi, err = fs2U1.Stat("a")
   307  	require.NoError(t, err)
   308  	checkDir(fi, false)
   309  
   310  	fi, err = fs2U1.Stat("a/foo")
   311  	require.NoError(t, err)
   312  	checkFile(fi, false)
   313  }
   314  
   315  func TestRename(t *testing.T) {
   316  	ctx, h, fs := makeFS(t, "")
   317  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   318  
   319  	rootNode, _, err := fs.config.KBFSOps().GetRootNode(
   320  		ctx, h, data.MasterBranch)
   321  	require.NoError(t, err)
   322  	testCreateFile(ctx, t, fs, "foo", rootNode)
   323  	err = fs.MkdirAll("a/b", os.FileMode(0600))
   324  	require.NoError(t, err)
   325  
   326  	f, err := fs.Open("foo")
   327  	require.NoError(t, err)
   328  	gotDataFoo := make([]byte, 1)
   329  	_, err = f.Read(gotDataFoo)
   330  	require.NoError(t, err)
   331  	err = f.Close()
   332  	require.NoError(t, err)
   333  
   334  	err = fs.Rename("foo", "a/b/bar")
   335  	require.NoError(t, err)
   336  
   337  	f, err = fs.Open("a/b/bar")
   338  	require.NoError(t, err)
   339  	gotDataBar := make([]byte, 1)
   340  	_, err = f.Read(gotDataBar)
   341  	require.NoError(t, err)
   342  	require.True(t, bytes.Equal(gotDataFoo, gotDataBar))
   343  	err = f.Close()
   344  	require.NoError(t, err)
   345  
   346  	_, err = fs.Open("foo")
   347  	require.NotNil(t, err)
   348  
   349  	err = fs.SyncAll()
   350  	require.NoError(t, err)
   351  }
   352  
   353  func TestRemove(t *testing.T) {
   354  	ctx, h, fs := makeFS(t, "")
   355  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   356  
   357  	rootNode, _, err := fs.config.KBFSOps().GetRootNode(
   358  		ctx, h, data.MasterBranch)
   359  	require.NoError(t, err)
   360  	testCreateFile(ctx, t, fs, "foo", rootNode)
   361  	err = fs.MkdirAll("a/b", os.FileMode(0600))
   362  	require.NoError(t, err)
   363  
   364  	// Remove a file.
   365  	err = fs.Remove("foo")
   366  	require.NoError(t, err)
   367  	_, err = fs.Open("foo")
   368  	require.NotNil(t, err)
   369  
   370  	// Removing "a" should fail because it's not empty.
   371  	err = fs.Remove("a")
   372  	require.NotNil(t, err)
   373  
   374  	// Remove an empty dir and verify it's gone.
   375  	err = fs.Remove("a/b")
   376  	require.NoError(t, err)
   377  	_, err = fs.Lstat("a/b")
   378  	require.NotNil(t, err)
   379  
   380  	err = fs.SyncAll()
   381  	require.NoError(t, err)
   382  }
   383  
   384  func TestReadDir(t *testing.T) {
   385  	ctx, h, fs := makeFS(t, "")
   386  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   387  
   388  	rootNode, _, err := fs.config.KBFSOps().GetRootNode(
   389  		ctx, h, data.MasterBranch)
   390  	require.NoError(t, err)
   391  	aNode, _, err := fs.config.KBFSOps().CreateDir(ctx, rootNode, testPPS("a"))
   392  	require.NoError(t, err)
   393  	testCreateFile(ctx, t, fs, "a/foo", aNode)
   394  	testCreateFile(ctx, t, fs, "a/bar", aNode)
   395  	expectedNames := map[string]bool{
   396  		"foo": true,
   397  		"bar": true,
   398  	}
   399  
   400  	fis, err := fs.ReadDir("a")
   401  	require.NoError(t, err)
   402  	require.Len(t, fis, len(expectedNames))
   403  	for _, fi := range fis {
   404  		require.True(t, expectedNames[fi.Name()])
   405  		delete(expectedNames, fi.Name())
   406  	}
   407  	require.Len(t, expectedNames, 0)
   408  }
   409  
   410  func TestMkdirAll(t *testing.T) {
   411  	ctx, _, fs := makeFS(t, "")
   412  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   413  
   414  	err := fs.MkdirAll("a/b", os.FileMode(0600))
   415  	require.NoError(t, err)
   416  
   417  	err = fs.MkdirAll("a/b/c/d", os.FileMode(0600))
   418  	require.NoError(t, err)
   419  
   420  	f, err := fs.Create("a/b/c/d/foo")
   421  	require.NoError(t, err)
   422  
   423  	err = f.Close()
   424  	require.NoError(t, err)
   425  }
   426  
   427  func TestSymlink(t *testing.T) {
   428  	ctx, _, fs := makeFS(t, "")
   429  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   430  
   431  	err := fs.MkdirAll("a/b/c", os.FileMode(0600))
   432  	require.NoError(t, err)
   433  
   434  	foo, err := fs.Create("a/b/c/foo")
   435  	require.NoError(t, err)
   436  
   437  	data := []byte{1, 2, 3, 4}
   438  	n, err := foo.Write(data)
   439  	require.Equal(t, len(data), n)
   440  	require.NoError(t, err)
   441  	err = foo.Close()
   442  	require.NoError(t, err)
   443  
   444  	t.Log("Basic file symlink in same dir")
   445  	err = fs.Symlink("foo", "a/b/c/bar")
   446  	require.NoError(t, err)
   447  
   448  	_, err = fs.Open("a/b/c/bar")
   449  	require.NoError(t, err)
   450  
   451  	t.Log("Make sure Symlink creates parent directories as needed")
   452  	err = fs.Symlink("../../foo", "a/b/c/d/e/bar")
   453  	require.NoError(t, err)
   454  
   455  	bar, err := fs.Open("a/b/c/d/e/bar")
   456  	require.NoError(t, err)
   457  
   458  	checkData := func(f billy.File) {
   459  		gotData := make([]byte, len(data))
   460  		n, err = f.Read(gotData)
   461  		require.Equal(t, len(data), n)
   462  		require.NoError(t, err)
   463  		require.True(t, bytes.Equal(data, gotData))
   464  	}
   465  	checkData(bar)
   466  
   467  	err = bar.Close()
   468  	require.NoError(t, err)
   469  
   470  	t.Log("File symlink across to a lower dir")
   471  	err = fs.Symlink("b/c/foo", "a/bar")
   472  	require.NoError(t, err)
   473  	bar, err = fs.Open("a/bar")
   474  	require.NoError(t, err)
   475  	checkData(bar)
   476  	err = bar.Close()
   477  	require.NoError(t, err)
   478  
   479  	t.Log("File symlink across to a higher dir")
   480  	err = fs.MkdirAll("a/b/c/d/e/f", os.FileMode(0600))
   481  	require.NoError(t, err)
   482  	err = fs.Symlink("../../../foo", "a/b/c/d/e/f/bar")
   483  	require.NoError(t, err)
   484  	bar, err = fs.Open("a/b/c/d/e/f/bar")
   485  	require.NoError(t, err)
   486  	checkData(bar)
   487  	err = bar.Close()
   488  	require.NoError(t, err)
   489  
   490  	t.Log("File across dir symlink")
   491  	err = fs.Symlink("b", "a/b2")
   492  	require.NoError(t, err)
   493  	bar, err = fs.Open("a/b2/c/bar")
   494  	require.NoError(t, err)
   495  	checkData(bar)
   496  	err = bar.Close()
   497  	require.NoError(t, err)
   498  
   499  	t.Log("Infinite symlink loop")
   500  	err = fs.Symlink("x", "y")
   501  	require.NoError(t, err)
   502  	err = fs.Symlink("y", "x")
   503  	require.NoError(t, err)
   504  	_, err = fs.Open("x")
   505  	require.NotNil(t, err)
   506  
   507  	t.Log("Symlink that tries to break chroot")
   508  	err = fs.Symlink("../../a", "a/breakout")
   509  	require.NoError(t, err)
   510  	_, err = fs.Open("a/breakout")
   511  	require.NotNil(t, err)
   512  
   513  	t.Log("Symlink to absolute path")
   514  	err = fs.Symlink("/etc/passwd", "absolute")
   515  	require.NoError(t, err)
   516  	_, err = fs.Open("absolute")
   517  	require.NotNil(t, err)
   518  
   519  	t.Log("Readlink")
   520  	link, err := fs.Readlink("a/bar")
   521  	require.NoError(t, err)
   522  	require.Equal(t, "b/c/foo", link)
   523  
   524  	fi, err := fs.Lstat("a/bar")
   525  	require.NoError(t, err)
   526  	require.Equal(t, "bar", fi.Name())
   527  
   528  	err = fs.SyncAll()
   529  	require.NoError(t, err)
   530  }
   531  
   532  func TestChmod(t *testing.T) {
   533  	ctx, _, fs := makeFS(t, "")
   534  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   535  
   536  	foo, err := fs.Create("foo")
   537  	require.NoError(t, err)
   538  	err = foo.Close()
   539  	require.NoError(t, err)
   540  
   541  	fi, err := fs.Stat("foo")
   542  	require.NoError(t, err)
   543  	require.True(t, fi.Mode()&0100 == 0)
   544  
   545  	err = fs.Chmod("foo", 0777)
   546  	require.NoError(t, err)
   547  
   548  	fi, err = fs.Stat("foo")
   549  	require.NoError(t, err)
   550  	require.True(t, fi.Mode()&0100 != 0)
   551  }
   552  
   553  func TestChtimes(t *testing.T) {
   554  	ctx, _, fs := makeFS(t, "")
   555  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   556  
   557  	clock := &clocktest.TestClock{}
   558  	clock.Set(time.Now())
   559  	fs.config.SetClock(clock)
   560  
   561  	foo, err := fs.Create("foo")
   562  	require.NoError(t, err)
   563  	err = foo.Close()
   564  	require.NoError(t, err)
   565  
   566  	fi, err := fs.Stat("foo")
   567  	require.NoError(t, err)
   568  	require.True(t, clock.Now().Equal(fi.ModTime()))
   569  
   570  	mtime := time.Date(2015, 1, 2, 3, 4, 5, 6, time.Local)
   571  	err = fs.Chtimes("foo", time.Now(), mtime)
   572  	require.NoError(t, err)
   573  
   574  	fi, err = fs.Stat("foo")
   575  	require.NoError(t, err)
   576  	require.Equal(t, mtime, fi.ModTime())
   577  }
   578  
   579  func TestChroot(t *testing.T) {
   580  	ctx, _, fs := makeFS(t, "")
   581  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   582  
   583  	require.Equal(t, "/keybase/private/user1", fs.Root())
   584  
   585  	err := fs.MkdirAll("a/b/c", os.FileMode(0600))
   586  	require.NoError(t, err)
   587  
   588  	foo, err := fs.Create("a/b/c/foo")
   589  	require.NoError(t, err)
   590  
   591  	data := []byte{1, 2, 3, 4}
   592  	n, err := foo.Write(data)
   593  	require.Equal(t, len(data), n)
   594  	require.NoError(t, err)
   595  	err = foo.Close()
   596  	require.NoError(t, err)
   597  
   598  	err = fs.SyncAll()
   599  	require.NoError(t, err)
   600  
   601  	t.Log("Make a new FS with a deeper root")
   602  	fs2, err := fs.Chroot("a/b")
   603  	require.NoError(t, err)
   604  
   605  	require.Equal(t, "/keybase/private/user1/a/b", fs2.Root())
   606  
   607  	f, err := fs2.Open("c/foo")
   608  	require.NoError(t, err)
   609  	gotData := make([]byte, len(data))
   610  	_, err = f.Read(gotData)
   611  	require.NoError(t, err)
   612  	require.True(t, bytes.Equal(data, gotData))
   613  	err = f.Close()
   614  	require.NoError(t, err)
   615  
   616  	t.Log("Attempt a breakout")
   617  	_, err = fs.Chroot("../../../etc/passwd")
   618  	require.NotNil(t, err)
   619  }
   620  
   621  func TestFileLocking(t *testing.T) {
   622  	_, _, fs, shutdown := makeFSWithJournal(t, "")
   623  	defer shutdown()
   624  
   625  	// TODO: Write an integration tests where we also check to make sure lock
   626  	// namespace isn't empty.
   627  	f, err := fs.Create("a")
   628  	require.NoError(t, err)
   629  
   630  	err = f.Lock()
   631  	require.NoError(t, err)
   632  
   633  	err = f.Unlock()
   634  	require.NoError(t, err)
   635  
   636  	// The lock has been released, and we haven't made any changes.
   637  	// This should be a no-op.
   638  	err = f.Unlock()
   639  	require.NoError(t, err)
   640  
   641  	// Make some more change so next Unlock actually needs to write a MD.
   642  	_, err = fs.Create("c")
   643  	require.NoError(t, err)
   644  
   645  	// Now we do have some stuff that needs to flush, but we don't
   646  	// have the lock, so this should complete without flushing
   647  	// anything.
   648  	err = f.Unlock()
   649  	require.NoError(t, err)
   650  
   651  	// Make sure the journal didn't flush.
   652  	err = fs.SyncAll()
   653  	require.NoError(t, err)
   654  	jManager, err := libkbfs.GetJournalManager(fs.config)
   655  	require.NoError(t, err)
   656  	status, err := jManager.JournalStatus(fs.root.GetFolderBranch().Tlf)
   657  	require.NoError(t, err)
   658  	require.NotEqual(t, kbfsmd.RevisionUninitialized, status.RevisionStart)
   659  
   660  	// Now manually flush again so the journal is clean.
   661  	err = jManager.FinishSingleOp(fs.ctx,
   662  		fs.root.GetFolderBranch().Tlf, nil, keybase1.MDPriorityNormal)
   663  	require.NoError(t, err)
   664  }
   665  
   666  func TestFileLockingExpiration(t *testing.T) {
   667  	_, _, fs, shutdown := makeFSWithJournal(t, "")
   668  	defer shutdown()
   669  
   670  	clock := &clocktest.TestClock{}
   671  	clock.Set(time.Now())
   672  	fs.config.SetClock(clock)
   673  
   674  	f, err := fs.Create("a")
   675  	require.NoError(t, err)
   676  
   677  	err = f.Lock()
   678  	require.NoError(t, err)
   679  
   680  	_, err = fs.Create("b")
   681  	require.NoError(t, err)
   682  
   683  	clock.Add(2 * time.Minute)
   684  
   685  	// Close/Unlock should fail because the clock expired.
   686  	err = f.Close()
   687  	require.Error(t, err)
   688  
   689  	// Shut down the MD server first to avoid state-checking, since
   690  	// the journal is in a weird state.
   691  	fs.config.MDServer().Shutdown()
   692  }
   693  
   694  func TestArchivedByRevision(t *testing.T) {
   695  	ctx, h, fs := makeFS(t, "")
   696  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fs.config)
   697  
   698  	rootNode, _, err := fs.config.KBFSOps().GetRootNode(
   699  		ctx, h, data.MasterBranch)
   700  	require.NoError(t, err)
   701  
   702  	testCreateFile(ctx, t, fs, "foo", rootNode)
   703  	fis, err := fs.ReadDir("")
   704  	require.NoError(t, err)
   705  	require.Len(t, fis, 1)
   706  
   707  	_, _, fsArchived := makeFSWithBranch(
   708  		t, data.MakeRevBranchName(kbfsmd.Revision(1)), "")
   709  	defer libkbfs.CheckConfigAndShutdown(ctx, t, fsArchived.config)
   710  	fis, err = fsArchived.ReadDir("")
   711  	require.NoError(t, err)
   712  	require.Len(t, fis, 0)
   713  }
   714  
   715  func TestEmptyFS(t *testing.T) {
   716  	ctx := libcontext.BackgroundContextWithCancellationDelayer()
   717  	config := libkbfs.MakeTestConfigOrBust(t, "user1", "user2")
   718  	defer libkbfs.CheckConfigAndShutdown(ctx, t, config)
   719  
   720  	h, err := tlfhandle.ParseHandle(
   721  		ctx, config.KBPKI(), config.MDOps(), nil, "user1", tlf.Private)
   722  	require.NoError(t, err)
   723  	fs, err := NewFSIfExists(
   724  		ctx, config, h, data.MasterBranch, "", "", keybase1.MDPriorityNormal)
   725  	require.NoError(t, err)
   726  
   727  	require.True(t, fs.IsEmpty())
   728  
   729  	fis, err := fs.ReadDir("")
   730  	require.NoError(t, err)
   731  	require.Len(t, fis, 0)
   732  
   733  	err = fs.MkdirAll("a", 0777)
   734  	require.Error(t, err)
   735  }