Loosen fchmodat AT_SYMLINK_NOFOLLOW test on symlink.

It has been reported in b2/19657449 and b2/19381040 that fchmodat
AT_SYMLINK_NOFOLLOW operation on symlink can succeed. It seems to be
controlled by kernel(version or configuration) or user configuration
whether chmod is allowed on symlinks. Unless we can disable chmod on
symlinks in bionic explicitly, we can not guarantee that the test can
pass. But it seems reasonable to allow chmod on symlink if kernel allows
to. So We prefer to loosen the test here, accepting both success and
failure when doing chmod operation on symlinks.

Bug: 19657449
Bug: 19381040
Change-Id: I780e84f0b50d0412fbac9f1c240d07e984892a28
This commit is contained in:
Yabin Cui 2015-03-09 13:55:18 -07:00
parent e86a86f9f2
commit c6e5874a4c

View File

@ -138,13 +138,18 @@ TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_nonexistant_file) {
#endif
}
static void AssertFileModeEquals(mode_t expected_mode, const char* filename) {
struct stat sb;
ASSERT_EQ(0, stat(filename, &sb));
mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
ASSERT_EQ(expected_mode & mask, static_cast<mode_t>(sb.st_mode) & mask);
}
TEST(sys_stat, fchmodat_file) {
TemporaryFile tf;
struct stat sb;
ASSERT_EQ(0, fchmodat(AT_FDCWD, tf.filename, 0751, 0));
ASSERT_EQ(0, fstat(tf.fd, &sb));
ASSERT_TRUE(0751 == (sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)));
AssertFileModeEquals(0751, tf.filename);
}
TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_file) {
@ -153,11 +158,9 @@ TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_file) {
int result = fchmodat(AT_FDCWD, tf.filename, 0751, AT_SYMLINK_NOFOLLOW);
#if defined(__BIONIC__)
struct stat sb;
ASSERT_EQ(0, result);
ASSERT_EQ(0, errno);
ASSERT_EQ(0, fstat(tf.fd, &sb));
ASSERT_TRUE(0751 == (sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)));
AssertFileModeEquals(0751, tf.filename);
#else
// glibc 2.19 does not implement AT_SYMLINK_NOFOLLOW and always
// returns ENOTSUP
@ -169,14 +172,12 @@ TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_file) {
TEST(sys_stat, fchmodat_symlink) {
TemporaryFile tf;
char linkname[255];
struct stat sb;
snprintf(linkname, sizeof(linkname), "%s.link", tf.filename);
ASSERT_EQ(0, symlink(tf.filename, linkname));
ASSERT_EQ(0, fchmodat(AT_FDCWD, linkname, 0751, 0));
ASSERT_EQ(0, fstat(tf.fd, &sb));
ASSERT_TRUE(0751 == (sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)));
AssertFileModeEquals(0751, tf.filename);
unlink(linkname);
}
@ -194,29 +195,54 @@ TEST(sys_stat, fchmodat_dangling_symlink) {
unlink(linkname);
}
static void AssertSymlinkModeEquals(mode_t expected_mode, const char* linkname) {
struct stat sb;
ASSERT_EQ(0, fstatat(AT_FDCWD, linkname, &sb, AT_SYMLINK_NOFOLLOW));
mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
ASSERT_EQ(expected_mode & mask, static_cast<mode_t>(sb.st_mode) & mask);
}
TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_with_symlink) {
TemporaryFile tf;
char linkname[255];
struct stat tf_sb;
ASSERT_EQ(0, stat(tf.filename, &tf_sb));
char linkname[255];
snprintf(linkname, sizeof(linkname), "%s.link", tf.filename);
ASSERT_EQ(0, symlink(tf.filename, linkname));
ASSERT_EQ(-1, fchmodat(AT_FDCWD, linkname, 0751, AT_SYMLINK_NOFOLLOW));
ASSERT_EQ(ENOTSUP, errno);
int result = fchmodat(AT_FDCWD, linkname, 0751, AT_SYMLINK_NOFOLLOW);
// It depends on the kernel whether chmod operation on symlink is allowed.
if (result == 0) {
AssertSymlinkModeEquals(0751, linkname);
} else {
ASSERT_EQ(-1, result);
ASSERT_EQ(ENOTSUP, errno);
}
// Target file mode shouldn't be modified.
AssertFileModeEquals(tf_sb.st_mode, tf.filename);
unlink(linkname);
}
TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_with_dangling_symlink) {
TemporaryFile tf;
char linkname[255];
char target[255];
snprintf(linkname, sizeof(linkname), "%s.link", tf.filename);
snprintf(target, sizeof(target), "%s.doesnotexist", tf.filename);
ASSERT_EQ(0, symlink(target, linkname));
ASSERT_EQ(-1, fchmodat(AT_FDCWD, linkname, 0751, AT_SYMLINK_NOFOLLOW));
ASSERT_EQ(ENOTSUP, errno);
int result = fchmodat(AT_FDCWD, linkname, 0751, AT_SYMLINK_NOFOLLOW);
// It depends on the kernel whether chmod operation on symlink is allowed.
if (result == 0) {
AssertSymlinkModeEquals(0751, linkname);
} else {
ASSERT_EQ(-1, result);
ASSERT_EQ(ENOTSUP, errno);
}
unlink(linkname);
}