@@ -6,8 +6,13 @@ import (
66	"os" 
77	"path/filepath" 
88	"strconv" 
9+ 	"strings" 
10+ 	"sync" 
11+ 	"syscall" 
912	"testing" 
1013	"time" 
14+ 
15+ 	"golang.org/x/sys/unix" 
1116)
1217
1318func  TestWriteCgroupFileHandlesInterrupt (t  * testing.T ) {
@@ -70,6 +75,75 @@ func TestOpenat2(t *testing.T) {
7075	}
7176}
7277
78+ func  TestCgroupRootHandleOpenedToAnotherFile (t  * testing.T ) {
79+ 	const  (
80+ 		memoryCgroupMount  =  "/sys/fs/cgroup/memory" 
81+ 		memoryLimit        =  "memory.limit_in_bytes" 
82+ 	)
83+ 	if  _ , err  :=  os .Stat (memoryCgroupMount ); err  !=  nil  {
84+ 		// most probably cgroupv2 
85+ 		t .Skip (err )
86+ 	}
87+ 
88+ 	cgroupName  :=  fmt .Sprintf ("test-eano-%d" , time .Now ().Nanosecond ())
89+ 	cgroupPath  :=  filepath .Join (memoryCgroupMount , cgroupName )
90+ 	if  err  :=  os .MkdirAll (cgroupPath , 0o755 ); err  !=  nil  {
91+ 		t .Fatal (err )
92+ 	}
93+ 	defer  os .RemoveAll (cgroupPath )
94+ 
95+ 	if  _ , err  :=  os .Stat (filepath .Join (cgroupPath , memoryLimit )); err  !=  nil  {
96+ 		// either cgroupv2, or memory controller is not available 
97+ 		t .Skip (err )
98+ 	}
99+ 
100+ 	// The cgroupRootHandle is opened when the openFile is called. 
101+ 	if  _ , err  :=  openFile (cgroupfsDir , filepath .Join ("memory" , cgroupName , memoryLimit ), os .O_RDONLY ); err  !=  nil  {
102+ 		t .Fatal (err )
103+ 	}
104+ 
105+ 	// Make sure the cgroupRootHandle is opened to another file. 
106+ 	if  err  :=  syscall .Close (int (cgroupRootHandle .Fd ())); err  !=  nil  {
107+ 		t .Fatal (err )
108+ 	}
109+ 	if  _ , err  :=  unix .Openat2 (- 1 , "/tmp" , & unix.OpenHow {Flags : unix .O_DIRECTORY  |  unix .O_PATH  |  unix .O_CLOEXEC }); err  !=  nil  {
110+ 		t .Fatal (err )
111+ 	}
112+ 
113+ 	var  readErr  * error 
114+ 	readErrLock  :=  sync.Mutex {}
115+ 	errCount  :=  0 
116+ 
117+ 	// The openFile returns error (may be multiple times) and the prepOnce is reset only once. 
118+ 	var  wg  sync.WaitGroup 
119+ 	for  i  :=  0 ; i  <  100 ; i ++  {
120+ 		wg .Add (1 )
121+ 		go  func (i  int ) {
122+ 			defer  wg .Done ()
123+ 			if  _ , err  :=  openFile (cgroupfsDir , filepath .Join ("memory" , cgroupName , memoryLimit ), os .O_RDONLY ); err  !=  nil  {
124+ 				readErrLock .Lock ()
125+ 				readErr  =  & err 
126+ 				errCount ++ 
127+ 				readErrLock .Unlock ()
128+ 			}
129+ 		}(i )
130+ 	}
131+ 	wg .Wait ()
132+ 
133+ 	if  errCount  ==  0  {
134+ 		t .Fatal ("At least one openFile should fail" )
135+ 	}
136+ 
137+ 	if  ! strings .Contains ((* readErr ).Error (), "unexpectedly opened to" ) {
138+ 		t .Fatalf ("openFile should fail with 'cgroupRootHandle %d unexpectedly opened to <another file>'" , cgroupRootHandle .Fd ())
139+ 	}
140+ 
141+ 	// The openFile should work after prepOnce is reset because the cgroupRootHandle is updated. 
142+ 	if  _ , err  :=  openFile (cgroupfsDir , filepath .Join ("memory" , cgroupName , memoryLimit ), os .O_RDONLY ); err  !=  nil  {
143+ 		t .Fatal (err )
144+ 	}
145+ }
146+ 
73147func  BenchmarkWriteFile (b  * testing.B ) {
74148	TestMode  =  true 
75149	defer  func () { TestMode  =  false  }()
0 commit comments