basicBackup.go 6.7 KB

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