vfs: make sure all 96 combinations of flags for Open work

This commit is contained in:
Nick Craig-Wood 2017-11-27 19:51:35 +00:00
parent d1b19f975d
commit 0978957a2e
4 changed files with 1357 additions and 16 deletions

228
vfs/make_open_tests.go Normal file
View File

@ -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")
}

987
vfs/open_test.go Normal file
View File

@ -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",
},
}

View File

@ -3,6 +3,7 @@ package vfs
import ( import (
"io" "io"
"os" "os"
"runtime"
"sync" "sync"
"github.com/ncw/rclone/fs" "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") 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{ fh = &RWFileHandle{
o: f.o, o: f.o,
file: f, file: f,
@ -69,6 +75,8 @@ func (fh *RWFileHandle) openPending(truncate bool) (err error) {
return nil return nil
} }
cacheFileOpenFlags := fh.flags
// if not truncating the file, need to read it first // if not truncating the file, need to read it first
if fh.flags&os.O_TRUNC == 0 && !truncate { if fh.flags&os.O_TRUNC == 0 && !truncate {
// Fetch the file if it hasn't changed // Fetch the file if it hasn't changed
@ -92,12 +100,30 @@ func (fh *RWFileHandle) openPending(truncate bool) (err error) {
} }
} }
} else { } 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.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)) 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 { if err != nil {
return errors.Wrap(err, "cache open file failed") 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 - // FIXME measure whether we actually did any writes or not -
// no writes means no transfer? // no writes means no transfer?
if rdwrMode == os.O_RDONLY { if rdwrMode == os.O_RDONLY && fh.flags&os.O_TRUNC == 0 {
fs.Debugf(fh.remote, "read only so not transferring") fs.Debugf(fh.remote, "read only and not truncating so not transferring")
return nil return nil
} }
@ -302,30 +328,35 @@ func (fh *RWFileHandle) Stat() (os.FileInfo, error) {
return fh.file, nil return fh.file, nil
} }
// Read bytes from the file // readFn is a general purpose read function - pass in a closure to do
func (fh *RWFileHandle) Read(b []byte) (n int, err error) { // the actual read
func (fh *RWFileHandle) readFn(read func() (int, error)) (n int, err error) {
fh.mu.Lock() fh.mu.Lock()
defer fh.mu.Unlock() defer fh.mu.Unlock()
if fh.closed { if fh.closed {
return 0, ECLOSED return 0, ECLOSED
} }
if fh.flags&accessModeMask == os.O_WRONLY {
return 0, EBADF
}
if err = fh.openPending(false); err != nil { if err = fh.openPending(false); err != nil {
return n, err 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 // ReadAt bytes from the file at off
func (fh *RWFileHandle) ReadAt(b []byte, off int64) (n int, err error) { func (fh *RWFileHandle) ReadAt(b []byte, off int64) (n int, err error) {
fh.mu.Lock() return fh.readFn(func() (int, error) {
defer fh.mu.Unlock() return fh.File.ReadAt(b, off)
if fh.closed { })
return 0, ECLOSED
}
if err = fh.openPending(false); err != nil {
return n, err
}
return fh.File.ReadAt(b, off)
} }
// Seek to new file position // Seek to new file position
@ -350,6 +381,9 @@ func (fh *RWFileHandle) writeFn(write func() error) (err error) {
if fh.closed { if fh.closed {
return ECLOSED return ECLOSED
} }
if fh.flags&accessModeMask == os.O_RDONLY {
return EBADF
}
if err = fh.openPending(false); err != nil { if err = fh.openPending(false); err != nil {
return err return err
} }
@ -421,5 +455,8 @@ func (fh *RWFileHandle) Sync() error {
if !fh.opened { if !fh.opened {
return nil return nil
} }
if fh.flags&accessModeMask == os.O_RDONLY {
return nil
}
return fh.File.Sync() return fh.File.Sync()
} }

View File

@ -2,6 +2,7 @@ package vfs
import ( import (
"io" "io"
"io/ioutil"
"os" "os"
"testing" "testing"
@ -286,6 +287,10 @@ func TestRWFileHandleMethodsWrite(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 7, n) assert.Equal(t, 7, n)
// Sync
err = fh.Sync()
assert.NoError(t, err)
// Stat // Stat
var fi os.FileInfo var fi os.FileInfo
fi, err = fh.Stat() fi, err = fh.Stat()
@ -434,3 +439,87 @@ func TestRWFileHandleReleaseWrite(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, fh.closed) 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)
}
}