goadb/sync_file_writer.go
Zach Klippenstein db28472ff7 FileWriter.Write will now always attempt to write the entire buffer, even if larger than max chunk size.
This is required to work with io.Copy, which returns an error if a write doesn't write the entire buffer but returns no error.
2015-12-30 03:05:56 -08:00

87 lines
2.4 KiB
Go

package goadb
import (
"fmt"
"io"
"os"
"time"
"github.com/zach-klippenstein/goadb/util"
"github.com/zach-klippenstein/goadb/wire"
)
// syncFileWriter wraps a SyncConn that has requested to send a file.
type syncFileWriter struct {
// The modification time to write in the footer.
// If 0, use the current time.
mtime time.Time
// Reader used to read data from the adb connection.
sender wire.SyncSender
}
var _ io.WriteCloser = &syncFileWriter{}
func newSyncFileWriter(s wire.SyncSender, mtime time.Time) io.WriteCloser {
return &syncFileWriter{
mtime: mtime,
sender: s,
}
}
/*
encodePathAndMode encodes a path and file mode as required for starting a send file stream.
From https://android.googlesource.com/platform/system/core/+/master/adb/SYNC.TXT:
The remote file name is split into two parts separated by the last
comma (","). The first part is the actual path, while the second is a decimal
encoded file mode containing the permissions of the file on device.
*/
func encodePathAndMode(path string, mode os.FileMode) []byte {
return []byte(fmt.Sprintf("%s,%d", path, uint32(mode.Perm())))
}
// Write writes the min of (len(buf), 64k).
func (w *syncFileWriter) Write(buf []byte) (n int, err error) {
written := 0
// If buf > 64k we'll have to send multiple chunks.
// TODO Refactor this into something that can coalesce smaller writes into a single chukn.
for len(buf) > 0 {
// Writes < 64k have a one-to-one mapping to chunks.
// If buffer is larger than the max, we'll return the max size and leave it up to the
// caller to handle correctly.
partialBuf := buf
if len(partialBuf) > wire.SyncMaxChunkSize {
partialBuf = partialBuf[:wire.SyncMaxChunkSize]
}
if err := w.sender.SendOctetString(wire.StatusSyncData); err != nil {
return written, err
}
if err := w.sender.SendBytes(partialBuf); err != nil {
return written, err
}
written += len(partialBuf)
buf = buf[len(partialBuf):]
}
return written, nil
}
func (w *syncFileWriter) Close() error {
if w.mtime.IsZero() {
w.mtime = time.Now()
}
if err := w.sender.SendOctetString(wire.StatusSyncDone); err != nil {
return util.WrapErrf(err, "error sending done chunk to close stream")
}
if err := w.sender.SendTime(w.mtime); err != nil {
return util.WrapErrf(err, "error writing file modification time")
}
return util.WrapErrf(w.sender.Close(), "error closing FileWriter")
}