From 0978957a2ecc82e934061db43dec48ef5534c17c Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 27 Nov 2017 19:51:35 +0000 Subject: [PATCH] vfs: make sure all 96 combinations of flags for Open work --- vfs/make_open_tests.go | 228 ++++++++++ vfs/open_test.go | 987 +++++++++++++++++++++++++++++++++++++++++ vfs/read_write.go | 69 ++- vfs/read_write_test.go | 89 ++++ 4 files changed, 1357 insertions(+), 16 deletions(-) create mode 100644 vfs/make_open_tests.go create mode 100644 vfs/open_test.go diff --git a/vfs/make_open_tests.go b/vfs/make_open_tests.go new file mode 100644 index 000000000..8e0a773ba --- /dev/null +++ b/vfs/make_open_tests.go @@ -0,0 +1,228 @@ +// This makes the open test suite +// +// Run with go run make_open_tests.go | gofmt > open_test.go +// +//+build none + +// FIXME include read too? + +package main + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "os" + "strings" +) + +// Interprets err into a vfs error +func whichError(err error) string { + switch err { + case nil: + return "nil" + case io.EOF: + return "io.EOF" + } + s := err.Error() + switch { + case strings.Contains(s, "no such file or directory"): + return "ENOENT" + case strings.Contains(s, "bad file descriptor"): + return "EBADF" + case strings.Contains(s, "file exists"): + return "EEXIST" + } + log.Fatalf("Unknown error: %v", err) + return "" +} + +// test Opening, reading and writing the file handle with the flags given +func test(fileName string, flags int, mode string) { + // first try with file not existing + _, err := os.Stat(fileName) + if !os.IsNotExist(err) { + log.Fatalf("File must not exist") + } + f, openNonExistentErr := os.OpenFile(fileName, flags, 0666) + + var readNonExistentErr error + var writeNonExistentErr error + if openNonExistentErr == nil { + // read some bytes + buf := []byte{0, 0} + _, readNonExistentErr = f.Read(buf) + + // write some bytes + _, writeNonExistentErr = f.Write([]byte("hello")) + + // close + err = f.Close() + if err != nil { + log.Fatalf("failed to close: %v", err) + } + } + + // write the file + f, err = os.Create(fileName) + if err != nil { + log.Fatalf("failed to create: %v", err) + } + n, err := f.Write([]byte("hello")) + if n != 5 || err != nil { + log.Fatalf("failed to write n=%d: %v", n, err) + } + // close + err = f.Close() + if err != nil { + log.Fatalf("failed to close: %v", err) + } + + // then open file and try with file existing + + f, openExistingErr := os.OpenFile(fileName, flags, 0666) + var readExistingErr error + var writeExistingErr error + if openExistingErr == nil { + // read some bytes + buf := []byte{0, 0} + _, readExistingErr = f.Read(buf) + + // write some bytes + n, writeExistingErr = f.Write([]byte("HEL")) + + // close + err = f.Close() + if err != nil { + log.Fatalf("failed to close: %v", err) + } + } + + // read the file + f, err = os.Open(fileName) + if err != nil { + log.Fatalf("failed to open: %v", err) + } + var buf = make([]byte, 64) + n, err = f.Read(buf) + if err != nil && err != io.EOF { + log.Fatalf("failed to read n=%d: %v", n, err) + } + err = f.Close() + if err != nil { + log.Fatalf("failed to close: %v", err) + } + contents := string(buf[:n]) + + // remove file + err = os.Remove(fileName) + if err != nil { + log.Fatalf("failed to remove: %v", err) + } + + // output the struct + fmt.Printf(`{ + flags: %s, + what: %q, + openNonExistentErr: %s, + readNonExistentErr: %s, + writeNonExistentErr: %s, + openExistingErr: %s, + readExistingErr: %s, + writeExistingErr: %s, + contents: %q, +},`, + mode, + mode, + whichError(openNonExistentErr), + whichError(readNonExistentErr), + whichError(writeNonExistentErr), + whichError(openExistingErr), + whichError(readExistingErr), + whichError(writeExistingErr), + contents) +} + +func main() { + fmt.Printf(`// data generated by go run make_open_tests.go | gofmt > open_test.go + +package vfs + +import ( + "os" + "io" +) + +// openTest describes a test of OpenFile +type openTest struct{ + flags int + what string + openNonExistentErr error + readNonExistentErr error + writeNonExistentErr error + openExistingErr error + readExistingErr error + writeExistingErr error + contents string +} + +// openTests is a suite of tests for OpenFile with all possible +// combination of flags. This obeys Unix semantics even on Windows. +var openTests = []openTest{ +`) + f, err := ioutil.TempFile("", "open-test") + if err != nil { + log.Fatal(err) + } + fileName := f.Name() + _ = f.Close() + err = os.Remove(fileName) + if err != nil { + log.Fatalf("failed to remove: %v", err) + } + for _, rwMode := range []int{os.O_RDONLY, os.O_WRONLY, os.O_RDWR} { + flags0 := rwMode + parts0 := []string{"os.O_RDONLY", "os.O_WRONLY", "os.O_RDWR"}[rwMode : rwMode+1] + for _, appendMode := range []int{0, os.O_APPEND} { + flags1 := flags0 | appendMode + parts1 := parts0 + if appendMode != 0 { + parts1 = append(parts1, "os.O_APPEND") + } + for _, createMode := range []int{0, os.O_CREATE} { + flags2 := flags1 | createMode + parts2 := parts1 + if createMode != 0 { + parts2 = append(parts2, "os.O_CREATE") + } + for _, exclMode := range []int{0, os.O_EXCL} { + flags3 := flags2 | exclMode + parts3 := parts2 + if exclMode != 0 { + parts3 = append(parts2, "os.O_EXCL") + } + for _, syncMode := range []int{0, os.O_SYNC} { + flags4 := flags3 | syncMode + parts4 := parts3 + if syncMode != 0 { + parts4 = append(parts4, "os.O_SYNC") + } + for _, truncMode := range []int{0, os.O_TRUNC} { + flags5 := flags4 | truncMode + parts5 := parts4 + if truncMode != 0 { + parts5 = append(parts5, "os.O_TRUNC") + } + textMode := strings.Join(parts5, "|") + flags := flags5 + + test(fileName, flags, textMode) + } + } + } + } + } + } + fmt.Printf("\n}\n") +} diff --git a/vfs/open_test.go b/vfs/open_test.go new file mode 100644 index 000000000..6ff4b65e6 --- /dev/null +++ b/vfs/open_test.go @@ -0,0 +1,987 @@ +// data generated by go run make_open_tests.go | gofmt > open_test.go + +package vfs + +import ( + "io" + "os" +) + +// openTest describes a test of OpenFile +type openTest struct { + flags int + what string + openNonExistentErr error + readNonExistentErr error + writeNonExistentErr error + openExistingErr error + readExistingErr error + writeExistingErr error + contents string +} + +// openTests is a suite of tests for OpenFile with all possible +// combination of flags. This obeys Unix semantics even on Windows. +var openTests = []openTest{ + { + flags: os.O_RDONLY, + what: "os.O_RDONLY", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: EBADF, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_TRUNC, + what: "os.O_RDONLY|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: EBADF, + contents: "", + }, { + flags: os.O_RDONLY | os.O_SYNC, + what: "os.O_RDONLY|os.O_SYNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: EBADF, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDONLY|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: EBADF, + contents: "", + }, { + flags: os.O_RDONLY | os.O_EXCL, + what: "os.O_RDONLY|os.O_EXCL", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: EBADF, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_EXCL | os.O_TRUNC, + what: "os.O_RDONLY|os.O_EXCL|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: EBADF, + contents: "", + }, { + flags: os.O_RDONLY | os.O_EXCL | os.O_SYNC, + what: "os.O_RDONLY|os.O_EXCL|os.O_SYNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: EBADF, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_EXCL | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDONLY|os.O_EXCL|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: EBADF, + contents: "", + }, { + flags: os.O_RDONLY | os.O_CREATE, + what: "os.O_RDONLY|os.O_CREATE", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: EBADF, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_CREATE | os.O_TRUNC, + what: "os.O_RDONLY|os.O_CREATE|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: EBADF, + contents: "", + }, { + flags: os.O_RDONLY | os.O_CREATE | os.O_SYNC, + what: "os.O_RDONLY|os.O_CREATE|os.O_SYNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: EBADF, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_CREATE | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDONLY|os.O_CREATE|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: EBADF, + contents: "", + }, { + flags: os.O_RDONLY | os.O_CREATE | os.O_EXCL, + what: "os.O_RDONLY|os.O_CREATE|os.O_EXCL", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_CREATE | os.O_EXCL | os.O_TRUNC, + what: "os.O_RDONLY|os.O_CREATE|os.O_EXCL|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_CREATE | os.O_EXCL | os.O_SYNC, + what: "os.O_RDONLY|os.O_CREATE|os.O_EXCL|os.O_SYNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDONLY|os.O_CREATE|os.O_EXCL|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_APPEND, + what: "os.O_RDONLY|os.O_APPEND", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: EBADF, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_TRUNC, + what: "os.O_RDONLY|os.O_APPEND|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: EBADF, + contents: "", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_SYNC, + what: "os.O_RDONLY|os.O_APPEND|os.O_SYNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: EBADF, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDONLY|os.O_APPEND|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: EBADF, + contents: "", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_EXCL, + what: "os.O_RDONLY|os.O_APPEND|os.O_EXCL", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: EBADF, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_EXCL | os.O_TRUNC, + what: "os.O_RDONLY|os.O_APPEND|os.O_EXCL|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: EBADF, + contents: "", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_EXCL | os.O_SYNC, + what: "os.O_RDONLY|os.O_APPEND|os.O_EXCL|os.O_SYNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: EBADF, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_EXCL | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDONLY|os.O_APPEND|os.O_EXCL|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: EBADF, + contents: "", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_CREATE, + what: "os.O_RDONLY|os.O_APPEND|os.O_CREATE", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: EBADF, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC, + what: "os.O_RDONLY|os.O_APPEND|os.O_CREATE|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: EBADF, + contents: "", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_CREATE | os.O_SYNC, + what: "os.O_RDONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: EBADF, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_CREATE | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: EBADF, + contents: "", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_CREATE | os.O_EXCL, + what: "os.O_RDONLY|os.O_APPEND|os.O_CREATE|os.O_EXCL", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_TRUNC, + what: "os.O_RDONLY|os.O_APPEND|os.O_CREATE|os.O_EXCL|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC, + what: "os.O_RDONLY|os.O_APPEND|os.O_CREATE|os.O_EXCL|os.O_SYNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDONLY | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDONLY|os.O_APPEND|os.O_CREATE|os.O_EXCL|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: EBADF, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_WRONLY, + what: "os.O_WRONLY", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HELlo", + }, { + flags: os.O_WRONLY | os.O_TRUNC, + what: "os.O_WRONLY|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_WRONLY | os.O_SYNC, + what: "os.O_WRONLY|os.O_SYNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HELlo", + }, { + flags: os.O_WRONLY | os.O_SYNC | os.O_TRUNC, + what: "os.O_WRONLY|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_WRONLY | os.O_EXCL, + what: "os.O_WRONLY|os.O_EXCL", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HELlo", + }, { + flags: os.O_WRONLY | os.O_EXCL | os.O_TRUNC, + what: "os.O_WRONLY|os.O_EXCL|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_WRONLY | os.O_EXCL | os.O_SYNC, + what: "os.O_WRONLY|os.O_EXCL|os.O_SYNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HELlo", + }, { + flags: os.O_WRONLY | os.O_EXCL | os.O_SYNC | os.O_TRUNC, + what: "os.O_WRONLY|os.O_EXCL|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_WRONLY | os.O_CREATE, + what: "os.O_WRONLY|os.O_CREATE", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HELlo", + }, { + flags: os.O_WRONLY | os.O_CREATE | os.O_TRUNC, + what: "os.O_WRONLY|os.O_CREATE|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_WRONLY | os.O_CREATE | os.O_SYNC, + what: "os.O_WRONLY|os.O_CREATE|os.O_SYNC", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HELlo", + }, { + flags: os.O_WRONLY | os.O_CREATE | os.O_SYNC | os.O_TRUNC, + what: "os.O_WRONLY|os.O_CREATE|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_WRONLY | os.O_CREATE | os.O_EXCL, + what: "os.O_WRONLY|os.O_CREATE|os.O_EXCL", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_WRONLY | os.O_CREATE | os.O_EXCL | os.O_TRUNC, + what: "os.O_WRONLY|os.O_CREATE|os.O_EXCL|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_WRONLY | os.O_CREATE | os.O_EXCL | os.O_SYNC, + what: "os.O_WRONLY|os.O_CREATE|os.O_EXCL|os.O_SYNC", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_WRONLY | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC, + what: "os.O_WRONLY|os.O_CREATE|os.O_EXCL|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_WRONLY | os.O_APPEND, + what: "os.O_WRONLY|os.O_APPEND", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "helloHEL", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_TRUNC, + what: "os.O_WRONLY|os.O_APPEND|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_SYNC, + what: "os.O_WRONLY|os.O_APPEND|os.O_SYNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "helloHEL", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_SYNC | os.O_TRUNC, + what: "os.O_WRONLY|os.O_APPEND|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_EXCL, + what: "os.O_WRONLY|os.O_APPEND|os.O_EXCL", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "helloHEL", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_EXCL | os.O_TRUNC, + what: "os.O_WRONLY|os.O_APPEND|os.O_EXCL|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_EXCL | os.O_SYNC, + what: "os.O_WRONLY|os.O_APPEND|os.O_EXCL|os.O_SYNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "helloHEL", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_EXCL | os.O_SYNC | os.O_TRUNC, + what: "os.O_WRONLY|os.O_APPEND|os.O_EXCL|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_CREATE, + what: "os.O_WRONLY|os.O_APPEND|os.O_CREATE", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "helloHEL", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC, + what: "os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_SYNC, + what: "os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "helloHEL", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_SYNC | os.O_TRUNC, + what: "os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: EBADF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_EXCL, + what: "os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_EXCL", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_TRUNC, + what: "os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_EXCL|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC, + what: "os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_EXCL|os.O_SYNC", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC, + what: "os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_EXCL|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: EBADF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDWR, + what: "os.O_RDWR", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: nil, + contents: "heHEL", + }, { + flags: os.O_RDWR | os.O_TRUNC, + what: "os.O_RDWR|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_RDWR | os.O_SYNC, + what: "os.O_RDWR|os.O_SYNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: nil, + contents: "heHEL", + }, { + flags: os.O_RDWR | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDWR|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_RDWR | os.O_EXCL, + what: "os.O_RDWR|os.O_EXCL", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: nil, + contents: "heHEL", + }, { + flags: os.O_RDWR | os.O_EXCL | os.O_TRUNC, + what: "os.O_RDWR|os.O_EXCL|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_RDWR | os.O_EXCL | os.O_SYNC, + what: "os.O_RDWR|os.O_EXCL|os.O_SYNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: nil, + contents: "heHEL", + }, { + flags: os.O_RDWR | os.O_EXCL | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDWR|os.O_EXCL|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_RDWR | os.O_CREATE, + what: "os.O_RDWR|os.O_CREATE", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: nil, + contents: "heHEL", + }, { + flags: os.O_RDWR | os.O_CREATE | os.O_TRUNC, + what: "os.O_RDWR|os.O_CREATE|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_RDWR | os.O_CREATE | os.O_SYNC, + what: "os.O_RDWR|os.O_CREATE|os.O_SYNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: nil, + contents: "heHEL", + }, { + flags: os.O_RDWR | os.O_CREATE | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDWR|os.O_CREATE|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_RDWR | os.O_CREATE | os.O_EXCL, + what: "os.O_RDWR|os.O_CREATE|os.O_EXCL", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDWR | os.O_CREATE | os.O_EXCL | os.O_TRUNC, + what: "os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDWR | os.O_CREATE | os.O_EXCL | os.O_SYNC, + what: "os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDWR | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDWR | os.O_APPEND, + what: "os.O_RDWR|os.O_APPEND", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: nil, + contents: "helloHEL", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_TRUNC, + what: "os.O_RDWR|os.O_APPEND|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_SYNC, + what: "os.O_RDWR|os.O_APPEND|os.O_SYNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: nil, + contents: "helloHEL", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDWR|os.O_APPEND|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_EXCL, + what: "os.O_RDWR|os.O_APPEND|os.O_EXCL", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: nil, + contents: "helloHEL", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_EXCL | os.O_TRUNC, + what: "os.O_RDWR|os.O_APPEND|os.O_EXCL|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_EXCL | os.O_SYNC, + what: "os.O_RDWR|os.O_APPEND|os.O_EXCL|os.O_SYNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: nil, + contents: "helloHEL", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_EXCL | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDWR|os.O_APPEND|os.O_EXCL|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: ENOENT, + readNonExistentErr: nil, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_CREATE, + what: "os.O_RDWR|os.O_APPEND|os.O_CREATE", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: nil, + contents: "helloHEL", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_CREATE | os.O_TRUNC, + what: "os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_CREATE | os.O_SYNC, + what: "os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_SYNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: nil, + writeExistingErr: nil, + contents: "helloHEL", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_CREATE | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: nil, + readExistingErr: io.EOF, + writeExistingErr: nil, + contents: "HEL", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_CREATE | os.O_EXCL, + what: "os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_EXCL", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_TRUNC, + what: "os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_EXCL|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC, + what: "os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_EXCL|os.O_SYNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, { + flags: os.O_RDWR | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC, + what: "os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_EXCL|os.O_SYNC|os.O_TRUNC", + openNonExistentErr: nil, + readNonExistentErr: io.EOF, + writeNonExistentErr: nil, + openExistingErr: EEXIST, + readExistingErr: nil, + writeExistingErr: nil, + contents: "hello", + }, +} diff --git a/vfs/read_write.go b/vfs/read_write.go index d66bb3d77..72a96f1b9 100644 --- a/vfs/read_write.go +++ b/vfs/read_write.go @@ -3,6 +3,7 @@ package vfs import ( "io" "os" + "runtime" "sync" "github.com/ncw/rclone/fs" @@ -44,6 +45,11 @@ func newRWFileHandle(d *Dir, f *File, remote string, flags int) (fh *RWFileHandl return nil, errors.Wrap(err, "open RW handle failed to make cache directory") } + // if O_CREATE and O_EXCL are set and if path already exists, then return EEXIST + if flags&(os.O_CREATE|os.O_EXCL) == os.O_CREATE|os.O_EXCL && f.exists() { + return nil, EEXIST + } + fh = &RWFileHandle{ o: f.o, file: f, @@ -69,6 +75,8 @@ func (fh *RWFileHandle) openPending(truncate bool) (err error) { return nil } + cacheFileOpenFlags := fh.flags + // if not truncating the file, need to read it first if fh.flags&os.O_TRUNC == 0 && !truncate { // Fetch the file if it hasn't changed @@ -92,12 +100,30 @@ func (fh *RWFileHandle) openPending(truncate bool) (err error) { } } } else { - // Set the size to 0 since we are truncating + // Set the size to 0 since we are truncating and flag we need to write it back fh.file.setSize(0) + fh.writeCalled = true + if fh.flags&os.O_CREATE != 0 && fh.file.exists() { + // create and empty file if it exists on the source + cacheFileOpenFlags |= os.O_CREATE + } + // Windows doesn't seem to deal well with O_TRUNC and + // certain access modes so so truncate the file if it + // exists in these cases. + if runtime.GOOS == "windows" && (fh.flags&accessModeMask == os.O_RDONLY || fh.flags|os.O_APPEND != 0) { + cacheFileOpenFlags &^= os.O_TRUNC + _, err = os.Stat(fh.osPath) + if err == nil { + err = os.Truncate(fh.osPath, 0) + if err != nil { + return errors.Wrap(err, "cache open failed to truncate") + } + } + } } fs.Debugf(fh.remote, "Opening cached copy with flags=%s", decodeOpenFlags(fh.flags)) - fd, err := os.OpenFile(fh.osPath, fh.flags|os.O_CREATE, 0600) + fd, err := os.OpenFile(fh.osPath, cacheFileOpenFlags, 0600) if err != nil { return errors.Wrap(err, "cache open file failed") } @@ -180,8 +206,8 @@ func (fh *RWFileHandle) close() (err error) { // FIXME measure whether we actually did any writes or not - // no writes means no transfer? - if rdwrMode == os.O_RDONLY { - fs.Debugf(fh.remote, "read only so not transferring") + if rdwrMode == os.O_RDONLY && fh.flags&os.O_TRUNC == 0 { + fs.Debugf(fh.remote, "read only and not truncating so not transferring") return nil } @@ -302,30 +328,35 @@ func (fh *RWFileHandle) Stat() (os.FileInfo, error) { return fh.file, nil } -// Read bytes from the file -func (fh *RWFileHandle) Read(b []byte) (n int, err error) { +// readFn is a general purpose read function - pass in a closure to do +// the actual read +func (fh *RWFileHandle) readFn(read func() (int, error)) (n int, err error) { fh.mu.Lock() defer fh.mu.Unlock() if fh.closed { return 0, ECLOSED } + if fh.flags&accessModeMask == os.O_WRONLY { + return 0, EBADF + } if err = fh.openPending(false); err != nil { return n, err } - return fh.File.Read(b) + return read() +} + +// Read bytes from the file +func (fh *RWFileHandle) Read(b []byte) (n int, err error) { + return fh.readFn(func() (int, error) { + return fh.File.Read(b) + }) } // ReadAt bytes from the file at off func (fh *RWFileHandle) ReadAt(b []byte, off int64) (n int, err error) { - fh.mu.Lock() - defer fh.mu.Unlock() - if fh.closed { - return 0, ECLOSED - } - if err = fh.openPending(false); err != nil { - return n, err - } - return fh.File.ReadAt(b, off) + return fh.readFn(func() (int, error) { + return fh.File.ReadAt(b, off) + }) } // Seek to new file position @@ -350,6 +381,9 @@ func (fh *RWFileHandle) writeFn(write func() error) (err error) { if fh.closed { return ECLOSED } + if fh.flags&accessModeMask == os.O_RDONLY { + return EBADF + } if err = fh.openPending(false); err != nil { return err } @@ -421,5 +455,8 @@ func (fh *RWFileHandle) Sync() error { if !fh.opened { return nil } + if fh.flags&accessModeMask == os.O_RDONLY { + return nil + } return fh.File.Sync() } diff --git a/vfs/read_write_test.go b/vfs/read_write_test.go index 6f482cf5a..90713e631 100644 --- a/vfs/read_write_test.go +++ b/vfs/read_write_test.go @@ -2,6 +2,7 @@ package vfs import ( "io" + "io/ioutil" "os" "testing" @@ -286,6 +287,10 @@ func TestRWFileHandleMethodsWrite(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 7, n) + // Sync + err = fh.Sync() + assert.NoError(t, err) + // Stat var fi os.FileInfo fi, err = fh.Stat() @@ -434,3 +439,87 @@ func TestRWFileHandleReleaseWrite(t *testing.T) { assert.NoError(t, err) assert.True(t, fh.closed) } + +func testRWFileHandleOpenTest(t *testing.T, vfs *VFS, test *openTest) { + fileName := "open-test-file" + + // first try with file not existing + _, err := vfs.Stat(fileName) + require.True(t, os.IsNotExist(err), test.what) + + f, openNonExistentErr := vfs.OpenFile(fileName, test.flags, 0666) + + var readNonExistentErr error + var writeNonExistentErr error + if openNonExistentErr == nil { + // read some bytes + buf := []byte{0, 0} + _, readNonExistentErr = f.Read(buf) + + // write some bytes + _, writeNonExistentErr = f.Write([]byte("hello")) + + // close + err = f.Close() + require.NoError(t, err, test.what) + } + + // write the file + f, err = vfs.OpenFile(fileName, os.O_WRONLY|os.O_CREATE, 0777) + require.NoError(t, err, test.what) + _, err = f.Write([]byte("hello")) + require.NoError(t, err, test.what) + err = f.Close() + require.NoError(t, err, test.what) + + // then open file and try with file existing + + f, openExistingErr := vfs.OpenFile(fileName, test.flags, 0666) + var readExistingErr error + var writeExistingErr error + if openExistingErr == nil { + // read some bytes + buf := []byte{0, 0} + _, readExistingErr = f.Read(buf) + + // write some bytes + _, writeExistingErr = f.Write([]byte("HEL")) + + // close + err = f.Close() + require.NoError(t, err, test.what) + } + + // read the file + f, err = vfs.OpenFile(fileName, os.O_RDONLY, 0) + require.NoError(t, err, test.what) + buf, err := ioutil.ReadAll(f) + require.NoError(t, err, test.what) + err = f.Close() + require.NoError(t, err, test.what) + contents := string(buf) + + // remove file + node, err := vfs.Stat(fileName) + require.NoError(t, err, test.what) + err = node.Remove() + require.NoError(t, err, test.what) + + // check + assert.Equal(t, test.readNonExistentErr, readNonExistentErr, "readNonExistentErr: %s: want=%v, got=%v", test.what, test.readNonExistentErr, readNonExistentErr) + assert.Equal(t, test.writeNonExistentErr, writeNonExistentErr, "writeNonExistentErr: %s: want=%v, got=%v", test.what, test.writeNonExistentErr, writeNonExistentErr) + assert.Equal(t, test.readExistingErr, readExistingErr, "readExistingErr: %s: want=%v, got=%v", test.what, test.readExistingErr, readExistingErr) + assert.Equal(t, test.writeExistingErr, writeExistingErr, "writeExistingErr: %s: want=%v, got=%v", test.what, test.writeExistingErr, writeExistingErr) + assert.Equal(t, test.contents, contents, test.what) +} + +func TestRWFileHandleOpenTests(t *testing.T) { + r := fstest.NewRun(t) + defer r.Finalise() + + vfs := New(r.Fremote, nil) + vfs.Opt.CacheMode = CacheModeFull + for _, test := range openTests { + testRWFileHandleOpenTest(t, vfs, &test) + } +}