aboutsummaryrefslogtreecommitdiffstats
path: root/squashfu
blob: ccf2cfd4df41aea8ad6df808f1fd66b31b97f420 (plain) (blame)
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/bin/bash

# Informational output w/ happy colors
debug () {
    [[ $DEBUG ]] && echo -e '\033[1;33mDEBUG ::\033[1;m ' $*
}

info () {
    echo -e '\033[1;34m::\033[1;m ' $*
}

die () {
    echo -e '\033[1;31mFATAL ::\033[1;m ' $* >&2
    exit 1
}

mount_squash () {
# Arguments: none
# Returns: return code of mount command
    mount -t loop,ro "$SEED" "${BKUP_ROOT}/ro"
    return $?
}

mount_union_with_bins () {
# Arguments: numbers of bins to be mounted (variable number)
# Returns: 0 on successful mount, non-zero on failure
    # Mount first as rw, shift, and mount the rest ro
    branches="br=${BINS_DIR}/$1=rw:"; shift
    for bin in $*; do
        branches="${branches}/bins/$bin=ro:"
    done
    branches="${branches}${BKUP_ROOT}/ro=ro"

    debug "mount -t aufs none "${BKUP_ROOT}/rw" -o udba=reval,$branches"
    mount -t aufs none "${BKUP_ROOT}/rw" -o udba=reval,$branches

    return $?
}

get_next_available_bin () {
# Arguments: none
# Returns: Numeric value of the next unused bin
    return $[ $(cut -d: -f1 "$BINVENTORY" | sort -n | tail -1) + 1 ]
}

sweep_bins () {
# Arguments: none
# Returns: none
    count=1

    # Make sure bins are numbered in order, clean up if not. In other words,
    # if we have 10 bins, make sure they're ordered 1 through 10.
    for bin in "${BINS_DIR}/*"; do
        if [[ ! -d "${BINS_DIR}/$count" ]]; then
            high_bin=$(ls "${BINS_DIR}" | sort -n | tail -1)
            mv "${BINS_DIR}/$high_bin" "${BINS_DIR}/$count"
            sed -i "/^$high_bin:/s/^$high_bin:/$count:/" "$BINVENTORY"
        fi
        count=$[ $count + 1 ]
    done

}

call_rsync () {
# Arguments: none
# Returns: return code from rsync

    # Parse includes and excludes from heredocs in config
    INCLUDES=($(sed -n '/^<<INCLUDES$/,/^INCLUDES$/p' $CONFIG | grep -vE "^<*INCLUDES$"))
    EXCLUDES=($(sed -n '/^<<EXCLUDES$/,/^EXCLUDES$/p' $CONFIG | \
                grep -vE "^<*EXCLUDES$" | \
                sed -n 's/\(.*\)/--exclude "\1"/p'))

    # rsync source to $BKUP_ROOT/rw
    debug "Rsync executing with:"
    debug ":   Options: ${RSYNC_OPTS[@]}"
    debug ":   Includes: ${INCLUDES[@]}"
    debug ":   Excludes: ${EXCLUDES[@]}"
    rsync ${RSYNC_OPTS[@]} ${INCLUDES[@]} ${EXCLUDES[@]} "${BKUP_ROOT}/rw"
    return $?
}

create_new_bin () {
# Arguments: 1, the number of the bin to create
# Returns: 0 on success, non-zero on error

    # Create new directory, fail if it exists (something's wrong)
    mkdir "${BINS_DIR}/$1"
    if [[ $? -ne 0 ]]; then
        return $?
    fi

    # Update binventory with new bin name and timestamp
    echo "${1}:$(stat -c %u ${BINS_DIR}/$1)" >> "$BINVENTORY"
    return $?

}

# Unmounting functions
unmount_union () {
# Args: none
# Returns: return code from umount
    umount "${BKUP_ROOT}/rw"
    return $?
}

unmount_seed () {
# Args: none
# Returns: return code from umount
    umount "${BKUP_ROOT}/ro"
    return $?
}

unmount_all () {
# Args: none
# Returns: none

    # Union MUST be unmounted first
    unmount_union
    unmoun_seed
}

check_for_resquash () {
# Args: none
# Returns: number of bins needing to be merged
    local number_of_bins=$(wc -l "$BINVENTORY")

    if [[ $number_of_bins -gt $MAX_BINS ]]; then
        return $[ $number_of_bins - $MIN_BINS ]
    else
        return 0
    fi
}

create_new_squash () {
# Args: number of bins to be squashed (as determined by check_for_resquash), -1 on initial creation
# Returns: 0 on success, non-zero on failure

    # If making first seed, create it empty and return
    if [[ $1 -eq -1 ]]; then
        mksquashfs "${BKUP_ROOT}/rw" "$SEED" -b 65536
        return $?
    fi

    # Determine oldest $1 bins and mount them with the current squash
    local old_bins=($(sort -n -r -t: -k2 "$BINVENTORY" | tail -$1 | cut -d: -f1))

    mount_union_with_bins ${old_bins[@]}

    # Create new squash with temp name
    mksquashfs "${BKUP_ROOT}/rw" "$SEED.replace" -b 65536

    # If the squash wasn't made correctly, we don't want to continue
    if [[ $? -ne 0 ]]; then
        return 1
    fi

    unmount_all

    # Replace old squash
    mv "${SEED}.replace" "$SEED"

    # Delete old bins, and remove entry from binventory
    for bin in $old_bins; do
        rm -rf "${BKUP_ROOT}/bins/$bin"
        sed -i "/^$bin:/d" "$BINVENTORY"
    done
}

create_new_incremental () {
# Args: none
# Returns: 0 on success, non-zero on error

    # Make a new bin for this incremenetal
    get_next_available_bin
    create_new_bin $?

    # Determine the mount order via binventory
    bin_order=($(sort -n -r -t:2 -k2 "$BINVENTORY" | cut -d: -f1))

    mount_union_with_bins ${BIN_ORDER[@]}

    # Die with error on mount, else start rsync
    if [[ $? -ne 0 ]]; then
        return 1;
    fi

    call_rsync

    return $?
}