@@ -136,6 +136,10 @@ struct mount_attr {
136136#define MOUNT_ATTR_IDMAP 0x00100000
137137#endif
138138
139+ #ifndef MOUNT_ATTR_NOSYMFOLLOW
140+ #define MOUNT_ATTR_NOSYMFOLLOW 0x00200000
141+ #endif
142+
139143static inline int sys_mount_setattr (int dfd , const char * path , unsigned int flags ,
140144 struct mount_attr * attr , size_t size )
141145{
@@ -235,6 +239,10 @@ static int prepare_unpriv_mountns(void)
235239 return 0 ;
236240}
237241
242+ #ifndef ST_NOSYMFOLLOW
243+ #define ST_NOSYMFOLLOW 0x2000 /* do not follow symlinks */
244+ #endif
245+
238246static int read_mnt_flags (const char * path )
239247{
240248 int ret ;
@@ -245,9 +253,9 @@ static int read_mnt_flags(const char *path)
245253 if (ret != 0 )
246254 return - EINVAL ;
247255
248- if (stat .f_flag &
249- ~( ST_RDONLY | ST_NOSUID | ST_NODEV | ST_NOEXEC | ST_NOATIME |
250- ST_NODIRATIME | ST_RELATIME | ST_SYNCHRONOUS | ST_MANDLOCK ))
256+ if (stat .f_flag & ~( ST_RDONLY | ST_NOSUID | ST_NODEV | ST_NOEXEC |
257+ ST_NOATIME | ST_NODIRATIME | ST_RELATIME |
258+ ST_SYNCHRONOUS | ST_MANDLOCK | ST_NOSYMFOLLOW ))
251259 return - EINVAL ;
252260
253261 mnt_flags = 0 ;
@@ -269,6 +277,8 @@ static int read_mnt_flags(const char *path)
269277 mnt_flags |= MS_SYNCHRONOUS ;
270278 if (stat .f_flag & ST_MANDLOCK )
271279 mnt_flags |= ST_MANDLOCK ;
280+ if (stat .f_flag & ST_NOSYMFOLLOW )
281+ mnt_flags |= ST_NOSYMFOLLOW ;
272282
273283 return mnt_flags ;
274284}
@@ -368,8 +378,13 @@ static bool mount_setattr_supported(void)
368378FIXTURE (mount_setattr ) {
369379};
370380
381+ #define NOSYMFOLLOW_TARGET "/mnt/A/AA/data"
382+ #define NOSYMFOLLOW_SYMLINK "/mnt/A/AA/symlink"
383+
371384FIXTURE_SETUP (mount_setattr )
372385{
386+ int fd = - EBADF ;
387+
373388 if (!mount_setattr_supported ())
374389 SKIP (return , "mount_setattr syscall not supported" );
375390
@@ -412,6 +427,11 @@ FIXTURE_SETUP(mount_setattr)
412427
413428 ASSERT_EQ (mount ("testing" , "/tmp/B/BB" , "devpts" ,
414429 MS_RELATIME | MS_NOEXEC | MS_RDONLY , 0 ), 0 );
430+
431+ fd = creat (NOSYMFOLLOW_TARGET , O_RDWR | O_CLOEXEC );
432+ ASSERT_GT (fd , 0 );
433+ ASSERT_EQ (symlink (NOSYMFOLLOW_TARGET , NOSYMFOLLOW_SYMLINK ), 0 );
434+ ASSERT_EQ (close (fd ), 0 );
415435}
416436
417437FIXTURE_TEARDOWN (mount_setattr )
@@ -1421,4 +1441,66 @@ TEST_F(mount_setattr_idmapped, idmap_mount_tree_invalid)
14211441 ASSERT_EQ (expected_uid_gid (open_tree_fd , "B/BB/b" , 0 , 0 , 0 ), 0 );
14221442}
14231443
1444+ TEST_F (mount_setattr , mount_attr_nosymfollow )
1445+ {
1446+ int fd ;
1447+ unsigned int old_flags = 0 , new_flags = 0 , expected_flags = 0 ;
1448+ struct mount_attr attr = {
1449+ .attr_set = MOUNT_ATTR_NOSYMFOLLOW ,
1450+ };
1451+
1452+ if (!mount_setattr_supported ())
1453+ SKIP (return , "mount_setattr syscall not supported" );
1454+
1455+ fd = open (NOSYMFOLLOW_SYMLINK , O_RDWR | O_CLOEXEC );
1456+ ASSERT_GT (fd , 0 );
1457+ ASSERT_EQ (close (fd ), 0 );
1458+
1459+ old_flags = read_mnt_flags ("/mnt/A" );
1460+ ASSERT_GT (old_flags , 0 );
1461+
1462+ ASSERT_EQ (sys_mount_setattr (-1 , "/mnt/A" , AT_RECURSIVE , & attr , sizeof (attr )), 0 );
1463+
1464+ expected_flags = old_flags ;
1465+ expected_flags |= ST_NOSYMFOLLOW ;
1466+
1467+ new_flags = read_mnt_flags ("/mnt/A" );
1468+ ASSERT_EQ (new_flags , expected_flags );
1469+
1470+ new_flags = read_mnt_flags ("/mnt/A/AA" );
1471+ ASSERT_EQ (new_flags , expected_flags );
1472+
1473+ new_flags = read_mnt_flags ("/mnt/A/AA/B" );
1474+ ASSERT_EQ (new_flags , expected_flags );
1475+
1476+ new_flags = read_mnt_flags ("/mnt/A/AA/B/BB" );
1477+ ASSERT_EQ (new_flags , expected_flags );
1478+
1479+ fd = open (NOSYMFOLLOW_SYMLINK , O_RDWR | O_CLOEXEC );
1480+ ASSERT_LT (fd , 0 );
1481+ ASSERT_EQ (errno , ELOOP );
1482+
1483+ attr .attr_set &= ~MOUNT_ATTR_NOSYMFOLLOW ;
1484+ attr .attr_clr |= MOUNT_ATTR_NOSYMFOLLOW ;
1485+
1486+ ASSERT_EQ (sys_mount_setattr (-1 , "/mnt/A" , AT_RECURSIVE , & attr , sizeof (attr )), 0 );
1487+
1488+ expected_flags &= ~ST_NOSYMFOLLOW ;
1489+ new_flags = read_mnt_flags ("/mnt/A" );
1490+ ASSERT_EQ (new_flags , expected_flags );
1491+
1492+ new_flags = read_mnt_flags ("/mnt/A/AA" );
1493+ ASSERT_EQ (new_flags , expected_flags );
1494+
1495+ new_flags = read_mnt_flags ("/mnt/A/AA/B" );
1496+ ASSERT_EQ (new_flags , expected_flags );
1497+
1498+ new_flags = read_mnt_flags ("/mnt/A/AA/B/BB" );
1499+ ASSERT_EQ (new_flags , expected_flags );
1500+
1501+ fd = open (NOSYMFOLLOW_SYMLINK , O_RDWR | O_CLOEXEC );
1502+ ASSERT_GT (fd , 0 );
1503+ ASSERT_EQ (close (fd ), 0 );
1504+ }
1505+
14241506TEST_HARNESS_MAIN
0 commit comments