github.com/scaleoutsean/fusego@v0.0.0-20220224074057-4a6429e46bb8/samples/memfs/posix_test.go (about) 1 // Copyright 2015 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Tests for the behavior of os.File objects on plain old posix file systems, 16 // for use in verifying the intended behavior of memfs. 17 18 package memfs_test 19 20 import ( 21 "context" 22 "io" 23 "io/ioutil" 24 "os" 25 "path" 26 "runtime" 27 "testing" 28 29 "github.com/scaleoutsean/fusego/fusetesting" 30 . "github.com/jacobsa/oglematchers" 31 . "github.com/jacobsa/ogletest" 32 ) 33 34 func TestPosix(t *testing.T) { RunTests(t) } 35 36 //////////////////////////////////////////////////////////////////////// 37 // Helpers 38 //////////////////////////////////////////////////////////////////////// 39 40 func getFileOffset(f *os.File) (offset int64, err error) { 41 const relativeToCurrent = 1 42 return f.Seek(0, relativeToCurrent) 43 } 44 45 //////////////////////////////////////////////////////////////////////// 46 // Boilerplate 47 //////////////////////////////////////////////////////////////////////// 48 49 type PosixTest struct { 50 ctx context.Context 51 52 // A temporary directory. 53 dir string 54 55 // Files to close when tearing down. Nil entries are skipped. 56 toClose []io.Closer 57 } 58 59 var _ SetUpInterface = &PosixTest{} 60 var _ TearDownInterface = &PosixTest{} 61 62 func init() { RegisterTestSuite(&PosixTest{}) } 63 64 func (t *PosixTest) SetUp(ti *TestInfo) { 65 var err error 66 67 t.ctx = ti.Ctx 68 69 // Create a temporary directory. 70 t.dir, err = ioutil.TempDir("", "posix_test") 71 if err != nil { 72 panic(err) 73 } 74 } 75 76 func (t *PosixTest) TearDown() { 77 // Close any files we opened. 78 for _, c := range t.toClose { 79 if c == nil { 80 continue 81 } 82 83 err := c.Close() 84 if err != nil { 85 panic(err) 86 } 87 } 88 89 // Remove the temporary directory. 90 err := os.RemoveAll(t.dir) 91 if err != nil { 92 panic(err) 93 } 94 } 95 96 //////////////////////////////////////////////////////////////////////// 97 // Test functions 98 //////////////////////////////////////////////////////////////////////// 99 100 func (t *PosixTest) WriteOverlapsEndOfFile() { 101 var err error 102 var n int 103 104 // Create a file. 105 f, err := os.Create(path.Join(t.dir, "foo")) 106 t.toClose = append(t.toClose, f) 107 AssertEq(nil, err) 108 109 // Make it 4 bytes long. 110 err = f.Truncate(4) 111 AssertEq(nil, err) 112 113 // Write the range [2, 6). 114 n, err = f.WriteAt([]byte("taco"), 2) 115 AssertEq(nil, err) 116 AssertEq(4, n) 117 118 // Read the full contents of the file. 119 contents, err := ioutil.ReadAll(f) 120 AssertEq(nil, err) 121 ExpectEq("\x00\x00taco", string(contents)) 122 } 123 124 func (t *PosixTest) WriteStartsAtEndOfFile() { 125 var err error 126 var n int 127 128 // Create a file. 129 f, err := os.Create(path.Join(t.dir, "foo")) 130 t.toClose = append(t.toClose, f) 131 AssertEq(nil, err) 132 133 // Make it 2 bytes long. 134 err = f.Truncate(2) 135 AssertEq(nil, err) 136 137 // Write the range [2, 6). 138 n, err = f.WriteAt([]byte("taco"), 2) 139 AssertEq(nil, err) 140 AssertEq(4, n) 141 142 // Read the full contents of the file. 143 contents, err := ioutil.ReadAll(f) 144 AssertEq(nil, err) 145 ExpectEq("\x00\x00taco", string(contents)) 146 } 147 148 func (t *PosixTest) WriteStartsPastEndOfFile() { 149 var err error 150 var n int 151 152 // Create a file. 153 f, err := os.Create(path.Join(t.dir, "foo")) 154 t.toClose = append(t.toClose, f) 155 AssertEq(nil, err) 156 157 // Write the range [2, 6). 158 n, err = f.WriteAt([]byte("taco"), 2) 159 AssertEq(nil, err) 160 AssertEq(4, n) 161 162 // Read the full contents of the file. 163 contents, err := ioutil.ReadAll(f) 164 AssertEq(nil, err) 165 ExpectEq("\x00\x00taco", string(contents)) 166 } 167 168 func (t *PosixTest) WriteStartsPastEndOfFile_AppendMode() { 169 return // WriteAt on O_APPEND files returns an error starting with Go 1.13 170 var err error 171 var n int 172 173 // Create a file. 174 f, err := os.OpenFile( 175 path.Join(t.dir, "foo"), 176 os.O_RDWR|os.O_APPEND|os.O_CREATE, 177 0600) 178 179 t.toClose = append(t.toClose, f) 180 AssertEq(nil, err) 181 182 // Write three bytes. 183 n, err = f.Write([]byte("111")) 184 AssertEq(nil, err) 185 AssertEq(3, n) 186 187 // Write at offset six. 188 n, err = f.WriteAt([]byte("222"), 6) 189 AssertEq(nil, err) 190 AssertEq(3, n) 191 192 // Read the full contents of the file. 193 // 194 // Linux's support for pwrite is buggy; the pwrite(2) man page says this: 195 // 196 // POSIX requires that opening a file with the O_APPEND flag should have 197 // no affect on the location at which pwrite() writes data. However, on 198 // Linux, if a file is opened with O_APPEND, pwrite() appends data to 199 // the end of the file, regardless of the value of offset. 200 // 201 contents, err := ioutil.ReadFile(f.Name()) 202 AssertEq(nil, err) 203 204 if runtime.GOOS == "linux" { 205 ExpectEq("111222", string(contents)) 206 } else { 207 ExpectEq("111\x00\x00\x00222", string(contents)) 208 } 209 } 210 211 func (t *PosixTest) WriteAtDoesntChangeOffset_NotAppendMode() { 212 var err error 213 var n int 214 215 // Create a file. 216 f, err := os.Create(path.Join(t.dir, "foo")) 217 t.toClose = append(t.toClose, f) 218 AssertEq(nil, err) 219 220 // Make it 16 bytes long. 221 err = f.Truncate(16) 222 AssertEq(nil, err) 223 224 // Seek to offset 4. 225 _, err = f.Seek(4, 0) 226 AssertEq(nil, err) 227 228 // Write the range [10, 14). 229 n, err = f.WriteAt([]byte("taco"), 2) 230 AssertEq(nil, err) 231 AssertEq(4, n) 232 233 // We should still be at offset 4. 234 offset, err := getFileOffset(f) 235 AssertEq(nil, err) 236 ExpectEq(4, offset) 237 } 238 239 func (t *PosixTest) WriteAtDoesntChangeOffset_AppendMode() { 240 return // WriteAt on O_APPEND files returns an error starting with Go 1.13 241 var err error 242 var n int 243 244 // Create a file in append mode. 245 f, err := os.OpenFile( 246 path.Join(t.dir, "foo"), 247 os.O_RDWR|os.O_APPEND|os.O_CREATE, 248 0600) 249 250 t.toClose = append(t.toClose, f) 251 AssertEq(nil, err) 252 253 // Make it 16 bytes long. 254 err = f.Truncate(16) 255 AssertEq(nil, err) 256 257 // Seek to offset 4. 258 _, err = f.Seek(4, 0) 259 AssertEq(nil, err) 260 261 // Write the range [10, 14). 262 n, err = f.WriteAt([]byte("taco"), 2) 263 AssertEq(nil, err) 264 AssertEq(4, n) 265 266 // We should still be at offset 4. 267 offset, err := getFileOffset(f) 268 AssertEq(nil, err) 269 ExpectEq(4, offset) 270 } 271 272 func (t *PosixTest) AppendMode() { 273 return // WriteAt on O_APPEND files returns an error starting with Go 1.13 274 var err error 275 var n int 276 var off int64 277 buf := make([]byte, 1024) 278 279 // Create a file with some contents. 280 fileName := path.Join(t.dir, "foo") 281 err = ioutil.WriteFile(fileName, []byte("Jello, "), 0600) 282 AssertEq(nil, err) 283 284 // Open the file in append mode. 285 f, err := os.OpenFile(fileName, os.O_RDWR|os.O_APPEND, 0600) 286 t.toClose = append(t.toClose, f) 287 AssertEq(nil, err) 288 289 // Seek to somewhere silly and then write. 290 off, err = f.Seek(2, 0) 291 AssertEq(nil, err) 292 AssertEq(2, off) 293 294 n, err = f.Write([]byte("world!")) 295 AssertEq(nil, err) 296 AssertEq(6, n) 297 298 // The offset should have been updated to point at the end of the file. 299 off, err = getFileOffset(f) 300 AssertEq(nil, err) 301 ExpectEq(13, off) 302 303 // A random write should still work, without updating the offset. 304 n, err = f.WriteAt([]byte("H"), 0) 305 AssertEq(nil, err) 306 AssertEq(1, n) 307 308 off, err = getFileOffset(f) 309 AssertEq(nil, err) 310 ExpectEq(13, off) 311 312 // Read back the contents of the file, which should be correct even though we 313 // seeked to a silly place before writing the world part. 314 // 315 // Linux's support for pwrite is buggy; the pwrite(2) man page says this: 316 // 317 // POSIX requires that opening a file with the O_APPEND flag should have 318 // no affect on the location at which pwrite() writes data. However, on 319 // Linux, if a file is opened with O_APPEND, pwrite() appends data to 320 // the end of the file, regardless of the value of offset. 321 // 322 // So we allow either the POSIX result or the Linux result. 323 n, err = f.ReadAt(buf, 0) 324 AssertEq(io.EOF, err) 325 326 if runtime.GOOS == "linux" { 327 ExpectEq("Jello, world!H", string(buf[:n])) 328 } else { 329 ExpectEq("Hello, world!", string(buf[:n])) 330 } 331 } 332 333 func (t *PosixTest) ReadsPastEndOfFile() { 334 var err error 335 var n int 336 buf := make([]byte, 1024) 337 338 // Create a file. 339 f, err := os.Create(path.Join(t.dir, "foo")) 340 t.toClose = append(t.toClose, f) 341 AssertEq(nil, err) 342 343 // Give it some contents. 344 n, err = f.Write([]byte("taco")) 345 AssertEq(nil, err) 346 AssertEq(4, n) 347 348 // Read a range overlapping EOF. 349 n, err = f.ReadAt(buf[:4], 2) 350 AssertEq(io.EOF, err) 351 ExpectEq(2, n) 352 ExpectEq("co", string(buf[:n])) 353 354 // Read a range starting at EOF. 355 n, err = f.ReadAt(buf[:4], 4) 356 AssertEq(io.EOF, err) 357 ExpectEq(0, n) 358 ExpectEq("", string(buf[:n])) 359 360 // Read a range starting past EOF. 361 n, err = f.ReadAt(buf[:4], 100) 362 AssertEq(io.EOF, err) 363 ExpectEq(0, n) 364 ExpectEq("", string(buf[:n])) 365 } 366 367 func (t *PosixTest) HardLinkDirectory() { 368 dirName := path.Join(t.dir, "dir") 369 370 // Create a directory. 371 err := os.Mkdir(dirName, 0700) 372 AssertEq(nil, err) 373 374 // Attempt to hard-link it to a new name. 375 err = os.Link(dirName, path.Join(t.dir, "other")) 376 377 AssertNe(nil, err) 378 ExpectThat(err, Error(HasSubstr("link"))) 379 ExpectThat(err, Error(HasSubstr("not permitted"))) 380 } 381 382 func (t *PosixTest) RmdirWhileOpenedForReading() { 383 var err error 384 385 // Create a directory. 386 err = os.Mkdir(path.Join(t.dir, "dir"), 0700) 387 AssertEq(nil, err) 388 389 // Open the directory for reading. 390 f, err := os.Open(path.Join(t.dir, "dir")) 391 defer func() { 392 if f != nil { 393 ExpectEq(nil, f.Close()) 394 } 395 }() 396 397 AssertEq(nil, err) 398 399 // Remove the directory. 400 err = os.Remove(path.Join(t.dir, "dir")) 401 AssertEq(nil, err) 402 403 // Create a new directory, with the same name even, and add some contents 404 // within it. 405 err = os.MkdirAll(path.Join(t.dir, "dir/foo"), 0700) 406 AssertEq(nil, err) 407 408 err = os.MkdirAll(path.Join(t.dir, "dir/bar"), 0700) 409 AssertEq(nil, err) 410 411 err = os.MkdirAll(path.Join(t.dir, "dir/baz"), 0700) 412 AssertEq(nil, err) 413 414 // We should still be able to stat the open file handle. 415 fi, err := f.Stat() 416 ExpectEq("dir", fi.Name()) 417 418 // Attempt to read from the directory. This shouldn't see any junk from the 419 // new directory. It should either succeed with an empty result or should 420 // return ENOENT. 421 names, err := f.Readdirnames(0) 422 423 if err != nil { 424 ExpectThat(err, Error(HasSubstr("no such file"))) 425 } else { 426 ExpectThat(names, ElementsAre()) 427 } 428 } 429 430 func (t *PosixTest) CreateInParallel_NoTruncate() { 431 fusetesting.RunCreateInParallelTest_NoTruncate(t.ctx, t.dir) 432 } 433 434 func (t *PosixTest) CreateInParallel_Truncate() { 435 fusetesting.RunCreateInParallelTest_Truncate(t.ctx, t.dir) 436 } 437 438 func (t *PosixTest) CreateInParallel_Exclusive() { 439 fusetesting.RunCreateInParallelTest_Exclusive(t.ctx, t.dir) 440 } 441 442 func (t *PosixTest) MkdirInParallel() { 443 fusetesting.RunMkdirInParallelTest(t.ctx, t.dir) 444 } 445 446 func (t *PosixTest) SymlinkInParallel() { 447 fusetesting.RunSymlinkInParallelTest(t.ctx, t.dir) 448 } 449 450 func (t *PosixTest) HardlinkInParallel() { 451 fusetesting.RunHardlinkInParallelTest(t.ctx, t.dir) 452 }