This repository has been archived by the owner on Nov 22, 2021. It is now read-only.
/
scanfolder.go
193 lines (164 loc) · 4.52 KB
/
scanfolder.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
package db
import (
"fmt"
enc "github.com/SchnorcherSepp/splitfs/encoding"
impl "github.com/SchnorcherSepp/storage/defaultimpl"
"golang.org/x/text/unicode/norm"
"log"
"os"
"path/filepath"
"sort"
"strings"
"time"
)
// FromScan scan a root folder and return a new db.
// Bundles and links are removed.
func FromScan(rootPath string, oldDB Db, debugLvl uint8, keyFile *enc.KeyFile) (newDB Db, changed bool, summary string, retErr error) {
// debug (0=off, 1=debug, 2=high)
debug := debugLvl >= impl.DebugLow
// replace oldDB with clone (first level)
clone := NewDb()
if oldDB.VFiles != nil {
for k, v := range oldDB.VFiles {
clone.VFiles[k] = v
}
}
oldDB = clone
// reset bundles
resetOldBundles(&oldDB)
// init
countNewOrUpdate := 0
newDB = NewDb()
// Walk
retErr = filepath.Walk(rootPath, func(absPath string, info os.FileInfo, err error) error {
// WalkFunc errors
if err != nil {
return err
}
// relative path
relPath, err := filepath.Rel(rootPath, absPath)
if err != nil {
return err
}
// UTF8 FIX: Text normalization
// https://blog.golang.org/normalization
relPath = norm.NFC.String(relPath)
// WINDOWS/LINUX FIX: path separator = '/'
relPath = strings.ReplaceAll(relPath, "\\", "/")
// get element attributes
isDir := info.IsDir()
mtime := info.ModTime().Unix()
size := info.Size()
// WINDOWS/LINUX FIX: set folder size to 0
if isDir {
size = 0
}
// if folder: get folder content
var dirEntries []FolderEl
if isDir {
dirEntries, err = getDirEntries(absPath)
if err != nil {
return err
}
}
// find element in old DB
e, ok := oldDB.VFiles[relPath]
// element not found (new) OR element changed
if !ok || e.FileSize != size || e.IsDir != isDir || e.MTime != mtime {
countNewOrUpdate++
changed = true
detail := ""
if !isDir {
start := time.Now()
// is file -> scan
vf, err := ScanFile(absPath, relPath, keyFile)
if err != nil {
return err
}
e = vf
// write detail
var sinceInSec = float64(time.Since(start)) / float64(time.Second)
if sinceInSec < 0.001 {
sinceInSec = 0.001
}
var sizeInMb = float64(e.FileSize) / (1024 * 1024)
detail = fmt.Sprintf("\t[%.2f MB/s]", sizeInMb/sinceInSec)
} else {
// is folder -> create
e = VirtFile{ // override db element (dir)
RelPath: relPath,
FileSize: 0,
MTime: mtime,
IsDir: isDir,
FolderContent: dirEntries,
}
}
if debug {
if !ok {
log.Printf("DEBUG: %s/ScanFolder: new: '%s'%s", packageName, relPath, detail)
} else {
log.Printf("DEBUG: %s/ScanFolder: changed: '%s'%s", packageName, relPath, detail)
}
}
}
// FIX: Always update the folder content. If no file changes have been made,
// the database will not be updated. If the database is updated, then the
// folder content will also be up to date.
e.FolderContent = dirEntries
// Delete exist elements from old db. At the end we can detect old item no longer available.
// If there is anything left, something has changed!
delete(oldDB.VFiles, relPath)
// Write the element to the new database and exit the walk function
newDB.VFiles[relPath] = e
return nil
})
// finale changed?
if len(oldDB.VFiles) > 0 {
changed = true
}
// statistic
summary = fmt.Sprintf("SCAN: error=%v, sum=%d, changed=%v, newOrUpdate=%d, removed=%d", retErr, len(newDB.VFiles), changed, countNewOrUpdate, len(oldDB.VFiles))
if debug {
log.Printf("DEBUG: %s/ScanFolder: %s", packageName, summary)
}
return
}
// ---------- HELPER -----------------------------------------------------------------------------------------------//
// getDirEntries return folder content
func getDirEntries(dir string) ([]FolderEl, error) {
// open folder
f, err := os.Open(dir)
if err != nil {
return nil, err
}
defer f.Close()
// read folder list
names, err := f.Readdirnames(-1)
if err != nil {
return nil, err
}
// sort list
sort.Strings(names)
// return stuff
retList := make([]FolderEl, 0, len(names))
for _, name := range names {
// sub-element is file oder folder
absPath := filepath.Join(dir, name)
info, err := os.Stat(absPath)
if err != nil {
return nil, err
}
isDir := info.IsDir()
// UTF8 FIX: Text normalization
// https://blog.golang.org/normalization
name = norm.NFC.String(name)
// WINDOWS/LINUX FIX: path separator = '/'
name = strings.ReplaceAll(name, "\\", "/")
// append
retList = append(retList, FolderEl{
RelPath: name,
IsDir: isDir,
})
}
return retList, nil
}