-
Notifications
You must be signed in to change notification settings - Fork 0
/
rsdecoder.go
133 lines (117 loc) · 3.18 KB
/
rsdecoder.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright 2015, Klaus Post, see LICENSE for details.
//
// Simple decoder example.
//
// The decoder reverses the process of "simple-encoder.go"
//
// To build an executable use:
//
// go build simple-decoder.go
//
// Simple Encoder/Decoder Shortcomings:
// * If the file size of the input isn't divisible by the number of data shards
// the output will contain extra zeroes
//
// * If the shard numbers isn't the same for the decoder as in the
// encoder, invalid output will be generated.
//
// * If values have changed in a shard, it cannot be reconstructed.
//
// * If two shards have been swapped, reconstruction will always fail.
// You need to supply the shards in the same order as they were given to you.
//
// The solution for this is to save a metadata file containing:
//
// * File size.
// * The number of data/parity shards.
// * HASH of each shard.
// * Order of the shards.
//
// If you save these properties, you should abe able to detect file corruption
// in a shard and be able to reconstruct your data if you have the needed number of shards left.
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"github.com/klauspost/reedsolomon"
)
const dataShards = 2
const parShards = 2
var outFile = flag.String("out", "", "Alternative output path/file")
func init() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, " simple-decoder [-flags] basefile.ext\nDo not add the number to the filename.\n")
fmt.Fprintf(os.Stderr, "Valid flags:\n")
flag.PrintDefaults()
}
}
func main() {
// Parse flags
flag.Parse()
args := flag.Args()
if len(args) != 1 {
fmt.Fprintf(os.Stderr, "Error: No filenames given\n")
flag.Usage()
os.Exit(1)
}
fname := args[0]
log.Println("filename : ", fname)
// Create matrix
enc, err := reedsolomon.New(dataShards, parShards)
checkErr(err)
// Create shards and load the data.
shards := make([][]byte, dataShards+parShards)
for i := range shards {
infn := fmt.Sprintf("%s.%d", fname, i)
log.Println("Opening", infn)
shards[i], err = ioutil.ReadFile(infn)
if err != nil {
log.Println("Error reading file", err)
shards[i] = nil
}
}
// Verify the shards
log.Println("verify : ", shards)
ok, err := enc.Verify(shards)
log.Println("verify : ", shards)
if ok {
log.Println("No reconstruction needed")
} else {
log.Printf("Verification failed. Reconstructing data : %v", err)
log.Print("Reconstruct before", shards)
err = enc.Reconstruct(shards)
log.Print("Reconstruct after", shards)
if err != nil {
log.Println("Reconstruct failed -", err)
os.Exit(1)
}
ok, err = enc.Verify(shards)
if !ok {
log.Println("Verification failed after reconstruction, data likely corrupted.")
os.Exit(1)
}
checkErr(err)
}
// Join the shards and write them
outfn := *outFile
if outfn == "" {
outfn = fname + ".out"
}
log.Println("Writing data to", outfn)
f, err := os.Create(outfn)
checkErr(err)
// We don't know the exact filesize.
err = enc.Join(f, shards, len(shards[0])*dataShards)
checkErr(err)
}
func checkErr(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %s", err.Error())
os.Exit(2)
}
}