You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

164 lines
4.2 KiB

12 years ago
12 years ago
12 years ago
12 years ago
  1. package main
  2. import (
  3. "archive/tar"
  4. "bytes"
  5. "fmt"
  6. "log"
  7. "os"
  8. "path"
  9. "code.google.com/p/weed-fs/go/directory"
  10. "code.google.com/p/weed-fs/go/storage"
  11. "strconv"
  12. "strings"
  13. "text/template"
  14. "time"
  15. )
  16. func init() {
  17. cmdExport.Run = runExport // break init cycle
  18. cmdExport.IsDebug = cmdExport.Flag.Bool("debug", false, "enable debug mode")
  19. }
  20. const (
  21. defaultFnFormat = `{{.Mime}}/{{.Id}}:{{.Name}}`
  22. )
  23. var cmdExport = &Command{
  24. UsageLine: "export -dir=/tmp -volumeId=234 -o=/dir/name.tar -fileNameFormat={{.Name}}",
  25. Short: "list or export files from one volume data file",
  26. Long: `List all files in a volume, or Export all files in a volume to a tar file if the output is specified.
  27. The format of file name in the tar file can be customized. Default is {{.Mime}}/{{.Id}}:{{.Name}}. Also available is {{Key}}.
  28. `,
  29. }
  30. var (
  31. exportVolumePath = cmdExport.Flag.String("dir", "/tmp", "input data directory to store volume data files")
  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. log.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. log.Fatalf("Create Volume Index [ERROR] %s\n", err)
  76. }
  77. defer indexFile.Close()
  78. nm := storage.LoadNeedleMap(indexFile)
  79. var version storage.Version
  80. err = storage.ScanVolumeFile(*exportVolumePath, vid, func(superBlock storage.SuperBlock) error {
  81. version = superBlock.Version
  82. return nil
  83. }, func(n *storage.Needle, offset uint32) error {
  84. debug("key", n.Id, "offset", offset, "size", n.Size, "disk_size", n.DiskSize(), "gzip", n.IsGzipped())
  85. nv, ok := nm.Get(n.Id)
  86. if ok && nv.Size > 0 {
  87. return walker(vid, n, version)
  88. } else {
  89. if !ok {
  90. debug("This seems deleted", n.Id)
  91. } else {
  92. debug("Id", n.Id, "size", n.Size)
  93. }
  94. }
  95. return nil
  96. })
  97. if err != nil {
  98. log.Fatalf("Export Volume File [ERROR] %s\n", err)
  99. }
  100. return true
  101. }
  102. type nameParams struct {
  103. Name string
  104. Id uint64
  105. Mime string
  106. Key string
  107. }
  108. func walker(vid storage.VolumeId, n *storage.Needle, version storage.Version) (err error) {
  109. key := directory.NewFileId(vid, n.Id, n.Cookie).String()
  110. if tarFh != nil {
  111. fnTmplBuf.Reset()
  112. if err = fnTmpl.Execute(fnTmplBuf,
  113. nameParams{Name: string(n.Name),
  114. Id: n.Id,
  115. Mime: string(n.Mime),
  116. Key: key,
  117. },
  118. ); err != nil {
  119. return err
  120. }
  121. nm := fnTmplBuf.String()
  122. if n.IsGzipped() && path.Ext(nm) != ".gz" {
  123. nm = nm + ".gz"
  124. }
  125. tarHeader.Name, tarHeader.Size = nm, int64(len(n.Data))
  126. if err = tarFh.WriteHeader(&tarHeader); err != nil {
  127. return err
  128. }
  129. _, err = tarFh.Write(n.Data)
  130. } else {
  131. size := n.DataSize
  132. if version == storage.Version1 {
  133. size = n.Size
  134. }
  135. fmt.Printf("key=%s Name=%s Size=%d gzip=%t mime=%s\n",
  136. key,
  137. n.Name,
  138. size,
  139. n.IsGzipped(),
  140. n.Mime,
  141. )
  142. }
  143. return
  144. }