@@ -2251,4 +2251,103 @@ TEST(thread_ns_inactive_after_exit)
22512251 ASSERT_TRUE (errno == ENOENT || errno == ESTALE );
22522252}
22532253
2254+ /*
2255+ * Test that a namespace remains active while a thread holds an fd to it.
2256+ * Even after the thread exits, the namespace should remain active as long as
2257+ * another thread holds a file descriptor to it.
2258+ */
2259+ TEST (thread_ns_fd_keeps_active )
2260+ {
2261+ pthread_t thread ;
2262+ struct thread_ns_info info ;
2263+ struct file_handle * handle ;
2264+ int pipefd [2 ];
2265+ int syncpipe [2 ];
2266+ int ret ;
2267+ char sync_byte ;
2268+ char buf [sizeof (* handle ) + MAX_HANDLE_SZ ];
2269+
2270+ ASSERT_EQ (pipe (pipefd ), 0 );
2271+ ASSERT_EQ (pipe (syncpipe ), 0 );
2272+
2273+ info .pipefd = pipefd [1 ];
2274+ info .syncfd_read = syncpipe [0 ];
2275+ info .syncfd_write = -1 ;
2276+ info .exit_code = -1 ;
2277+
2278+ /* Create thread that will create a namespace */
2279+ ret = pthread_create (& thread , NULL , thread_create_namespace , & info );
2280+ ASSERT_EQ (ret , 0 );
2281+
2282+ /* Read namespace ID from thread */
2283+ __u64 ns_id ;
2284+ ret = read (pipefd [0 ], & ns_id , sizeof (ns_id ));
2285+ if (ret != sizeof (ns_id )) {
2286+ sync_byte = 'X' ;
2287+ write (syncpipe [1 ], & sync_byte , 1 );
2288+ pthread_join (thread , NULL );
2289+ close (pipefd [0 ]);
2290+ close (pipefd [1 ]);
2291+ close (syncpipe [0 ]);
2292+ close (syncpipe [1 ]);
2293+ SKIP (return , "Failed to read namespace ID from thread" );
2294+ }
2295+
2296+ TH_LOG ("Thread created namespace with ID %llu" , (unsigned long long )ns_id );
2297+
2298+ /* Construct file handle */
2299+ handle = (struct file_handle * )buf ;
2300+ handle -> handle_bytes = sizeof (struct nsfs_file_handle );
2301+ handle -> handle_type = FILEID_NSFS ;
2302+ struct nsfs_file_handle * fh = (struct nsfs_file_handle * )handle -> f_handle ;
2303+ fh -> ns_id = ns_id ;
2304+ fh -> ns_type = 0 ;
2305+ fh -> ns_inum = 0 ;
2306+
2307+ /* Open namespace while thread is alive */
2308+ TH_LOG ("Opening namespace while thread is alive" );
2309+ int nsfd = open_by_handle_at (FD_NSFS_ROOT , handle , O_RDONLY );
2310+ ASSERT_GE (nsfd , 0 );
2311+
2312+ /* Signal thread to exit */
2313+ TH_LOG ("Signaling thread to exit" );
2314+ sync_byte = 'X' ;
2315+ write (syncpipe [1 ], & sync_byte , 1 );
2316+ close (syncpipe [1 ]);
2317+
2318+ /* Wait for thread to exit */
2319+ pthread_join (thread , NULL );
2320+ close (pipefd [0 ]);
2321+ close (pipefd [1 ]);
2322+ close (syncpipe [0 ]);
2323+
2324+ if (info .exit_code != 0 ) {
2325+ close (nsfd );
2326+ SKIP (return , "Thread failed to create namespace" );
2327+ }
2328+
2329+ TH_LOG ("Thread exited, but main thread holds fd - namespace should remain active" );
2330+
2331+ /* Namespace should still be active because we hold an fd */
2332+ int nsfd2 = open_by_handle_at (FD_NSFS_ROOT , handle , O_RDONLY );
2333+ ASSERT_GE (nsfd2 , 0 );
2334+
2335+ /* Verify it's the same namespace */
2336+ struct stat st1 , st2 ;
2337+ ASSERT_EQ (fstat (nsfd , & st1 ), 0 );
2338+ ASSERT_EQ (fstat (nsfd2 , & st2 ), 0 );
2339+ ASSERT_EQ (st1 .st_ino , st2 .st_ino );
2340+ close (nsfd2 );
2341+
2342+ TH_LOG ("Closing fd - namespace should become inactive" );
2343+ close (nsfd );
2344+
2345+ /* Now namespace should be inactive */
2346+ nsfd = open_by_handle_at (FD_NSFS_ROOT , handle , O_RDONLY );
2347+ ASSERT_LT (nsfd , 0 );
2348+ /* Should fail with ENOENT (inactive) or ESTALE (gone) */
2349+ TH_LOG ("Namespace inactive as expected: %s (errno=%d)" , strerror (errno ), errno );
2350+ ASSERT_TRUE (errno == ENOENT || errno == ESTALE );
2351+ }
2352+
22542353TEST_HARNESS_MAIN
0 commit comments