basicBackup.go 6.9 KB

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