forked from zach-klippenstein/goadb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sync_file_writer.go
109 lines (88 loc) · 2.8 KB
/
sync_file_writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package adb
import (
"encoding/binary"
"io"
"time"
"github.com/pkg/errors"
)
/*
Remove this. This is a bad/unessecarry abstraction. Use something like
WriteFile(string, io.Reader) instead.
And close method can be omitted!
This has less types, less state, better composition.
It can be easily transformed into an io.Reader or better io.WriterTo via
a closure and a wrapper.
Example:
type WriterFunc func([]byte) (int, error)
func (wf WriterFunc) Write(b []byte) (int, error) {
return wf(b)
}
var F WriterFunc = func(b []byte) (int, error) {
return d.WriteFile("myfile", bytes.NewBuffer(b))
}
*/
// 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.
modTime time.Time
// Reader used to read data from the adb connection.
sender io.WriteCloser
}
var _ io.WriteCloser = &syncFileWriter{}
/*
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.
*/
// 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) > syncMaxChunkSize {
partialBuf = partialBuf[:syncMaxChunkSize]
}
if _, err := w.Write([]byte(statusSyncData)); err != nil {
return written, err
}
// correct?
length := make([]byte, 4)
binary.LittleEndian.PutUint32(length, uint32(len(partialBuf)))
if _, err := w.sender.Write(length[:]); err != nil {
return written, err
}
n, err := w.sender.Write(partialBuf)
if err != nil {
return written + n, err
}
written += n
buf = buf[n:]
}
return written, nil
}
// TODO(jmh): implement
func (w *syncFileWriter) ReadFrom(r io.Reader) (int64, error) {
return 0, ErrNotImplemented
}
func (w *syncFileWriter) Close() error {
if w.modTime == (time.Time{}) {
w.modTime = time.Now()
}
// cancelation check?
buf := make([]byte, 8)
copy(buf[:4], statusSyncDone)
binary.LittleEndian.PutUint32(buf[4:], uint32(w.modTime.Unix()))
_, err := w.sender.Write(buf)
if err != nil {
return err
}
return errors.WithMessage(w.sender.Close(), "error closing FileWriter")
}