You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

114 lines
3.2 KiB

  1. package os_test
  2. import (
  3. "bytes"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "testing"
  8. "github.com/stretchr/testify/require"
  9. tmos "github.com/tendermint/tendermint/libs/os"
  10. )
  11. func TestCopyFile(t *testing.T) {
  12. tmpfile, err := os.CreateTemp("", "example")
  13. if err != nil {
  14. t.Fatal(err)
  15. }
  16. defer os.Remove(tmpfile.Name())
  17. content := []byte("hello world")
  18. if _, err := tmpfile.Write(content); err != nil {
  19. t.Fatal(err)
  20. }
  21. copyfile := fmt.Sprintf("%s.copy", tmpfile.Name())
  22. if err := tmos.CopyFile(tmpfile.Name(), copyfile); err != nil {
  23. t.Fatal(err)
  24. }
  25. if _, err := os.Stat(copyfile); os.IsNotExist(err) {
  26. t.Fatal("copy should exist")
  27. }
  28. data, err := os.ReadFile(copyfile)
  29. if err != nil {
  30. t.Fatal(err)
  31. }
  32. if !bytes.Equal(data, content) {
  33. t.Fatalf("copy file content differs: expected %v, got %v", content, data)
  34. }
  35. os.Remove(copyfile)
  36. }
  37. func TestEnsureDir(t *testing.T) {
  38. tmp, err := os.MkdirTemp("", "ensure-dir")
  39. require.NoError(t, err)
  40. defer os.RemoveAll(tmp)
  41. // Should be possible to create a new directory.
  42. err = tmos.EnsureDir(filepath.Join(tmp, "dir"), 0755)
  43. require.NoError(t, err)
  44. require.DirExists(t, filepath.Join(tmp, "dir"))
  45. // Should succeed on existing directory.
  46. err = tmos.EnsureDir(filepath.Join(tmp, "dir"), 0755)
  47. require.NoError(t, err)
  48. // Should fail on file.
  49. err = os.WriteFile(filepath.Join(tmp, "file"), []byte{}, 0644)
  50. require.NoError(t, err)
  51. err = tmos.EnsureDir(filepath.Join(tmp, "file"), 0755)
  52. require.Error(t, err)
  53. // Should allow symlink to dir.
  54. err = os.Symlink(filepath.Join(tmp, "dir"), filepath.Join(tmp, "linkdir"))
  55. require.NoError(t, err)
  56. err = tmos.EnsureDir(filepath.Join(tmp, "linkdir"), 0755)
  57. require.NoError(t, err)
  58. // Should error on symlink to file.
  59. err = os.Symlink(filepath.Join(tmp, "file"), filepath.Join(tmp, "linkfile"))
  60. require.NoError(t, err)
  61. err = tmos.EnsureDir(filepath.Join(tmp, "linkfile"), 0755)
  62. require.Error(t, err)
  63. }
  64. // Ensure that using CopyFile does not truncate the destination file before
  65. // the origin is positively a non-directory and that it is ready for copying.
  66. // See https://github.com/tendermint/tendermint/issues/6427
  67. func TestTrickedTruncation(t *testing.T) {
  68. tmpDir, err := os.MkdirTemp(os.TempDir(), "pwn_truncate")
  69. if err != nil {
  70. t.Fatal(err)
  71. }
  72. defer os.Remove(tmpDir)
  73. originalWALPath := filepath.Join(tmpDir, "wal")
  74. originalWALContent := []byte("I AM BECOME DEATH, DESTROYER OF ALL WORLDS!")
  75. if err := os.WriteFile(originalWALPath, originalWALContent, 0755); err != nil {
  76. t.Fatal(err)
  77. }
  78. // 1. Sanity check.
  79. readWAL, err := os.ReadFile(originalWALPath)
  80. if err != nil {
  81. t.Fatal(err)
  82. }
  83. if !bytes.Equal(readWAL, originalWALContent) {
  84. t.Fatalf("Cannot proceed as the content does not match\nGot: %q\nWant: %q", readWAL, originalWALContent)
  85. }
  86. // 2. Now cause the truncation of the original file.
  87. // It is absolutely legal to invoke os.Open on a directory.
  88. if err := tmos.CopyFile(tmpDir, originalWALPath); err == nil {
  89. t.Fatal("Expected an error")
  90. }
  91. // 3. Check the WAL's content
  92. reReadWAL, err := os.ReadFile(originalWALPath)
  93. if err != nil {
  94. t.Fatal(err)
  95. }
  96. if !bytes.Equal(reReadWAL, originalWALContent) {
  97. t.Fatalf("Oops, the WAL's content was changed :(\nGot: %q\nWant: %q", reReadWAL, originalWALContent)
  98. }
  99. }