forked from RepoMirrors/btrfs
1
0
Fork 0
btrfs/receive.go

137 lines
3.0 KiB
Go

package btrfs
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"hash/crc32"
"io"
"os"
"os/exec"
"path/filepath"
"syscall"
)
const nativeReceive = false
func Receive(r io.Reader, dstDir string) error {
if !nativeReceive {
buf := bytes.NewBuffer(nil)
cmd := exec.Command("btrfs", "receive", dstDir)
cmd.Stdin = r
cmd.Stderr = buf
if err := cmd.Run(); err != nil {
if buf.Len() != 0 {
return errors.New(buf.String())
}
return err
}
return nil
}
var err error
dstDir, err = filepath.Abs(dstDir)
if err != nil {
return err
}
realMnt, err := findMountRoot(dstDir)
if err != nil {
return err
}
dir, err := os.OpenFile(dstDir, os.O_RDONLY|syscall.O_NOATIME, 0755)
if err != nil {
return err
}
mnt, err := os.OpenFile(realMnt, os.O_RDONLY|syscall.O_NOATIME, 0755)
if err != nil {
return err
}
// We want to resolve the path to the subvolume we're sitting in
// so that we can adjust the paths of any subvols we want to receive in.
subvolID, err := getFileRootID(mnt)
if err != nil {
return err
}
sr, err := newStreamReader(r)
if err != nil {
return err
}
_, _, _ = dir, subvolID, sr
panic("not implemented")
}
type streamReader struct {
r io.Reader
hbuf []byte
buf *bytes.Buffer
}
type sendCommandArgs struct {
Type sendCmdAttr
Data []byte
}
type sendCommand struct {
Type sendCmd
Args []sendCommandArgs
}
func (sr *streamReader) ReadCommand() (*sendCommand, error) {
sr.buf.Reset()
var h cmdHeader
if sr.hbuf == nil {
sr.hbuf = make([]byte, h.Size())
}
if _, err := io.ReadFull(sr.r, sr.hbuf); err != nil {
return nil, err
} else if err = h.Unmarshal(sr.hbuf); err != nil {
return nil, err
}
if sr.buf == nil {
sr.buf = bytes.NewBuffer(nil)
}
if _, err := io.CopyN(sr.buf, sr.r, int64(h.Len)); err != nil {
return nil, err
}
tbl := crc32.MakeTable(0)
crc := crc32.Checksum(sr.buf.Bytes(), tbl)
if crc != h.Crc {
return nil, fmt.Errorf("crc missmatch in command: %x vs %x", crc, h.Crc)
}
cmd := sendCommand{Type: sendCmd(h.Cmd)}
var th tlvHeader
data := sr.buf.Bytes()
for {
if n := len(data); n < th.Size() {
if n != 0 {
return nil, io.ErrUnexpectedEOF
}
break
}
if err := th.Unmarshal(data); err != nil {
return nil, err
}
data = data[th.Size():]
if sendCmdAttr(th.Type) > sendAttrMax { // || th.Len > _BTRFS_SEND_BUF_SIZE {
return nil, fmt.Errorf("invalid tlv in cmd: %+v", th)
}
b := make([]byte, th.Len)
copy(b, data)
cmd.Args = append(cmd.Args, sendCommandArgs{Type: sendCmdAttr(th.Type), Data: b})
}
return &cmd, nil
}
func newStreamReader(r io.Reader) (*streamReader, error) {
buf := make([]byte, sendStreamMagicSize+4)
_, err := io.ReadFull(r, buf)
if err != nil {
return nil, err
} else if bytes.Compare(buf[:sendStreamMagicSize], []byte(sendStreamMagic)) != 0 {
return nil, errors.New("unexpected stream header")
}
version := binary.LittleEndian.Uint32(buf[sendStreamMagicSize:])
if version > sendStreamVersion {
return nil, fmt.Errorf("stream version %d not supported", version)
}
return &streamReader{r: r}, nil
}