github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/vfs/dir_test.go (about)

     1  package vfs
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"sort"
     8  	"testing"
     9  	"time"
    10  	"unsafe"
    11  
    12  	"github.com/rclone/rclone/fs"
    13  	"github.com/rclone/rclone/fs/operations"
    14  	"github.com/rclone/rclone/fstest"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func dirCreate(t *testing.T) (r *fstest.Run, vfs *VFS, dir *Dir, item fstest.Item) {
    20  	r, vfs = newTestVFS(t)
    21  
    22  	file1 := r.WriteObject(context.Background(), "dir/file1", "file1 contents", t1)
    23  	r.CheckRemoteItems(t, file1)
    24  
    25  	node, err := vfs.Stat("dir")
    26  	require.NoError(t, err)
    27  	require.True(t, node.IsDir())
    28  
    29  	return r, vfs, node.(*Dir), file1
    30  }
    31  
    32  func TestDirMethods(t *testing.T) {
    33  	_, vfs, dir, _ := dirCreate(t)
    34  
    35  	// String
    36  	assert.Equal(t, "dir/", dir.String())
    37  	assert.Equal(t, "<nil *Dir>", (*Dir)(nil).String())
    38  
    39  	// IsDir
    40  	assert.Equal(t, true, dir.IsDir())
    41  
    42  	// IsFile
    43  	assert.Equal(t, false, dir.IsFile())
    44  
    45  	// Mode
    46  	assert.Equal(t, vfs.Opt.DirPerms, dir.Mode())
    47  
    48  	// Name
    49  	assert.Equal(t, "dir", dir.Name())
    50  
    51  	// Path
    52  	assert.Equal(t, "dir", dir.Path())
    53  
    54  	// Sys
    55  	assert.Equal(t, nil, dir.Sys())
    56  
    57  	// SetSys
    58  	dir.SetSys(42)
    59  	assert.Equal(t, 42, dir.Sys())
    60  
    61  	// Inode
    62  	assert.NotEqual(t, uint64(0), dir.Inode())
    63  
    64  	// Node
    65  	assert.Equal(t, dir, dir.Node())
    66  
    67  	// ModTime
    68  	assert.WithinDuration(t, t1, dir.ModTime(), 100*365*24*60*60*time.Second)
    69  
    70  	// Size
    71  	assert.Equal(t, int64(0), dir.Size())
    72  
    73  	// Sync
    74  	assert.NoError(t, dir.Sync())
    75  
    76  	// DirEntry
    77  	assert.Equal(t, dir.entry, dir.DirEntry())
    78  
    79  	// VFS
    80  	assert.Equal(t, vfs, dir.VFS())
    81  }
    82  
    83  func TestDirForgetAll(t *testing.T) {
    84  	_, vfs, dir, file1 := dirCreate(t)
    85  
    86  	// Make sure / and dir are in cache
    87  	_, err := vfs.Stat(file1.Path)
    88  	require.NoError(t, err)
    89  
    90  	root, err := vfs.Root()
    91  	require.NoError(t, err)
    92  
    93  	assert.Equal(t, 1, len(root.items))
    94  	assert.Equal(t, 1, len(dir.items))
    95  	assert.False(t, root.read.IsZero())
    96  	assert.False(t, dir.read.IsZero())
    97  
    98  	dir.ForgetAll()
    99  	assert.Equal(t, 1, len(root.items))
   100  	assert.Equal(t, 0, len(dir.items))
   101  	assert.False(t, root.read.IsZero())
   102  	assert.True(t, dir.read.IsZero())
   103  
   104  	root.ForgetAll()
   105  	assert.Equal(t, 0, len(root.items))
   106  	assert.Equal(t, 0, len(dir.items))
   107  	assert.True(t, root.read.IsZero())
   108  }
   109  
   110  func TestDirForgetPath(t *testing.T) {
   111  	_, vfs, dir, file1 := dirCreate(t)
   112  
   113  	// Make sure / and dir are in cache
   114  	_, err := vfs.Stat(file1.Path)
   115  	require.NoError(t, err)
   116  
   117  	root, err := vfs.Root()
   118  	require.NoError(t, err)
   119  
   120  	assert.Equal(t, 1, len(root.items))
   121  	assert.Equal(t, 1, len(dir.items))
   122  	assert.False(t, root.read.IsZero())
   123  	assert.False(t, dir.read.IsZero())
   124  
   125  	root.ForgetPath("dir/notfound", fs.EntryObject)
   126  	assert.Equal(t, 1, len(root.items))
   127  	assert.Equal(t, 1, len(dir.items))
   128  	assert.False(t, root.read.IsZero())
   129  	assert.True(t, dir.read.IsZero())
   130  
   131  	root.ForgetPath("dir", fs.EntryDirectory)
   132  	assert.Equal(t, 1, len(root.items))
   133  	assert.Equal(t, 0, len(dir.items))
   134  	assert.True(t, root.read.IsZero())
   135  
   136  	root.ForgetPath("not/in/cache", fs.EntryDirectory)
   137  	assert.Equal(t, 1, len(root.items))
   138  	assert.Equal(t, 0, len(dir.items))
   139  }
   140  
   141  func TestDirWalk(t *testing.T) {
   142  	r, vfs, _, file1 := dirCreate(t)
   143  
   144  	file2 := r.WriteObject(context.Background(), "fil/a/b/c", "super long file", t1)
   145  	r.CheckRemoteItems(t, file1, file2)
   146  
   147  	root, err := vfs.Root()
   148  	require.NoError(t, err)
   149  
   150  	// Forget the cache since we put another object in
   151  	root.ForgetAll()
   152  
   153  	// Read the directories in
   154  	_, err = vfs.Stat("dir")
   155  	require.NoError(t, err)
   156  	_, err = vfs.Stat("fil/a/b")
   157  	require.NoError(t, err)
   158  	fil, err := vfs.Stat("fil")
   159  	require.NoError(t, err)
   160  
   161  	var result []string
   162  	fn := func(d *Dir) {
   163  		result = append(result, d.path)
   164  	}
   165  
   166  	result = nil
   167  	root.walk(fn)
   168  	sort.Strings(result) // sort as there is a map traversal involved
   169  	assert.Equal(t, []string{"", "dir", "fil", "fil/a", "fil/a/b"}, result)
   170  
   171  	assert.Nil(t, root.cachedDir("not found"))
   172  	if dir := root.cachedDir("dir"); assert.NotNil(t, dir) {
   173  		result = nil
   174  		dir.walk(fn)
   175  		assert.Equal(t, []string{"dir"}, result)
   176  	}
   177  	if dir := root.cachedDir("fil"); assert.NotNil(t, dir) {
   178  		result = nil
   179  		dir.walk(fn)
   180  		assert.Equal(t, []string{"fil/a/b", "fil/a", "fil"}, result)
   181  	}
   182  	if dir := fil.(*Dir); assert.NotNil(t, dir) {
   183  		result = nil
   184  		dir.walk(fn)
   185  		assert.Equal(t, []string{"fil/a/b", "fil/a", "fil"}, result)
   186  	}
   187  	if dir := root.cachedDir("fil/a"); assert.NotNil(t, dir) {
   188  		result = nil
   189  		dir.walk(fn)
   190  		assert.Equal(t, []string{"fil/a/b", "fil/a"}, result)
   191  	}
   192  	if dir := fil.(*Dir).cachedDir("a"); assert.NotNil(t, dir) {
   193  		result = nil
   194  		dir.walk(fn)
   195  		assert.Equal(t, []string{"fil/a/b", "fil/a"}, result)
   196  	}
   197  	if dir := root.cachedDir("fil/a"); assert.NotNil(t, dir) {
   198  		result = nil
   199  		dir.walk(fn)
   200  		assert.Equal(t, []string{"fil/a/b", "fil/a"}, result)
   201  	}
   202  	if dir := root.cachedDir("fil/a/b"); assert.NotNil(t, dir) {
   203  		result = nil
   204  		dir.walk(fn)
   205  		assert.Equal(t, []string{"fil/a/b"}, result)
   206  	}
   207  }
   208  
   209  func TestDirSetModTime(t *testing.T) {
   210  	_, vfs, dir, _ := dirCreate(t)
   211  
   212  	err := dir.SetModTime(t1)
   213  	require.NoError(t, err)
   214  	assert.WithinDuration(t, t1, dir.ModTime(), time.Second)
   215  
   216  	err = dir.SetModTime(t2)
   217  	require.NoError(t, err)
   218  	assert.WithinDuration(t, t2, dir.ModTime(), time.Second)
   219  
   220  	vfs.Opt.ReadOnly = true
   221  	err = dir.SetModTime(t2)
   222  	assert.Equal(t, EROFS, err)
   223  }
   224  
   225  func TestDirStat(t *testing.T) {
   226  	_, _, dir, _ := dirCreate(t)
   227  
   228  	node, err := dir.Stat("file1")
   229  	require.NoError(t, err)
   230  	_, ok := node.(*File)
   231  	assert.True(t, ok)
   232  	assert.Equal(t, int64(14), node.Size())
   233  	assert.Equal(t, "file1", node.Name())
   234  
   235  	_, err = dir.Stat("not found")
   236  	assert.Equal(t, ENOENT, err)
   237  }
   238  
   239  // This lists dir and checks the listing is as expected
   240  func checkListing(t *testing.T, dir *Dir, want []string) {
   241  	var got []string
   242  	nodes, err := dir.ReadDirAll()
   243  	require.NoError(t, err)
   244  	for _, node := range nodes {
   245  		got = append(got, fmt.Sprintf("%s,%d,%v", node.Name(), node.Size(), node.IsDir()))
   246  	}
   247  	assert.Equal(t, want, got)
   248  }
   249  
   250  func TestDirReadDirAll(t *testing.T) {
   251  	r, vfs := newTestVFS(t)
   252  
   253  	file1 := r.WriteObject(context.Background(), "dir/file1", "file1 contents", t1)
   254  	file2 := r.WriteObject(context.Background(), "dir/file2", "file2- contents", t2)
   255  	file3 := r.WriteObject(context.Background(), "dir/subdir/file3", "file3-- contents", t3)
   256  	r.CheckRemoteItems(t, file1, file2, file3)
   257  
   258  	node, err := vfs.Stat("dir")
   259  	require.NoError(t, err)
   260  	dir := node.(*Dir)
   261  
   262  	checkListing(t, dir, []string{"file1,14,false", "file2,15,false", "subdir,0,true"})
   263  
   264  	node, err = vfs.Stat("")
   265  	require.NoError(t, err)
   266  	root := node.(*Dir)
   267  
   268  	checkListing(t, root, []string{"dir,0,true"})
   269  
   270  	node, err = vfs.Stat("dir/subdir")
   271  	require.NoError(t, err)
   272  	subdir := node.(*Dir)
   273  
   274  	checkListing(t, subdir, []string{"file3,16,false"})
   275  
   276  	t.Run("Virtual", func(t *testing.T) {
   277  		// Add some virtual entries and check what happens
   278  		dir.AddVirtual("virtualFile", 17, false)
   279  		dir.AddVirtual("virtualDir", 0, true)
   280  		// Remove some existing entries
   281  		dir.DelVirtual("file2")
   282  		dir.DelVirtual("subdir")
   283  
   284  		checkListing(t, dir, []string{"file1,14,false", "virtualDir,0,true", "virtualFile,17,false"})
   285  
   286  		// Now action the deletes and uploads
   287  		_ = r.WriteObject(context.Background(), "dir/virtualFile", "virtualFile contents", t1)
   288  		_ = r.WriteObject(context.Background(), "dir/virtualDir/testFile", "testFile contents", t1)
   289  		o, err := r.Fremote.NewObject(context.Background(), "dir/file2")
   290  		require.NoError(t, err)
   291  		require.NoError(t, o.Remove(context.Background()))
   292  		require.NoError(t, operations.Purge(context.Background(), r.Fremote, "dir/subdir"))
   293  
   294  		// Force a directory reload...
   295  		dir.invalidateDir("dir")
   296  
   297  		checkListing(t, dir, []string{"file1,14,false", "virtualDir,0,true", "virtualFile,20,false"})
   298  
   299  		// check no virtuals left
   300  		dir.mu.Lock()
   301  		assert.Nil(t, dir.virtual)
   302  		dir.mu.Unlock()
   303  
   304  		// Add some virtual entries and check what happens
   305  		dir.AddVirtual("virtualFile2", 100, false)
   306  		dir.AddVirtual("virtualDir2", 0, true)
   307  		// Remove some existing entries
   308  		dir.DelVirtual("file1")
   309  
   310  		checkListing(t, dir, []string{"virtualDir,0,true", "virtualDir2,0,true", "virtualFile,20,false", "virtualFile2,100,false"})
   311  
   312  		// Force a directory reload...
   313  		dir.invalidateDir("dir")
   314  
   315  		want := []string{"file1,14,false", "virtualDir,0,true", "virtualDir2,0,true", "virtualFile,20,false", "virtualFile2,100,false"}
   316  		features := r.Fremote.Features()
   317  		if features.CanHaveEmptyDirectories {
   318  			// snip out virtualDir2 which will only be present if can't have empty dirs
   319  			want = append(want[:2], want[3:]...)
   320  		}
   321  		checkListing(t, dir, want)
   322  
   323  		// Check that forgetting the root doesn't invalidate the virtual entries
   324  		root.ForgetAll()
   325  
   326  		checkListing(t, dir, want)
   327  	})
   328  }
   329  
   330  func TestDirOpen(t *testing.T) {
   331  	_, _, dir, _ := dirCreate(t)
   332  
   333  	fd, err := dir.Open(os.O_RDONLY)
   334  	require.NoError(t, err)
   335  	_, ok := fd.(*DirHandle)
   336  	assert.True(t, ok)
   337  	require.NoError(t, fd.Close())
   338  
   339  	_, err = dir.Open(os.O_WRONLY)
   340  	assert.Equal(t, EPERM, err)
   341  }
   342  
   343  func TestDirCreate(t *testing.T) {
   344  	_, vfs, dir, _ := dirCreate(t)
   345  
   346  	origModTime := dir.ModTime()
   347  	time.Sleep(100 * time.Millisecond) // for low rez Windows timers
   348  	file, err := dir.Create("potato", os.O_WRONLY|os.O_CREATE)
   349  	require.NoError(t, err)
   350  	assert.Equal(t, int64(0), file.Size())
   351  	assert.True(t, dir.ModTime().After(origModTime))
   352  
   353  	fd, err := file.Open(os.O_WRONLY | os.O_CREATE)
   354  	require.NoError(t, err)
   355  
   356  	// FIXME Note that this fails with the current implementation
   357  	// until the file has been opened.
   358  
   359  	// file2, err := vfs.Stat("dir/potato")
   360  	// require.NoError(t, err)
   361  	// assert.Equal(t, file, file2)
   362  
   363  	n, err := fd.Write([]byte("hello"))
   364  	require.NoError(t, err)
   365  	assert.Equal(t, 5, n)
   366  
   367  	require.NoError(t, fd.Close())
   368  
   369  	file2, err := vfs.Stat("dir/potato")
   370  	require.NoError(t, err)
   371  	assert.Equal(t, int64(5), file2.Size())
   372  
   373  	// Try creating the file again - make sure we get the same file node
   374  	file3, err := dir.Create("potato", os.O_RDWR|os.O_CREATE)
   375  	require.NoError(t, err)
   376  	assert.Equal(t, int64(5), file3.Size())
   377  	assert.Equal(t, fmt.Sprintf("%p", file), fmt.Sprintf("%p", file3), "didn't return same node")
   378  
   379  	// Test read only fs creating new
   380  	vfs.Opt.ReadOnly = true
   381  	_, err = dir.Create("sausage", os.O_WRONLY|os.O_CREATE)
   382  	assert.Equal(t, EROFS, err)
   383  }
   384  
   385  func TestDirMkdir(t *testing.T) {
   386  	r, vfs, dir, file1 := dirCreate(t)
   387  
   388  	_, err := dir.Mkdir("file1")
   389  	assert.Error(t, err)
   390  
   391  	origModTime := dir.ModTime()
   392  	time.Sleep(100 * time.Millisecond) // for low rez Windows timers
   393  	sub, err := dir.Mkdir("sub")
   394  	assert.NoError(t, err)
   395  	assert.True(t, dir.ModTime().After(origModTime))
   396  
   397  	// check the vfs
   398  	checkListing(t, dir, []string{"file1,14,false", "sub,0,true"})
   399  	checkListing(t, sub, []string(nil))
   400  
   401  	// check the underlying r.Fremote
   402  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{"dir", "dir/sub"}, r.Fremote.Precision())
   403  
   404  	vfs.Opt.ReadOnly = true
   405  	_, err = dir.Mkdir("sausage")
   406  	assert.Equal(t, EROFS, err)
   407  }
   408  
   409  func TestDirMkdirSub(t *testing.T) {
   410  	r, vfs, dir, file1 := dirCreate(t)
   411  
   412  	_, err := dir.Mkdir("file1")
   413  	assert.Error(t, err)
   414  
   415  	sub, err := dir.Mkdir("sub")
   416  	assert.NoError(t, err)
   417  
   418  	subsub, err := sub.Mkdir("subsub")
   419  	assert.NoError(t, err)
   420  
   421  	// check the vfs
   422  	checkListing(t, dir, []string{"file1,14,false", "sub,0,true"})
   423  	checkListing(t, sub, []string{"subsub,0,true"})
   424  	checkListing(t, subsub, []string(nil))
   425  
   426  	// check the underlying r.Fremote
   427  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{"dir", "dir/sub", "dir/sub/subsub"}, r.Fremote.Precision())
   428  
   429  	vfs.Opt.ReadOnly = true
   430  	_, err = dir.Mkdir("sausage")
   431  	assert.Equal(t, EROFS, err)
   432  }
   433  
   434  func TestDirRemove(t *testing.T) {
   435  	r, vfs, dir, _ := dirCreate(t)
   436  
   437  	// check directory is there
   438  	node, err := vfs.Stat("dir")
   439  	require.NoError(t, err)
   440  	assert.True(t, node.IsDir())
   441  
   442  	err = dir.Remove()
   443  	assert.Equal(t, ENOTEMPTY, err)
   444  
   445  	// Delete the sub file
   446  	node, err = vfs.Stat("dir/file1")
   447  	require.NoError(t, err)
   448  	err = node.Remove()
   449  	require.NoError(t, err)
   450  
   451  	// Remove the now empty directory
   452  	err = dir.Remove()
   453  	require.NoError(t, err)
   454  
   455  	// check directory is not there
   456  	_, err = vfs.Stat("dir")
   457  	assert.Equal(t, ENOENT, err)
   458  
   459  	// check the vfs
   460  	root, err := vfs.Root()
   461  	require.NoError(t, err)
   462  	checkListing(t, root, []string(nil))
   463  
   464  	// check the underlying r.Fremote
   465  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{}, r.Fremote.Precision())
   466  
   467  	// read only check
   468  	vfs.Opt.ReadOnly = true
   469  	err = dir.Remove()
   470  	assert.Equal(t, EROFS, err)
   471  }
   472  
   473  func TestDirRemoveAll(t *testing.T) {
   474  	r, vfs, dir, _ := dirCreate(t)
   475  
   476  	// Remove the directory and contents
   477  	err := dir.RemoveAll()
   478  	require.NoError(t, err)
   479  
   480  	// check the vfs
   481  	root, err := vfs.Root()
   482  	require.NoError(t, err)
   483  	checkListing(t, root, []string(nil))
   484  
   485  	// check the underlying r.Fremote
   486  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{}, r.Fremote.Precision())
   487  
   488  	// read only check
   489  	vfs.Opt.ReadOnly = true
   490  	err = dir.RemoveAll()
   491  	assert.Equal(t, EROFS, err)
   492  }
   493  
   494  func TestDirRemoveName(t *testing.T) {
   495  	r, vfs, dir, _ := dirCreate(t)
   496  
   497  	origModTime := dir.ModTime()
   498  	time.Sleep(100 * time.Millisecond) // for low rez Windows timers
   499  	err := dir.RemoveName("file1")
   500  	require.NoError(t, err)
   501  	assert.True(t, dir.ModTime().After(origModTime))
   502  	checkListing(t, dir, []string(nil))
   503  	root, err := vfs.Root()
   504  	require.NoError(t, err)
   505  	checkListing(t, root, []string{"dir,0,true"})
   506  
   507  	// check the underlying r.Fremote
   508  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{}, []string{"dir"}, r.Fremote.Precision())
   509  
   510  	// read only check
   511  	vfs.Opt.ReadOnly = true
   512  	err = dir.RemoveName("potato")
   513  	assert.Equal(t, EROFS, err)
   514  }
   515  
   516  func TestDirRename(t *testing.T) {
   517  	r, vfs, dir, file1 := dirCreate(t)
   518  
   519  	features := r.Fremote.Features()
   520  	if features.DirMove == nil && features.Move == nil && features.Copy == nil {
   521  		t.Skip("can't rename directories")
   522  	}
   523  
   524  	file3 := r.WriteObject(context.Background(), "dir/file3", "file3 contents!", t1)
   525  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1, file3}, []string{"dir"}, r.Fremote.Precision())
   526  
   527  	root, err := vfs.Root()
   528  	require.NoError(t, err)
   529  
   530  	err = dir.Rename("not found", "tuba", dir)
   531  	assert.Equal(t, ENOENT, err)
   532  
   533  	// Rename a directory
   534  	err = root.Rename("dir", "dir2", root)
   535  	assert.NoError(t, err)
   536  	checkListing(t, root, []string{"dir2,0,true"})
   537  	checkListing(t, dir, []string{"file1,14,false", "file3,15,false"})
   538  
   539  	// check the underlying r.Fremote
   540  	file1.Path = "dir2/file1"
   541  	file3.Path = "dir2/file3"
   542  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1, file3}, []string{"dir2"}, r.Fremote.Precision())
   543  
   544  	// refetch dir
   545  	node, err := vfs.Stat("dir2")
   546  	assert.NoError(t, err)
   547  	dir = node.(*Dir)
   548  
   549  	// Rename a file
   550  	origModTime := dir.ModTime()
   551  	time.Sleep(100 * time.Millisecond) // for low rez Windows timers
   552  	err = dir.Rename("file1", "file2", root)
   553  	assert.NoError(t, err)
   554  	assert.True(t, dir.ModTime().After(origModTime))
   555  	checkListing(t, root, []string{"dir2,0,true", "file2,14,false"})
   556  	checkListing(t, dir, []string{"file3,15,false"})
   557  
   558  	// check the underlying r.Fremote
   559  	file1.Path = "file2"
   560  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1, file3}, []string{"dir2"}, r.Fremote.Precision())
   561  
   562  	// Rename a file on top of another file
   563  	err = root.Rename("file2", "file3", dir)
   564  	assert.NoError(t, err)
   565  	checkListing(t, root, []string{"dir2,0,true"})
   566  	checkListing(t, dir, []string{"file3,14,false"})
   567  
   568  	// check the underlying r.Fremote
   569  	file1.Path = "dir2/file3"
   570  	fstest.CheckListingWithPrecision(t, r.Fremote, []fstest.Item{file1}, []string{"dir2"}, r.Fremote.Precision())
   571  
   572  	// rename an empty directory
   573  	_, err = root.Mkdir("empty directory")
   574  	assert.NoError(t, err)
   575  	checkListing(t, root, []string{
   576  		"dir2,0,true",
   577  		"empty directory,0,true",
   578  	})
   579  	err = root.Rename("empty directory", "renamed empty directory", root)
   580  	assert.NoError(t, err)
   581  	checkListing(t, root, []string{
   582  		"dir2,0,true",
   583  		"renamed empty directory,0,true",
   584  	})
   585  	// ...we don't check the underlying f.Fremote because on
   586  	// bucket-based remotes the directory won't be there
   587  
   588  	// read only check
   589  	vfs.Opt.ReadOnly = true
   590  	err = dir.Rename("potato", "tuba", dir)
   591  	assert.Equal(t, EROFS, err)
   592  }
   593  
   594  func TestDirStructSize(t *testing.T) {
   595  	t.Logf("Dir struct has size %d bytes", unsafe.Sizeof(Dir{}))
   596  }