2 glocate -- ZFS-diff-friendly locate-like utility
3 Copyright (C) 2022-2023 Sergey Matveev <stargrave@stargrave.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
36 func (a BySrc) Len() int {
40 func (a BySrc) Swap(i, j int) {
41 a[i], a[j] = a[j], a[i]
44 func (a BySrc) Less(i, j int) bool {
45 return namesCmp(a[i].src, a[j].src) < 0
50 func (a EntByName) Len() int {
54 func (a EntByName) Swap(i, j int) {
55 a[i], a[j] = a[j], a[i]
58 func (a EntByName) Less(i, j int) bool {
59 return namesCmp(a[i].name, a[j].name) < 0
62 func updateWithDiff(dbPath, strip string) *os.File {
63 scanner := bufio.NewScanner(os.Stdin)
65 var delsNames []string
66 var addsNames []string
67 var modsNames []string
75 cols := strings.Split(t, "\t")
77 log.Fatalln("bad zfs-diff format")
79 isDir = cols[1] == "/"
80 name := deoctalize(strings.TrimPrefix(cols[2], strip))
81 name = strings.TrimRight(name, "/")
91 delsNames = append(delsNames, name)
93 addsNames = append(addsNames, name)
95 modsNames = append(modsNames, name)
98 log.Fatalln("bad zfs-diff format for R")
100 dst := "./" + deoctalize(strings.TrimPrefix(cols[3], strip))
103 rens = append(rens, Ren{
104 src: nameSplit(name),
108 delsNames = append(delsNames, name)
109 addsNames = append(addsNames, dst)
112 log.Fatalln("bad zfs-diff format")
116 entsReader := make(chan Ent, 1<<10)
117 db, err := os.Open(dbPath)
121 dels := make([][]string, 0, len(delsNames)+len(rens))
122 adds := make([][]string, 0, len(addsNames)+len(rens))
123 mods := make([]*Ent, 0, len(modsNames)+len(rens))
125 sort.Sort(BySrc(rens))
126 go reader(db, entsReader)
130 ent, ok = <-entsReader
136 if hasPrefix(ent.name, rens[0].src) {
137 dels = append(dels, ent.name)
139 append([]string{}, rens[0].dst...),
140 ent.name[len(rens[0].src):]...,
142 adds = append(adds, dst)
143 mods = append(mods, &Ent{name: dst})
145 // strip "/" from prefix directory
147 last := dst[len(dst)-1]
148 dst[len(dst)-1] = last[:len(last)-1]
161 for _, name := range delsNames {
162 dels = append(dels, nameSplit(name))
165 sort.Sort(ByName(dels))
167 for _, name := range addsNames {
168 adds = append(adds, nameSplit(name))
169 modsNames = append(modsNames, name)
172 sort.Sort(ByName(adds))
174 for _, name := range modsNames {
175 mods = append(mods, &Ent{name: nameSplit(name)})
178 sort.Sort(EntByName(mods))
180 for _, ent := range mods {
181 info, err = os.Stat(nameJoin(ent.name))
183 log.Println("can not stat:", nameJoin(ent.name), ":", err)
186 if info.Mode().IsRegular() {
187 ent.size = info.Size()
189 ent.mtime = info.ModTime().Unix()
192 _, err = db.Seek(0, io.SeekStart)
196 tmp0, err := os.CreateTemp(TmpDir, "glocate-idx")
200 defer os.Remove(tmp0.Name())
201 entsReader = make(chan Ent, 1<<10)
202 entsDirSizer := make(chan Ent, 1<<10)
203 entsWriter := make(chan Ent, 1<<10)
204 go reader(db, entsReader)
206 dirSizerJob := make(chan struct{})
208 sinkBack := make(chan Ent, 1)
210 dirSizer(&dirSizes, 1, sinkBack, entsDirSizer, entsWriter)
214 writerJob := make(chan struct{})
216 writer(tmp0, entsWriter)
220 for ent := range entsReader {
221 if len(dels) > 0 && namesCmp(ent.name, dels[0]) == 0 {
225 for len(adds) > 0 && namesCmp(adds[0], ent.name) < 0 {
226 if namesCmp(mods[0].name, adds[0]) != 0 {
227 panic("+ and M lists are out of sync")
231 mtime: mods[0].mtime,
234 entsDirSizer <- newEnt
238 if len(mods) > 0 && namesCmp(ent.name, mods[0].name) == 0 {
239 ent.mtime = mods[0].mtime
240 ent.size = mods[0].size
249 if namesCmp(mods[0].name, adds[0]) != 0 {
250 panic("+ and M lists are out of sync")
254 mtime: mods[0].mtime,
257 entsDirSizer <- newEnt
267 tmp1 := applyDirSizes(tmp0, dirSizes)
269 os.Remove(tmp0.Name())