Merge pull request #332 from peachfinance/Fstat-stat-options

Added a ClientOption to determine whetehr to use Fstat or Stat when File.WriteTo is being called to support strange behaviour on some servers
diff --git a/attrs.go b/attrs.go
index 58af665..a51cd09 100644
--- a/attrs.go
+++ b/attrs.go
@@ -16,8 +16,8 @@
 	sshFileXferAttrACmodTime   = 0x00000008
 	sshFileXferAttrExtented    = 0x80000000
 
-	sshFileXferAttrAll = sshFileXferAttrSize|sshFileXferAttrUIDGID|sshFileXferAttrPermissions|
-		sshFileXferAttrACmodTime|sshFileXferAttrExtented
+	sshFileXferAttrAll = sshFileXferAttrSize | sshFileXferAttrUIDGID | sshFileXferAttrPermissions |
+		sshFileXferAttrACmodTime | sshFileXferAttrExtented
 )
 
 // fileInfo is an artificial type designed to satisfy os.FileInfo.
@@ -176,7 +176,7 @@
 // toFileMode converts sftp filemode bits to the os.FileMode specification
 func toFileMode(mode uint32) os.FileMode {
 	var fm = os.FileMode(mode & 0777)
-	switch mode & syscall.S_IFMT {
+	switch mode & S_IFMT {
 	case syscall.S_IFBLK:
 		fm |= os.ModeDevice
 	case syscall.S_IFCHR:
diff --git a/request-interfaces.go b/request-interfaces.go
index 06106af..dd224cd 100644
--- a/request-interfaces.go
+++ b/request-interfaces.go
@@ -25,6 +25,8 @@
 // The request server code will call Close() on the returned io.WriterAt
 // ojbect if an io.Closer type assertion succeeds.
 // Note in cases of an error, the error text will be sent to the client.
+// Note when receiving an Append flag it is important to not open files using
+// O_APPEND if you plan to use WriteAt, as they conflict.
 // Called for Methods: Put, Open
 type FileWriter interface {
 	Filewrite(*Request) (io.WriterAt, error)
diff --git a/server.go b/server.go
index e7d6d81..905b75c 100644
--- a/server.go
+++ b/server.go
@@ -398,9 +398,9 @@
 		return statusFromError(p, syscall.EINVAL)
 	}
 
-	if p.hasPflags(sshFxfAppend) {
-		osFlags |= os.O_APPEND
-	}
+	// Don't use O_APPEND flag as it conflicts with WriteAt.
+	// The sshFxfAppend flag is a no-op here as the client sends the offsets.
+
 	if p.hasPflags(sshFxfCreat) {
 		osFlags |= os.O_CREATE
 	}
diff --git a/server_integration_test.go b/server_integration_test.go
index 384b6e4..1499dd8 100644
--- a/server_integration_test.go
+++ b/server_integration_test.go
@@ -649,6 +649,53 @@
 	}
 }
 
+func TestServerResume(t *testing.T) {
+	listenerGo, hostGo, portGo := testServer(t, GolangSFTP, READONLY)
+	defer listenerGo.Close()
+
+	tmpFileLocal := "/tmp/" + randName()
+	tmpFileRemote := "/tmp/" + randName()
+	defer os.RemoveAll(tmpFileLocal)
+	defer os.RemoveAll(tmpFileRemote)
+
+	t.Logf("put: local %v remote %v", tmpFileLocal, tmpFileRemote)
+
+	// create a local file with random contents to be pushed to the server
+	tmpFileLocalData := randData(2 * 1024 * 1024)
+	// only write half the data to simulate a split upload
+	half := 1024 * 1024
+	err := ioutil.WriteFile(tmpFileLocal, tmpFileLocalData[:half], 0644)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// sftp the first half of the file to the server
+	output, err := runSftpClient(t, "put "+tmpFileLocal+" "+tmpFileRemote,
+		"/", hostGo, portGo)
+	if err != nil {
+		t.Fatalf("runSftpClient failed: %v, output\n%v\n", err, output)
+	}
+
+	// write the full file out
+	err = ioutil.WriteFile(tmpFileLocal, tmpFileLocalData, 0644)
+	if err != nil {
+		t.Fatal(err)
+	}
+	// re-sftp the full file with the append flag set
+	output, err = runSftpClient(t, "put -a "+tmpFileLocal+" "+tmpFileRemote,
+		"/", hostGo, portGo)
+	if err != nil {
+		t.Fatalf("runSftpClient failed: %v, output\n%v\n", err, output)
+	}
+
+	// tmpFileRemote should now exist, with the same contents
+	if tmpFileRemoteData, err := ioutil.ReadFile(tmpFileRemote); err != nil {
+		t.Fatal(err)
+	} else if string(tmpFileLocalData) != string(tmpFileRemoteData) {
+		t.Fatal("contents of file incorrect after put")
+	}
+}
+
 func TestServerGet(t *testing.T) {
 	listenerGo, hostGo, portGo := testServer(t, GolangSFTP, READONLY)
 	defer listenerGo.Close()
diff --git a/syscall_fixed.go b/syscall_fixed.go
new file mode 100644
index 0000000..d404577
--- /dev/null
+++ b/syscall_fixed.go
@@ -0,0 +1,9 @@
+// +build plan9 windows js,wasm
+
+// Go defines S_IFMT on windows, plan9 and js/wasm as 0x1f000 instead of
+// 0xf000. None of the the other S_IFxyz values include the "1" (in 0x1f000)
+// which prevents them from matching the bitmask.
+
+package sftp
+
+const S_IFMT = 0xf000
diff --git a/syscall_good.go b/syscall_good.go
new file mode 100644
index 0000000..4c2b240
--- /dev/null
+++ b/syscall_good.go
@@ -0,0 +1,8 @@
+// +build !plan9,!windows
+// +build !js !wasm
+
+package sftp
+
+import "syscall"
+
+const S_IFMT = syscall.S_IFMT