basicBackup.go 7.0 KB

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