1 // glocate -- ZFS-diff-friendly locate-like utility
2 // Copyright (C) 2022-2024 Sergey Matveev <stargrave@stargrave.org>
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program. If not, see <http://www.gnu.org/licenses/>.
34 func (a BySrc) Len() int {
38 func (a BySrc) Swap(i, j int) {
39 a[i], a[j] = a[j], a[i]
42 func (a BySrc) Less(i, j int) bool {
43 return namesCmp(a[i].src, a[j].src) < 0
48 func (a EntByName) Len() int {
52 func (a EntByName) Swap(i, j int) {
53 a[i], a[j] = a[j], a[i]
56 func (a EntByName) Less(i, j int) bool {
57 return namesCmp(a[i].name, a[j].name) < 0
60 func updateWithDiff(dbPath, strip string) *os.File {
61 scanner := bufio.NewScanner(os.Stdin)
63 var delsNames []string
64 var addsNames []string
65 var modsNames []string
73 cols := strings.Split(t, "\t")
75 log.Fatalln("bad zfs-diff format")
77 isDir = cols[1] == "/"
78 name := deoctalize(strings.TrimPrefix(cols[2], strip))
79 name = strings.TrimRight(name, "/")
89 delsNames = append(delsNames, name)
91 addsNames = append(addsNames, name)
93 modsNames = append(modsNames, name)
96 log.Fatalln("bad zfs-diff format for R")
98 dst := "./" + deoctalize(strings.TrimPrefix(cols[3], strip))
101 rens = append(rens, Ren{
102 src: nameSplit(name),
106 delsNames = append(delsNames, name)
107 addsNames = append(addsNames, dst)
110 log.Fatalln("bad zfs-diff format")
114 entsReader := make(chan Ent, 1<<10)
115 db, err := os.Open(dbPath)
119 dels := make([][]string, 0, len(delsNames)+len(rens))
120 adds := make([][]string, 0, len(addsNames)+len(rens))
121 mods := make([]*Ent, 0, len(modsNames)+len(rens))
123 sort.Sort(BySrc(rens))
124 go reader(db, entsReader)
128 ent, ok = <-entsReader
134 if hasPrefix(ent.name, rens[0].src) {
135 dels = append(dels, ent.name)
137 append([]string{}, rens[0].dst...),
138 ent.name[len(rens[0].src):]...,
140 adds = append(adds, dst)
141 mods = append(mods, &Ent{name: dst})
143 // strip "/" from prefix directory
145 last := dst[len(dst)-1]
146 dst[len(dst)-1] = last[:len(last)-1]
159 for _, name := range delsNames {
160 dels = append(dels, nameSplit(name))
163 sort.Sort(ByName(dels))
165 for _, name := range addsNames {
166 adds = append(adds, nameSplit(name))
167 modsNames = append(modsNames, name)
170 sort.Sort(ByName(adds))
172 for _, name := range modsNames {
173 mods = append(mods, &Ent{name: nameSplit(name)})
176 sort.Sort(EntByName(mods))
178 for _, ent := range mods {
179 info, err = os.Stat(nameJoin(ent.name))
181 log.Println("can not stat:", nameJoin(ent.name), ":", err)
184 if info.Mode().IsRegular() {
185 ent.size = info.Size()
187 ent.mtime = info.ModTime().Unix()
190 _, err = db.Seek(0, io.SeekStart)
194 tmp0, err := os.CreateTemp(TmpDir, "glocate-idx")
198 defer os.Remove(tmp0.Name())
199 entsReader = make(chan Ent, 1<<10)
200 entsDirSizer := make(chan Ent, 1<<10)
201 entsWriter := make(chan Ent, 1<<10)
202 go reader(db, entsReader)
204 dirSizerJob := make(chan struct{})
206 sinkBack := make(chan Ent, 1)
208 dirSizer(&dirSizes, 1, sinkBack, entsDirSizer, entsWriter)
212 writerJob := make(chan struct{})
214 writer(tmp0, entsWriter)
218 for ent := range entsReader {
219 if len(dels) > 0 && namesCmp(ent.name, dels[0]) == 0 {
223 for len(adds) > 0 && namesCmp(adds[0], ent.name) < 0 {
224 if namesCmp(mods[0].name, adds[0]) != 0 {
225 panic("+ and M lists are out of sync")
229 mtime: mods[0].mtime,
232 entsDirSizer <- newEnt
236 if len(mods) > 0 && namesCmp(ent.name, mods[0].name) == 0 {
237 ent.mtime = mods[0].mtime
238 ent.size = mods[0].size
247 if namesCmp(mods[0].name, adds[0]) != 0 {
248 panic("+ and M lists are out of sync")
252 mtime: mods[0].mtime,
255 entsDirSizer <- newEnt
265 tmp1 := applyDirSizes(tmp0, dirSizes)
267 os.Remove(tmp0.Name())