Merge pull request #330 from pkg/issue-295-corrupt-resumes
Issue 295 corrupt resumes
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()