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:
+91
-10
@@ -1028,24 +1028,31 @@ func TestALeftoverWithNoAccessIdsIsSaidUnderAPathFreeName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Review (third) low: another filesystem mounted at the same point is not the one the warrant was for.
|
||||
func TestAnotherFilesystemAtTheSameMountPointIsNotTaken(t *testing.T) {
|
||||
// placedPool is a folder of a library on a pool the test says is mounted, with the pool's device name and filesystem
|
||||
// id as the test sets them, taken under a warrant.
|
||||
func placedPool(t *testing.T, source, fsid *string) (pool, lib, taken string) {
|
||||
t.Helper()
|
||||
dir := aclFolder(t)
|
||||
pool := filepath.Join(dir, "pool")
|
||||
lib := filepath.Join(pool, "media")
|
||||
pool = filepath.Join(dir, "pool")
|
||||
lib = filepath.Join(pool, "media")
|
||||
if err := os.MkdirAll(filepath.Join(lib, "old"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
keeping(t, []string{pool}, nil)
|
||||
source := "tank/a"
|
||||
was := MountSource
|
||||
wasSource, wasFsid := MountSource, MountFsid
|
||||
MountSource = func(point string) (string, string) {
|
||||
if point == pool {
|
||||
return source, "zfs"
|
||||
return *source, "ext4"
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
defer func() { MountSource = was }()
|
||||
MountFsid = func(point string) string {
|
||||
if point == pool {
|
||||
return *fsid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
t.Cleanup(func() { MountSource, MountFsid = wasSource, wasFsid })
|
||||
notTaken := accessJSON("radarr.media", lib, "read-write", false, "mesh-radarr:61001:read-write:container")
|
||||
if _, _, err := applyAccesses(t, store.State{}, notTaken); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1055,11 +1062,18 @@ func TestAnotherFilesystemAtTheSameMountPointIsNotTaken(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
<-done
|
||||
taken := accessJSON("radarr.media", lib, "read-write", true, "mesh-radarr:61001:read-write:container")
|
||||
taken = accessJSON("radarr.media", lib, "read-write", true, "mesh-radarr:61001:read-write:container")
|
||||
if report, _, _ := applyAccesses(t, store.State{}, taken); outcomeOf(report, "radarr.media").Action == "waiting" {
|
||||
t.Fatal("the folder taken waits on its own filesystem")
|
||||
}
|
||||
source = "tank/b" // another pool imported at the same mount point
|
||||
return pool, lib, taken
|
||||
}
|
||||
|
||||
// Review (third) low: another filesystem mounted at the same point is not the one the warrant was for.
|
||||
func TestAnotherFilesystemAtTheSameMountPointIsNotTaken(t *testing.T) {
|
||||
source, fsid := "/dev/sdb1", "0a0b0c0d01020304"
|
||||
_, lib, taken := placedPool(t, &source, &fsid)
|
||||
fsid = "1111222233334444" // another filesystem mounted at the same point
|
||||
report, _, err := applyAccesses(t, store.State{}, taken)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1072,6 +1086,28 @@ func TestAnotherFilesystemAtTheSameMountPointIsNotTaken(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Review (fourth): the same filesystem whose device the kernel renamed across a reboot (/dev/sdb1 to /dev/sdc1, one
|
||||
// filesystem id) is the same filesystem: still taken, still kept, and none of its entries walked away.
|
||||
func TestADeviceRenamedIsTheSameFilesystem(t *testing.T) {
|
||||
source, fsid := "/dev/sdb1", "0a0b0c0d01020304"
|
||||
_, lib, taken := placedPool(t, &source, &fsid)
|
||||
source = "/dev/sdc1"
|
||||
report, _, err := applyAccesses(t, store.State{}, taken)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if o := outcomeOf(report, "radarr.media"); o.Action == "waiting" {
|
||||
t.Fatalf("a device renamed reads as another filesystem: %+v", o)
|
||||
}
|
||||
ReconcileKept(context.Background())
|
||||
if got := effectiveOn(t, filepath.Join(lib, "old"), radarr); got != 7 {
|
||||
t.Fatalf("the library's entries were walked away: the identity may %s", permWords(got))
|
||||
}
|
||||
if left := Leftovers(); len(left) != 0 {
|
||||
t.Fatalf("a device renamed leaves entries said as left: %+v", left)
|
||||
}
|
||||
}
|
||||
|
||||
// Review L4: the controller's word that a folder was taken holds for the filesystem the warrant was carried out on;
|
||||
// moved to another, it is not taken, the wait names the new filesystem, and nothing is walked.
|
||||
func TestAMovedFolderIsNotTakenOnTheOldWarrant(t *testing.T) {
|
||||
@@ -1148,3 +1184,48 @@ func TestAHungReadIsSharedByEveryoneWhoAsks(t *testing.T) {
|
||||
t.Fatalf("%d reads of one path were started while the first hung", started)
|
||||
}
|
||||
}
|
||||
|
||||
// Review (fourth): entries left on the filesystem the folder is on now are never walked away — merged back into
|
||||
// the folder's users when it is kept there, held while it is not.
|
||||
func TestEntriesLeftOnTheFilesystemTheFolderIsOnAreNeverWalkedAway(t *testing.T) {
|
||||
source, fsid := "/dev/sdb1", "0a0b0c0d01020304"
|
||||
pool, lib, _ := placedPool(t, &source, &fsid)
|
||||
rec, _ := keptRecord(lib)
|
||||
users := rec.Users
|
||||
for _, kept := range []bool{false, true} {
|
||||
_ = KeptFolders.Update(lib, func(f *store.KeptFolder, _ bool) bool {
|
||||
f.Kept, f.Users = kept, nil
|
||||
f.Left = []store.KeptLeft{{Mount: pool, Placed: f.Placed, Users: users}}
|
||||
return true
|
||||
})
|
||||
ReconcileKept(context.Background())
|
||||
if got := effectiveOn(t, filepath.Join(lib, "old"), radarr); got != 7 {
|
||||
t.Fatalf("kept=%v: entries on the folder's own filesystem were walked away", kept)
|
||||
}
|
||||
r, _ := keptRecord(lib)
|
||||
if kept && (len(r.Left) != 0 || len(r.Users) != 1) {
|
||||
t.Errorf("kept: not merged back into the folder's users: %+v", r)
|
||||
}
|
||||
if !kept && len(r.Left) != 1 {
|
||||
t.Errorf("not kept: the entries' record was dropped: %+v", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Review (fourth): a TakenOn naming a mount point (an unmerged build's record) is not taken on whatever is mounted
|
||||
// there now.
|
||||
func TestATakenOnOfTheOldFormIsNotTaken(t *testing.T) {
|
||||
dir := aclFolder(t)
|
||||
keeping(t, nil, nil)
|
||||
lib := filepath.Join(dir, "media")
|
||||
if err := os.MkdirAll(filepath.Join(lib, "old"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = KeptFolders.Update(lib, func(f *store.KeptFolder, _ bool) bool {
|
||||
f.Mount, f.TakenOn = "/", "/"
|
||||
return true
|
||||
})
|
||||
if v := AccessVerdict(context.Background(), lib, true, true); v.Kind != VerdictWaitKeep {
|
||||
t.Fatalf("an old-form record is read as taken: %s", v.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user