Name a filesystem by its kernel id, never its device name (hq ADR 0325 fourth review)
An ext4 or xfs library mounted by UUID can come back from a reboot as another device (/dev/sdb1 as /dev/sdc1); naming the filesystem by mountinfo's source read that as a move, stripped the warranted library's entries and asked for a new Approve. A filesystem is now named by statfs's f_fsid (from the UUID, or the ZFS dataset), its type and its mount point. Entries left on the filesystem a folder is on now are merged back or held, never walked away, and a TakenOn naming a mount point is not taken.
This commit is contained in:
+25
-17
@@ -256,7 +256,7 @@ func assessFolder(ctx context.Context, path string, rec store.KeptFolder, taken
|
||||
}
|
||||
// The controller's word that the folder was taken holds for the filesystem the warrant was carried out on: a
|
||||
// folder moved to another is not taken, and asks again (§8).
|
||||
takenHere := taken && (takenOnID(rec.TakenOn) == here || (rec.TakenOn == "" && !moved))
|
||||
takenHere := taken && (rec.TakenOn == here || (rec.TakenOn == "" && !moved))
|
||||
switch {
|
||||
case rec.Kept && !moved:
|
||||
return folderVerdict{kind: folderKeep, mount: pl.Mount, how: rec.How}
|
||||
@@ -279,24 +279,16 @@ func assessFolder(ctx context.Context, path string, rec store.KeptFolder, taken
|
||||
func movedRecord(f store.KeptFolder, mount string) store.KeptFolder {
|
||||
left := f.Left
|
||||
if f.How == store.KeptByWarrant && len(f.Users) > 0 && f.Mount != "" {
|
||||
left = addLeft(left, f.Mount, f.Users)
|
||||
left = addLeft(left, f.Mount, f.Placed, f.Users)
|
||||
}
|
||||
return store.KeptFolder{Path: f.Path, Mount: mount, Placed: PlacedID(mount), Want: f.Want, IDs: f.IDs,
|
||||
Act: f.Act, TakenOn: f.TakenOn, Left: left}
|
||||
}
|
||||
|
||||
// takenOnID is a record's TakenOn as a filesystem's name: one written before names held the mount point itself.
|
||||
func takenOnID(takenOn string) string {
|
||||
if strings.HasPrefix(takenOn, "/") {
|
||||
return PlacedID(takenOn)
|
||||
}
|
||||
return takenOn
|
||||
}
|
||||
|
||||
// addLeft adds users' entries left on mount, once per mount and uid.
|
||||
func addLeft(left []store.KeptLeft, mount string, users []store.KeptUser) []store.KeptLeft {
|
||||
func addLeft(left []store.KeptLeft, mount, placed string, users []store.KeptUser) []store.KeptLeft {
|
||||
for i := range left {
|
||||
if left[i].Mount == mount {
|
||||
if left[i].Mount == mount && left[i].Placed == placed {
|
||||
have := usersOf(left[i].Users)
|
||||
for _, u := range users {
|
||||
if _, had := have[u.UID]; !had {
|
||||
@@ -306,20 +298,37 @@ func addLeft(left []store.KeptLeft, mount string, users []store.KeptUser) []stor
|
||||
return left
|
||||
}
|
||||
}
|
||||
return append(left, store.KeptLeft{Mount: mount, Users: append([]store.KeptUser(nil), users...)})
|
||||
return append(left, store.KeptLeft{Mount: mount, Placed: placed, Users: append([]store.KeptUser(nil), users...)})
|
||||
}
|
||||
|
||||
// PlacedID names the filesystem at mount for a wait, with no path in it: the controller asks again when the
|
||||
// folder's filesystem is not the one a warrant was given for.
|
||||
// folder's filesystem is not the one a warrant was given for. It is built from what stays the same across reboots
|
||||
// for one filesystem — the kernel's filesystem id (statfs f_fsid: ext4 and xfs derive it from the filesystem's
|
||||
// UUID, ZFS from the dataset), the filesystem's type and its mount point — and never from the device name
|
||||
// mountinfo gives as its source, which a reboot may renumber (/dev/sdb1 becoming /dev/sdc1).
|
||||
func PlacedID(mount string) string {
|
||||
if mount == "" {
|
||||
return ""
|
||||
}
|
||||
source, fstype := mountSourceOf(mount)
|
||||
sum := sha256.Sum256([]byte(mount + "\x00" + source + "\x00" + fstype))
|
||||
_, fstype := mountSourceOf(mount)
|
||||
fsid := fsidWithin(mount)
|
||||
sum := sha256.Sum256([]byte(mount + "\x00" + fsid + "\x00" + fstype))
|
||||
return "fs-" + hex.EncodeToString(sum[:6])
|
||||
}
|
||||
|
||||
// fsidWithin is MountFsid within statBound: empty when it does not answer, so a mount that is down is named by its
|
||||
// point and type alone, never held on.
|
||||
func fsidWithin(mount string) string {
|
||||
done := make(chan string, 1)
|
||||
go func() { done <- MountFsid(mount) }()
|
||||
select {
|
||||
case id := <-done:
|
||||
return id
|
||||
case <-time.After(statBound):
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// statBound is how long a read of a folder may take before it is said not to answer: a hard network mount whose
|
||||
// server is down blocks a stat for ever, and one access must never hold the apply.
|
||||
var statBound = 5 * time.Second
|
||||
@@ -534,7 +543,6 @@ func applyAccessFolder(ctx context.Context, r *declaration.Access, in inputs) (O
|
||||
// The controller says it was taken, and this engine has no record of where: here, now.
|
||||
f.TakenOn = PlacedID(v.mount)
|
||||
}
|
||||
f.TakenOn = takenOnID(f.TakenOn)
|
||||
if first {
|
||||
f.How, f.At = v.how, time.Now().UTC()
|
||||
// A folder that held nothing has nothing under it to walk; one the controller says was taken
|
||||
|
||||
Reference in New Issue
Block a user