basicBackup.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package hybridBackup
  2. import (
  3. "errors"
  4. "log"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "time"
  9. )
  10. /*
  11. Basic Backup
  12. This script handle basic backup process
  13. */
  14. func executeBackup(backupConfig *BackupTask, deepBackup bool) (string, error) {
  15. copiedFileList := []string{}
  16. rootPath := filepath.ToSlash(filepath.Clean(backupConfig.ParentPath))
  17. //Check if the backup parent root is identical / within backup disk
  18. parentRootAbs, err := filepath.Abs(backupConfig.ParentPath)
  19. if err != nil {
  20. return "", errors.New("Unable to resolve parent disk path")
  21. }
  22. backupRootAbs, err := filepath.Abs(filepath.Join(backupConfig.DiskPath, "/backup/"))
  23. if err != nil {
  24. return "", errors.New("Unable to resolve backup disk path")
  25. }
  26. if len(parentRootAbs) >= len(backupRootAbs) {
  27. if parentRootAbs[:len(backupRootAbs)] == backupRootAbs {
  28. //parent root is within backup root. Raise configuration error
  29. log.Println("*HyperBackup* Invalid backup cycle: Parent drive is located inside backup drive")
  30. return "", errors.New("Configuration Error. Skipping backup cycle.")
  31. }
  32. }
  33. //Add file cycles
  34. fastWalk(rootPath, func(filename string) error {
  35. if filepath.Base(filename) == "aofs.db" || filepath.Base(filename) == "aofs.db.lock" {
  36. //Reserved filename, skipping
  37. return nil
  38. }
  39. //Get the target paste location
  40. rootAbs, _ := filepath.Abs(rootPath)
  41. fileAbs, _ := filepath.Abs(filename)
  42. rootAbs = filepath.ToSlash(filepath.Clean(rootAbs))
  43. fileAbs = filepath.ToSlash(filepath.Clean(fileAbs))
  44. relPath := strings.ReplaceAll(fileAbs, rootAbs, "")
  45. assumedTargetPosition := filepath.Join(backupConfig.DiskPath, "/backup/", relPath)
  46. if !deepBackup {
  47. //Shallow copy. Only do copy base on file exists or not
  48. //This is used to reduce the time for reading the file metatag
  49. if !fileExists(assumedTargetPosition) {
  50. //Target file not exists in backup disk. Make a copy
  51. if !fileExists(filepath.Dir(assumedTargetPosition)) {
  52. //Folder containing this file not exists. Create it
  53. os.MkdirAll(filepath.Dir(assumedTargetPosition), 0755)
  54. }
  55. //Copy the file to target
  56. err := BufferedLargeFileCopy(fileAbs, assumedTargetPosition, 1024)
  57. if err != nil {
  58. log.Println("[HybridBackup] Copy Failed for file "+filepath.Base(fileAbs), err.Error(), " Skipping.")
  59. } else {
  60. //No problem. Add this filepath into the list
  61. copiedFileList = append(copiedFileList, assumedTargetPosition)
  62. }
  63. }
  64. } else {
  65. //Deep copy. Check and match the modtime of each file
  66. if !fileExists(assumedTargetPosition) {
  67. if !fileExists(filepath.Dir(assumedTargetPosition)) {
  68. //Folder containing this file not exists. Create it
  69. os.MkdirAll(filepath.Dir(assumedTargetPosition), 0755)
  70. }
  71. //Copy the file to target
  72. err := BufferedLargeFileCopy(fileAbs, assumedTargetPosition, 1024)
  73. if err != nil {
  74. log.Println("[HybridBackup] Copy Failed for file "+filepath.Base(fileAbs), err.Error(), " Skipping.")
  75. return nil
  76. } else {
  77. //No problem. Add this filepath into the list
  78. copiedFileList = append(copiedFileList, assumedTargetPosition)
  79. }
  80. } else {
  81. //Target file already exists.
  82. //Check if it has been modified since the last cycle time
  83. lastModTime := lastModTime(fileAbs)
  84. if lastModTime > backupConfig.LastCycleTime {
  85. //Check if their hash matches
  86. srcHash, err := getFileHash(fileAbs)
  87. if err != nil {
  88. log.Println("[HybridBackup] Hash calculation failed for file "+filepath.Base(fileAbs), err.Error(), " Skipping.")
  89. return nil
  90. }
  91. targetHash, err := getFileHash(assumedTargetPosition)
  92. if err != nil {
  93. log.Println("[HybridBackup] Hash calculation failed for file "+filepath.Base(assumedTargetPosition), err.Error(), " Skipping.")
  94. return nil
  95. }
  96. if srcHash != targetHash {
  97. log.Println("[Debug] Hash mismatch. Copying ", fileAbs)
  98. //This file has been recently changed. Copy it to new location
  99. err = BufferedLargeFileCopy(fileAbs, assumedTargetPosition, 1024)
  100. if err != nil {
  101. log.Println("[HybridBackup] Copy Failed for file "+filepath.Base(fileAbs), err.Error(), " Skipping.")
  102. } else {
  103. //No problem. Add this filepath into the list
  104. copiedFileList = append(copiedFileList, assumedTargetPosition)
  105. }
  106. //Check if this file is in the remove marker list. If yes, pop it from the list
  107. _, ok := backupConfig.DeleteFileMarkers[relPath]
  108. if ok {
  109. //File exists. remove it from delete file amrker
  110. delete(backupConfig.DeleteFileMarkers, relPath)
  111. log.Println("Removing ", relPath, " from delete marker list")
  112. }
  113. }
  114. }
  115. }
  116. }
  117. ///Remove file cycle
  118. backupDriveRootPath := filepath.ToSlash(filepath.Clean(filepath.Join(backupConfig.DiskPath, "/backup/")))
  119. fastWalk(backupConfig.DiskPath, func(filename string) error {
  120. if filepath.Base(filename) == "aofs.db" || filepath.Base(filename) == "aofs.db.lock" {
  121. //Reserved filename, skipping
  122. return nil
  123. }
  124. //Get the target paste location
  125. rootAbs, _ := filepath.Abs(backupDriveRootPath)
  126. fileAbs, _ := filepath.Abs(filename)
  127. rootAbs = filepath.ToSlash(filepath.Clean(rootAbs))
  128. fileAbs = filepath.ToSlash(filepath.Clean(fileAbs))
  129. thisFileRel := filename[len(backupDriveRootPath):]
  130. originalFileOnDiskPath := filepath.ToSlash(filepath.Clean(filepath.Join(backupConfig.ParentPath, thisFileRel)))
  131. //Check if the taget file not exists and this file has been here for more than 24h
  132. if !fileExists(originalFileOnDiskPath) {
  133. //This file not exists. Check if it is in the delete file marker for more than 24 hours
  134. val, ok := backupConfig.DeleteFileMarkers[thisFileRel]
  135. if !ok {
  136. //This file is newly deleted. Push into the marker map
  137. backupConfig.DeleteFileMarkers[thisFileRel] = time.Now().Unix()
  138. log.Println("[Debug] Adding " + filename + " to delete marker")
  139. } else {
  140. //This file has been marked. Check if it is time to delete
  141. if time.Now().Unix()-val > 3600*24 {
  142. log.Println("[Debug] Deleting " + filename)
  143. //Remove the backup file
  144. os.RemoveAll(filename)
  145. //Remove file from delete file markers
  146. delete(backupConfig.DeleteFileMarkers, thisFileRel)
  147. }
  148. }
  149. }
  150. return nil
  151. })
  152. return nil
  153. })
  154. return "", nil
  155. }
  156. func listBasicRestorables(task *BackupTask) ([]*RestorableFile, error) {
  157. restorableFiles, err := task.compareRootPaths()
  158. return restorableFiles, err
  159. }