summaryrefslogtreecommitdiff
path: root/morphlib/recv-hole
blob: f15fd87719ab3ea6d86f7ef5d4fb01f2bbd5221d (plain)
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
110
111
112
113
114
115
116
#!/bin/sh
#
# Receive a data stream describing a sparse file, and reproduce it,
# either to a named file or stdout.
#
# The data stream is simple: it's a sequence of DATA or HOLE records:
#
#    DATA
#    123
#    <123 bytes of binary data, NOT including newline at the end>
#
#    HOLE
#    123
#
# This shell script can be executed over ssh (given to ssh as an arguemnt,
# with suitable escaping) on a different computer. This allows a large
# sparse file (e.g., disk image) be transferred quickly.


set -eu


die()
{
    echo "$@" 1>&2
    exit 1
}


recv_hole_to_file()
{
    local n

    read n
    truncate --size "+$n" "$1"
}


recv_data_to_file()
{
    local n
    read n

    local blocksize=1048576
    local blocks="$(echo "$n" / "$blocksize" | bc)"
    local extra="$(echo "$n" % "$blocksize" | bc)"

    xfer_data_to_stdout "$blocksize" "$blocks" >> "$1"
    xfer_data_to_stdout 1 "$extra" >> "$1"
}


recv_hole_to_stdout()
{
    local n
    read n
    (echo "$n"; cat /dev/zero) | recv_data_to_stdout
}


recv_data_to_stdout()
{
    local n
    read n

    local blocksize=1048576
    local blocks="$(echo "$n" / "$blocksize" | bc)"
    local extra="$(echo "$n" % "$blocksize" | bc)"

    xfer_data_to_stdout "$blocksize" "$blocks"
    xfer_data_to_stdout 1 "$extra"
}


xfer_data_to_stdout()
{
    local log="$(mktemp)"
    if ! dd "bs=$1" count="$2" iflag=fullblock status=noxfer 2> "$log"
    then
        cat "$log" 1>&2
        rm -f "$log"
        exit 1
    else
        rm -f "$log"
    fi
}


type="$1"
case "$type" in
    file)
        output="$2"
        truncate --size=0 "$output"
        while read what
        do
            case "$what" in
                DATA) recv_data_to_file "$output" ;;
                HOLE) recv_hole_to_file "$output" ;;
                *) die "Unknown instruction: $what" ;;
            esac
        done
        ;;
    vbox)
        output="$2"
        disk_size="$3"
        while read what
        do
            case "$what" in
                DATA) recv_data_to_stdout ;;
                HOLE) recv_hole_to_stdout ;;
                *) die "Unknown instruction: $what" ;;
            esac
        done |
        VBoxManage convertfromraw stdin "$output" "$disk_size"
        ;;
esac