Rename host library to master throughout
The two libraries are now "master" and "slave" everywhere: UI text, API JSON fields (master_root, rel_master, master_size, ...), Go identifiers, database columns, quality verdicts, quarantine subfolders and docs. Existing databases are upgraded by a new migration that renames the columns in place and rewrites stored "host" values, so pairs, history, cached verdicts and restorable quarantine entries carry over. Network "host" usages (SplitHostPort, the Origin/Host check) are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d06b6d546d
commit
5b088bb4a6
19 files changed
+339
-326
No files matched your search
+79
-79
@@ -19,15 +19,15 @@ import (
|
||||
)
|
||||
|
||||
type LinkOptions struct {
|
||||
// AllowDowngrade confirms linking when the host copy scored lower.
|
||||
// AllowDowngrade confirms linking when the master copy scored lower.
|
||||
AllowDowngrade bool
|
||||
Trigger string
|
||||
}
|
||||
|
||||
// snapshot holds the verified state of both files right before an operation.
|
||||
type snapshot struct {
|
||||
hostAbs, slaveAbs string
|
||||
hi, si fsops.Info
|
||||
masterAbs, slaveAbs string
|
||||
mi, si fsops.Info
|
||||
}
|
||||
|
||||
// preflight re-validates a match against the filesystem: both files must be
|
||||
@@ -36,28 +36,28 @@ type snapshot struct {
|
||||
func preflight(l *libs, m store.Match) (snapshot, error) {
|
||||
var s snapshot
|
||||
var err error
|
||||
if s.hostAbs, err = l.host.Resolve(m.RelHost); err != nil {
|
||||
if s.masterAbs, err = l.master.Resolve(m.RelMaster); err != nil {
|
||||
return s, err
|
||||
}
|
||||
if s.slaveAbs, err = l.slave.Resolve(m.RelSlave); err != nil {
|
||||
return s, err
|
||||
}
|
||||
if s.hi, err = fsops.RegularFile(s.hostAbs); err != nil {
|
||||
return s, fmt.Errorf("host file: %w", err)
|
||||
if s.mi, err = fsops.RegularFile(s.masterAbs); err != nil {
|
||||
return s, fmt.Errorf("master file: %w", err)
|
||||
}
|
||||
if s.si, err = fsops.RegularFile(s.slaveAbs); err != nil {
|
||||
return s, fmt.Errorf("slave file: %w", err)
|
||||
}
|
||||
if s.hi.Ino != m.HostIno || !s.hi.Unchanged(m.HostSize, m.HostMtime) {
|
||||
return s, fmt.Errorf("host %w", fsops.ErrChanged)
|
||||
if s.mi.Ino != m.MasterIno || !s.mi.Unchanged(m.MasterSize, m.MasterMtime) {
|
||||
return s, fmt.Errorf("master %w", fsops.ErrChanged)
|
||||
}
|
||||
if s.si.Ino != m.SlaveIno || !s.si.Unchanged(m.SlaveSize, m.SlaveMtime) {
|
||||
return s, fmt.Errorf("slave %w", fsops.ErrChanged)
|
||||
}
|
||||
if s.hi.Dev != s.si.Dev || s.hi.Dev != l.dev {
|
||||
if s.mi.Dev != s.si.Dev || s.mi.Dev != l.dev {
|
||||
return s, fsops.ErrCrossDevice
|
||||
}
|
||||
if s.hi.SameInode(s.si) {
|
||||
if s.mi.SameInode(s.si) {
|
||||
return s, errors.New("already linked")
|
||||
}
|
||||
return s, nil
|
||||
@@ -128,7 +128,7 @@ func (e *Engine) finish(ctx context.Context, o *store.Operation, err error) erro
|
||||
e.log.Warn("operation failed", "op", o.ID, "action", o.Action, "slave", o.RelSlave, "err", err)
|
||||
} else {
|
||||
o.Status = store.OpDone
|
||||
e.log.Info("operation done", "op", o.ID, "action", o.Action, "slave", o.RelSlave, "host", o.RelHost)
|
||||
e.log.Info("operation done", "op", o.ID, "action", o.Action, "slave", o.RelSlave, "master", o.RelMaster)
|
||||
}
|
||||
if ferr := e.st.FinishOp(ctx, o.ID, o.Status, o.Error); ferr != nil && err == nil {
|
||||
return ferr
|
||||
@@ -142,10 +142,10 @@ func verdictOf(m store.Match) probe.Verdict {
|
||||
return v
|
||||
}
|
||||
|
||||
// Link replaces the slave file with a hardlink of the host file.
|
||||
// Link replaces the slave file with a hardlink of the master file.
|
||||
//
|
||||
// Identical files are re-compared byte for byte right before linking and no
|
||||
// quarantine copy is kept (the data exists in the host). Non-identical files
|
||||
// quarantine copy is kept (the data exists in the master). Non-identical files
|
||||
// are quarantined first so the operation can be undone.
|
||||
func (e *Engine) Link(ctx context.Context, matchID int64, opt LinkOptions) (store.Operation, error) {
|
||||
e.mu.Lock()
|
||||
@@ -176,7 +176,7 @@ func (e *Engine) Link(ctx context.Context, matchID int64, opt LinkOptions) (stor
|
||||
return store.Operation{}, err
|
||||
}
|
||||
if identical {
|
||||
same, err := fsops.SameContent(s.hostAbs, s.slaveAbs)
|
||||
same, err := fsops.SameContent(s.masterAbs, s.slaveAbs)
|
||||
if err != nil {
|
||||
return store.Operation{}, err
|
||||
}
|
||||
@@ -186,7 +186,7 @@ func (e *Engine) Link(ctx context.Context, matchID int64, opt LinkOptions) (stor
|
||||
}
|
||||
}
|
||||
|
||||
newRel := filepath.Join(filepath.Dir(m.RelSlave), filepath.Base(m.RelHost))
|
||||
newRel := filepath.Join(filepath.Dir(m.RelSlave), filepath.Base(m.RelMaster))
|
||||
newAbs, err := l.slave.Resolve(newRel)
|
||||
if err != nil {
|
||||
return store.Operation{}, err
|
||||
@@ -198,7 +198,7 @@ func (e *Engine) Link(ctx context.Context, matchID int64, opt LinkOptions) (stor
|
||||
}
|
||||
}
|
||||
|
||||
o := store.Operation{PairID: l.pair.ID, Action: store.ActLink, RelHost: m.RelHost, RelSlave: m.RelSlave,
|
||||
o := store.Operation{PairID: l.pair.ID, Action: store.ActLink, RelMaster: m.RelMaster, RelSlave: m.RelSlave,
|
||||
RelNew: newRel, Trigger: opt.Trigger}
|
||||
if identical {
|
||||
o.Bytes = s.si.Size // space freed
|
||||
@@ -226,7 +226,7 @@ func (e *Engine) Link(ctx context.Context, matchID int64, opt LinkOptions) (stor
|
||||
}
|
||||
}
|
||||
if !rename {
|
||||
if err := fsops.ReplaceWithLink(s.hostAbs, s.hi, s.slaveAbs); err != nil {
|
||||
if err := fsops.ReplaceWithLink(s.masterAbs, s.mi, s.slaveAbs); err != nil {
|
||||
if q != "" {
|
||||
if cur, e2 := fsops.Lstat(s.slaveAbs); e2 == nil && cur.SameInode(s.si) {
|
||||
dropQuarantine(l, q, s.si) // original untouched
|
||||
@@ -237,7 +237,7 @@ func (e *Engine) Link(ctx context.Context, matchID int64, opt LinkOptions) (stor
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := fsops.LinkNew(s.hostAbs, s.hi, newAbs); err != nil {
|
||||
if err := fsops.LinkNew(s.masterAbs, s.mi, newAbs); err != nil {
|
||||
if q != "" {
|
||||
dropQuarantine(l, q, s.si)
|
||||
e.clearQuarantine(ctx, o.ID)
|
||||
@@ -259,7 +259,7 @@ func (e *Engine) clearQuarantine(ctx context.Context, opID int64) {
|
||||
}
|
||||
|
||||
// Promote is the override path: the slave copy is better, so it replaces the
|
||||
// host file (whose original is quarantined) and both libraries share it.
|
||||
// master file (whose original is quarantined) and both libraries share it.
|
||||
func (e *Engine) Promote(ctx context.Context, matchID int64, confirmed bool) (store.Operation, error) {
|
||||
if !confirmed {
|
||||
return store.Operation{}, ErrConfirm
|
||||
@@ -282,20 +282,20 @@ func (e *Engine) Promote(ctx context.Context, matchID int64, confirmed bool) (st
|
||||
if err != nil {
|
||||
return store.Operation{}, err
|
||||
}
|
||||
newHostRel := filepath.Join(filepath.Dir(m.RelHost), filepath.Base(m.RelSlave))
|
||||
newHostAbs, err := l.host.Resolve(newHostRel)
|
||||
newMasterRel := filepath.Join(filepath.Dir(m.RelMaster), filepath.Base(m.RelSlave))
|
||||
newMasterAbs, err := l.master.Resolve(newMasterRel)
|
||||
if err != nil {
|
||||
return store.Operation{}, err
|
||||
}
|
||||
rename := newHostRel != m.RelHost
|
||||
rename := newMasterRel != m.RelMaster
|
||||
if rename {
|
||||
if _, err := os.Lstat(newHostAbs); err == nil {
|
||||
return store.Operation{}, fmt.Errorf("host already has %s: %w", newHostRel, fsops.ErrExists)
|
||||
if _, err := os.Lstat(newMasterAbs); err == nil {
|
||||
return store.Operation{}, fmt.Errorf("master already has %s: %w", newMasterRel, fsops.ErrExists)
|
||||
}
|
||||
}
|
||||
|
||||
o := store.Operation{PairID: l.pair.ID, Action: store.ActPromote, RelHost: m.RelHost, RelSlave: m.RelSlave,
|
||||
RelNew: newHostRel, QuarantineSide: "host", Trigger: "manual"}
|
||||
o := store.Operation{PairID: l.pair.ID, Action: store.ActPromote, RelMaster: m.RelMaster, RelSlave: m.RelSlave,
|
||||
RelNew: newMasterRel, QuarantineSide: "master", Trigger: "manual"}
|
||||
if err := e.begin(ctx, &o); err != nil {
|
||||
return o, err
|
||||
}
|
||||
@@ -303,31 +303,31 @@ func (e *Engine) Promote(ctx context.Context, matchID int64, confirmed bool) (st
|
||||
return o, nil
|
||||
}
|
||||
err = func() error {
|
||||
q := quarantinePath(l, o.ID, "host", m.RelHost)
|
||||
if err := quarantine(s.hostAbs, s.hi, q); err != nil {
|
||||
q := quarantinePath(l, o.ID, "master", m.RelMaster)
|
||||
if err := quarantine(s.masterAbs, s.mi, q); err != nil {
|
||||
return fmt.Errorf("quarantine: %w", err)
|
||||
}
|
||||
o.QuarantinePath = q
|
||||
if _, err := e.st.DB.ExecContext(ctx, `UPDATE operations SET quarantine_path = ? WHERE id = ?`, q, o.ID); err != nil {
|
||||
dropQuarantine(l, q, s.hi)
|
||||
dropQuarantine(l, q, s.mi)
|
||||
return err
|
||||
}
|
||||
if !rename {
|
||||
if err := fsops.ReplaceWithLink(s.slaveAbs, s.si, s.hostAbs); err != nil {
|
||||
if cur, e2 := fsops.Lstat(s.hostAbs); e2 == nil && cur.SameInode(s.hi) {
|
||||
dropQuarantine(l, q, s.hi)
|
||||
if err := fsops.ReplaceWithLink(s.slaveAbs, s.si, s.masterAbs); err != nil {
|
||||
if cur, e2 := fsops.Lstat(s.masterAbs); e2 == nil && cur.SameInode(s.mi) {
|
||||
dropQuarantine(l, q, s.mi)
|
||||
e.clearQuarantine(ctx, o.ID)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := fsops.LinkNew(s.slaveAbs, s.si, newHostAbs); err != nil {
|
||||
dropQuarantine(l, q, s.hi)
|
||||
if err := fsops.LinkNew(s.slaveAbs, s.si, newMasterAbs); err != nil {
|
||||
dropQuarantine(l, q, s.mi)
|
||||
e.clearQuarantine(ctx, o.ID)
|
||||
return err
|
||||
}
|
||||
return removeOriginal(s.hostAbs, s.hi)
|
||||
return removeOriginal(s.masterAbs, s.mi)
|
||||
}()
|
||||
err = e.finish(ctx, &o, err)
|
||||
e.refresh(ctx, l, m.RelSlave)
|
||||
@@ -368,15 +368,15 @@ func expand(ctx context.Context, l *libs, root fsops.Root, rels []string) ([]str
|
||||
return sortedKeys(set), nil
|
||||
}
|
||||
|
||||
// StartBuild hardlinks the chosen host files/folders into the slave library,
|
||||
// mirroring the host path. Existing slave files are never overwritten.
|
||||
// StartBuild hardlinks the chosen master files/folders into the slave library,
|
||||
// mirroring the master path. Existing slave files are never overwritten.
|
||||
func (e *Engine) StartBuild(pairID int64, rels []string) (*Job, error) {
|
||||
return e.startJob(pairID, "build", func(ctx context.Context, j *Job) error {
|
||||
l, err := e.loadPair(ctx, pairID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
files, err := expand(ctx, l, l.host, rels)
|
||||
files, err := expand(ctx, l, l.master, rels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -395,7 +395,7 @@ func (e *Engine) StartBuild(pairID int64, rels []string) (*Job, error) {
|
||||
func (e *Engine) buildOne(ctx context.Context, j *Job, l *libs, rel string) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
hostAbs, err := l.host.Resolve(rel)
|
||||
masterAbs, err := l.master.Resolve(rel)
|
||||
if err != nil {
|
||||
j.skip(rel + ": " + err.Error())
|
||||
return
|
||||
@@ -405,20 +405,20 @@ func (e *Engine) buildOne(ctx context.Context, j *Job, l *libs, rel string) {
|
||||
j.skip(rel + ": " + err.Error())
|
||||
return
|
||||
}
|
||||
hi, err := fsops.RegularFile(hostAbs)
|
||||
mi, err := fsops.RegularFile(masterAbs)
|
||||
if err != nil {
|
||||
j.skip(rel + ": " + err.Error())
|
||||
return
|
||||
}
|
||||
if si, err := fsops.Lstat(slaveAbs); err == nil {
|
||||
if si.SameInode(hi) {
|
||||
if si.SameInode(mi) {
|
||||
j.count("already linked")
|
||||
} else {
|
||||
j.skip(rel + ": a different file already exists in the slave library (scan, then use Review)")
|
||||
}
|
||||
return
|
||||
}
|
||||
o := store.Operation{PairID: l.pair.ID, Action: store.ActBuild, RelHost: rel, RelSlave: rel, RelNew: rel, Trigger: "build"}
|
||||
o := store.Operation{PairID: l.pair.ID, Action: store.ActBuild, RelMaster: rel, RelSlave: rel, RelNew: rel, Trigger: "build"}
|
||||
if err := e.begin(ctx, &o); err != nil {
|
||||
j.skip(rel + ": " + err.Error())
|
||||
return
|
||||
@@ -429,7 +429,7 @@ func (e *Engine) buildOne(ctx context.Context, j *Job, l *libs, rel string) {
|
||||
}
|
||||
err = fsops.MkdirAllLike(filepath.Dir(slaveAbs))
|
||||
if err == nil {
|
||||
err = fsops.LinkNew(hostAbs, hi, slaveAbs)
|
||||
err = fsops.LinkNew(masterAbs, mi, slaveAbs)
|
||||
}
|
||||
if err = e.finish(ctx, &o, err); err != nil {
|
||||
j.skip(rel + ": " + err.Error())
|
||||
@@ -439,7 +439,7 @@ func (e *Engine) buildOne(ctx context.Context, j *Job, l *libs, rel string) {
|
||||
e.refresh(ctx, l, rel)
|
||||
}
|
||||
|
||||
// StartRemove deletes slave files that are hardlinks of the host file at the
|
||||
// StartRemove deletes slave files that are hardlinks of the master file at the
|
||||
// same path. Anything that is not such a link is left alone.
|
||||
func (e *Engine) StartRemove(pairID int64, rels []string) (*Job, error) {
|
||||
return e.startJob(pairID, "remove", func(ctx context.Context, j *Job) error {
|
||||
@@ -466,15 +466,15 @@ func (e *Engine) StartRemove(pairID int64, rels []string) (*Job, error) {
|
||||
func (e *Engine) removeOne(ctx context.Context, j *Job, l *libs, rel string) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
hostAbs, err1 := l.host.Resolve(rel)
|
||||
masterAbs, err1 := l.master.Resolve(rel)
|
||||
slaveAbs, err2 := l.slave.Resolve(rel)
|
||||
if err1 != nil || err2 != nil {
|
||||
j.skip(rel + ": invalid path")
|
||||
return
|
||||
}
|
||||
hi, err := fsops.RegularFile(hostAbs)
|
||||
mi, err := fsops.RegularFile(masterAbs)
|
||||
if err != nil {
|
||||
j.skip(rel + ": no host file at the same path, so it is not removed")
|
||||
j.skip(rel + ": no master file at the same path, so it is not removed")
|
||||
return
|
||||
}
|
||||
si, err := fsops.RegularFile(slaveAbs)
|
||||
@@ -482,11 +482,11 @@ func (e *Engine) removeOne(ctx context.Context, j *Job, l *libs, rel string) {
|
||||
j.skip(rel + ": " + err.Error())
|
||||
return
|
||||
}
|
||||
if !si.SameInode(hi) {
|
||||
j.skip(rel + ": not a hardlink of the host file, so it is not removed")
|
||||
if !si.SameInode(mi) {
|
||||
j.skip(rel + ": not a hardlink of the master file, so it is not removed")
|
||||
return
|
||||
}
|
||||
o := store.Operation{PairID: l.pair.ID, Action: store.ActRemove, RelHost: rel, RelSlave: rel, Trigger: "build"}
|
||||
o := store.Operation{PairID: l.pair.ID, Action: store.ActRemove, RelMaster: rel, RelSlave: rel, Trigger: "build"}
|
||||
if err := e.begin(ctx, &o); err != nil {
|
||||
j.skip(rel + ": " + err.Error())
|
||||
return
|
||||
@@ -495,7 +495,7 @@ func (e *Engine) removeOne(ctx context.Context, j *Job, l *libs, rel string) {
|
||||
j.count("planned")
|
||||
return
|
||||
}
|
||||
err = fsops.RemoveLink(slaveAbs, hi)
|
||||
err = fsops.RemoveLink(slaveAbs, mi)
|
||||
if err = e.finish(ctx, &o, err); err != nil {
|
||||
j.skip(rel + ": " + err.Error())
|
||||
return
|
||||
@@ -533,7 +533,7 @@ func (e *Engine) Undo(ctx context.Context, opID int64) error {
|
||||
}
|
||||
return p
|
||||
}
|
||||
hostAbs := res(l.host, o.RelHost)
|
||||
masterAbs := res(l.master, o.RelMaster)
|
||||
slaveAbs := res(l.slave, o.RelSlave)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -541,24 +541,24 @@ func (e *Engine) Undo(ctx context.Context, opID int64) error {
|
||||
|
||||
switch o.Action {
|
||||
case store.ActBuild:
|
||||
hi, err := fsops.RegularFile(hostAbs)
|
||||
mi, err := fsops.RegularFile(masterAbs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fsops.RemoveLink(slaveAbs, hi); err != nil {
|
||||
if err := fsops.RemoveLink(slaveAbs, mi); err != nil {
|
||||
return err
|
||||
}
|
||||
pruneEmpty(filepath.Dir(slaveAbs), l.slave.Path)
|
||||
|
||||
case store.ActRemove:
|
||||
hi, err := fsops.RegularFile(hostAbs)
|
||||
mi, err := fsops.RegularFile(masterAbs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fsops.MkdirAllLike(filepath.Dir(slaveAbs)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fsops.LinkNew(hostAbs, hi, slaveAbs); err != nil {
|
||||
if err := fsops.LinkNew(masterAbs, mi, slaveAbs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -567,7 +567,7 @@ func (e *Engine) Undo(ctx context.Context, opID int64) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hi, err := fsops.RegularFile(hostAbs)
|
||||
mi, err := fsops.RegularFile(masterAbs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -575,8 +575,8 @@ func (e *Engine) Undo(ctx context.Context, opID int64) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !cur.SameInode(hi) {
|
||||
return errors.New("the slave file is no longer linked to the host file; refusing to undo")
|
||||
if !cur.SameInode(mi) {
|
||||
return errors.New("the slave file is no longer linked to the master file; refusing to undo")
|
||||
}
|
||||
renamed := o.RelNew != o.RelSlave
|
||||
if renamed {
|
||||
@@ -586,7 +586,7 @@ func (e *Engine) Undo(ctx context.Context, opID int64) error {
|
||||
}
|
||||
if o.QuarantinePath == "" {
|
||||
// Identical link: give the slave its own copy of the bytes again.
|
||||
if err := fsops.CopyReplace(hostAbs, slaveAbs, hi.Mode); err != nil {
|
||||
if err := fsops.CopyReplace(masterAbs, slaveAbs, mi.Mode); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@@ -605,14 +605,14 @@ func (e *Engine) Undo(ctx context.Context, opID int64) error {
|
||||
dropQuarantine(l, o.QuarantinePath, qi)
|
||||
}
|
||||
if renamed {
|
||||
if err := fsops.RemoveLink(newAbs, hi); err != nil {
|
||||
if err := fsops.RemoveLink(newAbs, mi); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
e.refresh(ctx, l, o.RelNew)
|
||||
|
||||
case store.ActPromote:
|
||||
newHostAbs := res(l.host, o.RelNew)
|
||||
newMasterAbs := res(l.master, o.RelNew)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -620,25 +620,25 @@ func (e *Engine) Undo(ctx context.Context, opID int64) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cur, err := fsops.RegularFile(newHostAbs)
|
||||
cur, err := fsops.RegularFile(newMasterAbs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !cur.SameInode(si) {
|
||||
return errors.New("the host file changed after promotion; refusing to undo")
|
||||
return errors.New("the master file changed after promotion; refusing to undo")
|
||||
}
|
||||
qi, err := e.quarantined(l, o.QuarantinePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if o.RelNew == o.RelHost {
|
||||
err = fsops.ReplaceWithLink(o.QuarantinePath, qi, hostAbs)
|
||||
if o.RelNew == o.RelMaster {
|
||||
err = fsops.ReplaceWithLink(o.QuarantinePath, qi, masterAbs)
|
||||
} else {
|
||||
if _, e2 := os.Lstat(hostAbs); e2 == nil {
|
||||
return fmt.Errorf("%s: %w", o.RelHost, fsops.ErrExists)
|
||||
if _, e2 := os.Lstat(masterAbs); e2 == nil {
|
||||
return fmt.Errorf("%s: %w", o.RelMaster, fsops.ErrExists)
|
||||
}
|
||||
if err = fsops.LinkNew(o.QuarantinePath, qi, hostAbs); err == nil {
|
||||
err = fsops.RemoveLink(newHostAbs, si)
|
||||
if err = fsops.LinkNew(o.QuarantinePath, qi, masterAbs); err == nil {
|
||||
err = fsops.RemoveLink(newMasterAbs, si)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
@@ -732,8 +732,8 @@ func (e *Engine) Recover(ctx context.Context) {
|
||||
}
|
||||
for _, o := range ops {
|
||||
if p, err := e.st.Pair(ctx, o.PairID); err == nil {
|
||||
for _, root := range []string{p.SlaveRoot, p.HostRoot} {
|
||||
for _, rel := range []string{o.RelSlave, o.RelNew, o.RelHost} {
|
||||
for _, root := range []string{p.SlaveRoot, p.MasterRoot} {
|
||||
for _, rel := range []string{o.RelSlave, o.RelNew, o.RelMaster} {
|
||||
if rel == "" {
|
||||
continue
|
||||
}
|
||||
@@ -763,15 +763,15 @@ type TreeEntry struct {
|
||||
Linked int `json:"linked,omitempty"`
|
||||
}
|
||||
|
||||
// Tree lists a host directory and shows how much of it is in the slave.
|
||||
// Tree lists a master directory and shows how much of it is in the slave.
|
||||
func (e *Engine) Tree(ctx context.Context, pairID int64, rel string) ([]TreeEntry, error) {
|
||||
l, err := e.loadPair(ctx, pairID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dir := l.host.Path
|
||||
dir := l.master.Path
|
||||
if rel != "" {
|
||||
if dir, err = l.host.Resolve(rel); err != nil {
|
||||
if dir, err = l.master.Resolve(rel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -779,7 +779,7 @@ func (e *Engine) Tree(ctx context.Context, pairID int64, rel string) ([]TreeEntr
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
linked, _ := e.st.LinkedHostPaths(ctx, pairID, rel)
|
||||
linked, _ := e.st.LinkedMasterPaths(ctx, pairID, rel)
|
||||
perChild := map[string]int{}
|
||||
for _, p := range linked {
|
||||
sub := p
|
||||
@@ -807,16 +807,16 @@ func (e *Engine) Tree(ctx context.Context, pairID int64, rel string) ([]TreeEntr
|
||||
if !l.hasExt(name) {
|
||||
continue
|
||||
}
|
||||
hi, err := fsops.Lstat(filepath.Join(dir, name))
|
||||
mi, err := fsops.Lstat(filepath.Join(dir, name))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
te.Size = hi.Size
|
||||
te.Size = mi.Size
|
||||
te.State = "missing"
|
||||
if sp, err := l.slave.Resolve(child); err == nil {
|
||||
if si, err := fsops.Lstat(sp); err == nil {
|
||||
te.State = "conflict"
|
||||
if si.SameInode(hi) {
|
||||
if si.SameInode(mi) {
|
||||
te.State = "linked"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user