@@ -101,16 +101,28 @@ def test_rename_dir(monitored_dir, ignored_dir, server):
101101 ignored_dir: Temporary directory path that is not monitored by fact.
102102 server: The server instance to communicate with.
103103 """
104+ def touch_test_files (directory , process = None ) -> list [Event ]:
105+ events = []
106+ for i in range (3 ):
107+ with open (os .path .join (directory , f'{ i } .txt' ), 'w' ) as f :
108+ f .write ('This is a test' )
109+ if process is not None :
110+ events .append (
111+ Event (process = process ,
112+ event_type = EventType .OPEN ,
113+ file = os .path .join (directory , f'{ i } .txt' ),
114+ host_path = os .path .join (directory , f'{ i } .txt' ))
115+ )
116+ return events
117+
104118 # Directory Under Test
105119 dut = os .path .join (monitored_dir , 'some-dir' )
106120 new_dut = os .path .join (monitored_dir , 'other-dir' )
107121 ignored_dut = os .path .join (ignored_dir , 'some-dir' )
108122 new_ignored_dut = os .path .join (ignored_dir , 'other-dir' )
109123
110124 os .mkdir (ignored_dut )
111- for i in range (3 ):
112- with open (os .path .join (ignored_dut , f'{ i } .txt' ), 'w' ) as f :
113- f .write ('This is a test' )
125+ touch_test_files (ignored_dut )
114126
115127 # This rename should generate no events
116128 os .rename (ignored_dut , new_ignored_dut )
@@ -126,16 +138,81 @@ def test_rename_dir(monitored_dir, ignored_dir, server):
126138 host_path = dut , old_file = new_ignored_dut , old_host_path = '' ),
127139 ])
128140
129- # The following two event should produce full events without scanning the FS.
141+ events = touch_test_files (dut , p )
142+
143+ # The following renames should produce full events without scanning the FS.
130144 os .rename (dut , new_dut )
131- os .rename (new_dut , ignored_dut )
145+ events .extend ([
146+ Event (process = p , event_type = EventType .RENAME , file = new_dut ,
147+ host_path = new_dut , old_file = dut , old_host_path = dut ),
148+ # Check the renamed subfiles are properly tracked
149+ * touch_test_files (new_dut , p ),
150+ ])
132151
133- events = [
134- Event (process = p , event_type = EventType .RENAME ,
135- file = new_dut , host_path = new_dut , old_file = dut , old_host_path = dut ),
152+ os .rename (new_dut , ignored_dut )
153+ events .append (
136154 Event (process = p , event_type = EventType .RENAME ,
137155 file = ignored_dut , host_path = '' , old_file = new_dut , old_host_path = new_dut ),
138- ]
156+ )
157+
158+ server .wait_events (events )
159+
160+
161+ @pytest .mark .parametrize ('from_monitored,to_monitored' , [
162+ pytest .param (True , True , id = 'both_monitored' ),
163+ pytest .param (False , True , id = 'target_monitored' ),
164+ pytest .param (True , False , id = 'bullet_monitored' ),
165+ ])
166+ def test_rename_overwrite (from_monitored , to_monitored , monitored_dir , ignored_dir , server ):
167+ events = []
168+ p = Process .from_proc ()
169+ if from_monitored :
170+ bullet = os .path .join (monitored_dir , 'bullet.txt' )
171+ events .append (
172+ Event (process = p ,
173+ event_type = EventType .CREATION ,
174+ file = bullet ,
175+ host_path = bullet ,
176+ ))
177+ else :
178+ bullet = os .path .join (ignored_dir , 'bullet.txt' )
179+
180+ if to_monitored :
181+ target = os .path .join (monitored_dir , 'target.txt' )
182+ events .append (
183+ Event (process = p ,
184+ event_type = EventType .CREATION ,
185+ file = target ,
186+ host_path = target ,
187+ ))
188+ else :
189+ target = os .path .join (ignored_dir , 'target.txt' )
190+
191+ # Create both files in the order they are expected in `events`
192+ for path in [bullet , target ]:
193+ with open (path , 'w' ) as f :
194+ f .write ('This is a test' )
195+
196+ os .rename (bullet , target )
197+ events .append (
198+ Event (process = p ,
199+ event_type = EventType .RENAME ,
200+ file = target ,
201+ host_path = target if to_monitored else '' ,
202+ old_file = bullet ,
203+ old_host_path = bullet if from_monitored else '' ,
204+ ))
205+
206+ if to_monitored :
207+ # One final event to check the mapping is persisted correctly
208+ with open (target , 'w' ) as f :
209+ f .write ('Check mapping' )
210+ events .append (
211+ Event (process = p ,
212+ event_type = EventType .OPEN ,
213+ file = target ,
214+ host_path = target ,
215+ ))
139216
140217 server .wait_events (events )
141218
@@ -203,3 +280,69 @@ def test_mounted_dir(test_container, ignored_dir, server):
203280 ]
204281
205282 server .wait_events (events )
283+
284+
285+ def test_cross_mountpoints (test_container , monitored_dir , server ):
286+ """
287+ Attempt to rename files/directories across mountpoints
288+
289+ This test does not necessarily fit here, since it won't trigger the
290+ path_rename LSM hook, but the test ensures that this hook is
291+ defintely not triggered when an inode crosses a mount point, so it
292+ doesn't fit but it kind of does? ¯\\ _(ツ)_/¯
293+ """
294+ mounted_file = '/unmonitored/test.txt'
295+ host_path = os .path .join (monitored_dir , 'test.txt' )
296+ ovfs_file = '/container-dir/test.txt'
297+
298+ test_container .exec_run (f'touch { mounted_file } ' )
299+ # Get owner uid and gid before moving the file
300+ stat = os .stat (host_path )
301+ owner_uid = stat .st_uid
302+ owner_gid = stat .st_gid
303+ mode = stat .st_mode
304+
305+ test_container .exec_run (f'mv { mounted_file } { ovfs_file } ' )
306+ test_container .exec_run (f'mv { ovfs_file } { mounted_file } ' )
307+
308+ touch = Process .in_container (
309+ exe_path = '/usr/bin/touch' ,
310+ args = f'touch { mounted_file } ' ,
311+ name = 'touch' ,
312+ container_id = test_container .id [:12 ],
313+ )
314+ first_rename = Process .in_container (
315+ exe_path = '/usr/bin/mv' ,
316+ args = f'mv { mounted_file } { ovfs_file } ' ,
317+ name = 'mv' ,
318+ container_id = test_container .id [:12 ],
319+ )
320+ second_rename = Process .in_container (
321+ exe_path = '/usr/bin/mv' ,
322+ args = f'mv { ovfs_file } { mounted_file } ' ,
323+ name = 'mv' ,
324+ container_id = test_container .id [:12 ],
325+ )
326+
327+ server .wait_events ([
328+ Event (process = touch , event_type = EventType .OPEN ,
329+ file = mounted_file , host_path = host_path ),
330+ Event (process = first_rename , event_type = EventType .CREATION ,
331+ file = ovfs_file , host_path = '' ),
332+ Event (process = first_rename , event_type = EventType .OPEN ,
333+ file = ovfs_file , host_path = '' ),
334+ Event (process = first_rename , event_type = EventType .OWNERSHIP ,
335+ file = ovfs_file , host_path = '' , owner_uid = owner_uid , owner_gid = owner_gid ),
336+ Event (process = first_rename , event_type = EventType .PERMISSION ,
337+ file = ovfs_file , host_path = '' , mode = mode ),
338+ Event (process = first_rename , event_type = EventType .UNLINK ,
339+ file = mounted_file , host_path = host_path ),
340+ Event (process = second_rename , event_type = EventType .CREATION ,
341+ file = mounted_file , host_path = host_path ),
342+ Event (process = second_rename , event_type = EventType .OWNERSHIP ,
343+ file = mounted_file , host_path = host_path , owner_uid = owner_uid , owner_gid = owner_gid ),
344+ Event (process = second_rename , event_type = EventType .PERMISSION ,
345+ file = mounted_file , host_path = host_path , mode = mode ),
346+ Event (process = second_rename , event_type = EventType .UNLINK ,
347+ file = ovfs_file , host_path = '' ),
348+ ])
0 commit comments