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