github.com/number571/tendermint@v0.34.11-gost/libs/os/os_test.go (about) 1 package os_test 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "syscall" 11 "testing" 12 "time" 13 14 tmos "github.com/number571/tendermint/libs/os" 15 "github.com/stretchr/testify/require" 16 ) 17 18 func TestCopyFile(t *testing.T) { 19 tmpfile, err := ioutil.TempFile("", "example") 20 if err != nil { 21 t.Fatal(err) 22 } 23 defer os.Remove(tmpfile.Name()) 24 content := []byte("hello world") 25 if _, err := tmpfile.Write(content); err != nil { 26 t.Fatal(err) 27 } 28 29 copyfile := fmt.Sprintf("%s.copy", tmpfile.Name()) 30 if err := tmos.CopyFile(tmpfile.Name(), copyfile); err != nil { 31 t.Fatal(err) 32 } 33 if _, err := os.Stat(copyfile); os.IsNotExist(err) { 34 t.Fatal("copy should exist") 35 } 36 data, err := ioutil.ReadFile(copyfile) 37 if err != nil { 38 t.Fatal(err) 39 } 40 if !bytes.Equal(data, content) { 41 t.Fatalf("copy file content differs: expected %v, got %v", content, data) 42 } 43 os.Remove(copyfile) 44 } 45 46 func TestTrapSignal(t *testing.T) { 47 if os.Getenv("TM_TRAP_SIGNAL_TEST") == "1" { 48 t.Log("inside test process") 49 killer() 50 return 51 } 52 53 cmd, _, mockStderr := newTestProgram(t, "TM_TRAP_SIGNAL_TEST") 54 55 err := cmd.Run() 56 if err == nil { 57 wantStderr := "exiting" 58 if mockStderr.String() != wantStderr { 59 t.Fatalf("stderr: want %q, got %q", wantStderr, mockStderr.String()) 60 } 61 62 return 63 } 64 65 if e, ok := err.(*exec.ExitError); ok && !e.Success() { 66 t.Fatalf("wrong exit code, want 0, got %d", e.ExitCode()) 67 } 68 69 t.Fatal("this error should not be triggered") 70 } 71 72 func TestEnsureDir(t *testing.T) { 73 tmp, err := ioutil.TempDir("", "ensure-dir") 74 require.NoError(t, err) 75 defer os.RemoveAll(tmp) 76 77 // Should be possible to create a new directory. 78 err = tmos.EnsureDir(filepath.Join(tmp, "dir"), 0755) 79 require.NoError(t, err) 80 require.DirExists(t, filepath.Join(tmp, "dir")) 81 82 // Should succeed on existing directory. 83 err = tmos.EnsureDir(filepath.Join(tmp, "dir"), 0755) 84 require.NoError(t, err) 85 86 // Should fail on file. 87 err = ioutil.WriteFile(filepath.Join(tmp, "file"), []byte{}, 0644) 88 require.NoError(t, err) 89 err = tmos.EnsureDir(filepath.Join(tmp, "file"), 0755) 90 require.Error(t, err) 91 92 // Should allow symlink to dir. 93 err = os.Symlink(filepath.Join(tmp, "dir"), filepath.Join(tmp, "linkdir")) 94 require.NoError(t, err) 95 err = tmos.EnsureDir(filepath.Join(tmp, "linkdir"), 0755) 96 require.NoError(t, err) 97 98 // Should error on symlink to file. 99 err = os.Symlink(filepath.Join(tmp, "file"), filepath.Join(tmp, "linkfile")) 100 require.NoError(t, err) 101 err = tmos.EnsureDir(filepath.Join(tmp, "linkfile"), 0755) 102 require.Error(t, err) 103 } 104 105 type mockLogger struct{} 106 107 func (ml mockLogger) Info(msg string, keyvals ...interface{}) {} 108 109 func killer() { 110 logger := mockLogger{} 111 112 tmos.TrapSignal(logger, func() { _, _ = fmt.Fprintf(os.Stderr, "exiting") }) 113 time.Sleep(1 * time.Second) 114 115 p, err := os.FindProcess(os.Getpid()) 116 if err != nil { 117 panic(err) 118 } 119 120 if err := p.Signal(syscall.SIGTERM); err != nil { 121 panic(err) 122 } 123 124 time.Sleep(1 * time.Second) 125 } 126 127 func newTestProgram(t *testing.T, environVar string) (cmd *exec.Cmd, stdout *bytes.Buffer, stderr *bytes.Buffer) { 128 t.Helper() 129 130 cmd = exec.Command(os.Args[0], "-test.run="+t.Name()) 131 stdout, stderr = bytes.NewBufferString(""), bytes.NewBufferString("") 132 cmd.Env = append(os.Environ(), fmt.Sprintf("%s=1", environVar)) 133 cmd.Stdout = stdout 134 cmd.Stderr = stderr 135 136 return 137 } 138 139 // Ensure that using CopyFile does not truncate the destination file before 140 // the origin is positively a non-directory and that it is ready for copying. 141 // See https://github.com/number571/tendermint/issues/6427 142 func TestTrickedTruncation(t *testing.T) { 143 tmpDir, err := ioutil.TempDir(os.TempDir(), "pwn_truncate") 144 if err != nil { 145 t.Fatal(err) 146 } 147 defer os.Remove(tmpDir) 148 149 originalWALPath := filepath.Join(tmpDir, "wal") 150 originalWALContent := []byte("I AM BECOME DEATH, DESTROYER OF ALL WORLDS!") 151 if err := ioutil.WriteFile(originalWALPath, originalWALContent, 0755); err != nil { 152 t.Fatal(err) 153 } 154 155 // 1. Sanity check. 156 readWAL, err := ioutil.ReadFile(originalWALPath) 157 if err != nil { 158 t.Fatal(err) 159 } 160 if !bytes.Equal(readWAL, originalWALContent) { 161 t.Fatalf("Cannot proceed as the content does not match\nGot: %q\nWant: %q", readWAL, originalWALContent) 162 } 163 164 // 2. Now cause the truncation of the original file. 165 // It is absolutely legal to invoke os.Open on a directory. 166 if err := tmos.CopyFile(tmpDir, originalWALPath); err == nil { 167 t.Fatal("Expected an error") 168 } 169 170 // 3. Check the WAL's content 171 reReadWAL, err := ioutil.ReadFile(originalWALPath) 172 if err != nil { 173 t.Fatal(err) 174 } 175 if !bytes.Equal(reReadWAL, originalWALContent) { 176 t.Fatalf("Oops, the WAL's content was changed :(\nGot: %q\nWant: %q", reReadWAL, originalWALContent) 177 } 178 }