1 // SPDX-License-Identifier: GPL-2.0
10 #include <sys/mount.h>
13 #include <sys/statvfs.h>
14 #include <sys/sysinfo.h>
21 #include <linux/mount.h>
23 #include "../kselftest_harness.h"
26 #define CLONE_NEWNS 0x00020000
30 #define CLONE_NEWUSER 0x10000000
38 #define MS_RELATIME (1 << 21)
41 #ifndef MS_STRICTATIME
42 #define MS_STRICTATIME (1 << 24)
45 #ifndef MOUNT_ATTR_RDONLY
46 #define MOUNT_ATTR_RDONLY 0x00000001
49 #ifndef MOUNT_ATTR_NOSUID
50 #define MOUNT_ATTR_NOSUID 0x00000002
53 #ifndef MOUNT_ATTR_NOEXEC
54 #define MOUNT_ATTR_NOEXEC 0x00000008
57 #ifndef MOUNT_ATTR_NODIRATIME
58 #define MOUNT_ATTR_NODIRATIME 0x00000080
61 #ifndef MOUNT_ATTR__ATIME
62 #define MOUNT_ATTR__ATIME 0x00000070
65 #ifndef MOUNT_ATTR_RELATIME
66 #define MOUNT_ATTR_RELATIME 0x00000000
69 #ifndef MOUNT_ATTR_NOATIME
70 #define MOUNT_ATTR_NOATIME 0x00000010
73 #ifndef MOUNT_ATTR_STRICTATIME
74 #define MOUNT_ATTR_STRICTATIME 0x00000020
78 #define AT_RECURSIVE 0x8000
82 #define MS_SHARED (1 << 20)
85 #define DEFAULT_THREADS 4
86 #define ptr_to_int(p) ((int)((intptr_t)(p)))
87 #define int_to_ptr(u) ((void *)((intptr_t)(u)))
89 #ifndef __NR_mount_setattr
91 #define __NR_mount_setattr 552
92 #elif defined _MIPS_SIM
93 #if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */
94 #define __NR_mount_setattr (442 + 4000)
96 #if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */
97 #define __NR_mount_setattr (442 + 6000)
99 #if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */
100 #define __NR_mount_setattr (442 + 5000)
102 #elif defined __ia64__
103 #define __NR_mount_setattr (442 + 1024)
105 #define __NR_mount_setattr 442
109 #ifndef __NR_open_tree
110 #if defined __alpha__
111 #define __NR_open_tree 538
112 #elif defined _MIPS_SIM
113 #if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */
114 #define __NR_open_tree 4428
116 #if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */
117 #define __NR_open_tree 6428
119 #if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */
120 #define __NR_open_tree 5428
122 #elif defined __ia64__
123 #define __NR_open_tree (428 + 1024)
125 #define __NR_open_tree 428
129 #ifndef MOUNT_ATTR_IDMAP
130 #define MOUNT_ATTR_IDMAP 0x00100000
133 #ifndef MOUNT_ATTR_NOSYMFOLLOW
134 #define MOUNT_ATTR_NOSYMFOLLOW 0x00200000
137 static inline int sys_mount_setattr(int dfd, const char *path, unsigned int flags,
138 struct mount_attr *attr, size_t size)
140 return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);
143 #ifndef OPEN_TREE_CLONE
144 #define OPEN_TREE_CLONE 1
147 #ifndef OPEN_TREE_CLOEXEC
148 #define OPEN_TREE_CLOEXEC O_CLOEXEC
152 #define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */
155 static inline int sys_open_tree(int dfd, const char *filename, unsigned int flags)
157 return syscall(__NR_open_tree, dfd, filename, flags);
160 static ssize_t write_nointr(int fd, const void *buf, size_t count)
165 ret = write(fd, buf, count);
166 } while (ret < 0 && errno == EINTR);
171 static int write_file(const char *path, const void *buf, size_t count)
176 fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);
180 ret = write_nointr(fd, buf, count);
182 if (ret < 0 || (size_t)ret != count)
188 static int create_and_enter_userns(void)
197 if (unshare(CLONE_NEWUSER))
200 if (write_file("/proc/self/setgroups", "deny", sizeof("deny") - 1) &&
204 snprintf(map, sizeof(map), "0 %d 1", uid);
205 if (write_file("/proc/self/uid_map", map, strlen(map)))
209 snprintf(map, sizeof(map), "0 %d 1", gid);
210 if (write_file("/proc/self/gid_map", map, strlen(map)))
222 static int prepare_unpriv_mountns(void)
224 if (create_and_enter_userns())
227 if (unshare(CLONE_NEWNS))
230 if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0))
236 #ifndef ST_NOSYMFOLLOW
237 #define ST_NOSYMFOLLOW 0x2000 /* do not follow symlinks */
240 static int read_mnt_flags(const char *path)
244 unsigned int mnt_flags;
246 ret = statvfs(path, &stat);
250 if (stat.f_flag & ~(ST_RDONLY | ST_NOSUID | ST_NODEV | ST_NOEXEC |
251 ST_NOATIME | ST_NODIRATIME | ST_RELATIME |
252 ST_SYNCHRONOUS | ST_MANDLOCK | ST_NOSYMFOLLOW))
256 if (stat.f_flag & ST_RDONLY)
257 mnt_flags |= MS_RDONLY;
258 if (stat.f_flag & ST_NOSUID)
259 mnt_flags |= MS_NOSUID;
260 if (stat.f_flag & ST_NODEV)
261 mnt_flags |= MS_NODEV;
262 if (stat.f_flag & ST_NOEXEC)
263 mnt_flags |= MS_NOEXEC;
264 if (stat.f_flag & ST_NOATIME)
265 mnt_flags |= MS_NOATIME;
266 if (stat.f_flag & ST_NODIRATIME)
267 mnt_flags |= MS_NODIRATIME;
268 if (stat.f_flag & ST_RELATIME)
269 mnt_flags |= MS_RELATIME;
270 if (stat.f_flag & ST_SYNCHRONOUS)
271 mnt_flags |= MS_SYNCHRONOUS;
272 if (stat.f_flag & ST_MANDLOCK)
273 mnt_flags |= ST_MANDLOCK;
274 if (stat.f_flag & ST_NOSYMFOLLOW)
275 mnt_flags |= ST_NOSYMFOLLOW;
280 static char *get_field(char *src, int nfields)
285 for (i = 0; i < nfields; i++) {
286 while (*p && *p != ' ' && *p != '\t')
298 static void null_endofword(char *word)
300 while (*word && *word != ' ' && *word != '\t')
305 static bool is_shared_mount(const char *path)
311 f = fopen("/proc/self/mountinfo", "re");
315 while (getline(&line, &len, f) != -1) {
318 target = get_field(line, 4);
322 opts = get_field(target, 2);
326 null_endofword(target);
328 if (strcmp(target, path) != 0)
331 null_endofword(opts);
332 if (strstr(opts, "shared:"))
342 static void *mount_setattr_thread(void *data)
344 struct mount_attr attr = {
345 .attr_set = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID,
347 .propagation = MS_SHARED,
350 if (sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)))
351 pthread_exit(int_to_ptr(-1));
353 pthread_exit(int_to_ptr(0));
356 /* Attempt to de-conflict with the selftests tree. */
358 #define SKIP(s, ...) XFAIL(s, ##__VA_ARGS__)
361 static bool mount_setattr_supported(void)
365 ret = sys_mount_setattr(-EBADF, "", AT_EMPTY_PATH, NULL, 0);
366 if (ret < 0 && errno == ENOSYS)
372 FIXTURE(mount_setattr) {
375 #define NOSYMFOLLOW_TARGET "/mnt/A/AA/data"
376 #define NOSYMFOLLOW_SYMLINK "/mnt/A/AA/symlink"
378 FIXTURE_SETUP(mount_setattr)
382 if (!mount_setattr_supported())
383 SKIP(return, "mount_setattr syscall not supported");
385 ASSERT_EQ(prepare_unpriv_mountns(), 0);
387 (void)umount2("/mnt", MNT_DETACH);
388 (void)umount2("/tmp", MNT_DETACH);
390 ASSERT_EQ(mount("testing", "/tmp", "tmpfs", MS_NOATIME | MS_NODEV,
391 "size=100000,mode=700"), 0);
393 ASSERT_EQ(mkdir("/tmp/B", 0777), 0);
395 ASSERT_EQ(mount("testing", "/tmp/B", "tmpfs", MS_NOATIME | MS_NODEV,
396 "size=100000,mode=700"), 0);
398 ASSERT_EQ(mkdir("/tmp/B/BB", 0777), 0);
400 ASSERT_EQ(mount("testing", "/tmp/B/BB", "tmpfs", MS_NOATIME | MS_NODEV,
401 "size=100000,mode=700"), 0);
403 ASSERT_EQ(mount("testing", "/mnt", "tmpfs", MS_NOATIME | MS_NODEV,
404 "size=100000,mode=700"), 0);
406 ASSERT_EQ(mkdir("/mnt/A", 0777), 0);
408 ASSERT_EQ(mount("testing", "/mnt/A", "tmpfs", MS_NOATIME | MS_NODEV,
409 "size=100000,mode=700"), 0);
411 ASSERT_EQ(mkdir("/mnt/A/AA", 0777), 0);
413 ASSERT_EQ(mount("/tmp", "/mnt/A/AA", NULL, MS_BIND | MS_REC, NULL), 0);
415 ASSERT_EQ(mkdir("/mnt/B", 0777), 0);
417 ASSERT_EQ(mount("testing", "/mnt/B", "ramfs",
418 MS_NOATIME | MS_NODEV | MS_NOSUID, 0), 0);
420 ASSERT_EQ(mkdir("/mnt/B/BB", 0777), 0);
422 ASSERT_EQ(mount("testing", "/tmp/B/BB", "devpts",
423 MS_RELATIME | MS_NOEXEC | MS_RDONLY, 0), 0);
425 fd = creat(NOSYMFOLLOW_TARGET, O_RDWR | O_CLOEXEC);
427 ASSERT_EQ(symlink(NOSYMFOLLOW_TARGET, NOSYMFOLLOW_SYMLINK), 0);
428 ASSERT_EQ(close(fd), 0);
431 FIXTURE_TEARDOWN(mount_setattr)
433 if (!mount_setattr_supported())
434 SKIP(return, "mount_setattr syscall not supported");
436 (void)umount2("/mnt/A", MNT_DETACH);
437 (void)umount2("/tmp", MNT_DETACH);
440 TEST_F(mount_setattr, invalid_attributes)
442 struct mount_attr invalid_attr = {
443 .attr_set = (1U << 31),
446 if (!mount_setattr_supported())
447 SKIP(return, "mount_setattr syscall not supported");
449 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr,
450 sizeof(invalid_attr)), 0);
452 invalid_attr.attr_set = 0;
453 invalid_attr.attr_clr = (1U << 31);
454 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr,
455 sizeof(invalid_attr)), 0);
457 invalid_attr.attr_clr = 0;
458 invalid_attr.propagation = (1U << 31);
459 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr,
460 sizeof(invalid_attr)), 0);
462 invalid_attr.attr_set = (1U << 31);
463 invalid_attr.attr_clr = (1U << 31);
464 invalid_attr.propagation = (1U << 31);
465 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr,
466 sizeof(invalid_attr)), 0);
468 ASSERT_NE(sys_mount_setattr(-1, "mnt/A", AT_RECURSIVE, &invalid_attr,
469 sizeof(invalid_attr)), 0);
472 TEST_F(mount_setattr, extensibility)
474 unsigned int old_flags = 0, new_flags = 0, expected_flags = 0;
476 struct mount_attr invalid_attr = {};
477 struct mount_attr_large {
478 struct mount_attr attr1;
479 struct mount_attr attr2;
480 struct mount_attr attr3;
483 if (!mount_setattr_supported())
484 SKIP(return, "mount_setattr syscall not supported");
486 old_flags = read_mnt_flags("/mnt/A");
487 ASSERT_GT(old_flags, 0);
489 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, NULL,
490 sizeof(invalid_attr)), 0);
491 ASSERT_EQ(errno, EFAULT);
493 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, (void *)s,
494 sizeof(invalid_attr)), 0);
495 ASSERT_EQ(errno, EINVAL);
497 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr, 0), 0);
498 ASSERT_EQ(errno, EINVAL);
500 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr,
501 sizeof(invalid_attr) / 2), 0);
502 ASSERT_EQ(errno, EINVAL);
504 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &invalid_attr,
505 sizeof(invalid_attr) / 2), 0);
506 ASSERT_EQ(errno, EINVAL);
508 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE,
509 (void *)&large_attr, sizeof(large_attr)), 0);
511 large_attr.attr3.attr_set = MOUNT_ATTR_RDONLY;
512 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE,
513 (void *)&large_attr, sizeof(large_attr)), 0);
515 large_attr.attr3.attr_set = 0;
516 large_attr.attr1.attr_set = MOUNT_ATTR_RDONLY;
517 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE,
518 (void *)&large_attr, sizeof(large_attr)), 0);
520 expected_flags = old_flags;
521 expected_flags |= MS_RDONLY;
523 new_flags = read_mnt_flags("/mnt/A");
524 ASSERT_EQ(new_flags, expected_flags);
526 new_flags = read_mnt_flags("/mnt/A/AA");
527 ASSERT_EQ(new_flags, expected_flags);
529 new_flags = read_mnt_flags("/mnt/A/AA/B");
530 ASSERT_EQ(new_flags, expected_flags);
532 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
533 ASSERT_EQ(new_flags, expected_flags);
536 TEST_F(mount_setattr, basic)
538 unsigned int old_flags = 0, new_flags = 0, expected_flags = 0;
539 struct mount_attr attr = {
540 .attr_set = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOEXEC | MOUNT_ATTR_RELATIME,
541 .attr_clr = MOUNT_ATTR__ATIME,
544 if (!mount_setattr_supported())
545 SKIP(return, "mount_setattr syscall not supported");
547 old_flags = read_mnt_flags("/mnt/A");
548 ASSERT_GT(old_flags, 0);
550 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", 0, &attr, sizeof(attr)), 0);
552 expected_flags = old_flags;
553 expected_flags |= MS_RDONLY;
554 expected_flags |= MS_NOEXEC;
555 expected_flags &= ~MS_NOATIME;
556 expected_flags |= MS_RELATIME;
558 new_flags = read_mnt_flags("/mnt/A");
559 ASSERT_EQ(new_flags, expected_flags);
561 new_flags = read_mnt_flags("/mnt/A/AA");
562 ASSERT_EQ(new_flags, old_flags);
564 new_flags = read_mnt_flags("/mnt/A/AA/B");
565 ASSERT_EQ(new_flags, old_flags);
567 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
568 ASSERT_EQ(new_flags, old_flags);
571 TEST_F(mount_setattr, basic_recursive)
574 unsigned int old_flags = 0, new_flags = 0, expected_flags = 0;
575 struct mount_attr attr = {
576 .attr_set = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOEXEC | MOUNT_ATTR_RELATIME,
577 .attr_clr = MOUNT_ATTR__ATIME,
580 if (!mount_setattr_supported())
581 SKIP(return, "mount_setattr syscall not supported");
583 old_flags = read_mnt_flags("/mnt/A");
584 ASSERT_GT(old_flags, 0);
586 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
588 expected_flags = old_flags;
589 expected_flags |= MS_RDONLY;
590 expected_flags |= MS_NOEXEC;
591 expected_flags &= ~MS_NOATIME;
592 expected_flags |= MS_RELATIME;
594 new_flags = read_mnt_flags("/mnt/A");
595 ASSERT_EQ(new_flags, expected_flags);
597 new_flags = read_mnt_flags("/mnt/A/AA");
598 ASSERT_EQ(new_flags, expected_flags);
600 new_flags = read_mnt_flags("/mnt/A/AA/B");
601 ASSERT_EQ(new_flags, expected_flags);
603 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
604 ASSERT_EQ(new_flags, expected_flags);
606 memset(&attr, 0, sizeof(attr));
607 attr.attr_clr = MOUNT_ATTR_RDONLY;
608 attr.propagation = MS_SHARED;
609 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
611 expected_flags &= ~MS_RDONLY;
612 new_flags = read_mnt_flags("/mnt/A");
613 ASSERT_EQ(new_flags, expected_flags);
615 ASSERT_EQ(is_shared_mount("/mnt/A"), true);
617 new_flags = read_mnt_flags("/mnt/A/AA");
618 ASSERT_EQ(new_flags, expected_flags);
620 ASSERT_EQ(is_shared_mount("/mnt/A/AA"), true);
622 new_flags = read_mnt_flags("/mnt/A/AA/B");
623 ASSERT_EQ(new_flags, expected_flags);
625 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B"), true);
627 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
628 ASSERT_EQ(new_flags, expected_flags);
630 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B/BB"), true);
632 fd = open("/mnt/A/AA/B/b", O_RDWR | O_CLOEXEC | O_CREAT | O_EXCL, 0777);
636 * We're holding a fd open for writing so this needs to fail somewhere
637 * in the middle and the mount options need to be unchanged.
639 attr.attr_set = MOUNT_ATTR_RDONLY;
640 ASSERT_LT(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
642 new_flags = read_mnt_flags("/mnt/A");
643 ASSERT_EQ(new_flags, expected_flags);
645 ASSERT_EQ(is_shared_mount("/mnt/A"), true);
647 new_flags = read_mnt_flags("/mnt/A/AA");
648 ASSERT_EQ(new_flags, expected_flags);
650 ASSERT_EQ(is_shared_mount("/mnt/A/AA"), true);
652 new_flags = read_mnt_flags("/mnt/A/AA/B");
653 ASSERT_EQ(new_flags, expected_flags);
655 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B"), true);
657 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
658 ASSERT_EQ(new_flags, expected_flags);
660 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B/BB"), true);
662 EXPECT_EQ(close(fd), 0);
665 TEST_F(mount_setattr, mount_has_writers)
668 unsigned int old_flags = 0, new_flags = 0;
669 struct mount_attr attr = {
670 .attr_set = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOEXEC | MOUNT_ATTR_RELATIME,
671 .attr_clr = MOUNT_ATTR__ATIME,
672 .propagation = MS_SHARED,
675 if (!mount_setattr_supported())
676 SKIP(return, "mount_setattr syscall not supported");
678 old_flags = read_mnt_flags("/mnt/A");
679 ASSERT_GT(old_flags, 0);
681 fd = open("/mnt/A/AA/B/b", O_RDWR | O_CLOEXEC | O_CREAT | O_EXCL, 0777);
685 * We're holding a fd open to a mount somwhere in the middle so this
686 * needs to fail somewhere in the middle. After this the mount options
687 * need to be unchanged.
689 ASSERT_LT(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
691 new_flags = read_mnt_flags("/mnt/A");
692 ASSERT_EQ(new_flags, old_flags);
694 ASSERT_EQ(is_shared_mount("/mnt/A"), false);
696 new_flags = read_mnt_flags("/mnt/A/AA");
697 ASSERT_EQ(new_flags, old_flags);
699 ASSERT_EQ(is_shared_mount("/mnt/A/AA"), false);
701 new_flags = read_mnt_flags("/mnt/A/AA/B");
702 ASSERT_EQ(new_flags, old_flags);
704 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B"), false);
706 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
707 ASSERT_EQ(new_flags, old_flags);
709 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B/BB"), false);
711 dfd = open("/mnt/A/AA/B", O_DIRECTORY | O_CLOEXEC);
713 EXPECT_EQ(fsync(dfd), 0);
714 EXPECT_EQ(close(dfd), 0);
716 EXPECT_EQ(fsync(fd), 0);
717 EXPECT_EQ(close(fd), 0);
719 /* All writers are gone so this should succeed. */
720 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
723 TEST_F(mount_setattr, mixed_mount_options)
725 unsigned int old_flags1 = 0, old_flags2 = 0, new_flags = 0, expected_flags = 0;
726 struct mount_attr attr = {
727 .attr_clr = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NOEXEC | MOUNT_ATTR__ATIME,
728 .attr_set = MOUNT_ATTR_RELATIME,
731 if (!mount_setattr_supported())
732 SKIP(return, "mount_setattr syscall not supported");
734 old_flags1 = read_mnt_flags("/mnt/B");
735 ASSERT_GT(old_flags1, 0);
737 old_flags2 = read_mnt_flags("/mnt/B/BB");
738 ASSERT_GT(old_flags2, 0);
740 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/B", AT_RECURSIVE, &attr, sizeof(attr)), 0);
742 expected_flags = old_flags2;
743 expected_flags &= ~(MS_RDONLY | MS_NOEXEC | MS_NOATIME | MS_NOSUID);
744 expected_flags |= MS_RELATIME;
746 new_flags = read_mnt_flags("/mnt/B");
747 ASSERT_EQ(new_flags, expected_flags);
749 expected_flags = old_flags2;
750 expected_flags &= ~(MS_RDONLY | MS_NOEXEC | MS_NOATIME | MS_NOSUID);
751 expected_flags |= MS_RELATIME;
753 new_flags = read_mnt_flags("/mnt/B/BB");
754 ASSERT_EQ(new_flags, expected_flags);
757 TEST_F(mount_setattr, time_changes)
759 unsigned int old_flags = 0, new_flags = 0, expected_flags = 0;
760 struct mount_attr attr = {
761 .attr_set = MOUNT_ATTR_NODIRATIME | MOUNT_ATTR_NOATIME,
764 if (!mount_setattr_supported())
765 SKIP(return, "mount_setattr syscall not supported");
767 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
769 attr.attr_set = MOUNT_ATTR_STRICTATIME;
770 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
772 attr.attr_set = MOUNT_ATTR_STRICTATIME | MOUNT_ATTR_NOATIME;
773 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
775 attr.attr_set = MOUNT_ATTR_STRICTATIME | MOUNT_ATTR_NOATIME;
776 attr.attr_clr = MOUNT_ATTR__ATIME;
777 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
780 attr.attr_clr = MOUNT_ATTR_STRICTATIME;
781 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
783 attr.attr_clr = MOUNT_ATTR_NOATIME;
784 ASSERT_NE(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
786 old_flags = read_mnt_flags("/mnt/A");
787 ASSERT_GT(old_flags, 0);
789 attr.attr_set = MOUNT_ATTR_NODIRATIME | MOUNT_ATTR_NOATIME;
790 attr.attr_clr = MOUNT_ATTR__ATIME;
791 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
793 expected_flags = old_flags;
794 expected_flags |= MS_NOATIME;
795 expected_flags |= MS_NODIRATIME;
797 new_flags = read_mnt_flags("/mnt/A");
798 ASSERT_EQ(new_flags, expected_flags);
800 new_flags = read_mnt_flags("/mnt/A/AA");
801 ASSERT_EQ(new_flags, expected_flags);
803 new_flags = read_mnt_flags("/mnt/A/AA/B");
804 ASSERT_EQ(new_flags, expected_flags);
806 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
807 ASSERT_EQ(new_flags, expected_flags);
809 memset(&attr, 0, sizeof(attr));
810 attr.attr_set &= ~MOUNT_ATTR_NOATIME;
811 attr.attr_set |= MOUNT_ATTR_RELATIME;
812 attr.attr_clr |= MOUNT_ATTR__ATIME;
813 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
815 expected_flags &= ~MS_NOATIME;
816 expected_flags |= MS_RELATIME;
818 new_flags = read_mnt_flags("/mnt/A");
819 ASSERT_EQ(new_flags, expected_flags);
821 new_flags = read_mnt_flags("/mnt/A/AA");
822 ASSERT_EQ(new_flags, expected_flags);
824 new_flags = read_mnt_flags("/mnt/A/AA/B");
825 ASSERT_EQ(new_flags, expected_flags);
827 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
828 ASSERT_EQ(new_flags, expected_flags);
830 memset(&attr, 0, sizeof(attr));
831 attr.attr_set &= ~MOUNT_ATTR_RELATIME;
832 attr.attr_set |= MOUNT_ATTR_STRICTATIME;
833 attr.attr_clr |= MOUNT_ATTR__ATIME;
834 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
836 expected_flags &= ~MS_RELATIME;
838 new_flags = read_mnt_flags("/mnt/A");
839 ASSERT_EQ(new_flags, expected_flags);
841 new_flags = read_mnt_flags("/mnt/A/AA");
842 ASSERT_EQ(new_flags, expected_flags);
844 new_flags = read_mnt_flags("/mnt/A/AA/B");
845 ASSERT_EQ(new_flags, expected_flags);
847 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
848 ASSERT_EQ(new_flags, expected_flags);
850 memset(&attr, 0, sizeof(attr));
851 attr.attr_set &= ~MOUNT_ATTR_STRICTATIME;
852 attr.attr_set |= MOUNT_ATTR_NOATIME;
853 attr.attr_clr |= MOUNT_ATTR__ATIME;
854 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
856 expected_flags |= MS_NOATIME;
857 new_flags = read_mnt_flags("/mnt/A");
858 ASSERT_EQ(new_flags, expected_flags);
860 new_flags = read_mnt_flags("/mnt/A/AA");
861 ASSERT_EQ(new_flags, expected_flags);
863 new_flags = read_mnt_flags("/mnt/A/AA/B");
864 ASSERT_EQ(new_flags, expected_flags);
866 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
867 ASSERT_EQ(new_flags, expected_flags);
869 memset(&attr, 0, sizeof(attr));
870 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
872 new_flags = read_mnt_flags("/mnt/A");
873 ASSERT_EQ(new_flags, expected_flags);
875 new_flags = read_mnt_flags("/mnt/A/AA");
876 ASSERT_EQ(new_flags, expected_flags);
878 new_flags = read_mnt_flags("/mnt/A/AA/B");
879 ASSERT_EQ(new_flags, expected_flags);
881 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
882 ASSERT_EQ(new_flags, expected_flags);
884 memset(&attr, 0, sizeof(attr));
885 attr.attr_clr = MOUNT_ATTR_NODIRATIME;
886 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
888 expected_flags &= ~MS_NODIRATIME;
890 new_flags = read_mnt_flags("/mnt/A");
891 ASSERT_EQ(new_flags, expected_flags);
893 new_flags = read_mnt_flags("/mnt/A/AA");
894 ASSERT_EQ(new_flags, expected_flags);
896 new_flags = read_mnt_flags("/mnt/A/AA/B");
897 ASSERT_EQ(new_flags, expected_flags);
899 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
900 ASSERT_EQ(new_flags, expected_flags);
903 TEST_F(mount_setattr, multi_threaded)
905 int i, j, nthreads, ret = 0;
906 unsigned int old_flags = 0, new_flags = 0, expected_flags = 0;
907 pthread_attr_t pattr;
908 pthread_t threads[DEFAULT_THREADS];
910 if (!mount_setattr_supported())
911 SKIP(return, "mount_setattr syscall not supported");
913 old_flags = read_mnt_flags("/mnt/A");
914 ASSERT_GT(old_flags, 0);
916 /* Try to change mount options from multiple threads. */
917 nthreads = get_nprocs_conf();
918 if (nthreads > DEFAULT_THREADS)
919 nthreads = DEFAULT_THREADS;
921 pthread_attr_init(&pattr);
922 for (i = 0; i < nthreads; i++)
923 ASSERT_EQ(pthread_create(&threads[i], &pattr, mount_setattr_thread, NULL), 0);
925 for (j = 0; j < i; j++) {
928 EXPECT_EQ(pthread_join(threads[j], &retptr), 0);
930 ret += ptr_to_int(retptr);
933 pthread_attr_destroy(&pattr);
937 expected_flags = old_flags;
938 expected_flags |= MS_RDONLY;
939 expected_flags |= MS_NOSUID;
940 new_flags = read_mnt_flags("/mnt/A");
941 ASSERT_EQ(new_flags, expected_flags);
943 ASSERT_EQ(is_shared_mount("/mnt/A"), true);
945 new_flags = read_mnt_flags("/mnt/A/AA");
946 ASSERT_EQ(new_flags, expected_flags);
948 ASSERT_EQ(is_shared_mount("/mnt/A/AA"), true);
950 new_flags = read_mnt_flags("/mnt/A/AA/B");
951 ASSERT_EQ(new_flags, expected_flags);
953 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B"), true);
955 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
956 ASSERT_EQ(new_flags, expected_flags);
958 ASSERT_EQ(is_shared_mount("/mnt/A/AA/B/BB"), true);
961 TEST_F(mount_setattr, wrong_user_namespace)
964 struct mount_attr attr = {
965 .attr_set = MOUNT_ATTR_RDONLY,
968 if (!mount_setattr_supported())
969 SKIP(return, "mount_setattr syscall not supported");
971 EXPECT_EQ(create_and_enter_userns(), 0);
972 ret = sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr));
974 ASSERT_EQ(errno, EPERM);
977 TEST_F(mount_setattr, wrong_mount_namespace)
980 struct mount_attr attr = {
981 .attr_set = MOUNT_ATTR_RDONLY,
984 if (!mount_setattr_supported())
985 SKIP(return, "mount_setattr syscall not supported");
987 fd = open("/mnt/A", O_DIRECTORY | O_CLOEXEC);
990 ASSERT_EQ(unshare(CLONE_NEWNS), 0);
992 ret = sys_mount_setattr(fd, "", AT_EMPTY_PATH | AT_RECURSIVE, &attr, sizeof(attr));
994 ASSERT_EQ(errno, EINVAL);
997 FIXTURE(mount_setattr_idmapped) {
1000 FIXTURE_SETUP(mount_setattr_idmapped)
1002 int img_fd = -EBADF;
1004 ASSERT_EQ(unshare(CLONE_NEWNS), 0);
1006 ASSERT_EQ(mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0), 0);
1008 (void)umount2("/mnt", MNT_DETACH);
1009 (void)umount2("/tmp", MNT_DETACH);
1011 ASSERT_EQ(mount("testing", "/tmp", "tmpfs", MS_NOATIME | MS_NODEV,
1012 "size=100000,mode=700"), 0);
1014 ASSERT_EQ(mkdir("/tmp/B", 0777), 0);
1015 ASSERT_EQ(mknodat(-EBADF, "/tmp/B/b", S_IFREG | 0644, 0), 0);
1016 ASSERT_EQ(chown("/tmp/B/b", 0, 0), 0);
1018 ASSERT_EQ(mount("testing", "/tmp/B", "tmpfs", MS_NOATIME | MS_NODEV,
1019 "size=100000,mode=700"), 0);
1021 ASSERT_EQ(mkdir("/tmp/B/BB", 0777), 0);
1022 ASSERT_EQ(mknodat(-EBADF, "/tmp/B/BB/b", S_IFREG | 0644, 0), 0);
1023 ASSERT_EQ(chown("/tmp/B/BB/b", 0, 0), 0);
1025 ASSERT_EQ(mount("testing", "/tmp/B/BB", "tmpfs", MS_NOATIME | MS_NODEV,
1026 "size=100000,mode=700"), 0);
1028 ASSERT_EQ(mount("testing", "/mnt", "tmpfs", MS_NOATIME | MS_NODEV,
1029 "size=100000,mode=700"), 0);
1031 ASSERT_EQ(mkdir("/mnt/A", 0777), 0);
1033 ASSERT_EQ(mount("testing", "/mnt/A", "tmpfs", MS_NOATIME | MS_NODEV,
1034 "size=100000,mode=700"), 0);
1036 ASSERT_EQ(mkdir("/mnt/A/AA", 0777), 0);
1038 ASSERT_EQ(mount("/tmp", "/mnt/A/AA", NULL, MS_BIND | MS_REC, NULL), 0);
1040 ASSERT_EQ(mkdir("/mnt/B", 0777), 0);
1042 ASSERT_EQ(mount("testing", "/mnt/B", "ramfs",
1043 MS_NOATIME | MS_NODEV | MS_NOSUID, 0), 0);
1045 ASSERT_EQ(mkdir("/mnt/B/BB", 0777), 0);
1047 ASSERT_EQ(mount("testing", "/tmp/B/BB", "devpts",
1048 MS_RELATIME | MS_NOEXEC | MS_RDONLY, 0), 0);
1050 ASSERT_EQ(mkdir("/mnt/C", 0777), 0);
1051 ASSERT_EQ(mkdir("/mnt/D", 0777), 0);
1052 img_fd = openat(-EBADF, "/mnt/C/ext4.img", O_CREAT | O_WRONLY, 0600);
1053 ASSERT_GE(img_fd, 0);
1054 ASSERT_EQ(ftruncate(img_fd, 1024 * 2048), 0);
1055 ASSERT_EQ(system("mkfs.ext4 -q /mnt/C/ext4.img"), 0);
1056 ASSERT_EQ(system("mount -o loop -t ext4 /mnt/C/ext4.img /mnt/D/"), 0);
1057 ASSERT_EQ(close(img_fd), 0);
1060 FIXTURE_TEARDOWN(mount_setattr_idmapped)
1062 (void)umount2("/mnt/A", MNT_DETACH);
1063 (void)umount2("/tmp", MNT_DETACH);
1067 * Validate that negative fd values are rejected.
1069 TEST_F(mount_setattr_idmapped, invalid_fd_negative)
1071 struct mount_attr attr = {
1072 .attr_set = MOUNT_ATTR_IDMAP,
1073 .userns_fd = -EBADF,
1076 if (!mount_setattr_supported())
1077 SKIP(return, "mount_setattr syscall not supported");
1079 ASSERT_NE(sys_mount_setattr(-1, "/", 0, &attr, sizeof(attr)), 0) {
1080 TH_LOG("failure: created idmapped mount with negative fd");
1085 * Validate that excessively large fd values are rejected.
1087 TEST_F(mount_setattr_idmapped, invalid_fd_large)
1089 struct mount_attr attr = {
1090 .attr_set = MOUNT_ATTR_IDMAP,
1091 .userns_fd = INT64_MAX,
1094 if (!mount_setattr_supported())
1095 SKIP(return, "mount_setattr syscall not supported");
1097 ASSERT_NE(sys_mount_setattr(-1, "/", 0, &attr, sizeof(attr)), 0) {
1098 TH_LOG("failure: created idmapped mount with too large fd value");
1103 * Validate that closed fd values are rejected.
1105 TEST_F(mount_setattr_idmapped, invalid_fd_closed)
1108 struct mount_attr attr = {
1109 .attr_set = MOUNT_ATTR_IDMAP,
1112 if (!mount_setattr_supported())
1113 SKIP(return, "mount_setattr syscall not supported");
1115 fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
1117 ASSERT_GE(close(fd), 0);
1119 attr.userns_fd = fd;
1120 ASSERT_NE(sys_mount_setattr(-1, "/", 0, &attr, sizeof(attr)), 0) {
1121 TH_LOG("failure: created idmapped mount with closed fd");
1126 * Validate that the initial user namespace is rejected.
1128 TEST_F(mount_setattr_idmapped, invalid_fd_initial_userns)
1130 int open_tree_fd = -EBADF;
1131 struct mount_attr attr = {
1132 .attr_set = MOUNT_ATTR_IDMAP,
1135 if (!mount_setattr_supported())
1136 SKIP(return, "mount_setattr syscall not supported");
1138 open_tree_fd = sys_open_tree(-EBADF, "/mnt/D",
1140 AT_SYMLINK_NOFOLLOW |
1141 OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE);
1142 ASSERT_GE(open_tree_fd, 0);
1144 attr.userns_fd = open("/proc/1/ns/user", O_RDONLY | O_CLOEXEC);
1145 ASSERT_GE(attr.userns_fd, 0);
1146 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0);
1147 ASSERT_EQ(errno, EPERM);
1148 ASSERT_EQ(close(attr.userns_fd), 0);
1149 ASSERT_EQ(close(open_tree_fd), 0);
1152 static int map_ids(pid_t pid, unsigned long nsid, unsigned long hostid,
1153 unsigned long range)
1155 char map[100], procfile[256];
1157 snprintf(procfile, sizeof(procfile), "/proc/%d/uid_map", pid);
1158 snprintf(map, sizeof(map), "%lu %lu %lu", nsid, hostid, range);
1159 if (write_file(procfile, map, strlen(map)))
1163 snprintf(procfile, sizeof(procfile), "/proc/%d/gid_map", pid);
1164 snprintf(map, sizeof(map), "%lu %lu %lu", nsid, hostid, range);
1165 if (write_file(procfile, map, strlen(map)))
1171 #define __STACK_SIZE (8 * 1024 * 1024)
1172 static pid_t do_clone(int (*fn)(void *), void *arg, int flags)
1176 stack = malloc(__STACK_SIZE);
1181 return __clone2(fn, stack, __STACK_SIZE, flags | SIGCHLD, arg, NULL);
1183 return clone(fn, stack + __STACK_SIZE, flags | SIGCHLD, arg, NULL);
1187 static int get_userns_fd_cb(void *data)
1189 return kill(getpid(), SIGSTOP);
1192 static int wait_for_pid(pid_t pid)
1197 ret = waitpid(pid, &status, 0);
1205 if (!WIFEXITED(status))
1208 return WEXITSTATUS(status);
1211 static int get_userns_fd(unsigned long nsid, unsigned long hostid, unsigned long range)
1217 pid = do_clone(get_userns_fd_cb, NULL, CLONE_NEWUSER);
1221 ret = map_ids(pid, nsid, hostid, range);
1225 snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
1226 ret = open(path, O_RDONLY | O_CLOEXEC);
1233 * Validate that an attached mount in our mount namespace cannot be idmapped.
1234 * (The kernel enforces that the mount's mount namespace and the caller's mount
1237 TEST_F(mount_setattr_idmapped, attached_mount_inside_current_mount_namespace)
1239 int open_tree_fd = -EBADF;
1240 struct mount_attr attr = {
1241 .attr_set = MOUNT_ATTR_IDMAP,
1244 if (!mount_setattr_supported())
1245 SKIP(return, "mount_setattr syscall not supported");
1247 open_tree_fd = sys_open_tree(-EBADF, "/mnt/D",
1250 AT_SYMLINK_NOFOLLOW |
1252 ASSERT_GE(open_tree_fd, 0);
1254 attr.userns_fd = get_userns_fd(0, 10000, 10000);
1255 ASSERT_GE(attr.userns_fd, 0);
1256 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0);
1257 ASSERT_EQ(close(attr.userns_fd), 0);
1258 ASSERT_EQ(close(open_tree_fd), 0);
1262 * Validate that idmapping a mount is rejected if the mount's mount namespace
1263 * and our mount namespace don't match.
1264 * (The kernel enforces that the mount's mount namespace and the caller's mount
1267 TEST_F(mount_setattr_idmapped, attached_mount_outside_current_mount_namespace)
1269 int open_tree_fd = -EBADF;
1270 struct mount_attr attr = {
1271 .attr_set = MOUNT_ATTR_IDMAP,
1274 if (!mount_setattr_supported())
1275 SKIP(return, "mount_setattr syscall not supported");
1277 open_tree_fd = sys_open_tree(-EBADF, "/mnt/D",
1280 AT_SYMLINK_NOFOLLOW |
1282 ASSERT_GE(open_tree_fd, 0);
1284 ASSERT_EQ(unshare(CLONE_NEWNS), 0);
1286 attr.userns_fd = get_userns_fd(0, 10000, 10000);
1287 ASSERT_GE(attr.userns_fd, 0);
1288 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr,
1290 ASSERT_EQ(close(attr.userns_fd), 0);
1291 ASSERT_EQ(close(open_tree_fd), 0);
1295 * Validate that an attached mount in our mount namespace can be idmapped.
1297 TEST_F(mount_setattr_idmapped, detached_mount_inside_current_mount_namespace)
1299 int open_tree_fd = -EBADF;
1300 struct mount_attr attr = {
1301 .attr_set = MOUNT_ATTR_IDMAP,
1304 if (!mount_setattr_supported())
1305 SKIP(return, "mount_setattr syscall not supported");
1307 open_tree_fd = sys_open_tree(-EBADF, "/mnt/D",
1310 AT_SYMLINK_NOFOLLOW |
1313 ASSERT_GE(open_tree_fd, 0);
1315 /* Changing mount properties on a detached mount. */
1316 attr.userns_fd = get_userns_fd(0, 10000, 10000);
1317 ASSERT_GE(attr.userns_fd, 0);
1318 ASSERT_EQ(sys_mount_setattr(open_tree_fd, "",
1319 AT_EMPTY_PATH, &attr, sizeof(attr)), 0);
1320 ASSERT_EQ(close(attr.userns_fd), 0);
1321 ASSERT_EQ(close(open_tree_fd), 0);
1325 * Validate that a detached mount not in our mount namespace can be idmapped.
1327 TEST_F(mount_setattr_idmapped, detached_mount_outside_current_mount_namespace)
1329 int open_tree_fd = -EBADF;
1330 struct mount_attr attr = {
1331 .attr_set = MOUNT_ATTR_IDMAP,
1334 if (!mount_setattr_supported())
1335 SKIP(return, "mount_setattr syscall not supported");
1337 open_tree_fd = sys_open_tree(-EBADF, "/mnt/D",
1340 AT_SYMLINK_NOFOLLOW |
1343 ASSERT_GE(open_tree_fd, 0);
1345 ASSERT_EQ(unshare(CLONE_NEWNS), 0);
1347 /* Changing mount properties on a detached mount. */
1348 attr.userns_fd = get_userns_fd(0, 10000, 10000);
1349 ASSERT_GE(attr.userns_fd, 0);
1350 ASSERT_EQ(sys_mount_setattr(open_tree_fd, "",
1351 AT_EMPTY_PATH, &attr, sizeof(attr)), 0);
1352 ASSERT_EQ(close(attr.userns_fd), 0);
1353 ASSERT_EQ(close(open_tree_fd), 0);
1357 * Validate that currently changing the idmapping of an idmapped mount fails.
1359 TEST_F(mount_setattr_idmapped, change_idmapping)
1361 int open_tree_fd = -EBADF;
1362 struct mount_attr attr = {
1363 .attr_set = MOUNT_ATTR_IDMAP,
1366 if (!mount_setattr_supported())
1367 SKIP(return, "mount_setattr syscall not supported");
1369 open_tree_fd = sys_open_tree(-EBADF, "/mnt/D",
1372 AT_SYMLINK_NOFOLLOW |
1375 ASSERT_GE(open_tree_fd, 0);
1377 attr.userns_fd = get_userns_fd(0, 10000, 10000);
1378 ASSERT_GE(attr.userns_fd, 0);
1379 ASSERT_EQ(sys_mount_setattr(open_tree_fd, "",
1380 AT_EMPTY_PATH, &attr, sizeof(attr)), 0);
1381 ASSERT_EQ(close(attr.userns_fd), 0);
1383 /* Change idmapping on a detached mount that is already idmapped. */
1384 attr.userns_fd = get_userns_fd(0, 20000, 10000);
1385 ASSERT_GE(attr.userns_fd, 0);
1386 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0);
1387 ASSERT_EQ(close(attr.userns_fd), 0);
1388 ASSERT_EQ(close(open_tree_fd), 0);
1391 static bool expected_uid_gid(int dfd, const char *path, int flags,
1392 uid_t expected_uid, gid_t expected_gid)
1397 ret = fstatat(dfd, path, &st, flags);
1401 return st.st_uid == expected_uid && st.st_gid == expected_gid;
1404 TEST_F(mount_setattr_idmapped, idmap_mount_tree_invalid)
1406 int open_tree_fd = -EBADF;
1407 struct mount_attr attr = {
1408 .attr_set = MOUNT_ATTR_IDMAP,
1411 if (!mount_setattr_supported())
1412 SKIP(return, "mount_setattr syscall not supported");
1414 ASSERT_EQ(expected_uid_gid(-EBADF, "/tmp/B/b", 0, 0, 0), 0);
1415 ASSERT_EQ(expected_uid_gid(-EBADF, "/tmp/B/BB/b", 0, 0, 0), 0);
1417 open_tree_fd = sys_open_tree(-EBADF, "/mnt/A",
1421 AT_SYMLINK_NOFOLLOW |
1424 ASSERT_GE(open_tree_fd, 0);
1426 attr.userns_fd = get_userns_fd(0, 10000, 10000);
1427 ASSERT_GE(attr.userns_fd, 0);
1428 ASSERT_NE(sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr)), 0);
1429 ASSERT_EQ(close(attr.userns_fd), 0);
1430 ASSERT_EQ(close(open_tree_fd), 0);
1432 ASSERT_EQ(expected_uid_gid(-EBADF, "/tmp/B/b", 0, 0, 0), 0);
1433 ASSERT_EQ(expected_uid_gid(-EBADF, "/tmp/B/BB/b", 0, 0, 0), 0);
1434 ASSERT_EQ(expected_uid_gid(open_tree_fd, "B/b", 0, 0, 0), 0);
1435 ASSERT_EQ(expected_uid_gid(open_tree_fd, "B/BB/b", 0, 0, 0), 0);
1438 TEST_F(mount_setattr, mount_attr_nosymfollow)
1441 unsigned int old_flags = 0, new_flags = 0, expected_flags = 0;
1442 struct mount_attr attr = {
1443 .attr_set = MOUNT_ATTR_NOSYMFOLLOW,
1446 if (!mount_setattr_supported())
1447 SKIP(return, "mount_setattr syscall not supported");
1449 fd = open(NOSYMFOLLOW_SYMLINK, O_RDWR | O_CLOEXEC);
1451 ASSERT_EQ(close(fd), 0);
1453 old_flags = read_mnt_flags("/mnt/A");
1454 ASSERT_GT(old_flags, 0);
1456 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
1458 expected_flags = old_flags;
1459 expected_flags |= ST_NOSYMFOLLOW;
1461 new_flags = read_mnt_flags("/mnt/A");
1462 ASSERT_EQ(new_flags, expected_flags);
1464 new_flags = read_mnt_flags("/mnt/A/AA");
1465 ASSERT_EQ(new_flags, expected_flags);
1467 new_flags = read_mnt_flags("/mnt/A/AA/B");
1468 ASSERT_EQ(new_flags, expected_flags);
1470 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
1471 ASSERT_EQ(new_flags, expected_flags);
1473 fd = open(NOSYMFOLLOW_SYMLINK, O_RDWR | O_CLOEXEC);
1475 ASSERT_EQ(errno, ELOOP);
1477 attr.attr_set &= ~MOUNT_ATTR_NOSYMFOLLOW;
1478 attr.attr_clr |= MOUNT_ATTR_NOSYMFOLLOW;
1480 ASSERT_EQ(sys_mount_setattr(-1, "/mnt/A", AT_RECURSIVE, &attr, sizeof(attr)), 0);
1482 expected_flags &= ~ST_NOSYMFOLLOW;
1483 new_flags = read_mnt_flags("/mnt/A");
1484 ASSERT_EQ(new_flags, expected_flags);
1486 new_flags = read_mnt_flags("/mnt/A/AA");
1487 ASSERT_EQ(new_flags, expected_flags);
1489 new_flags = read_mnt_flags("/mnt/A/AA/B");
1490 ASSERT_EQ(new_flags, expected_flags);
1492 new_flags = read_mnt_flags("/mnt/A/AA/B/BB");
1493 ASSERT_EQ(new_flags, expected_flags);
1495 fd = open(NOSYMFOLLOW_SYMLINK, O_RDWR | O_CLOEXEC);
1497 ASSERT_EQ(close(fd), 0);