dir_test.go 17 KB

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