@@ -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,77 @@ 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+ 			_ , err  :=  openFile (cgroupfsDir , filepath .Join ("memory" , cgroupName , memoryLimit ), os .O_RDONLY )
124+ 			t .Logf ("openFile attempt %d: %v\n " , i , err )
125+ 			if  err  !=  nil  {
126+ 				readErrLock .Lock ()
127+ 				readErr  =  & err 
128+ 				errCount ++ 
129+ 				readErrLock .Unlock ()
130+ 			}
131+ 		}(i )
132+ 	}
133+ 	wg .Wait ()
134+ 
135+ 	if  errCount  ==  0  {
136+ 		t .Fatal ("At least one openFile should fail" )
137+ 	}
138+ 
139+ 	if  ! strings .Contains ((* readErr ).Error (), "unexpectedly opened to" ) {
140+ 		t .Fatalf ("openFile should fail with 'cgroupRootHandle %d unexpectedly opened to <another file>'" , cgroupRootHandle .Fd ())
141+ 	}
142+ 
143+ 	// The openFile should work after prepOnce is reset because the cgroupRootHandle is updated. 
144+ 	if  _ , err  :=  openFile (cgroupfsDir , filepath .Join ("memory" , cgroupName , memoryLimit ), os .O_RDONLY ); err  !=  nil  {
145+ 		t .Fatal (err )
146+ 	}
147+ }
148+ 
73149func  BenchmarkWriteFile (b  * testing.B ) {
74150	TestMode  =  true 
75151	defer  func () { TestMode  =  false  }()
0 commit comments