snaoshotOpr.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package hybridBackup
  2. import (
  3. "errors"
  4. "fmt"
  5. "log"
  6. "os"
  7. "path/filepath"
  8. "strings"
  9. )
  10. /*
  11. snapshotOpr.go
  12. Restore snapshot for a certain user in the snapshot
  13. The steps basically as follows.
  14. 1. Check and validate the snapshot
  15. 2. Iterate and restore all files contained in that snapshot to source drive if it is owned by the user
  16. 3. Get the snapshot link file. Restore all files with pointer still exists and owned by the user
  17. */
  18. //Restore a snapshot by task and name
  19. func restoreSnapshotByName(backupTask *BackupTask, snapshotName string, username *string) error {
  20. //Step 1: Check and validate snapshot
  21. snapshotBaseFolder := filepath.Join(backupTask.DiskPath, "/version/", snapshotName)
  22. snapshotRestoreDirectory := filepath.ToSlash(filepath.Clean(backupTask.ParentPath))
  23. if !fileExists(snapshotBaseFolder) {
  24. return errors.New("Given snapshot ID not found")
  25. }
  26. if !fileExists(filepath.Join(snapshotBaseFolder, "snapshot.datalink")) {
  27. return errors.New("Snapshot corrupted. snapshot.datalink pointer file not found.")
  28. }
  29. log.Println("[HybridBackup] Restoring from snapshot ID: ", filepath.Base(snapshotBaseFolder))
  30. //Step 2: Restore all the files changed during that snapshot period
  31. fastWalk(snapshotBaseFolder, func(filename string) error {
  32. //Skip the datalink file
  33. if filepath.Base(filename) == "snapshot.datalink" {
  34. return nil
  35. }
  36. //Calculate the relative path of this file
  37. relPath, err := filepath.Rel(snapshotBaseFolder, filepath.ToSlash(filename))
  38. if err != nil {
  39. //Just skip this cycle
  40. return nil
  41. }
  42. assumedRestoreLocation := filepath.ToSlash(filepath.Join(snapshotRestoreDirectory, relPath))
  43. allowRestore := false
  44. if username == nil {
  45. //Restore all files
  46. allowRestore = true
  47. } else {
  48. //Restore only files owned by this user
  49. isOwnedByThisUser := snapshotFileBelongsToUser("/"+filepath.ToSlash(relPath), *username)
  50. if isOwnedByThisUser {
  51. allowRestore = true
  52. }
  53. }
  54. if allowRestore {
  55. //Check if the restore file parent folder exists.
  56. if !fileExists(filepath.Dir(assumedRestoreLocation)) {
  57. os.MkdirAll(filepath.Dir(assumedRestoreLocation), 0775)
  58. }
  59. //Copy this file from backup to source, overwriting source if exists
  60. err := BufferedLargeFileCopy(filepath.ToSlash(filename), filepath.ToSlash(assumedRestoreLocation), 0775)
  61. if err != nil {
  62. log.Println("[HybridBackup] Restore failed: " + err.Error())
  63. }
  64. }
  65. return nil
  66. })
  67. //Step 3: Restore files from datalinking file
  68. linkMap, err := readLinkFile(snapshotBaseFolder)
  69. if err != nil {
  70. return err
  71. }
  72. for relPath, restorePointer := range linkMap.UnchangedFile {
  73. //Get the assume restore position and source location
  74. sourceFileLocation := filepath.ToSlash(filepath.Join(backupTask.DiskPath, "/version/", "/"+restorePointer+"/", relPath))
  75. assumedRestoreLocation := filepath.ToSlash(filepath.Join(snapshotRestoreDirectory, relPath))
  76. //Check if the restore file parent folder exists.
  77. if snapshotFileBelongsToUser(filepath.ToSlash(relPath), *username) {
  78. if !fileExists(filepath.Dir(assumedRestoreLocation)) {
  79. os.MkdirAll(filepath.Dir(assumedRestoreLocation), 0775)
  80. }
  81. //Copy this file from backup to source, overwriting source if exists
  82. BufferedLargeFileCopy(filepath.ToSlash(sourceFileLocation), filepath.ToSlash(assumedRestoreLocation), 0775)
  83. log.Println("[HybridBackup] Restored " + assumedRestoreLocation + " for user " + *username)
  84. }
  85. }
  86. return nil
  87. }
  88. /*
  89. Merge Snapshot
  90. This function is used to merge old snapshots if the system is running out of space
  91. the two snapshot has to be sequential
  92. */
  93. func mergeOldestSnapshots(backupTask *BackupTask) error {
  94. //Get all snapshot names from disk path
  95. files, err := filepath.Glob(filepath.ToSlash(filepath.Clean(filepath.Join(backupTask.DiskPath, "/version/"))) + "/*")
  96. if err != nil {
  97. return err
  98. }
  99. snapshots := []string{}
  100. for _, file := range files {
  101. if isDir(file) && fileExists(filepath.Join(file, "snapshot.datalink")) {
  102. //This is a snapshot file
  103. snapshots = append(snapshots, file)
  104. }
  105. }
  106. if len(snapshots) < 2 {
  107. return errors.New("Not enough snapshot to merge")
  108. }
  109. olderSnapshotDir := filepath.ToSlash(snapshots[0])
  110. newerSnapshitDir := filepath.ToSlash(snapshots[1])
  111. //Check if both snapshot exists
  112. if !fileExists(olderSnapshotDir) || !fileExists(newerSnapshitDir) {
  113. log.Println("[HybridBackup] Snapshot merge failed: Snapshot folder not found")
  114. return errors.New("Snapshot folder not found")
  115. }
  116. //Check if link file exists
  117. linkFileLocation := filepath.Join(newerSnapshitDir, "snapshot.datalink")
  118. if !fileExists(linkFileLocation) {
  119. log.Println("[HybridBackup] Snapshot link file not found.")
  120. return errors.New("Snapshot link file not found")
  121. }
  122. //Get linker file
  123. linkMap, err := readLinkFile(newerSnapshitDir)
  124. if err != nil {
  125. linkMap = &LinkFileMap{
  126. UnchangedFile: map[string]string{},
  127. DeletedFiles: map[string]string{},
  128. }
  129. }
  130. log.Println("[HybridBackup] Merging two snapshots in background")
  131. //All file ready. Merge both snapshots
  132. rootAbs, _ := filepath.Abs(olderSnapshotDir)
  133. rootAbs = filepath.ToSlash(filepath.Clean(rootAbs))
  134. fastWalk(olderSnapshotDir, func(filename string) error {
  135. fileAbs, _ := filepath.Abs(filename)
  136. fileAbs = filepath.ToSlash(filepath.Clean(fileAbs))
  137. relPath := filepath.ToSlash(strings.ReplaceAll(fileAbs, rootAbs, ""))
  138. mergeAssumedLocation := filepath.Join(newerSnapshitDir, relPath)
  139. if !fileExists(mergeAssumedLocation) {
  140. //Check if this is in delete marker. If yes, skip this
  141. _, ok := linkMap.DeletedFiles[relPath]
  142. if !ok {
  143. //This is not in delete map. Move it
  144. //This must use rename instead of copy because of lack of space issue
  145. if !fileExists(filepath.Dir(mergeAssumedLocation)) {
  146. os.MkdirAll(filepath.Dir(mergeAssumedLocation), 0775)
  147. }
  148. err = os.Rename(filename, mergeAssumedLocation)
  149. if err != nil {
  150. return err
  151. }
  152. } else {
  153. fmt.Println("Disposing file: ", relPath)
  154. }
  155. }
  156. return nil
  157. })
  158. //Rewrite all other datalink file to make olderSnapshot name to new snapshot name
  159. oldLink := filepath.Base(olderSnapshotDir)
  160. newLink := filepath.Base(newerSnapshitDir)
  161. for i := 1; i < len(snapshots); i++ {
  162. err = updateLinkerPointer(snapshots[i], oldLink, newLink)
  163. if err != nil {
  164. log.Println("[HybridBackup] Link file update file: " + filepath.Base(snapshots[i]))
  165. }
  166. fmt.Println("Updating link file for " + filepath.Base(snapshots[i]))
  167. }
  168. //Remove the old snapshot folder structure
  169. err = os.RemoveAll(olderSnapshotDir)
  170. return err
  171. }