@@ -4,17 +4,29 @@ import (
44 "context"
55 "os"
66 "path/filepath"
7+ "syscall"
78 "testing"
89
910 "github.com/kernel/hypeman/lib/hypervisor"
1011 "github.com/stretchr/testify/assert"
1112 "github.com/stretchr/testify/require"
1213)
1314
14- // TestInstallForkSharedMemFile_SymlinksSourceMemFile verifies that the helper
15- // creates a symlink at the fork's snapshot mem-file path pointing back to the
16- // source instance's mem-file.
17- func TestInstallForkSharedMemFile_SymlinksSourceMemFile (t * testing.T ) {
15+ func sameInode (t * testing.T , a , b string ) bool {
16+ t .Helper ()
17+ ai , err := os .Stat (a )
18+ require .NoError (t , err )
19+ bi , err := os .Stat (b )
20+ require .NoError (t , err )
21+ as := ai .Sys ().(* syscall.Stat_t )
22+ bs := bi .Sys ().(* syscall.Stat_t )
23+ return as .Ino == bs .Ino && as .Dev == bs .Dev
24+ }
25+
26+ // TestInstallForkSharedMemFile_HardlinksSourceMemFile verifies that the helper
27+ // creates a hardlink at the fork's snapshot mem-file path that shares the
28+ // source instance's mem-file inode.
29+ func TestInstallForkSharedMemFile_HardlinksSourceMemFile (t * testing.T ) {
1830 t .Parallel ()
1931
2032 mgr , _ := newStorageOnlyManager (t )
@@ -32,16 +44,13 @@ func TestInstallForkSharedMemFile_SymlinksSourceMemFile(t *testing.T) {
3244 forkMem := filepath .Join (forkDir , "snapshots" , "snapshot-latest" , templateSharedMemFileName )
3345 info , err := os .Lstat (forkMem )
3446 require .NoError (t , err )
35- assert .NotZero (t , info .Mode ()& os .ModeSymlink , "fork mem-file must be a symlink, not a regular file" )
36-
37- target , err := os .Readlink (forkMem )
38- require .NoError (t , err )
39- assert .Equal (t , srcMem , target )
47+ assert .True (t , info .Mode ().IsRegular (), "fork mem-file must be a regular file (hardlink), not a symlink" )
48+ assert .True (t , sameInode (t , srcMem , forkMem ), "fork mem-file must share the source's inode" )
4049}
4150
4251// TestInstallForkSharedMemFile_ErrorsWhenSourceMissing makes sure the helper
43- // refuses to silently create a dangling symlink when the source mem-file does
44- // not exist.
52+ // refuses to silently create a dangling link when the source mem-file does not
53+ // exist.
4554func TestInstallForkSharedMemFile_ErrorsWhenSourceMissing (t * testing.T ) {
4655 t .Parallel ()
4756
@@ -54,7 +63,7 @@ func TestInstallForkSharedMemFile_ErrorsWhenSourceMissing(t *testing.T) {
5463
5564// TestForkFirecrackerSharesMemFile_FromTemplate verifies the end-to-end fork
5665// path: when the source is a Firecracker Template instance, the fork's
57- // mem-file is a symlink to the source's mem-file instead of a copy. This
66+ // mem-file is a hardlink to the source's mem-file instead of a copy. This
5867// preserves the firecracker MAP_PRIVATE COW semantics that let multiple forks
5968// share the heavy backing file.
6069func TestForkFirecrackerSharesMemFile_FromTemplate (t * testing.T ) {
@@ -84,11 +93,8 @@ func TestForkFirecrackerSharesMemFile_FromTemplate(t *testing.T) {
8493 forkMem := filepath .Join (mgr .paths .InstanceSnapshotLatest (forked .Id ), templateSharedMemFileName )
8594 info , err := os .Lstat (forkMem )
8695 require .NoError (t , err )
87- assert .NotZero (t , info .Mode ()& os .ModeSymlink , "fork mem-file must be a symlink for firecracker fan-out" )
88-
89- target , err := os .Readlink (forkMem )
90- require .NoError (t , err )
91- assert .Equal (t , srcMem , target )
96+ assert .True (t , info .Mode ().IsRegular (), "fork mem-file must be a regular file (hardlink) for firecracker fan-out" )
97+ assert .True (t , sameInode (t , srcMem , forkMem ), "fork mem-file must share the source's inode" )
9298}
9399
94100// TestForkFirecrackerStandbySourceDoesNotShareMemFile guards the
@@ -121,7 +127,8 @@ func TestForkFirecrackerStandbySourceDoesNotShareMemFile(t *testing.T) {
121127 forkMem := filepath .Join (mgr .paths .InstanceSnapshotLatest (forked .Id ), templateSharedMemFileName )
122128 info , err := os .Lstat (forkMem )
123129 require .NoError (t , err )
124- assert .Zero (t , info .Mode ()& os .ModeSymlink , "standby-source fork mem-file must be a regular file copy, not a symlink" )
130+ require .True (t , info .Mode ().IsRegular (), "standby-source fork mem-file must be a regular file copy" )
131+ assert .False (t , sameInode (t , srcMem , forkMem ), "standby-source fork mem-file must be a copy, not a hardlink to source" )
125132}
126133
127134// promoteFixtureToTemplate marks the source's stored metadata as a Template
0 commit comments