1
0

stream-encoder.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. //+build ignore
  2. // Copyright 2015, Klaus Post, see LICENSE for details.
  3. //
  4. // Simple stream encoder example
  5. //
  6. // The encoder encodes a single file into a number of shards
  7. // To reverse the process see "stream-decoder.go"
  8. //
  9. // To build an executable use:
  10. //
  11. // go build stream-encoder.go
  12. //
  13. // Simple Encoder/Decoder Shortcomings:
  14. // * If the file size of the input isn't dividable by the number of data shards
  15. // the output will contain extra zeroes
  16. //
  17. // * If the shard numbers isn't the same for the decoder as in the
  18. // encoder, invalid output will be generated.
  19. //
  20. // * If values have changed in a shard, it cannot be reconstructed.
  21. //
  22. // * If two shards have been swapped, reconstruction will always fail.
  23. // You need to supply the shards in the same order as they were given to you.
  24. //
  25. // The solution for this is to save a metadata file containing:
  26. //
  27. // * File size.
  28. // * The number of data/parity shards.
  29. // * HASH of each shard.
  30. // * Order of the shards.
  31. //
  32. // If you save these properties, you should abe able to detect file corruption
  33. // in a shard and be able to reconstruct your data if you have the needed number of shards left.
  34. package main
  35. import (
  36. "flag"
  37. "fmt"
  38. "os"
  39. "path/filepath"
  40. "io"
  41. "github.com/klauspost/reedsolomon"
  42. )
  43. var dataShards = flag.Int("data", 4, "Number of shards to split the data into, must be below 257.")
  44. var parShards = flag.Int("par", 2, "Number of parity shards")
  45. var outDir = flag.String("out", "", "Alternative output directory")
  46. func init() {
  47. flag.Usage = func() {
  48. fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
  49. fmt.Fprintf(os.Stderr, " %s [-flags] filename.ext\n\n", os.Args[0])
  50. fmt.Fprintf(os.Stderr, "Valid flags:\n")
  51. flag.PrintDefaults()
  52. }
  53. }
  54. func main() {
  55. // Parse command line parameters.
  56. flag.Parse()
  57. args := flag.Args()
  58. if len(args) != 1 {
  59. fmt.Fprintf(os.Stderr, "Error: No input filename given\n")
  60. flag.Usage()
  61. os.Exit(1)
  62. }
  63. if *dataShards > 257 {
  64. fmt.Fprintf(os.Stderr, "Error: Too many data shards\n")
  65. os.Exit(1)
  66. }
  67. fname := args[0]
  68. // Create encoding matrix.
  69. enc, err := reedsolomon.NewStream(*dataShards, *parShards)
  70. checkErr(err)
  71. fmt.Println("Opening", fname)
  72. f, err := os.Open(fname)
  73. checkErr(err)
  74. instat, err := f.Stat()
  75. checkErr(err)
  76. shards := *dataShards + *parShards
  77. out := make([]*os.File, shards)
  78. // Create the resulting files.
  79. dir, file := filepath.Split(fname)
  80. if *outDir != "" {
  81. dir = *outDir
  82. }
  83. for i := range out {
  84. outfn := fmt.Sprintf("%s.%d", file, i)
  85. fmt.Println("Creating", outfn)
  86. out[i], err = os.Create(filepath.Join(dir, outfn))
  87. checkErr(err)
  88. }
  89. // Split into files.
  90. data := make([]io.Writer, *dataShards)
  91. for i := range data {
  92. data[i] = out[i]
  93. }
  94. // Do the split
  95. err = enc.Split(f, data, instat.Size())
  96. checkErr(err)
  97. // Close and re-open the files.
  98. input := make([]io.Reader, *dataShards)
  99. for i := range data {
  100. out[i].Close()
  101. f, err := os.Open(out[i].Name())
  102. checkErr(err)
  103. input[i] = f
  104. defer f.Close()
  105. }
  106. // Create parity output writers
  107. parity := make([]io.Writer, *parShards)
  108. for i := range parity {
  109. parity[i] = out[*dataShards+i]
  110. defer out[*dataShards+i].Close()
  111. }
  112. // Encode parity
  113. err = enc.Encode(input, parity)
  114. checkErr(err)
  115. fmt.Printf("File split into %d data + %d parity shards.\n", *dataShards, *parShards)
  116. }
  117. func checkErr(err error) {
  118. if err != nil {
  119. fmt.Fprintf(os.Stderr, "Error: %s", err.Error())
  120. os.Exit(2)
  121. }
  122. }