167 lines
4.4 KiB

12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
  1. package main
  2. import (
  3. "archive/tar"
  4. "bytes"
  5. "code.google.com/p/weed-fs/go/glog"
  6. "code.google.com/p/weed-fs/go/storage"
  7. "fmt"
  8. "os"
  9. "path"
  10. "strconv"
  11. "strings"
  12. "text/template"
  13. "time"
  14. )
  15. func init() {
  16. cmdExport.Run = runExport // break init cycle
  17. cmdExport.IsDebug = cmdExport.Flag.Bool("debug", false, "enable debug mode")
  18. }
  19. const (
  20. defaultFnFormat = `{{.Mime}}/{{.Id}}:{{.Name}}`
  21. )
  22. var cmdExport = &Command{
  23. UsageLine: "export -dir=/tmp -volumeId=234 -o=/dir/name.tar -fileNameFormat={{.Name}}",
  24. Short: "list or export files from one volume data file",
  25. Long: `List all files in a volume, or Export all files in a volume to a tar file if the output is specified.
  26. The format of file name in the tar file can be customized. Default is {{.Mime}}/{{.Id}}:{{.Name}}. Also available is {{.Key}}.
  27. `,
  28. }
  29. var (
  30. exportVolumePath = cmdExport.Flag.String("dir", "/tmp", "input data directory to store volume data files")
  31. exportCollection = cmdExport.Flag.String("collection", "", "the volume collection name")
  32. exportVolumeId = cmdExport.Flag.Int("volumeId", -1, "a volume id. The volume should already exist in the dir. The volume index file should not exist.")
  33. dest = cmdExport.Flag.String("o", "", "output tar file name, must ends with .tar, or just a \"-\" for stdout")
  34. format = cmdExport.Flag.String("fileNameFormat", defaultFnFormat, "filename format, default to {{.Mime}}/{{.Id}}:{{.Name}}")
  35. tarFh *tar.Writer
  36. tarHeader tar.Header
  37. fnTmpl *template.Template
  38. fnTmplBuf = bytes.NewBuffer(nil)
  39. )
  40. func runExport(cmd *Command, args []string) bool {
  41. if *exportVolumeId == -1 {
  42. return false
  43. }
  44. var err error
  45. if *dest != "" {
  46. if *dest != "-" && !strings.HasSuffix(*dest, ".tar") {
  47. fmt.Println("the output file", *dest, "should be '-' or end with .tar")
  48. return false
  49. }
  50. if fnTmpl, err = template.New("name").Parse(*format); err != nil {
  51. fmt.Println("cannot parse format " + *format + ": " + err.Error())
  52. return false
  53. }
  54. var fh *os.File
  55. if *dest == "-" {
  56. fh = os.Stdout
  57. } else {
  58. if fh, err = os.Create(*dest); err != nil {
  59. glog.Fatalf("cannot open output tar %s: %s", *dest, err)
  60. }
  61. }
  62. defer fh.Close()
  63. tarFh = tar.NewWriter(fh)
  64. defer tarFh.Close()
  65. t := time.Now()
  66. tarHeader = tar.Header{Mode: 0644,
  67. ModTime: t, Uid: os.Getuid(), Gid: os.Getgid(),
  68. Typeflag: tar.TypeReg,
  69. AccessTime: t, ChangeTime: t}
  70. }
  71. fileName := strconv.Itoa(*exportVolumeId)
  72. vid := storage.VolumeId(*exportVolumeId)
  73. indexFile, err := os.OpenFile(path.Join(*exportVolumePath, fileName+".idx"), os.O_RDONLY, 0644)
  74. if err != nil {
  75. glog.Fatalf("Create Volume Index [ERROR] %s\n", err)
  76. }
  77. defer indexFile.Close()
  78. nm, err := storage.LoadNeedleMap(indexFile)
  79. if err != nil {
  80. glog.Fatalf("cannot load needle map from %s: %s", indexFile, err)
  81. }
  82. var version storage.Version
  83. err = storage.ScanVolumeFile(*exportVolumePath, *exportCollection, vid, func(superBlock storage.SuperBlock) error {
  84. version = superBlock.Version
  85. return nil
  86. }, func(n *storage.Needle, offset int64) error {
  87. debug("key", n.Id, "offset", offset, "size", n.Size, "disk_size", n.DiskSize(), "gzip", n.IsGzipped())
  88. nv, ok := nm.Get(n.Id)
  89. if ok && nv.Size > 0 {
  90. return walker(vid, n, version)
  91. } else {
  92. if !ok {
  93. debug("This seems deleted", n.Id, "size", n.Size)
  94. } else {
  95. debug("Id", n.Id, "size", n.Size)
  96. }
  97. }
  98. return nil
  99. })
  100. if err != nil {
  101. glog.Fatalf("Export Volume File [ERROR] %s\n", err)
  102. }
  103. return true
  104. }
  105. type nameParams struct {
  106. Name string
  107. Id uint64
  108. Mime string
  109. Key string
  110. }
  111. func walker(vid storage.VolumeId, n *storage.Needle, version storage.Version) (err error) {
  112. key := storage.NewFileId(vid, n.Id, n.Cookie).String()
  113. if tarFh != nil {
  114. fnTmplBuf.Reset()
  115. if err = fnTmpl.Execute(fnTmplBuf,
  116. nameParams{Name: string(n.Name),
  117. Id: n.Id,
  118. Mime: string(n.Mime),
  119. Key: key,
  120. },
  121. ); err != nil {
  122. return err
  123. }
  124. nm := fnTmplBuf.String()
  125. if n.IsGzipped() && path.Ext(nm) != ".gz" {
  126. nm = nm + ".gz"
  127. }
  128. tarHeader.Name, tarHeader.Size = nm, int64(len(n.Data))
  129. if err = tarFh.WriteHeader(&tarHeader); err != nil {
  130. return err
  131. }
  132. _, err = tarFh.Write(n.Data)
  133. } else {
  134. size := n.DataSize
  135. if version == storage.Version1 {
  136. size = n.Size
  137. }
  138. fmt.Printf("key=%s Name=%s Size=%d gzip=%t mime=%s\n",
  139. key,
  140. n.Name,
  141. size,
  142. n.IsGzipped(),
  143. n.Mime,
  144. )
  145. }
  146. return
  147. }