gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/unlink.cc (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <errno.h> 16 #include <fcntl.h> 17 #include <unistd.h> 18 19 #include "gtest/gtest.h" 20 #include "absl/strings/str_cat.h" 21 #include "test/util/capability_util.h" 22 #include "test/util/file_descriptor.h" 23 #include "test/util/fs_util.h" 24 #include "test/util/temp_path.h" 25 #include "test/util/test_util.h" 26 27 namespace gvisor { 28 namespace testing { 29 30 namespace { 31 32 TEST(UnlinkTest, IsDir) { 33 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 34 35 EXPECT_THAT(unlink(dir.path().c_str()), SyscallFailsWithErrno(EISDIR)); 36 } 37 38 TEST(UnlinkTest, DirNotEmpty) { 39 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 40 41 int fd; 42 std::string path = JoinPath(dir.path(), "ExistingFile"); 43 EXPECT_THAT(fd = open(path.c_str(), O_RDWR | O_CREAT, 0666), 44 SyscallSucceeds()); 45 EXPECT_THAT(close(fd), SyscallSucceeds()); 46 EXPECT_THAT(rmdir(dir.path().c_str()), SyscallFailsWithErrno(ENOTEMPTY)); 47 } 48 49 TEST(UnlinkTest, Rmdir) { 50 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 51 EXPECT_THAT(rmdir(dir.path().c_str()), SyscallSucceeds()); 52 } 53 54 TEST(UnlinkTest, AtDir) { 55 int dirfd; 56 auto tmpdir = GetAbsoluteTestTmpdir(); 57 EXPECT_THAT(dirfd = open(tmpdir.c_str(), O_DIRECTORY, 0), SyscallSucceeds()); 58 59 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(tmpdir)); 60 auto dir_relpath = 61 ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(tmpdir, dir.path())); 62 EXPECT_THAT(unlinkat(dirfd, dir_relpath.c_str(), AT_REMOVEDIR), 63 SyscallSucceeds()); 64 ASSERT_THAT(close(dirfd), SyscallSucceeds()); 65 } 66 67 TEST(UnlinkTest, AtDirDegradedPermissions) { 68 // Drop capabilities that allow us to override file and directory permissions. 69 AutoCapability cap1(CAP_DAC_OVERRIDE, false); 70 AutoCapability cap2(CAP_DAC_READ_SEARCH, false); 71 72 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 73 74 int dirfd; 75 ASSERT_THAT(dirfd = open(dir.path().c_str(), O_DIRECTORY, 0), 76 SyscallSucceeds()); 77 78 std::string sub_dir = JoinPath(dir.path(), "NewDir"); 79 EXPECT_THAT(mkdir(sub_dir.c_str(), 0755), SyscallSucceeds()); 80 EXPECT_THAT(fchmod(dirfd, 0444), SyscallSucceeds()); 81 EXPECT_THAT(unlinkat(dirfd, "NewDir", AT_REMOVEDIR), 82 SyscallFailsWithErrno(EACCES)); 83 ASSERT_THAT(close(dirfd), SyscallSucceeds()); 84 } 85 86 // Files cannot be unlinked if the parent is not writable and executable. 87 TEST(UnlinkTest, ParentDegradedPermissions) { 88 // Drop capabilities that allow us to override file and directory permissions. 89 AutoCapability cap1(CAP_DAC_OVERRIDE, false); 90 AutoCapability cap2(CAP_DAC_READ_SEARCH, false); 91 92 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 93 auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); 94 95 ASSERT_THAT(chmod(dir.path().c_str(), 0000), SyscallSucceeds()); 96 97 struct stat st; 98 ASSERT_THAT(stat(file.path().c_str(), &st), SyscallFailsWithErrno(EACCES)); 99 ASSERT_THAT(unlinkat(AT_FDCWD, file.path().c_str(), 0), 100 SyscallFailsWithErrno(EACCES)); 101 102 // Non-existent files also return EACCES. 103 const std::string nonexist = JoinPath(dir.path(), "doesnotexist"); 104 ASSERT_THAT(stat(nonexist.c_str(), &st), SyscallFailsWithErrno(EACCES)); 105 ASSERT_THAT(unlinkat(AT_FDCWD, nonexist.c_str(), 0), 106 SyscallFailsWithErrno(EACCES)); 107 } 108 109 TEST(UnlinkTest, AtBad) { 110 int dirfd; 111 EXPECT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_DIRECTORY, 0), 112 SyscallSucceeds()); 113 114 // Try removing a directory as a file. 115 std::string path = JoinPath(GetAbsoluteTestTmpdir(), "NewDir"); 116 EXPECT_THAT(mkdir(path.c_str(), 0755), SyscallSucceeds()); 117 EXPECT_THAT(unlinkat(dirfd, "NewDir", 0), SyscallFailsWithErrno(EISDIR)); 118 EXPECT_THAT(unlinkat(dirfd, "NewDir", AT_REMOVEDIR), SyscallSucceeds()); 119 120 // Try removing a file as a directory. 121 int fd; 122 EXPECT_THAT(fd = openat(dirfd, "UnlinkAtFile", O_RDWR | O_CREAT, 0666), 123 SyscallSucceeds()); 124 EXPECT_THAT(unlinkat(dirfd, "UnlinkAtFile", AT_REMOVEDIR), 125 SyscallFailsWithErrno(ENOTDIR)); 126 EXPECT_THAT(unlinkat(dirfd, "UnlinkAtFile/", 0), 127 SyscallFailsWithErrno(ENOTDIR)); 128 ASSERT_THAT(close(fd), SyscallSucceeds()); 129 EXPECT_THAT(unlinkat(dirfd, "UnlinkAtFile", 0), SyscallSucceeds()); 130 131 // Cleanup. 132 ASSERT_THAT(close(dirfd), SyscallSucceeds()); 133 } 134 135 TEST(UnlinkTest, AbsTmpFile) { 136 int fd; 137 std::string path = JoinPath(GetAbsoluteTestTmpdir(), "ExistingFile"); 138 EXPECT_THAT(fd = open(path.c_str(), O_RDWR | O_CREAT, 0666), 139 SyscallSucceeds()); 140 EXPECT_THAT(close(fd), SyscallSucceeds()); 141 EXPECT_THAT(unlink(path.c_str()), SyscallSucceeds()); 142 } 143 144 TEST(UnlinkTest, TooLongName) { 145 EXPECT_THAT(unlink(std::vector<char>(16384, '0').data()), 146 SyscallFailsWithErrno(ENAMETOOLONG)); 147 } 148 149 TEST(UnlinkTest, BadNamePtr) { 150 EXPECT_THAT(unlink(reinterpret_cast<char*>(1)), 151 SyscallFailsWithErrno(EFAULT)); 152 } 153 154 TEST(UnlinkTest, AtFile) { 155 const FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE( 156 Open(GetAbsoluteTestTmpdir(), O_DIRECTORY, 0666)); 157 int fd; 158 EXPECT_THAT(fd = openat(dirfd.get(), "UnlinkAtFile", O_RDWR | O_CREAT, 0666), 159 SyscallSucceeds()); 160 EXPECT_THAT(close(fd), SyscallSucceeds()); 161 EXPECT_THAT(unlinkat(dirfd.get(), "UnlinkAtFile", 0), SyscallSucceeds()); 162 } 163 164 TEST(UnlinkTest, OpenFile) { 165 // We can't save unlinked file unless they are on tmpfs. 166 const DisableSave ds; 167 auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 168 int fd; 169 EXPECT_THAT(fd = open(file.path().c_str(), O_RDWR, 0666), SyscallSucceeds()); 170 EXPECT_THAT(unlink(file.path().c_str()), SyscallSucceeds()); 171 EXPECT_THAT(close(fd), SyscallSucceeds()); 172 } 173 174 TEST(UnlinkTest, CannotRemoveDots) { 175 auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 176 const std::string self = JoinPath(file.path(), "."); 177 ASSERT_THAT(unlink(self.c_str()), SyscallFailsWithErrno(ENOTDIR)); 178 const std::string parent = JoinPath(file.path(), ".."); 179 ASSERT_THAT(unlink(parent.c_str()), SyscallFailsWithErrno(ENOTDIR)); 180 } 181 182 TEST(UnlinkTest, CannotRemoveRoot) { 183 ASSERT_THAT(unlinkat(-1, "/", AT_REMOVEDIR), SyscallFailsWithErrno(EBUSY)); 184 } 185 186 TEST(UnlinkTest, CannotRemoveRootWithAtDir) { 187 const FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE( 188 Open(GetAbsoluteTestTmpdir(), O_DIRECTORY, 0666)); 189 ASSERT_THAT(unlinkat(dirfd.get(), "/", AT_REMOVEDIR), 190 SyscallFailsWithErrno(EBUSY)); 191 } 192 193 TEST(RmdirTest, CannotRemoveDots) { 194 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 195 const std::string self = JoinPath(dir.path(), "."); 196 ASSERT_THAT(rmdir(self.c_str()), SyscallFailsWithErrno(EINVAL)); 197 const std::string parent = JoinPath(dir.path(), ".."); 198 ASSERT_THAT(rmdir(parent.c_str()), SyscallFailsWithErrno(ENOTEMPTY)); 199 } 200 201 TEST(RmdirTest, CanRemoveWithTrailingSlashes) { 202 auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 203 const std::string slash = absl::StrCat(dir1.path(), "/"); 204 ASSERT_THAT(rmdir(slash.c_str()), SyscallSucceeds()); 205 auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 206 const std::string slashslash = absl::StrCat(dir2.path(), "//"); 207 ASSERT_THAT(rmdir(slashslash.c_str()), SyscallSucceeds()); 208 } 209 210 TEST(UnlinkTest, UnlinkAtEmptyPath) { 211 auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 212 213 auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); 214 auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); 215 EXPECT_THAT(unlinkat(fd.get(), "", 0), SyscallFailsWithErrno(ENOENT)); 216 217 auto dirInDir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); 218 auto dirFD = ASSERT_NO_ERRNO_AND_VALUE( 219 Open(dirInDir.path(), O_RDONLY | O_DIRECTORY, 0666)); 220 EXPECT_THAT(unlinkat(dirFD.get(), "", AT_REMOVEDIR), 221 SyscallFailsWithErrno(ENOENT)); 222 } 223 224 } // namespace 225 226 } // namespace testing 227 } // namespace gvisor