Skip to content

Commit 30d1a55

Browse files
committed
Merge tag 'fs.mount_setattr.nosymfollow.v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux
Pull mount_setattr updates from Christian Brauner: "A few releases ago the old mount API gained support for a mount options which prevents following symlinks on a given mount. This adds support for it in the new mount api through the MOUNT_ATTR_NOSYMFOLLOW flag via mount_setattr() and fsmount(). With mount_setattr() that flag can even be applied recursively. There's an additional ack from Ross Zwisler who originally authored the nosymfollow patch. As I've already had the patches in my for-next I didn't add his ack explicitly" * tag 'fs.mount_setattr.nosymfollow.v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux: tests: test MOUNT_ATTR_NOSYMFOLLOW with mount_setattr() mount: Support "nosymfollow" in new mount api
2 parents 65090f3 + 5990b5d commit 30d1a55

File tree

3 files changed

+92
-6
lines changed

3 files changed

+92
-6
lines changed

fs/namespace.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3464,9 +3464,10 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
34643464
return ret;
34653465
}
34663466

3467-
#define FSMOUNT_VALID_FLAGS \
3468-
(MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NODEV | \
3469-
MOUNT_ATTR_NOEXEC | MOUNT_ATTR__ATIME | MOUNT_ATTR_NODIRATIME)
3467+
#define FSMOUNT_VALID_FLAGS \
3468+
(MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NODEV | \
3469+
MOUNT_ATTR_NOEXEC | MOUNT_ATTR__ATIME | MOUNT_ATTR_NODIRATIME | \
3470+
MOUNT_ATTR_NOSYMFOLLOW)
34703471

34713472
#define MOUNT_SETATTR_VALID_FLAGS (FSMOUNT_VALID_FLAGS | MOUNT_ATTR_IDMAP)
34723473

@@ -3487,6 +3488,8 @@ static unsigned int attr_flags_to_mnt_flags(u64 attr_flags)
34873488
mnt_flags |= MNT_NOEXEC;
34883489
if (attr_flags & MOUNT_ATTR_NODIRATIME)
34893490
mnt_flags |= MNT_NODIRATIME;
3491+
if (attr_flags & MOUNT_ATTR_NOSYMFOLLOW)
3492+
mnt_flags |= MNT_NOSYMFOLLOW;
34903493

34913494
return mnt_flags;
34923495
}

include/uapi/linux/mount.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ enum fsconfig_command {
120120
#define MOUNT_ATTR_STRICTATIME 0x00000020 /* - Always perform atime updates */
121121
#define MOUNT_ATTR_NODIRATIME 0x00000080 /* Do not update directory access times */
122122
#define MOUNT_ATTR_IDMAP 0x00100000 /* Idmap mount to @userns_fd in struct mount_attr. */
123+
#define MOUNT_ATTR_NOSYMFOLLOW 0x00200000 /* Do not follow symlinks */
123124

124125
/*
125126
* mount_setattr()

tools/testing/selftests/mount_setattr/mount_setattr_test.c

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
139143
static 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+
238246
static 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)
368378
FIXTURE(mount_setattr) {
369379
};
370380

381+
#define NOSYMFOLLOW_TARGET "/mnt/A/AA/data"
382+
#define NOSYMFOLLOW_SYMLINK "/mnt/A/AA/symlink"
383+
371384
FIXTURE_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

417437
FIXTURE_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+
14241506
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)