Parse more bits out of file modes in sync mode.
This commit is contained in:
parent
28a745dc7a
commit
d0b99d9f57
|
@ -16,7 +16,7 @@ var port = flag.Int("p", wire.AdbPort, "")
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
client := adb.NewHostClientDialer(wire.NewDialer("", *port))
|
client := adb.NewHostClientPort(*port)
|
||||||
fmt.Println("Starting server…")
|
fmt.Println("Starting server…")
|
||||||
client.StartServer()
|
client.StartServer()
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,7 @@ import (
|
||||||
"github.com/zach-klippenstein/goadb/wire"
|
"github.com/zach-klippenstein/goadb/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// DeviceClient communicates with a specific Android device.
|
||||||
DeviceClient communicates with a specific Android device.
|
|
||||||
*/
|
|
||||||
type DeviceClient struct {
|
type DeviceClient struct {
|
||||||
dialer nilSafeDialer
|
dialer nilSafeDialer
|
||||||
descriptor *DeviceDescriptor
|
descriptor *DeviceDescriptor
|
||||||
|
|
|
@ -2,10 +2,20 @@ package goadb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/zach-klippenstein/goadb/wire"
|
"github.com/zach-klippenstein/goadb/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DirEntry holds information about a directory entry on a device.
|
||||||
|
type DirEntry struct {
|
||||||
|
Name string
|
||||||
|
Mode os.FileMode
|
||||||
|
Size int32
|
||||||
|
ModifiedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
// DirEntries iterates over directory entries.
|
// DirEntries iterates over directory entries.
|
||||||
type DirEntries struct {
|
type DirEntries struct {
|
||||||
scanner wire.SyncScanner
|
scanner wire.SyncScanner
|
||||||
|
|
|
@ -28,6 +28,10 @@ func NewHostClient() *HostClient {
|
||||||
return NewHostClientDialer(nil)
|
return NewHostClientDialer(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewHostClientPort(port int) *HostClient {
|
||||||
|
return NewHostClientDialer(wire.NewDialer("", port))
|
||||||
|
}
|
||||||
|
|
||||||
func NewHostClientDialer(d wire.Dialer) *HostClient {
|
func NewHostClientDialer(d wire.Dialer) *HostClient {
|
||||||
return &HostClient{nilSafeDialer{d}}
|
return &HostClient{nilSafeDialer{d}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ type MockServer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MockServer) Dial() (*wire.Conn, error) {
|
func (s *MockServer) Dial() (*wire.Conn, error) {
|
||||||
return wire.NewConn(s, s, s.Close), nil
|
return wire.NewConn(s, s), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MockServer) ReadStatus() (wire.StatusCode, error) {
|
func (s *MockServer) ReadStatus() (wire.StatusCode, error) {
|
||||||
|
|
|
@ -4,24 +4,10 @@ package goadb
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/zach-klippenstein/goadb/wire"
|
"github.com/zach-klippenstein/goadb/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
DirEntry holds information about a directory entry on a device.
|
|
||||||
|
|
||||||
Unfortunately, adb doesn't seem to set the directory bit for directories.
|
|
||||||
*/
|
|
||||||
type DirEntry struct {
|
|
||||||
Name string
|
|
||||||
Mode os.FileMode
|
|
||||||
Size int32
|
|
||||||
ModifiedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func stat(conn *wire.SyncConn, path string) (*DirEntry, error) {
|
func stat(conn *wire.SyncConn, path string) (*DirEntry, error) {
|
||||||
if err := conn.SendOctetString("STAT"); err != nil {
|
if err := conn.SendOctetString("STAT"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -16,7 +16,7 @@ func TestReadNextChunk(t *testing.T) {
|
||||||
// Read 1st chunk
|
// Read 1st chunk
|
||||||
reader, err := readNextChunk(s)
|
reader, err := readNextChunk(s)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 6, reader.(*io.LimitedReader).N)
|
assert.Equal(t, int64(6), reader.(*io.LimitedReader).N)
|
||||||
buf := make([]byte, 10)
|
buf := make([]byte, 10)
|
||||||
n, err := reader.Read(buf)
|
n, err := reader.Read(buf)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -26,7 +26,7 @@ func TestReadNextChunk(t *testing.T) {
|
||||||
// Read 2nd chunk
|
// Read 2nd chunk
|
||||||
reader, err = readNextChunk(s)
|
reader, err = readNextChunk(s)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 5, reader.(*io.LimitedReader).N)
|
assert.Equal(t, int64(5), reader.(*io.LimitedReader).N)
|
||||||
buf = make([]byte, 10)
|
buf = make([]byte, 10)
|
||||||
n, err = reader.Read(buf)
|
n, err = reader.Read(buf)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
20
wire/conn.go
20
wire/conn.go
|
@ -26,19 +26,10 @@ You should still always call Close() when you're done with the connection.
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
Scanner
|
Scanner
|
||||||
Sender
|
Sender
|
||||||
closer func() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConn(scanner Scanner, sender Sender, closer func() error) *Conn {
|
func NewConn(scanner Scanner, sender Sender) *Conn {
|
||||||
return &Conn{scanner, sender, closer}
|
return &Conn{scanner, sender}
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the underlying connection.
|
|
||||||
func (c *Conn) Close() error {
|
|
||||||
if c.closer != nil {
|
|
||||||
return c.closer()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSyncConn returns connection that can operate in sync mode.
|
// NewSyncConn returns connection that can operate in sync mode.
|
||||||
|
@ -64,3 +55,10 @@ func (conn *Conn) RoundTripSingleResponse(req []byte) (resp []byte, err error) {
|
||||||
|
|
||||||
return conn.ReadMessage()
|
return conn.ReadMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) Close() error {
|
||||||
|
if err := conn.Sender.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return conn.Scanner.Close()
|
||||||
|
}
|
||||||
|
|
|
@ -48,7 +48,6 @@ func (d *netDialer) Dial() (*Conn, error) {
|
||||||
conn := &Conn{
|
conn := &Conn{
|
||||||
Scanner: NewScanner(netConn),
|
Scanner: NewScanner(netConn),
|
||||||
Sender: NewSender(netConn),
|
Sender: NewSender(netConn),
|
||||||
closer: netConn.Close,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent leaking the network connection, not sure if TCPConn does this itself.
|
// Prevent leaking the network connection, not sure if TCPConn does this itself.
|
||||||
|
|
33
wire/filemode.go
Normal file
33
wire/filemode.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// ADB file modes seem to only be 16 bits.
|
||||||
|
// Values are taken from http://linux.die.net/include/bits/stat.h.
|
||||||
|
const (
|
||||||
|
ModeDir uint32 = 0040000
|
||||||
|
ModeSymlink = 0120000
|
||||||
|
ModeSocket = 0140000
|
||||||
|
ModeFifo = 0010000
|
||||||
|
ModeCharDevice = 0020000
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseFileModeFromAdb(modeFromSync uint32) (filemode os.FileMode) {
|
||||||
|
// The ADB filemode uses the permission bits defined in Go's os package, but
|
||||||
|
// we need to parse the other bits manually.
|
||||||
|
switch {
|
||||||
|
case modeFromSync&ModeSymlink == ModeSymlink:
|
||||||
|
filemode = os.ModeSymlink
|
||||||
|
case modeFromSync&ModeDir == ModeDir:
|
||||||
|
filemode = os.ModeDir
|
||||||
|
case modeFromSync&ModeSocket == ModeSocket:
|
||||||
|
filemode = os.ModeSocket
|
||||||
|
case modeFromSync&ModeFifo == ModeFifo:
|
||||||
|
filemode = os.ModeNamedPipe
|
||||||
|
case modeFromSync&ModeCharDevice == ModeCharDevice:
|
||||||
|
filemode = os.ModeCharDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
filemode |= os.FileMode(modeFromSync).Perm()
|
||||||
|
return
|
||||||
|
}
|
|
@ -29,14 +29,17 @@ type Scanner interface {
|
||||||
ReadStatus() (StatusCode, error)
|
ReadStatus() (StatusCode, error)
|
||||||
ReadMessage() ([]byte, error)
|
ReadMessage() ([]byte, error)
|
||||||
ReadUntilEof() ([]byte, error)
|
ReadUntilEof() ([]byte, error)
|
||||||
|
|
||||||
NewSyncScanner() SyncScanner
|
NewSyncScanner() SyncScanner
|
||||||
|
|
||||||
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type realScanner struct {
|
type realScanner struct {
|
||||||
reader io.Reader
|
reader io.ReadCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewScanner(r io.Reader) Scanner {
|
func NewScanner(r io.ReadCloser) Scanner {
|
||||||
return &realScanner{r}
|
return &realScanner{r}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +111,10 @@ func ReadStatusFailureAsError(s Scanner, req []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *realScanner) Close() error {
|
||||||
|
return s.reader.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *realScanner) readLength() (int, error) {
|
func (s *realScanner) readLength() (int, error) {
|
||||||
lengthHex := make([]byte, 4)
|
lengthHex := make([]byte, 4)
|
||||||
n, err := io.ReadFull(s.reader, lengthHex)
|
n, err := io.ReadFull(s.reader, lengthHex)
|
||||||
|
|
|
@ -89,8 +89,10 @@ func NewScannerString(str string) *realScanner {
|
||||||
|
|
||||||
// NewEofBuffer returns a bytes.Buffer of str that returns an EOF error
|
// NewEofBuffer returns a bytes.Buffer of str that returns an EOF error
|
||||||
// at the end of input, instead of just returning 0 bytes read.
|
// at the end of input, instead of just returning 0 bytes read.
|
||||||
func NewEofBuffer(str string) *bufio.Reader {
|
func NewEofBuffer(str string) *TestReader {
|
||||||
return bufio.NewReader(io.LimitReader(bytes.NewBufferString(str), int64(len(str))))
|
limitReader := io.LimitReader(bytes.NewBufferString(str), int64(len(str)))
|
||||||
|
bufReader := bufio.NewReader(limitReader)
|
||||||
|
return &TestReader{bufReader}
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertEof(t *testing.T, s *realScanner) {
|
func assertEof(t *testing.T, s *realScanner) {
|
||||||
|
@ -104,3 +106,13 @@ func assertNotEof(t *testing.T, s *realScanner) {
|
||||||
assert.Equal(t, 1, n)
|
assert.Equal(t, 1, n)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestReader is a wrapper around a bufio.Reader that implements io.Closer.
|
||||||
|
type TestReader struct {
|
||||||
|
*bufio.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *TestReader) Close() error {
|
||||||
|
// No-op.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -8,14 +8,17 @@ import (
|
||||||
// Sender sends messages to the server.
|
// Sender sends messages to the server.
|
||||||
type Sender interface {
|
type Sender interface {
|
||||||
SendMessage(msg []byte) error
|
SendMessage(msg []byte) error
|
||||||
|
|
||||||
NewSyncSender() SyncSender
|
NewSyncSender() SyncSender
|
||||||
|
|
||||||
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type realSender struct {
|
type realSender struct {
|
||||||
writer io.Writer
|
writer io.WriteCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSender(w io.Writer) Sender {
|
func NewSender(w io.WriteCloser) Sender {
|
||||||
return &realSender{w}
|
return &realSender{w}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,4 +39,8 @@ func (s *realSender) NewSyncSender() SyncSender {
|
||||||
return NewSyncSender(s.writer)
|
return NewSyncSender(s.writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *realSender) Close() error {
|
||||||
|
return s.writer.Close()
|
||||||
|
}
|
||||||
|
|
||||||
var _ Sender = &realSender{}
|
var _ Sender = &realSender{}
|
||||||
|
|
|
@ -21,7 +21,17 @@ func TestWriteEmptyMessage(t *testing.T) {
|
||||||
assert.Equal(t, "0000", b.String())
|
assert.Equal(t, "0000", b.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestSender() (Sender, *bytes.Buffer) {
|
func NewTestSender() (Sender, *TestWriter) {
|
||||||
var buf bytes.Buffer
|
w := new(TestWriter)
|
||||||
return NewSender(&buf), &buf
|
return NewSender(w), w
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestWriter is a wrapper around a bytes.Buffer that implements io.Closer.
|
||||||
|
type TestWriter struct {
|
||||||
|
bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *TestWriter) Close() error {
|
||||||
|
// No-op.
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
168
wire/sync.go
168
wire/sync.go
|
@ -1,14 +1,6 @@
|
||||||
// TODO(z): Write SyncSender.SendBytes().
|
// TODO(z): Write SyncSender.SendBytes().
|
||||||
package wire
|
package wire
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Chunks cannot be longer than 64k.
|
// Chunks cannot be longer than 64k.
|
||||||
MaxChunkSize = 64 * 1024
|
MaxChunkSize = 64 * 1024
|
||||||
|
@ -36,163 +28,3 @@ type SyncConn struct {
|
||||||
SyncScanner
|
SyncScanner
|
||||||
SyncSender
|
SyncSender
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SyncConn) Close() error {
|
|
||||||
return c.SyncScanner.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
type SyncScanner interface {
|
|
||||||
// ReadOctetString reads a 4-byte string.
|
|
||||||
ReadOctetString() (string, error)
|
|
||||||
ReadInt32() (int32, error)
|
|
||||||
ReadFileMode() (os.FileMode, error)
|
|
||||||
ReadTime() (time.Time, error)
|
|
||||||
|
|
||||||
// Reads an octet length, followed by length bytes.
|
|
||||||
ReadString() (string, error)
|
|
||||||
|
|
||||||
// Reads an octet length, and returns a reader that will read length
|
|
||||||
// bytes (see io.LimitReader). The returned reader should be fully
|
|
||||||
// read before reading anything off the Scanner again.
|
|
||||||
ReadBytes() (io.Reader, error)
|
|
||||||
|
|
||||||
// Closes the underlying reader.
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type SyncSender interface {
|
|
||||||
// SendOctetString sends a 4-byte string.
|
|
||||||
SendOctetString(string) error
|
|
||||||
SendInt32(int32) error
|
|
||||||
SendFileMode(os.FileMode) error
|
|
||||||
SendTime(time.Time) error
|
|
||||||
|
|
||||||
// Sends len(bytes) as an octet, followed by bytes.
|
|
||||||
SendString(str string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type realSyncScanner struct {
|
|
||||||
io.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
type realSyncSender struct {
|
|
||||||
io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSyncScanner(r io.Reader) SyncScanner {
|
|
||||||
return &realSyncScanner{r}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSyncSender(w io.Writer) SyncSender {
|
|
||||||
return &realSyncSender{w}
|
|
||||||
}
|
|
||||||
|
|
||||||
func RequireOctetString(s SyncScanner, expected string) error {
|
|
||||||
actual, err := s.ReadOctetString()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("expected to read '%s', got err: %v", expected, err)
|
|
||||||
}
|
|
||||||
if actual != expected {
|
|
||||||
return fmt.Errorf("expected to read '%s', got '%s'", expected, actual)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *realSyncScanner) ReadOctetString() (string, error) {
|
|
||||||
octet := make([]byte, 4)
|
|
||||||
n, err := io.ReadFull(s.Reader, octet)
|
|
||||||
if err != nil && err != io.ErrUnexpectedEOF {
|
|
||||||
return "", err
|
|
||||||
} else if err == io.ErrUnexpectedEOF {
|
|
||||||
return "", incompleteMessage("octet", n, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(octet), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *realSyncSender) SendOctetString(str string) error {
|
|
||||||
if len(str) != 4 {
|
|
||||||
return fmt.Errorf("octet string must be exactly 4 bytes: '%s'", str)
|
|
||||||
}
|
|
||||||
return writeFully(s.Writer, []byte(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *realSyncScanner) ReadInt32() (int32, error) {
|
|
||||||
var value int32
|
|
||||||
err := binary.Read(s.Reader, binary.LittleEndian, &value)
|
|
||||||
return value, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *realSyncSender) SendInt32(val int32) error {
|
|
||||||
return binary.Write(s.Writer, binary.LittleEndian, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *realSyncScanner) ReadFileMode() (os.FileMode, error) {
|
|
||||||
var value uint32
|
|
||||||
err := binary.Read(s.Reader, binary.LittleEndian, &value)
|
|
||||||
return os.FileMode(value), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *realSyncSender) SendFileMode(mode os.FileMode) error {
|
|
||||||
return binary.Write(s.Writer, binary.LittleEndian, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *realSyncScanner) ReadTime() (time.Time, error) {
|
|
||||||
seconds, err := s.ReadInt32()
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return time.Unix(int64(seconds), 0).UTC(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *realSyncSender) SendTime(t time.Time) error {
|
|
||||||
return s.SendInt32(int32(t.Unix()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *realSyncScanner) ReadString() (string, error) {
|
|
||||||
length, err := s.ReadInt32()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes := make([]byte, length)
|
|
||||||
n, err := io.ReadFull(s.Reader, bytes)
|
|
||||||
if err != nil && err != io.ErrUnexpectedEOF {
|
|
||||||
return "", err
|
|
||||||
} else if err == io.ErrUnexpectedEOF {
|
|
||||||
return "", incompleteMessage("bytes", n, int(length))
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(bytes), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *realSyncSender) SendString(str string) error {
|
|
||||||
length := len(str)
|
|
||||||
if length > MaxChunkSize {
|
|
||||||
// This limit might not apply to filenames, but it's big enough
|
|
||||||
// that I don't think it will be a problem.
|
|
||||||
return fmt.Errorf("str must be <= %d in length", MaxChunkSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.SendInt32(int32(length)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return writeFully(s.Writer, []byte(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *realSyncScanner) ReadBytes() (io.Reader, error) {
|
|
||||||
length, err := s.ReadInt32()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return io.LimitReader(s.Reader, int64(length)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *realSyncScanner) Close() error {
|
|
||||||
if closer, ok := s.Reader.(io.Closer); ok {
|
|
||||||
return closer.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
112
wire/sync_scanner.go
Normal file
112
wire/sync_scanner.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SyncScanner interface {
|
||||||
|
// ReadOctetString reads a 4-byte string.
|
||||||
|
ReadOctetString() (string, error)
|
||||||
|
ReadInt32() (int32, error)
|
||||||
|
ReadFileMode() (os.FileMode, error)
|
||||||
|
ReadTime() (time.Time, error)
|
||||||
|
|
||||||
|
// Reads an octet length, followed by length bytes.
|
||||||
|
ReadString() (string, error)
|
||||||
|
|
||||||
|
// Reads an octet length, and returns a reader that will read length
|
||||||
|
// bytes (see io.LimitReader). The returned reader should be fully
|
||||||
|
// read before reading anything off the Scanner again.
|
||||||
|
ReadBytes() (io.Reader, error)
|
||||||
|
|
||||||
|
// Closes the underlying reader.
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type realSyncScanner struct {
|
||||||
|
io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSyncScanner(r io.Reader) SyncScanner {
|
||||||
|
return &realSyncScanner{r}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RequireOctetString(s SyncScanner, expected string) error {
|
||||||
|
actual, err := s.ReadOctetString()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("expected to read '%s', got err: %v", expected, err)
|
||||||
|
}
|
||||||
|
if actual != expected {
|
||||||
|
return fmt.Errorf("expected to read '%s', got '%s'", expected, actual)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *realSyncScanner) ReadOctetString() (string, error) {
|
||||||
|
octet := make([]byte, 4)
|
||||||
|
n, err := io.ReadFull(s.Reader, octet)
|
||||||
|
if err != nil && err != io.ErrUnexpectedEOF {
|
||||||
|
return "", err
|
||||||
|
} else if err == io.ErrUnexpectedEOF {
|
||||||
|
return "", incompleteMessage("octet", n, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(octet), nil
|
||||||
|
}
|
||||||
|
func (s *realSyncScanner) ReadInt32() (int32, error) {
|
||||||
|
var value int32
|
||||||
|
err := binary.Read(s.Reader, binary.LittleEndian, &value)
|
||||||
|
return value, err
|
||||||
|
}
|
||||||
|
func (s *realSyncScanner) ReadFileMode() (filemode os.FileMode, err error) {
|
||||||
|
var value uint32
|
||||||
|
err = binary.Read(s.Reader, binary.LittleEndian, &value)
|
||||||
|
if err == nil {
|
||||||
|
filemode = ParseFileModeFromAdb(value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (s *realSyncScanner) ReadTime() (time.Time, error) {
|
||||||
|
seconds, err := s.ReadInt32()
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Unix(int64(seconds), 0).UTC(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *realSyncScanner) ReadString() (string, error) {
|
||||||
|
length, err := s.ReadInt32()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes := make([]byte, length)
|
||||||
|
n, err := io.ReadFull(s.Reader, bytes)
|
||||||
|
if err != nil && err != io.ErrUnexpectedEOF {
|
||||||
|
return "", err
|
||||||
|
} else if err == io.ErrUnexpectedEOF {
|
||||||
|
return "", incompleteMessage("bytes", n, int(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(bytes), nil
|
||||||
|
}
|
||||||
|
func (s *realSyncScanner) ReadBytes() (io.Reader, error) {
|
||||||
|
length, err := s.ReadInt32()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return io.LimitReader(s.Reader, int64(length)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *realSyncScanner) Close() error {
|
||||||
|
if closer, ok := s.Reader.(io.Closer); ok {
|
||||||
|
return closer.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
61
wire/sync_sender.go
Normal file
61
wire/sync_sender.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SyncSender interface {
|
||||||
|
// SendOctetString sends a 4-byte string.
|
||||||
|
SendOctetString(string) error
|
||||||
|
SendInt32(int32) error
|
||||||
|
SendFileMode(os.FileMode) error
|
||||||
|
SendTime(time.Time) error
|
||||||
|
|
||||||
|
// Sends len(bytes) as an octet, followed by bytes.
|
||||||
|
SendString(str string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type realSyncSender struct {
|
||||||
|
io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSyncSender(w io.Writer) SyncSender {
|
||||||
|
return &realSyncSender{w}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *realSyncSender) SendOctetString(str string) error {
|
||||||
|
if len(str) != 4 {
|
||||||
|
return fmt.Errorf("octet string must be exactly 4 bytes: '%s'", str)
|
||||||
|
}
|
||||||
|
return writeFully(s.Writer, []byte(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *realSyncSender) SendInt32(val int32) error {
|
||||||
|
return binary.Write(s.Writer, binary.LittleEndian, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *realSyncSender) SendFileMode(mode os.FileMode) error {
|
||||||
|
return binary.Write(s.Writer, binary.LittleEndian, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *realSyncSender) SendTime(t time.Time) error {
|
||||||
|
return s.SendInt32(int32(t.Unix()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *realSyncSender) SendString(str string) error {
|
||||||
|
length := len(str)
|
||||||
|
if length > MaxChunkSize {
|
||||||
|
// This limit might not apply to filenames, but it's big enough
|
||||||
|
// that I don't think it will be a problem.
|
||||||
|
return fmt.Errorf("str must be <= %d in length", MaxChunkSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.SendInt32(int32(length)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeFully(s.Writer, []byte(str))
|
||||||
|
}
|
Loading…
Reference in a new issue