/
mybackup.go
204 lines (174 loc) · 5.3 KB
/
mybackup.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package main
// executables must use the package main
import (
"fmt"
"os"
"log"
"bufio"
"strings"
"io"
"io/ioutil"
"time"
)
type FileHandlerForLineInFile func(line string)
const (
logFileNamePrefix = "backupLog"
logFileNameSuffix = "log"
)
var logFile *os.File
/*
Input argument: Complete or relative path to a file that specifies the following:
A file where each line specifies a folder that is to be backed up (recursively) or a file to be backed up
*/
func main() {
checkScriptArgsAndExitIfRequired()
createLogFile(os.Args[2])
readFile()
}
func createLogFile(fullFolderPath string) {
_, err := os.Stat(fullFolderPath)
if err != nil && os.IsNotExist(err) {
createTargetFolder("", fullFolderPath)
}
currentTime := time.Now().Format(time.UnixDate)
logFile = createTargetFile(logFileNamePrefix + "-" + currentTime + "." + logFileNameSuffix, fullFolderPath)
log.SetOutput(logFile)
}
func ReadFileLineByLine(filename, targetFolder string, handler FileHandlerForLineInFile) {
file := getFileHandle(filename)
handleFile(file, targetFolder, handler)
}
func getFileHandle(filename string) *os.File {
file, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
return file
}
func handleFile(file *os.File, targetFolder string, handler FileHandlerForLineInFile) {
doHandleFileConcurrently(file, targetFolder, handler)
}
func doHandleFileConcurrently(file *os.File, targetFolder string, handler FileHandlerForLineInFile) {
reader := bufio.NewReader(file)
scanner := bufio.NewScanner(reader)
var syncStructure []chan bool
defer file.Close()
startTime := time.Now()
for scanner.Scan() {
c := make(chan bool)
syncStructure = append(syncStructure, c)
filenameToBeCopied := scanner.Text()
go func() {
doHandle(filenameToBeCopied, targetFolder, handler, c)
}()
}
count := len(syncStructure)
for i := 0; i < count; i++ {
<- syncStructure[i]
}
endTime := time.Now()
duration := endTime.Sub(startTime)
log.Println("Time taken: " + duration.String())
}
func doHandle(filename, targetFolder string, handler FileHandlerForLineInFile, c chan bool) {
handler(filename)
c <- true
}
func readFile() {
ReadFileLineByLine(os.Args[1], os.Args[2], func(line string) { doBackup(line, os.Args[2]) })
}
func doBackup(filename, targetFolder string) {
targetFolder = stripFileOrFolderPathOfTrailingPathSeparator(targetFolder)
if isFolderToBeCopiedTargetFolderOrInTargetFolder(filename, targetFolder) {
log.Println(filename + " is the same as, or within the target folder " + targetFolder + ". This artifact has not been copied")
return
}
file := getFileHandle(filename)
if file == nil {
return
}
fInfo, err := file.Stat()
if (err != nil) {
log.Println(err)
return
}
mode := fInfo.Mode()
if mode.IsDir() {
copyFolder(filename, targetFolder, file)
} else {
doCopyFile(filename, targetFolder, file)
}
}
func copyFolder(fullPathOfFolderToBeCopied, targetFolder string, folderToCopy *os.File) {
folderToBeCopiedInto := createTargetFolder(fullPathOfFolderToBeCopied, targetFolder)
files, _ := ioutil.ReadDir(fullPathOfFolderToBeCopied)
for _, f := range files {
doBackup(fullPathOfFolderToBeCopied + string(os.PathSeparator) + f.Name(), folderToBeCopiedInto)
}
}
func performDoBackupAsync(filename, targetFolder string, c chan string) {
doBackup(filename, targetFolder)
c <- filename
}
func createTargetFolder(fullFilePath, targetFolder string) string {
filenameWithoutPath := getFileNameWithoutPath(fullFilePath)
fullPathOfFolderToBeCreated := targetFolder + string(os.PathSeparator) + filenameWithoutPath
log.Println("Creating folder " + fullPathOfFolderToBeCreated)
err := os.MkdirAll(fullPathOfFolderToBeCreated, 0777)
if err != nil {
log.Println(err)
}
return fullPathOfFolderToBeCreated
}
func getFileNameWithoutPath(fullPath string) string {
slices := strings.Split(fullPath, string(os.PathSeparator))
return slices[len(slices) - 1]
}
func doCopyFile(filename, targetFolder string, sourceFile *os.File) {
targetFile := createTargetFile(filename, targetFolder)
copyFile(sourceFile, targetFile)
log.Println(filename + " file copied")
}
func copyFile(sourceFile, targetFile *os.File ) {
_, err := io.Copy(targetFile, sourceFile)
defer sourceFile.Close()
defer targetFile.Close()
if err != nil {
log.Println(err)
}
targetFile.Sync()
}
func createTargetFile(filename, targetFolder string) *os.File {
filenameWithoutPath := getFileNameWithoutPath(filename)
file, err := os.Create(targetFolder + string(os.PathSeparator) + filenameWithoutPath)
if err != nil {
log.Println(err)
return nil
}
return file
}
func stripFileOrFolderPathOfTrailingPathSeparator(fullPath string) string {
sep := string(os.PathSeparator)
if strings.HasSuffix(fullPath, sep) {
return strings.TrimSuffix(fullPath, sep)
}
return fullPath
}
func isFolderToBeCopiedTargetFolderOrInTargetFolder(fullPathOfFolderToBeCopied, targetFolder string) bool {
if fullPathOfFolderToBeCopied == targetFolder {
return true
}
if strings.Contains(fullPathOfFolderToBeCopied, targetFolder) {
return true
}
return false
}
func checkScriptArgsAndExitIfRequired() {
if len(os.Args) < 3 {
fmt.Printf(getUsageMessage())
os.Exit(0)
}
}
func getUsageMessage() string {
return "usage: " + os.Args[0] + " <input file> <target folder>\n input file: path to file which holds on each line the file or folders to be recursively backed up\n"
}