simple-decoder.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. //+build ignore
  2. // Copyright 2015, Klaus Post, see LICENSE for details.
  3. //
  4. // Simple decoder example.
  5. //
  6. // The decoder reverses the process of "simple-encoder.go"
  7. //
  8. // To build an executable use:
  9. //
  10. // go build simple-decoder.go
  11. //
  12. // Simple Encoder/Decoder Shortcomings:
  13. // * If the file size of the input isn't diviable by the number of data shards
  14. // the output will contain extra zeroes
  15. //
  16. // * If the shard numbers isn't the same for the decoder as in the
  17. // encoder, invalid output will be generated.
  18. //
  19. // * If values have changed in a shard, it cannot be reconstructed.
  20. //
  21. // * If two shards have been swapped, reconstruction will always fail.
  22. // You need to supply the shards in the same order as they were given to you.
  23. //
  24. // The solution for this is to save a metadata file containing:
  25. //
  26. // * File size.
  27. // * The number of data/parity shards.
  28. // * HASH of each shard.
  29. // * Order of the shards.
  30. //
  31. // If you save these properties, you should abe able to detect file corruption
  32. // in a shard and be able to reconstruct your data if you have the needed number of shards left.
  33. package main
  34. import (
  35. "flag"
  36. "fmt"
  37. "io/ioutil"
  38. "os"
  39. "github.com/klauspost/reedsolomon"
  40. )
  41. var dataShards = flag.Int("data", 4, "Number of shards to split the data into")
  42. var parShards = flag.Int("par", 2, "Number of parity shards")
  43. var outFile = flag.String("out", "", "Alternative output path/file")
  44. func init() {
  45. flag.Usage = func() {
  46. fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
  47. fmt.Fprintf(os.Stderr, " simple-decoder [-flags] basefile.ext\nDo not add the number to the filename.\n")
  48. fmt.Fprintf(os.Stderr, "Valid flags:\n")
  49. flag.PrintDefaults()
  50. }
  51. }
  52. func main() {
  53. // Parse flags
  54. flag.Parse()
  55. args := flag.Args()
  56. if len(args) != 1 {
  57. fmt.Fprintf(os.Stderr, "Error: No filenames given\n")
  58. flag.Usage()
  59. os.Exit(1)
  60. }
  61. fname := args[0]
  62. // Create matrix
  63. enc, err := reedsolomon.New(*dataShards, *parShards)
  64. checkErr(err)
  65. // Create shards and load the data.
  66. shards := make([][]byte, *dataShards+*parShards)
  67. for i := range shards {
  68. infn := fmt.Sprintf("%s.%d", fname, i)
  69. fmt.Println("Opening", infn)
  70. shards[i], err = ioutil.ReadFile(infn)
  71. if err != nil {
  72. fmt.Println("Error reading file", err)
  73. shards[i] = nil
  74. }
  75. }
  76. // Verify the shards
  77. ok, err := enc.Verify(shards)
  78. if ok {
  79. fmt.Println("No reconstruction needed")
  80. } else {
  81. fmt.Println("Verification failed. Reconstructing data")
  82. err = enc.Reconstruct(shards)
  83. if err != nil {
  84. fmt.Println("Reconstruct failed -", err)
  85. os.Exit(1)
  86. }
  87. ok, err = enc.Verify(shards)
  88. if !ok {
  89. fmt.Println("Verification failed after reconstruction, data likely corrupted.")
  90. os.Exit(1)
  91. }
  92. checkErr(err)
  93. }
  94. // Join the shards and write them
  95. outfn := *outFile
  96. if outfn == "" {
  97. outfn = fname
  98. }
  99. fmt.Println("Writing data to", outfn)
  100. f, err := os.Create(outfn)
  101. checkErr(err)
  102. // We don't know the exact filesize.
  103. err = enc.Join(f, shards, len(shards[0])**dataShards)
  104. checkErr(err)
  105. }
  106. func checkErr(err error) {
  107. if err != nil {
  108. fmt.Fprintf(os.Stderr, "Error: %s", err.Error())
  109. os.Exit(2)
  110. }
  111. }