gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/inotify.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 <fcntl.h> 16 #include <libgen.h> 17 #include <sched.h> 18 #include <sys/epoll.h> 19 #include <sys/inotify.h> 20 #include <sys/ioctl.h> 21 #include <sys/sendfile.h> 22 #include <sys/time.h> 23 #include <sys/xattr.h> 24 25 #include <atomic> 26 #include <list> 27 #include <string> 28 #include <vector> 29 30 #include "absl/strings/str_cat.h" 31 #include "absl/strings/str_format.h" 32 #include "absl/strings/str_join.h" 33 #include "absl/synchronization/mutex.h" 34 #include "absl/time/clock.h" 35 #include "absl/time/time.h" 36 #include "test/util/epoll_util.h" 37 #include "test/util/file_descriptor.h" 38 #include "test/util/fs_util.h" 39 #include "test/util/multiprocess_util.h" 40 #include "test/util/posix_error.h" 41 #include "test/util/temp_path.h" 42 #include "test/util/test_util.h" 43 #include "test/util/thread_util.h" 44 45 namespace gvisor { 46 namespace testing { 47 namespace { 48 49 using ::absl::StreamFormat; 50 using ::absl::StrFormat; 51 52 constexpr int kBufSize = 1024; 53 54 // C++-friendly version of struct inotify_event. 55 struct Event { 56 int32_t wd; 57 uint32_t mask; 58 uint32_t cookie; 59 uint32_t len; 60 std::string name; 61 62 Event(uint32_t mask, int32_t wd, absl::string_view name, uint32_t cookie) 63 : wd(wd), 64 mask(mask), 65 cookie(cookie), 66 len(name.size()), 67 name(std::string(name)) {} 68 Event(uint32_t mask, int32_t wd, absl::string_view name) 69 : Event(mask, wd, name, 0) {} 70 Event(uint32_t mask, int32_t wd) : Event(mask, wd, "", 0) {} 71 Event() : Event(0, 0, "", 0) {} 72 }; 73 74 // Prints the symbolic name for a struct inotify_event's 'mask' field. 75 std::string FlagString(uint32_t flags) { 76 std::vector<std::string> names; 77 78 #define EMIT(target) \ 79 if (flags & target) { \ 80 names.push_back(#target); \ 81 flags &= ~target; \ 82 } 83 84 EMIT(IN_ACCESS); 85 EMIT(IN_ATTRIB); 86 EMIT(IN_CLOSE_WRITE); 87 EMIT(IN_CLOSE_NOWRITE); 88 EMIT(IN_CREATE); 89 EMIT(IN_DELETE); 90 EMIT(IN_DELETE_SELF); 91 EMIT(IN_MODIFY); 92 EMIT(IN_MOVE_SELF); 93 EMIT(IN_MOVED_FROM); 94 EMIT(IN_MOVED_TO); 95 EMIT(IN_OPEN); 96 97 EMIT(IN_DONT_FOLLOW); 98 EMIT(IN_EXCL_UNLINK); 99 EMIT(IN_ONESHOT); 100 EMIT(IN_ONLYDIR); 101 102 EMIT(IN_IGNORED); 103 EMIT(IN_ISDIR); 104 EMIT(IN_Q_OVERFLOW); 105 EMIT(IN_UNMOUNT); 106 107 #undef EMIT 108 109 // If we have anything left over at the end, print it as a hex value. 110 if (flags) { 111 names.push_back(absl::StrCat("0x", absl::Hex(flags))); 112 } 113 114 return absl::StrJoin(names, "|"); 115 } 116 117 std::string DumpEvent(const Event& event) { 118 return StrFormat( 119 "%s, wd=%d%s%s", FlagString(event.mask), event.wd, 120 (event.len > 0) ? StrFormat(", name=%s", event.name) : "", 121 (event.cookie > 0) ? StrFormat(", cookie=%ud", event.cookie) : ""); 122 } 123 124 std::string DumpEvents(const std::vector<Event>& events, int indent_level) { 125 std::stringstream ss; 126 ss << StreamFormat("%d event%s:\n", events.size(), 127 (events.size() > 1) ? "s" : ""); 128 int i = 0; 129 for (const Event& ev : events) { 130 ss << StreamFormat("%sevents[%d]: %s\n", std::string(indent_level, '\t'), 131 i++, DumpEvent(ev)); 132 } 133 return ss.str(); 134 } 135 136 // A matcher which takes an expected list of events to match against another 137 // list of inotify events, in order. This is similar to the ElementsAre matcher, 138 // but displays more informative messages on mismatch. 139 class EventsAreMatcher 140 : public ::testing::MatcherInterface<std::vector<Event>> { 141 public: 142 explicit EventsAreMatcher(std::vector<Event> references) 143 : references_(std::move(references)) {} 144 145 bool MatchAndExplain( 146 std::vector<Event> events, 147 ::testing::MatchResultListener* const listener) const override { 148 if (references_.size() != events.size()) { 149 *listener << StreamFormat("\n\tCount mismatch, got %s", 150 DumpEvents(events, 2)); 151 return false; 152 } 153 154 bool success = true; 155 for (unsigned int i = 0; i < references_.size(); ++i) { 156 const Event& reference = references_[i]; 157 const Event& target = events[i]; 158 159 if (target.mask != reference.mask || target.wd != reference.wd || 160 target.name != reference.name || target.cookie != reference.cookie) { 161 *listener << StreamFormat("\n\tMismatch at index %d, want %s, got %s,", 162 i, DumpEvent(reference), DumpEvent(target)); 163 success = false; 164 } 165 } 166 167 if (!success) { 168 *listener << StreamFormat("\n\tIn total of %s", DumpEvents(events, 2)); 169 } 170 return success; 171 } 172 173 void DescribeTo(::std::ostream* const os) const override { 174 *os << StreamFormat("%s", DumpEvents(references_, 1)); 175 } 176 177 void DescribeNegationTo(::std::ostream* const os) const override { 178 *os << StreamFormat("mismatch from %s", DumpEvents(references_, 1)); 179 } 180 181 private: 182 std::vector<Event> references_; 183 }; 184 185 ::testing::Matcher<std::vector<Event>> Are(std::vector<Event> events) { 186 return MakeMatcher(new EventsAreMatcher(std::move(events))); 187 } 188 189 // Similar to the EventsAre matcher, but the order of events are ignored. 190 class UnorderedEventsAreMatcher 191 : public ::testing::MatcherInterface<std::vector<Event>> { 192 public: 193 explicit UnorderedEventsAreMatcher(std::vector<Event> references) 194 : references_(std::move(references)) {} 195 196 bool MatchAndExplain( 197 std::vector<Event> events, 198 ::testing::MatchResultListener* const listener) const override { 199 if (references_.size() != events.size()) { 200 *listener << StreamFormat("\n\tCount mismatch, got %s", 201 DumpEvents(events, 2)); 202 return false; 203 } 204 205 std::vector<Event> unmatched(references_); 206 207 for (const Event& candidate : events) { 208 for (auto it = unmatched.begin(); it != unmatched.end();) { 209 const Event& reference = *it; 210 if (candidate.mask == reference.mask && candidate.wd == reference.wd && 211 candidate.name == reference.name && 212 candidate.cookie == reference.cookie) { 213 it = unmatched.erase(it); 214 break; 215 } else { 216 ++it; 217 } 218 } 219 } 220 221 // Anything left unmatched? If so, the matcher fails. 222 if (!unmatched.empty()) { 223 *listener << StreamFormat("\n\tFailed to match %s", 224 DumpEvents(unmatched, 2)); 225 *listener << StreamFormat("\n\tIn total of %s", DumpEvents(events, 2)); 226 return false; 227 } 228 229 return true; 230 } 231 232 void DescribeTo(::std::ostream* const os) const override { 233 *os << StreamFormat("unordered %s", DumpEvents(references_, 1)); 234 } 235 236 void DescribeNegationTo(::std::ostream* const os) const override { 237 *os << StreamFormat("mismatch from unordered %s", 238 DumpEvents(references_, 1)); 239 } 240 241 private: 242 std::vector<Event> references_; 243 }; 244 245 ::testing::Matcher<std::vector<Event>> AreUnordered(std::vector<Event> events) { 246 return MakeMatcher(new UnorderedEventsAreMatcher(std::move(events))); 247 } 248 249 // Reads events from an inotify fd until either EOF, or read returns EAGAIN. 250 PosixErrorOr<std::vector<Event>> DrainEvents(int fd) { 251 std::vector<Event> events; 252 while (true) { 253 int events_size = 0; 254 if (ioctl(fd, FIONREAD, &events_size) < 0) { 255 return PosixError(errno, "ioctl(FIONREAD) failed on inotify fd"); 256 } 257 // Deliberately use a buffer that is larger than necessary, expecting to 258 // only read events_size bytes. 259 std::vector<char> buf(events_size + kBufSize, 0); 260 const ssize_t readlen = read(fd, buf.data(), buf.size()); 261 MaybeSave(); 262 // Read error? 263 if (readlen < 0) { 264 if (errno == EAGAIN) { 265 // If EAGAIN, no more events at the moment. Return what we have so far. 266 return events; 267 } 268 // Some other read error. Return an error. Right now if we encounter this 269 // after already reading some events, they get lost. However, we don't 270 // expect to see any error, and the calling test will fail immediately if 271 // we signal an error anyways, so this is acceptable. 272 return PosixError(errno, "read() failed on inotify fd"); 273 } 274 if (readlen < static_cast<int>(sizeof(struct inotify_event))) { 275 // Impossibly short read. 276 return PosixError( 277 EIO, 278 "read() didn't return enough data represent even a single event"); 279 } 280 if (readlen != events_size) { 281 return PosixError(EINVAL, absl::StrCat("read ", readlen, 282 " bytes, expected ", events_size)); 283 } 284 if (readlen == 0) { 285 // EOF. 286 return events; 287 } 288 289 // Normal read. 290 const char* cursor = buf.data(); 291 while (cursor < (buf.data() + readlen)) { 292 struct inotify_event event = {}; 293 memcpy(&event, cursor, sizeof(struct inotify_event)); 294 295 Event ev; 296 ev.wd = event.wd; 297 ev.mask = event.mask; 298 ev.cookie = event.cookie; 299 ev.len = event.len; 300 if (event.len > 0) { 301 TEST_CHECK(static_cast<int>(sizeof(struct inotify_event) + event.len) <= 302 readlen); 303 ev.name = std::string(cursor + 304 offsetof(struct inotify_event, name)); // NOLINT 305 // Name field should always be smaller than event.len, otherwise we have 306 // a buffer overflow. The two sizes aren't equal because the string 307 // constructor will stop at the first null byte, while event.name may be 308 // padded up to event.len using multiple null bytes. 309 TEST_CHECK(ev.name.size() <= event.len); 310 } 311 312 events.push_back(ev); 313 cursor += sizeof(struct inotify_event) + event.len; 314 } 315 } 316 } 317 318 PosixErrorOr<FileDescriptor> InotifyInit1(int flags) { 319 int fd = inotify_init1(flags); 320 if (fd < 0) { 321 return PosixError(errno, "inotify_init1() failed"); 322 } 323 return FileDescriptor(fd); 324 } 325 326 PosixErrorOr<int> InotifyAddWatch(int fd, const std::string& path, 327 uint32_t mask) { 328 int wd = inotify_add_watch(fd, path.c_str(), mask); 329 if (wd < 0) { 330 return PosixError(errno, "inotify_add_watch() failed"); 331 } 332 return wd; 333 } 334 335 TEST(Inotify, IllegalSeek) { 336 // TODO: b/298787679 - this test fails on 6.0+ kernels. 337 SKIP_IF(!IsRunningOnGvisor()); 338 339 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(0)); 340 EXPECT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); 341 } 342 343 TEST(Inotify, IllegalPread) { 344 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(0)); 345 int val; 346 EXPECT_THAT(pread(fd.get(), &val, sizeof(val), 0), 347 SyscallFailsWithErrno(ESPIPE)); 348 } 349 350 TEST(Inotify, IllegalPwrite) { 351 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(0)); 352 EXPECT_THAT(pwrite(fd.get(), "x", 1, 0), SyscallFailsWithErrno(ESPIPE)); 353 } 354 355 TEST(Inotify, IllegalWrite) { 356 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(0)); 357 int val = 0; 358 EXPECT_THAT(write(fd.get(), &val, sizeof(val)), SyscallFailsWithErrno(EBADF)); 359 } 360 361 TEST(Inotify, InitFlags) { 362 EXPECT_THAT(inotify_init1(IN_NONBLOCK | IN_CLOEXEC), SyscallSucceeds()); 363 EXPECT_THAT(inotify_init1(12345), SyscallFailsWithErrno(EINVAL)); 364 } 365 366 TEST(Inotify, NonBlockingReadReturnsEagain) { 367 const FileDescriptor fd = 368 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 369 std::vector<char> buf(kBufSize, 0); 370 371 // The read below should return fail with EAGAIN because there is no data to 372 // read and we've specified IN_NONBLOCK. We're guaranteed that there is no 373 // data to read because we haven't registered any watches yet. 374 EXPECT_THAT(read(fd.get(), buf.data(), buf.size()), 375 SyscallFailsWithErrno(EAGAIN)); 376 } 377 378 TEST(Inotify, AddWatchOnInvalidFdFails) { 379 // Garbage fd. 380 EXPECT_THAT( 381 inotify_add_watch(-1, GetAbsoluteTestTmpdir().c_str(), IN_ALL_EVENTS), 382 SyscallFailsWithErrno(EBADF)); 383 EXPECT_THAT( 384 inotify_add_watch(1337, GetAbsoluteTestTmpdir().c_str(), IN_ALL_EVENTS), 385 SyscallFailsWithErrno(EBADF)); 386 387 // Non-inotify fds. 388 EXPECT_THAT( 389 inotify_add_watch(0, GetAbsoluteTestTmpdir().c_str(), IN_ALL_EVENTS), 390 SyscallFailsWithErrno(EINVAL)); 391 EXPECT_THAT( 392 inotify_add_watch(1, GetAbsoluteTestTmpdir().c_str(), IN_ALL_EVENTS), 393 SyscallFailsWithErrno(EINVAL)); 394 EXPECT_THAT( 395 inotify_add_watch(2, GetAbsoluteTestTmpdir().c_str(), IN_ALL_EVENTS), 396 SyscallFailsWithErrno(EINVAL)); 397 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( 398 Open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY)); 399 EXPECT_THAT(inotify_add_watch(fd.get(), GetAbsoluteTestTmpdir().c_str(), 400 IN_ALL_EVENTS), 401 SyscallFailsWithErrno(EINVAL)); 402 } 403 404 TEST(Inotify, RemovingWatchGeneratesEvent) { 405 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 406 const FileDescriptor fd = 407 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 408 409 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 410 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 411 EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallSucceeds()); 412 413 // Read events, ensure the first event is IN_IGNORED. 414 const std::vector<Event> events = 415 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 416 EXPECT_THAT(events, Are({Event(IN_IGNORED, wd)})); 417 } 418 419 TEST(Inotify, CanDeleteFileAfterRemovingWatch) { 420 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 421 TempPath file1 = 422 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 423 424 const FileDescriptor fd = 425 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 426 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 427 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 428 429 EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallSucceeds()); 430 file1.reset(); 431 } 432 433 TEST(Inotify, RemoveWatchAfterDeletingFileFails) { 434 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 435 TempPath file1 = 436 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 437 438 const FileDescriptor fd = 439 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 440 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 441 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 442 443 file1.reset(); 444 const std::vector<Event> events = 445 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 446 EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd), Event(IN_DELETE_SELF, wd), 447 Event(IN_IGNORED, wd)})); 448 449 EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallFailsWithErrno(EINVAL)); 450 } 451 452 TEST(Inotify, DuplicateWatchRemovalFails) { 453 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 454 455 const FileDescriptor fd = 456 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 457 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 458 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 459 460 EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallSucceeds()); 461 EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallFailsWithErrno(EINVAL)); 462 } 463 464 TEST(Inotify, ConcurrentFileDeletionAndWatchRemoval) { 465 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 466 467 const FileDescriptor fd = 468 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 469 const std::string filename = NewTempAbsPathInDir(root.path()); 470 471 auto file_create_delete = [filename]() { 472 const DisableSave ds; // Too expensive. 473 for (int i = 0; i < 100; ++i) { 474 FileDescriptor file_fd = 475 ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT, S_IRUSR | S_IWUSR)); 476 // Close before unlinking (although S/R is disabled). Some filesystems 477 // cannot restore an open fd on an unlinked file. 478 file_fd.reset(); 479 EXPECT_THAT(unlink(filename.c_str()), SyscallSucceeds()); 480 } 481 }; 482 483 const int shared_fd = fd.get(); // We need to pass it to the thread. 484 auto add_remove_watch = [shared_fd, filename]() { 485 for (int i = 0; i < 100; ++i) { 486 int wd = inotify_add_watch(shared_fd, filename.c_str(), IN_ALL_EVENTS); 487 MaybeSave(); 488 if (wd != -1) { 489 // Watch added successfully, try removal. 490 if (inotify_rm_watch(shared_fd, wd)) { 491 // If removal fails, the only acceptable reason is if the wd 492 // is invalid, which will be the case if we try to remove 493 // the watch after the file has been deleted. 494 EXPECT_EQ(errno, EINVAL); 495 } 496 } else { 497 // Add watch failed, this should only fail if the target file doesn't 498 // exist. 499 EXPECT_EQ(errno, ENOENT); 500 } 501 } 502 }; 503 504 ScopedThread t1(file_create_delete); 505 ScopedThread t2(add_remove_watch); 506 } 507 508 TEST(Inotify, DeletingChildGeneratesEvents) { 509 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 510 TempPath file1 = 511 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 512 513 const FileDescriptor fd = 514 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 515 const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( 516 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 517 const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( 518 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 519 520 const std::string file1_path = file1.reset(); 521 522 const std::vector<Event> events = 523 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 524 ASSERT_THAT( 525 events, 526 AreUnordered({Event(IN_ATTRIB, file1_wd), Event(IN_DELETE_SELF, file1_wd), 527 Event(IN_IGNORED, file1_wd), 528 Event(IN_DELETE, root_wd, Basename(file1_path))})); 529 } 530 531 // Creating a file in "parent/child" should generate events for child, but not 532 // parent. 533 TEST(Inotify, CreatingFileGeneratesEvents) { 534 const TempPath parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 535 const TempPath child = 536 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(parent.path())); 537 538 const FileDescriptor fd = 539 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 540 ASSERT_NO_ERRNO_AND_VALUE( 541 InotifyAddWatch(fd.get(), parent.path(), IN_ALL_EVENTS)); 542 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 543 InotifyAddWatch(fd.get(), child.path(), IN_ALL_EVENTS)); 544 545 // Create a new file in the directory. 546 const TempPath file1 = 547 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(child.path())); 548 const std::vector<Event> events = 549 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 550 551 // The library function we use to create the new file opens it for writing to 552 // create it and sets permissions on it, so we expect the three extra events. 553 ASSERT_THAT(events, Are({Event(IN_CREATE, wd, Basename(file1.path())), 554 Event(IN_OPEN, wd, Basename(file1.path())), 555 Event(IN_CLOSE_WRITE, wd, Basename(file1.path())), 556 Event(IN_ATTRIB, wd, Basename(file1.path()))})); 557 } 558 559 TEST(Inotify, ReadingFileGeneratesAccessEvent) { 560 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 561 const FileDescriptor fd = 562 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 563 const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 564 root.path(), "some content", TempPath::kDefaultFileMode)); 565 566 const FileDescriptor file1_fd = 567 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); 568 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 569 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 570 571 char buf; 572 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 573 574 const std::vector<Event> events = 575 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 576 ASSERT_THAT(events, Are({Event(IN_ACCESS, wd, Basename(file1.path()))})); 577 } 578 579 TEST(Inotify, WritingFileGeneratesModifyEvent) { 580 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 581 const FileDescriptor fd = 582 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 583 const TempPath file1 = 584 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 585 586 const FileDescriptor file1_fd = 587 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); 588 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 589 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 590 591 const std::string data = "some content"; 592 EXPECT_THAT(write(file1_fd.get(), data.c_str(), data.length()), 593 SyscallSucceeds()); 594 595 const std::vector<Event> events = 596 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 597 ASSERT_THAT(events, Are({Event(IN_MODIFY, wd, Basename(file1.path()))})); 598 } 599 600 TEST(Inotify, SizeZeroReadWriteGeneratesNothing) { 601 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 602 const FileDescriptor fd = 603 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 604 const TempPath file1 = 605 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 606 607 const FileDescriptor file1_fd = 608 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); 609 ASSERT_NO_ERRNO_AND_VALUE( 610 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 611 612 // Read from the empty file. 613 int val; 614 ASSERT_THAT(read(file1_fd.get(), &val, sizeof(val)), 615 SyscallSucceedsWithValue(0)); 616 617 // Write zero bytes. 618 ASSERT_THAT(write(file1_fd.get(), "", 0), SyscallSucceedsWithValue(0)); 619 620 const std::vector<Event> events = 621 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 622 ASSERT_THAT(events, Are({})); 623 } 624 625 TEST(Inotify, FailedFileCreationGeneratesNoEvents) { 626 const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 627 const std::string dir_path = dir.path(); 628 const FileDescriptor fd = 629 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 630 ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch(fd.get(), dir_path, IN_ALL_EVENTS)); 631 632 const char* p = dir_path.c_str(); 633 ASSERT_THAT(mkdir(p, 0777), SyscallFails()); 634 ASSERT_THAT(mknod(p, S_IFIFO, 0777), SyscallFails()); 635 ASSERT_THAT(symlink(p, p), SyscallFails()); 636 ASSERT_THAT(link(p, p), SyscallFails()); 637 std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 638 ASSERT_THAT(events, Are({})); 639 } 640 641 TEST(Inotify, WatchSetAfterOpenReportsCloseFdEvent) { 642 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 643 const FileDescriptor fd = 644 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 645 const TempPath file1 = 646 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 647 648 FileDescriptor file1_fd_writable = 649 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); 650 FileDescriptor file1_fd_not_writable = 651 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); 652 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 653 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 654 655 file1_fd_writable.reset(); // Close file1_fd_writable. 656 std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 657 ASSERT_THAT(events, Are({Event(IN_CLOSE_WRITE, wd, Basename(file1.path()))})); 658 659 file1_fd_not_writable.reset(); // Close file1_fd_not_writable. 660 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 661 ASSERT_THAT(events, 662 Are({Event(IN_CLOSE_NOWRITE, wd, Basename(file1.path()))})); 663 } 664 665 TEST(Inotify, ChildrenDeletionInWatchedDirGeneratesEvent) { 666 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 667 const FileDescriptor fd = 668 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 669 670 TempPath file1 = 671 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 672 TempPath dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 673 674 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 675 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 676 677 const std::string file1_path = file1.reset(); 678 const std::string dir1_path = dir1.release(); 679 EXPECT_THAT(rmdir(dir1_path.c_str()), SyscallSucceeds()); 680 681 const std::vector<Event> events = 682 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 683 684 ASSERT_THAT(events, 685 Are({Event(IN_DELETE, wd, Basename(file1_path)), 686 Event(IN_DELETE | IN_ISDIR, wd, Basename(dir1_path))})); 687 } 688 689 TEST(Inotify, RmdirOnWatchedTargetGeneratesEvent) { 690 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 691 const FileDescriptor fd = 692 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 693 694 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 695 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 696 697 EXPECT_THAT(rmdir(root.path().c_str()), SyscallSucceeds()); 698 const std::vector<Event> events = 699 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 700 ASSERT_THAT(events, Are({Event(IN_DELETE_SELF, wd), Event(IN_IGNORED, wd)})); 701 } 702 703 TEST(Inotify, MoveGeneratesEvents) { 704 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 705 const FileDescriptor fd = 706 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 707 708 TempPath file1 = 709 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 710 711 const TempPath dir1 = 712 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 713 const TempPath dir2 = 714 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 715 716 const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( 717 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 718 const int dir1_wd = ASSERT_NO_ERRNO_AND_VALUE( 719 InotifyAddWatch(fd.get(), dir1.path(), IN_ALL_EVENTS)); 720 const int dir2_wd = ASSERT_NO_ERRNO_AND_VALUE( 721 InotifyAddWatch(fd.get(), dir2.path(), IN_ALL_EVENTS)); 722 // Test move from root -> root. 723 std::string newpath = NewTempAbsPathInDir(root.path()); 724 std::string oldpath = file1.release(); 725 EXPECT_THAT(rename(oldpath.c_str(), newpath.c_str()), SyscallSucceeds()); 726 file1.reset(newpath); 727 std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 728 ASSERT_THAT( 729 events, 730 Are({Event(IN_MOVED_FROM, root_wd, Basename(oldpath), events[0].cookie), 731 Event(IN_MOVED_TO, root_wd, Basename(newpath), events[1].cookie)})); 732 EXPECT_NE(events[0].cookie, 0); 733 EXPECT_EQ(events[0].cookie, events[1].cookie); 734 uint32_t last_cookie = events[0].cookie; 735 736 // Test move from root -> root/dir1. 737 newpath = NewTempAbsPathInDir(dir1.path()); 738 oldpath = file1.release(); 739 EXPECT_THAT(rename(oldpath.c_str(), newpath.c_str()), SyscallSucceeds()); 740 file1.reset(newpath); 741 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 742 ASSERT_THAT( 743 events, 744 Are({Event(IN_MOVED_FROM, root_wd, Basename(oldpath), events[0].cookie), 745 Event(IN_MOVED_TO, dir1_wd, Basename(newpath), events[1].cookie)})); 746 // Cookies should be distinct between distinct rename events. 747 EXPECT_NE(events[0].cookie, last_cookie); 748 EXPECT_EQ(events[0].cookie, events[1].cookie); 749 last_cookie = events[0].cookie; 750 751 // Test move from root/dir1 -> root/dir2. 752 newpath = NewTempAbsPathInDir(dir2.path()); 753 oldpath = file1.release(); 754 EXPECT_THAT(rename(oldpath.c_str(), newpath.c_str()), SyscallSucceeds()); 755 file1.reset(newpath); 756 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 757 ASSERT_THAT( 758 events, 759 Are({Event(IN_MOVED_FROM, dir1_wd, Basename(oldpath), events[0].cookie), 760 Event(IN_MOVED_TO, dir2_wd, Basename(newpath), events[1].cookie)})); 761 EXPECT_NE(events[0].cookie, last_cookie); 762 EXPECT_EQ(events[0].cookie, events[1].cookie); 763 last_cookie = events[0].cookie; 764 } 765 766 TEST(Inotify, MoveWatchedTargetGeneratesEvents) { 767 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 768 const FileDescriptor fd = 769 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 770 771 TempPath file1 = 772 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 773 774 const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( 775 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 776 const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( 777 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 778 779 const std::string newpath = NewTempAbsPathInDir(root.path()); 780 const std::string oldpath = file1.release(); 781 EXPECT_THAT(rename(oldpath.c_str(), newpath.c_str()), SyscallSucceeds()); 782 file1.reset(newpath); 783 const std::vector<Event> events = 784 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 785 ASSERT_THAT( 786 events, 787 Are({Event(IN_MOVED_FROM, root_wd, Basename(oldpath), events[0].cookie), 788 Event(IN_MOVED_TO, root_wd, Basename(newpath), events[1].cookie), 789 // Self move events do not have a cookie. 790 Event(IN_MOVE_SELF, file1_wd)})); 791 EXPECT_NE(events[0].cookie, 0); 792 EXPECT_EQ(events[0].cookie, events[1].cookie); 793 } 794 795 // Tests that close events are only emitted when a file description drops its 796 // last reference. 797 TEST(Inotify, DupFD) { 798 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 799 const FileDescriptor inotify_fd = 800 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 801 802 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 803 InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS)); 804 805 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); 806 FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); 807 808 std::vector<Event> events = 809 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 810 EXPECT_THAT(events, Are({ 811 Event(IN_OPEN, wd), 812 })); 813 814 fd.reset(); 815 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 816 EXPECT_THAT(events, Are({})); 817 818 fd2.reset(); 819 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 820 EXPECT_THAT(events, Are({ 821 Event(IN_CLOSE_NOWRITE, wd), 822 })); 823 } 824 825 TEST(Inotify, CoalesceEvents) { 826 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 827 const FileDescriptor fd = 828 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 829 830 const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 831 root.path(), "some content", TempPath::kDefaultFileMode)); 832 833 FileDescriptor file1_fd = 834 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); 835 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 836 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 837 838 // Read the file a few times. This will would generate multiple IN_ACCESS 839 // events but they should get coalesced to a single event. 840 char buf; 841 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 842 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 843 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 844 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 845 846 // Use the close event verify that we haven't simply left the additional 847 // IN_ACCESS events unread. 848 file1_fd.reset(); // Close file1_fd. 849 850 const std::string file1_name = std::string(Basename(file1.path())); 851 std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 852 ASSERT_THAT(events, Are({Event(IN_ACCESS, wd, file1_name), 853 Event(IN_CLOSE_NOWRITE, wd, file1_name)})); 854 855 // Now let's try interleaving other events into a stream of repeated events. 856 file1_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); 857 858 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 859 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 860 EXPECT_THAT(write(file1_fd.get(), "x", 1), SyscallSucceeds()); 861 EXPECT_THAT(write(file1_fd.get(), "x", 1), SyscallSucceeds()); 862 EXPECT_THAT(write(file1_fd.get(), "x", 1), SyscallSucceeds()); 863 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 864 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 865 866 file1_fd.reset(); // Close the file. 867 868 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 869 ASSERT_THAT( 870 events, 871 Are({Event(IN_OPEN, wd, file1_name), Event(IN_ACCESS, wd, file1_name), 872 Event(IN_MODIFY, wd, file1_name), Event(IN_ACCESS, wd, file1_name), 873 Event(IN_CLOSE_WRITE, wd, file1_name)})); 874 875 // Ensure events aren't coalesced if they are from different files. 876 const TempPath file2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 877 root.path(), "some content", TempPath::kDefaultFileMode)); 878 // Discard events resulting from creation of file2. 879 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 880 881 file1_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); 882 FileDescriptor file2_fd = 883 ASSERT_NO_ERRNO_AND_VALUE(Open(file2.path(), O_RDONLY)); 884 885 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 886 EXPECT_THAT(read(file2_fd.get(), &buf, 1), SyscallSucceeds()); 887 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 888 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 889 890 // Close both files. 891 file1_fd.reset(); 892 file2_fd.reset(); 893 894 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 895 const std::string file2_name = std::string(Basename(file2.path())); 896 ASSERT_THAT( 897 events, 898 Are({Event(IN_OPEN, wd, file1_name), Event(IN_OPEN, wd, file2_name), 899 Event(IN_ACCESS, wd, file1_name), Event(IN_ACCESS, wd, file2_name), 900 Event(IN_ACCESS, wd, file1_name), 901 Event(IN_CLOSE_NOWRITE, wd, file1_name), 902 Event(IN_CLOSE_NOWRITE, wd, file2_name)})); 903 } 904 905 TEST(Inotify, ClosingInotifyFdWithoutRemovingWatchesWorks) { 906 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 907 const FileDescriptor fd = 908 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 909 910 const TempPath file1 = 911 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 912 const FileDescriptor file1_fd = 913 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); 914 915 ASSERT_NO_ERRNO_AND_VALUE( 916 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 917 // Note: The check on close will happen in FileDescriptor::~FileDescriptor(). 918 } 919 920 TEST(Inotify, NestedWatches) { 921 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 922 const FileDescriptor fd = 923 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 924 925 const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 926 root.path(), "some content", TempPath::kDefaultFileMode)); 927 const FileDescriptor file1_fd = 928 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); 929 930 const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( 931 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 932 const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( 933 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 934 935 // Read from file1. This should generate an event for both watches. 936 char buf; 937 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 938 939 const std::vector<Event> events = 940 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 941 ASSERT_THAT(events, Are({Event(IN_ACCESS, root_wd, Basename(file1.path())), 942 Event(IN_ACCESS, file1_wd)})); 943 } 944 945 TEST(Inotify, ConcurrentThreadsGeneratingEvents) { 946 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 947 const FileDescriptor fd = 948 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 949 950 std::vector<TempPath> files; 951 files.reserve(10); 952 for (int i = 0; i < 10; i++) { 953 files.emplace_back(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 954 root.path(), "some content", TempPath::kDefaultFileMode))); 955 } 956 957 auto test_thread = [&files]() { 958 uint32_t seed = time(nullptr); 959 for (int i = 0; i < 20; i++) { 960 const TempPath& file = files[rand_r(&seed) % files.size()]; 961 const FileDescriptor file_fd = 962 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY)); 963 TEST_PCHECK(write(file_fd.get(), "x", 1) == 1); 964 } 965 }; 966 967 ASSERT_NO_ERRNO_AND_VALUE( 968 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 969 970 std::list<ScopedThread> threads; 971 for (int i = 0; i < 3; i++) { 972 threads.emplace_back(test_thread); 973 } 974 for (auto& t : threads) { 975 t.Join(); 976 } 977 978 const std::vector<Event> events = 979 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 980 // 3 threads doing 20 iterations, 3 events per iteration (open, write, 981 // close). However, some events may be coalesced, and we can't reliably 982 // predict how they'll be coalesced since the test threads aren't 983 // synchronized. We can only check that we aren't getting unexpected events. 984 for (const Event& ev : events) { 985 EXPECT_NE(ev.mask & (IN_OPEN | IN_MODIFY | IN_CLOSE_WRITE), 0); 986 } 987 } 988 989 TEST(Inotify, ReadWithTooSmallBufferFails) { 990 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 991 const TempPath file1 = 992 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 993 const FileDescriptor fd = 994 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 995 996 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 997 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 998 999 // Open the file to queue an event. This event will not have a filename, so 1000 // reading from the inotify fd should return sizeof(struct inotify_event) 1001 // bytes of data. 1002 FileDescriptor file1_fd = 1003 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); 1004 std::vector<char> buf(kBufSize, 0); 1005 ssize_t readlen; 1006 1007 // Try a buffer too small to hold any potential event. This is rejected 1008 // outright without the event being dequeued. 1009 EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event) - 1), 1010 SyscallFailsWithErrno(EINVAL)); 1011 // Try a buffer just large enough. This should succeeed. 1012 EXPECT_THAT( 1013 readlen = read(fd.get(), buf.data(), sizeof(struct inotify_event)), 1014 SyscallSucceeds()); 1015 EXPECT_EQ(readlen, sizeof(struct inotify_event)); 1016 // Event queue is now empty, the next read should return EAGAIN. 1017 EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event)), 1018 SyscallFailsWithErrno(EAGAIN)); 1019 1020 // Now put a watch on the directory, so that generated events contain a name. 1021 EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallSucceeds()); 1022 1023 // Drain the event generated from the watch removal. 1024 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1025 1026 ASSERT_NO_ERRNO_AND_VALUE( 1027 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1028 1029 file1_fd.reset(); // Close file to generate an event. 1030 1031 // Try a buffer too small to hold any event and one too small to hold an event 1032 // with a name. These should both fail without consuming the event. 1033 EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event) - 1), 1034 SyscallFailsWithErrno(EINVAL)); 1035 EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event)), 1036 SyscallFailsWithErrno(EINVAL)); 1037 // Now try with a large enough buffer. This should return the one event. 1038 EXPECT_THAT(readlen = read(fd.get(), buf.data(), buf.size()), 1039 SyscallSucceeds()); 1040 EXPECT_GE(readlen, 1041 sizeof(struct inotify_event) + Basename(file1.path()).size()); 1042 // With the single event read, the queue should once again be empty. 1043 EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event)), 1044 SyscallFailsWithErrno(EAGAIN)); 1045 } 1046 1047 TEST(Inotify, BlockingReadOnInotifyFd) { 1048 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1049 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(0)); 1050 const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 1051 root.path(), "some content", TempPath::kDefaultFileMode)); 1052 1053 const FileDescriptor file1_fd = 1054 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); 1055 1056 ASSERT_NO_ERRNO_AND_VALUE( 1057 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1058 1059 // Spawn a thread performing a blocking read for new events on the inotify fd. 1060 std::vector<char> buf(kBufSize, 0); 1061 const int shared_fd = fd.get(); // The thread needs it. 1062 ScopedThread t([shared_fd, &buf]() { 1063 ssize_t readlen; 1064 EXPECT_THAT(readlen = read(shared_fd, buf.data(), buf.size()), 1065 SyscallSucceeds()); 1066 }); 1067 1068 // Perform a read on the watched file, which should generate an IN_ACCESS 1069 // event, unblocking the event_reader thread. 1070 char c; 1071 EXPECT_THAT(read(file1_fd.get(), &c, 1), SyscallSucceeds()); 1072 1073 // Wait for the thread to read the event and exit. 1074 t.Join(); 1075 1076 // Make sure the event we got back is sane. 1077 uint32_t event_mask; 1078 memcpy(&event_mask, buf.data() + offsetof(struct inotify_event, mask), 1079 sizeof(event_mask)); 1080 EXPECT_EQ(event_mask, IN_ACCESS); 1081 } 1082 1083 TEST(Inotify, WatchOnRelativePath) { 1084 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1085 const FileDescriptor fd = 1086 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1087 const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 1088 root.path(), "some content", TempPath::kDefaultFileMode)); 1089 1090 const FileDescriptor file1_fd = 1091 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); 1092 1093 // Change working directory to root. 1094 const FileDescriptor cwd = ASSERT_NO_ERRNO_AND_VALUE(Open(".", O_PATH)); 1095 EXPECT_THAT(chdir(root.path().c_str()), SyscallSucceeds()); 1096 1097 // Add a watch on file1 with a relative path. 1098 const int wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( 1099 fd.get(), std::string(Basename(file1.path())), IN_ALL_EVENTS)); 1100 1101 // Perform a read on file1, this should generate an IN_ACCESS event. 1102 char c; 1103 EXPECT_THAT(read(file1_fd.get(), &c, 1), SyscallSucceeds()); 1104 1105 const std::vector<Event> events = 1106 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1107 EXPECT_THAT(events, Are({Event(IN_ACCESS, wd)})); 1108 1109 // Explicitly reset the working directory so that we don't continue to 1110 // reference "root". Once the test ends, "root" will get unlinked. If we 1111 // continue to hold a reference, random save/restore tests can fail if a save 1112 // is triggered after "root" is unlinked; we can't save deleted fs objects 1113 // with active references. 1114 EXPECT_THAT(fchdir(cwd.get()), SyscallSucceeds()); 1115 } 1116 1117 TEST(Inotify, ZeroLengthReadWriteDoesNotGenerateEvent) { 1118 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1119 const FileDescriptor fd = 1120 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1121 1122 const char kContent[] = "some content"; 1123 TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 1124 root.path(), kContent, TempPath::kDefaultFileMode)); 1125 const int kContentSize = sizeof(kContent) - 1; 1126 1127 const FileDescriptor file1_fd = 1128 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); 1129 1130 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 1131 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1132 1133 std::vector<char> buf(kContentSize, 0); 1134 // Read all available data. 1135 ssize_t readlen; 1136 EXPECT_THAT(readlen = read(file1_fd.get(), buf.data(), kContentSize), 1137 SyscallSucceeds()); 1138 EXPECT_EQ(readlen, kContentSize); 1139 // Drain all events and make sure we got the IN_ACCESS for the read. 1140 std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1141 EXPECT_THAT(events, Are({Event(IN_ACCESS, wd, Basename(file1.path()))})); 1142 1143 // Now try read again. This should be a 0-length read, since we're at EOF. 1144 char c; 1145 EXPECT_THAT(readlen = read(file1_fd.get(), &c, 1), SyscallSucceeds()); 1146 EXPECT_EQ(readlen, 0); 1147 // We should have no new events. 1148 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1149 EXPECT_TRUE(events.empty()); 1150 1151 // Try issuing a zero-length read. 1152 EXPECT_THAT(readlen = read(file1_fd.get(), &c, 0), SyscallSucceeds()); 1153 EXPECT_EQ(readlen, 0); 1154 // We should have no new events. 1155 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1156 EXPECT_TRUE(events.empty()); 1157 1158 // Try issuing a zero-length write. 1159 ssize_t writelen; 1160 EXPECT_THAT(writelen = write(file1_fd.get(), &c, 0), SyscallSucceeds()); 1161 EXPECT_EQ(writelen, 0); 1162 // We should have no new events. 1163 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1164 EXPECT_TRUE(events.empty()); 1165 } 1166 1167 TEST(Inotify, ChmodGeneratesAttribEvent) { 1168 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1169 const TempPath file1 = 1170 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 1171 1172 FileDescriptor root_fd = 1173 ASSERT_NO_ERRNO_AND_VALUE(Open(root.path(), O_RDONLY)); 1174 FileDescriptor file1_fd = 1175 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); 1176 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1177 1178 const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( 1179 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1180 const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( 1181 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 1182 1183 auto verify_chmod_events = [&]() { 1184 std::vector<Event> events = 1185 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1186 ASSERT_THAT(events, Are({Event(IN_ATTRIB, root_wd, Basename(file1.path())), 1187 Event(IN_ATTRIB, file1_wd)})); 1188 }; 1189 1190 // Don't do cooperative S/R tests for any of the {f}chmod* syscalls below, the 1191 // test will always fail because nodes cannot be saved when they have stricter 1192 // permissions than the original host node. 1193 const DisableSave ds; 1194 1195 // Chmod. 1196 ASSERT_THAT(chmod(file1.path().c_str(), S_IWGRP), SyscallSucceeds()); 1197 verify_chmod_events(); 1198 1199 // Fchmod. 1200 ASSERT_THAT(fchmod(file1_fd.get(), S_IRGRP | S_IWGRP), SyscallSucceeds()); 1201 verify_chmod_events(); 1202 1203 // Fchmodat. 1204 const std::string file1_basename = std::string(Basename(file1.path())); 1205 ASSERT_THAT(fchmodat(root_fd.get(), file1_basename.c_str(), S_IWGRP, 0), 1206 SyscallSucceeds()); 1207 verify_chmod_events(); 1208 1209 // Make sure the chmod'ed file descriptors are destroyed before DisableSave 1210 // is destructed. 1211 root_fd.reset(); 1212 file1_fd.reset(); 1213 } 1214 1215 TEST(Inotify, TruncateGeneratesModifyEvent) { 1216 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1217 const TempPath file1 = 1218 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 1219 const FileDescriptor file1_fd = 1220 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); 1221 1222 const FileDescriptor fd = 1223 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1224 const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( 1225 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1226 const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( 1227 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 1228 1229 auto verify_truncate_events = [&]() { 1230 std::vector<Event> events = 1231 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1232 ASSERT_THAT(events, Are({Event(IN_MODIFY, root_wd, Basename(file1.path())), 1233 Event(IN_MODIFY, file1_wd)})); 1234 }; 1235 1236 // Truncate. 1237 EXPECT_THAT(truncate(file1.path().c_str(), 4096), SyscallSucceeds()); 1238 verify_truncate_events(); 1239 1240 // Ftruncate. 1241 EXPECT_THAT(ftruncate(file1_fd.get(), 8192), SyscallSucceeds()); 1242 verify_truncate_events(); 1243 1244 // No events if truncate fails. 1245 EXPECT_THAT(ftruncate(file1_fd.get(), -1), SyscallFailsWithErrno(EINVAL)); 1246 const std::vector<Event> events = 1247 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1248 ASSERT_THAT(events, Are({})); 1249 } 1250 1251 TEST(Inotify, GetdentsGeneratesAccessEvent) { 1252 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1253 const TempPath file1 = 1254 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 1255 1256 const FileDescriptor fd = 1257 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1258 1259 ASSERT_NO_ERRNO_AND_VALUE( 1260 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1261 ASSERT_NO_ERRNO_AND_VALUE( 1262 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 1263 1264 // This internally calls getdents(2). We also expect to see an open/close 1265 // event for the dirfd. 1266 ASSERT_NO_ERRNO_AND_VALUE(ListDir(root.path(), false)); 1267 const std::vector<Event> events = 1268 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1269 1270 // Linux only seems to generate access events on getdents() on some 1271 // calls. Allow the test to pass even if it isn't generated. gVisor will 1272 // always generate the IN_ACCESS event so the test will at least ensure gVisor 1273 // behaves reasonably. 1274 int i = 0; 1275 EXPECT_EQ(events[i].mask, IN_OPEN | IN_ISDIR); 1276 ++i; 1277 if (IsRunningOnGvisor()) { 1278 EXPECT_EQ(events[i].mask, IN_ACCESS | IN_ISDIR); 1279 ++i; 1280 } else { 1281 if (events[i].mask == (IN_ACCESS | IN_ISDIR)) { 1282 // Skip over the IN_ACCESS event on Linux, it only shows up some of the 1283 // time so we can't assert its existence. 1284 ++i; 1285 } 1286 } 1287 EXPECT_EQ(events[i].mask, IN_CLOSE_NOWRITE | IN_ISDIR); 1288 } 1289 1290 TEST(Inotify, MknodGeneratesCreateEvent) { 1291 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1292 const FileDescriptor fd = 1293 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1294 1295 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 1296 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1297 1298 const TempPath file1(root.path() + "/file1"); 1299 ASSERT_THAT(mknod(file1.path().c_str(), S_IFREG, 0), SyscallSucceeds()); 1300 1301 const std::vector<Event> events = 1302 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1303 ASSERT_THAT(events, Are({Event(IN_CREATE, wd, Basename(file1.path()))})); 1304 } 1305 1306 TEST(Inotify, SymlinkGeneratesCreateEvent) { 1307 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1308 const TempPath file1 = 1309 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 1310 const TempPath link1(NewTempAbsPathInDir(root.path())); 1311 const FileDescriptor fd = 1312 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1313 1314 const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( 1315 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1316 ASSERT_NO_ERRNO(InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 1317 1318 ASSERT_THAT(symlink(file1.path().c_str(), link1.path().c_str()), 1319 SyscallSucceeds()); 1320 1321 const std::vector<Event> events = 1322 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1323 1324 ASSERT_THAT(events, Are({Event(IN_CREATE, root_wd, Basename(link1.path()))})); 1325 } 1326 1327 TEST(Inotify, SymlinkFollow) { 1328 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 1329 const TempPath link(NewTempAbsPath()); 1330 ASSERT_THAT(symlink(file.path().c_str(), link.path().c_str()), 1331 SyscallSucceeds()); 1332 1333 const FileDescriptor fd = 1334 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1335 const int file_wd = ASSERT_NO_ERRNO_AND_VALUE( 1336 InotifyAddWatch(fd.get(), link.path(), IN_ALL_EVENTS)); 1337 const int link_wd = ASSERT_NO_ERRNO_AND_VALUE( 1338 InotifyAddWatch(fd.get(), link.path(), IN_ALL_EVENTS | IN_DONT_FOLLOW)); 1339 1340 ASSERT_NO_ERRNO(Unlink(file.path())); 1341 ASSERT_NO_ERRNO(Unlink(link.path())); 1342 1343 const std::vector<Event> events = 1344 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1345 1346 ASSERT_THAT( 1347 events, 1348 Are({Event(IN_ATTRIB, file_wd), Event(IN_DELETE_SELF, file_wd), 1349 Event(IN_IGNORED, file_wd), Event(IN_ATTRIB, link_wd), 1350 Event(IN_DELETE_SELF, link_wd), Event(IN_IGNORED, link_wd)})); 1351 } 1352 1353 TEST(Inotify, LinkGeneratesAttribAndCreateEvents) { 1354 // Inotify does not work properly with hard links in gofer and overlay fs. 1355 SKIP_IF(IsRunningOnGvisor() && 1356 !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir()))); 1357 1358 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1359 const TempPath file1 = 1360 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 1361 const TempPath link1(root.path() + "/link1"); 1362 const FileDescriptor fd = 1363 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1364 1365 const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( 1366 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1367 const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( 1368 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 1369 1370 ASSERT_THAT(link(file1.path().c_str(), link1.path().c_str()), 1371 SyscallSucceeds()); 1372 1373 const std::vector<Event> events = 1374 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1375 ASSERT_THAT(events, Are({Event(IN_ATTRIB, file1_wd), 1376 Event(IN_CREATE, root_wd, Basename(link1.path()))})); 1377 } 1378 1379 TEST(Inotify, UtimesGeneratesAttribEvent) { 1380 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1381 const FileDescriptor fd = 1382 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1383 const TempPath file1 = 1384 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 1385 1386 const FileDescriptor file1_fd = 1387 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); 1388 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 1389 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1390 1391 const struct timeval times[2] = {{1, 0}, {2, 0}}; 1392 EXPECT_THAT(futimes(file1_fd.get(), times), SyscallSucceeds()); 1393 1394 const std::vector<Event> events = 1395 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1396 ASSERT_THAT(events, Are({Event(IN_ATTRIB, wd, Basename(file1.path()))})); 1397 } 1398 1399 TEST(Inotify, HardlinksReuseSameWatch) { 1400 // Inotify does not work properly with hard links in gofer and overlay fs. 1401 SKIP_IF(IsRunningOnGvisor() && 1402 !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir()))); 1403 1404 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1405 TempPath file = 1406 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 1407 1408 TempPath file2(root.path() + "/file2"); 1409 ASSERT_THAT(link(file.path().c_str(), file2.path().c_str()), 1410 SyscallSucceeds()); 1411 1412 const FileDescriptor fd = 1413 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1414 1415 const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( 1416 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1417 const int file_wd = ASSERT_NO_ERRNO_AND_VALUE( 1418 InotifyAddWatch(fd.get(), file.path(), IN_ALL_EVENTS)); 1419 const int file2_wd = ASSERT_NO_ERRNO_AND_VALUE( 1420 InotifyAddWatch(fd.get(), file2.path(), IN_ALL_EVENTS)); 1421 1422 // The watch descriptors for watches on different links to the same file 1423 // should be identical. 1424 EXPECT_NE(root_wd, file_wd); 1425 EXPECT_EQ(file_wd, file2_wd); 1426 1427 FileDescriptor file_fd = 1428 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY)); 1429 1430 std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1431 ASSERT_THAT(events, 1432 AreUnordered({Event(IN_OPEN, root_wd, Basename(file.path())), 1433 Event(IN_OPEN, file_wd)})); 1434 1435 // For the next step, we want to ensure all fds to the file are closed. Do 1436 // that now and drain the resulting events. 1437 file_fd.reset(); 1438 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1439 ASSERT_THAT( 1440 events, 1441 AreUnordered({Event(IN_CLOSE_WRITE, root_wd, Basename(file.path())), 1442 Event(IN_CLOSE_WRITE, file_wd)})); 1443 1444 // Try removing the link and let's see what events show up. Note that after 1445 // this, we still have a link to the file so the watch shouldn't be 1446 // automatically removed. 1447 const std::string file2_path = file2.reset(); 1448 1449 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1450 ASSERT_THAT(events, 1451 AreUnordered({Event(IN_ATTRIB, file2_wd), 1452 Event(IN_DELETE, root_wd, Basename(file2_path))})); 1453 1454 // Now remove the other link. Since this is the last link to the file, the 1455 // watch should be automatically removed. 1456 const std::string file_path = file.reset(); 1457 1458 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1459 ASSERT_THAT( 1460 events, 1461 AreUnordered({Event(IN_ATTRIB, file_wd), Event(IN_DELETE_SELF, file_wd), 1462 Event(IN_IGNORED, file_wd), 1463 Event(IN_DELETE, root_wd, Basename(file_path))})); 1464 } 1465 1466 // Calling mkdir within "parent/child" should generate an event for child, but 1467 // not parent. 1468 TEST(Inotify, MkdirGeneratesCreateEventWithDirFlag) { 1469 const TempPath parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1470 const TempPath child = 1471 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(parent.path())); 1472 const FileDescriptor fd = 1473 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1474 ASSERT_NO_ERRNO_AND_VALUE( 1475 InotifyAddWatch(fd.get(), parent.path(), IN_ALL_EVENTS)); 1476 const int child_wd = ASSERT_NO_ERRNO_AND_VALUE( 1477 InotifyAddWatch(fd.get(), child.path(), IN_ALL_EVENTS)); 1478 1479 const TempPath dir1(NewTempAbsPathInDir(child.path())); 1480 ASSERT_THAT(mkdir(dir1.path().c_str(), 0777), SyscallSucceeds()); 1481 1482 const std::vector<Event> events = 1483 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1484 ASSERT_THAT( 1485 events, 1486 Are({Event(IN_CREATE | IN_ISDIR, child_wd, Basename(dir1.path()))})); 1487 } 1488 1489 TEST(Inotify, MultipleInotifyInstancesAndWatchesAllGetEvents) { 1490 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1491 const TempPath file1 = 1492 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 1493 1494 const FileDescriptor file1_fd = 1495 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); 1496 constexpr int kNumFds = 30; 1497 std::vector<FileDescriptor> inotify_fds; 1498 1499 for (int i = 0; i < kNumFds; ++i) { 1500 const DisableSave ds; // Too expensive. 1501 inotify_fds.emplace_back( 1502 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK))); 1503 const FileDescriptor& fd = inotify_fds[inotify_fds.size() - 1]; // Back. 1504 ASSERT_NO_ERRNO_AND_VALUE( 1505 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1506 ASSERT_NO_ERRNO_AND_VALUE( 1507 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 1508 } 1509 1510 const std::string data = "some content"; 1511 EXPECT_THAT(write(file1_fd.get(), data.c_str(), data.length()), 1512 SyscallSucceeds()); 1513 1514 for (const FileDescriptor& fd : inotify_fds) { 1515 const DisableSave ds; // Too expensive. 1516 const std::vector<Event> events = 1517 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1518 if (events.size() >= 2) { 1519 EXPECT_EQ(events[0].mask, IN_MODIFY); 1520 EXPECT_EQ(events[0].wd, 1); 1521 EXPECT_EQ(events[0].name, Basename(file1.path())); 1522 EXPECT_EQ(events[1].mask, IN_MODIFY); 1523 EXPECT_EQ(events[1].wd, 2); 1524 EXPECT_EQ(events[1].name, ""); 1525 } 1526 } 1527 } 1528 1529 TEST(Inotify, EventsGoUpAtMostOneLevel) { 1530 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1531 const TempPath dir1 = 1532 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 1533 TempPath file1 = 1534 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); 1535 const FileDescriptor fd = 1536 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1537 1538 ASSERT_NO_ERRNO_AND_VALUE( 1539 InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); 1540 const int dir1_wd = ASSERT_NO_ERRNO_AND_VALUE( 1541 InotifyAddWatch(fd.get(), dir1.path(), IN_ALL_EVENTS)); 1542 1543 const std::string file1_path = file1.reset(); 1544 1545 const std::vector<Event> events = 1546 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1547 ASSERT_THAT(events, Are({Event(IN_DELETE, dir1_wd, Basename(file1_path))})); 1548 } 1549 1550 TEST(Inotify, DuplicateWatchReturnsSameWatchDescriptor) { 1551 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1552 const TempPath file1 = 1553 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 1554 const FileDescriptor fd = 1555 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1556 1557 const int wd1 = ASSERT_NO_ERRNO_AND_VALUE( 1558 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 1559 const int wd2 = ASSERT_NO_ERRNO_AND_VALUE( 1560 InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); 1561 1562 EXPECT_EQ(wd1, wd2); 1563 1564 const FileDescriptor file1_fd = 1565 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); 1566 const std::vector<Event> events = 1567 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1568 // The watch shouldn't be duplicated, we only expect one event. 1569 ASSERT_THAT(events, Are({Event(IN_OPEN, wd1)})); 1570 } 1571 1572 TEST(Inotify, UnmatchedEventsAreDiscarded) { 1573 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1574 TempPath file1 = 1575 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 1576 const FileDescriptor fd = 1577 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1578 1579 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 1580 InotifyAddWatch(fd.get(), file1.path(), IN_ACCESS)); 1581 1582 FileDescriptor file1_fd = 1583 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); 1584 1585 std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1586 // We only asked for access events, the open event should be discarded. 1587 ASSERT_THAT(events, Are({})); 1588 1589 // IN_IGNORED events are always generated, regardless of the mask. 1590 file1_fd.reset(); 1591 file1.reset(); 1592 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1593 ASSERT_THAT(events, Are({Event(IN_IGNORED, wd)})); 1594 } 1595 1596 TEST(Inotify, AddWatchWithInvalidEventMaskFails) { 1597 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1598 const FileDescriptor fd = 1599 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1600 1601 EXPECT_THAT(inotify_add_watch(fd.get(), root.path().c_str(), 0), 1602 SyscallFailsWithErrno(EINVAL)); 1603 } 1604 1605 TEST(Inotify, AddWatchOnInvalidPathFails) { 1606 const TempPath nonexistent(NewTempAbsPath()); 1607 const FileDescriptor fd = 1608 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1609 1610 // Non-existent path. 1611 EXPECT_THAT( 1612 inotify_add_watch(fd.get(), nonexistent.path().c_str(), IN_CREATE), 1613 SyscallFailsWithErrno(ENOENT)); 1614 1615 // Garbage path pointer. 1616 EXPECT_THAT(inotify_add_watch(fd.get(), nullptr, IN_CREATE), 1617 SyscallFailsWithErrno(EFAULT)); 1618 } 1619 1620 TEST(Inotify, InOnlyDirFlagRespected) { 1621 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1622 const TempPath file1 = 1623 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 1624 const FileDescriptor fd = 1625 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1626 1627 EXPECT_THAT( 1628 inotify_add_watch(fd.get(), root.path().c_str(), IN_ACCESS | IN_ONLYDIR), 1629 SyscallSucceeds()); 1630 1631 EXPECT_THAT( 1632 inotify_add_watch(fd.get(), file1.path().c_str(), IN_ACCESS | IN_ONLYDIR), 1633 SyscallFailsWithErrno(ENOTDIR)); 1634 } 1635 1636 TEST(Inotify, MaskAddMergesWithExistingEventMask) { 1637 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1638 const TempPath file1 = 1639 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); 1640 const FileDescriptor fd = 1641 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1642 1643 FileDescriptor file1_fd = 1644 ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); 1645 1646 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 1647 InotifyAddWatch(fd.get(), file1.path(), IN_OPEN | IN_CLOSE_WRITE)); 1648 1649 const std::string data = "some content"; 1650 EXPECT_THAT(write(file1_fd.get(), data.c_str(), data.length()), 1651 SyscallSucceeds()); 1652 1653 // We shouldn't get any events, since IN_MODIFY wasn't in the event mask. 1654 std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1655 ASSERT_THAT(events, Are({})); 1656 1657 // Add IN_MODIFY to event mask. 1658 ASSERT_NO_ERRNO_AND_VALUE( 1659 InotifyAddWatch(fd.get(), file1.path(), IN_MODIFY | IN_MASK_ADD)); 1660 1661 EXPECT_THAT(write(file1_fd.get(), data.c_str(), data.length()), 1662 SyscallSucceeds()); 1663 1664 // This time we should get the modify event. 1665 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1666 ASSERT_THAT(events, Are({Event(IN_MODIFY, wd)})); 1667 1668 // Now close the fd. If the modify event was added to the event mask rather 1669 // than replacing the event mask we won't get the close event. 1670 file1_fd.reset(); 1671 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1672 ASSERT_THAT(events, Are({Event(IN_CLOSE_WRITE, wd)})); 1673 } 1674 1675 // Test that control events bits are not considered when checking event mask. 1676 TEST(Inotify, ControlEvents) { 1677 const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1678 const FileDescriptor fd = 1679 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1680 1681 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 1682 InotifyAddWatch(fd.get(), dir.path(), IN_ACCESS)); 1683 1684 // Check that events in the mask are dispatched and that control bits are 1685 // part of the event mask. 1686 std::vector<std::string> files = 1687 ASSERT_NO_ERRNO_AND_VALUE(ListDir(dir.path(), false)); 1688 ASSERT_EQ(files.size(), 2); 1689 1690 const std::vector<Event> events1 = 1691 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1692 ASSERT_THAT(events1, Are({Event(IN_ACCESS | IN_ISDIR, wd)})); 1693 1694 // Check that events not in the mask are discarded. 1695 const FileDescriptor dir_fd = 1696 ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY)); 1697 1698 const std::vector<Event> events2 = 1699 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 1700 ASSERT_THAT(events2, Are({})); 1701 } 1702 1703 // Regression test to ensure epoll and directory access doesn't deadlock. 1704 TEST(Inotify, EpollNoDeadlock) { 1705 const DisableSave ds; // Too many syscalls. 1706 1707 const FileDescriptor fd = 1708 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1709 1710 // Create lots of directories and watch all of them. 1711 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1712 std::vector<TempPath> children; 1713 for (size_t i = 0; i < 1000; ++i) { 1714 auto child = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); 1715 ASSERT_NO_ERRNO_AND_VALUE( 1716 InotifyAddWatch(fd.get(), child.path(), IN_ACCESS)); 1717 children.emplace_back(std::move(child)); 1718 } 1719 1720 // Run epoll_wait constantly in a separate thread. 1721 std::atomic<bool> done(false); 1722 ScopedThread th([&fd, &done] { 1723 for (auto start = absl::Now(); absl::Now() - start < absl::Seconds(5);) { 1724 FileDescriptor epoll_fd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); 1725 ASSERT_NO_ERRNO(RegisterEpollFD(epoll_fd.get(), fd.get(), 1726 EPOLLIN | EPOLLOUT | EPOLLET, 0)); 1727 struct epoll_event result[1]; 1728 EXPECT_THAT(RetryEINTR(epoll_wait)(epoll_fd.get(), result, 1, -1), 1729 SyscallSucceedsWithValue(1)); 1730 1731 sched_yield(); 1732 } 1733 done = true; 1734 }); 1735 1736 // While epoll thread is running, constantly access all directories to 1737 // generate inotify events. 1738 while (!done) { 1739 std::vector<std::string> files = 1740 ASSERT_NO_ERRNO_AND_VALUE(ListDir(root.path(), false)); 1741 ASSERT_EQ(files.size(), 1002); 1742 for (const auto& child : files) { 1743 if (child == "." || child == "..") { 1744 continue; 1745 } 1746 ASSERT_NO_ERRNO_AND_VALUE(ListDir(JoinPath(root.path(), child), false)); 1747 } 1748 sched_yield(); 1749 } 1750 } 1751 1752 TEST(Inotify, Fallocate) { 1753 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 1754 const FileDescriptor fd = 1755 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); 1756 1757 const FileDescriptor inotify_fd = 1758 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1759 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 1760 InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS)); 1761 1762 // Do an arbitrary modification with fallocate. 1763 ASSERT_THAT(RetryEINTR(fallocate)(fd.get(), 0, 0, 123), SyscallSucceeds()); 1764 std::vector<Event> events = 1765 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1766 EXPECT_THAT(events, Are({Event(IN_MODIFY, wd)})); 1767 } 1768 1769 TEST(Inotify, Utimensat) { 1770 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 1771 const FileDescriptor fd = 1772 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); 1773 1774 const FileDescriptor inotify_fd = 1775 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1776 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 1777 InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS)); 1778 1779 // Just update the access time. 1780 struct timespec times[2] = {}; 1781 times[0].tv_nsec = UTIME_NOW; 1782 times[1].tv_nsec = UTIME_OMIT; 1783 ASSERT_THAT(RetryEINTR(utimensat)(AT_FDCWD, file.path().c_str(), times, 0), 1784 SyscallSucceeds()); 1785 std::vector<Event> events = 1786 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1787 EXPECT_THAT(events, Are({Event(IN_ACCESS, wd)})); 1788 1789 // Just the modify time. 1790 times[0].tv_nsec = UTIME_OMIT; 1791 times[1].tv_nsec = UTIME_NOW; 1792 ASSERT_THAT(utimensat(AT_FDCWD, file.path().c_str(), times, 0), 1793 SyscallSucceeds()); 1794 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1795 EXPECT_THAT(events, Are({Event(IN_MODIFY, wd)})); 1796 1797 // Both together. 1798 times[0].tv_nsec = UTIME_NOW; 1799 times[1].tv_nsec = UTIME_NOW; 1800 ASSERT_THAT(utimensat(AT_FDCWD, file.path().c_str(), times, 0), 1801 SyscallSucceeds()); 1802 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1803 EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd)})); 1804 } 1805 1806 TEST(Inotify, Sendfile) { 1807 const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1808 const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE( 1809 TempPath::CreateFileWith(root.path(), "x", 0644)); 1810 const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 1811 const FileDescriptor in = 1812 ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); 1813 const FileDescriptor out = 1814 ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); 1815 1816 // Create separate inotify instances for the in and out fds. If both watches 1817 // were on the same instance, we would have discrepancies between Linux and 1818 // gVisor (order of events, duplicate events), which is not that important 1819 // since inotify is asynchronous anyway. 1820 const FileDescriptor in_inotify = 1821 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1822 const FileDescriptor out_inotify = 1823 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1824 const int in_wd = ASSERT_NO_ERRNO_AND_VALUE( 1825 InotifyAddWatch(in_inotify.get(), in_file.path(), IN_ALL_EVENTS)); 1826 const int out_wd = ASSERT_NO_ERRNO_AND_VALUE( 1827 InotifyAddWatch(out_inotify.get(), out_file.path(), IN_ALL_EVENTS)); 1828 1829 ASSERT_THAT(sendfile(out.get(), in.get(), /*offset=*/nullptr, 1), 1830 SyscallSucceeds()); 1831 1832 // Expect a single access event and a single modify event. 1833 std::vector<Event> in_events = 1834 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(in_inotify.get())); 1835 std::vector<Event> out_events = 1836 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(out_inotify.get())); 1837 EXPECT_THAT(in_events, Are({Event(IN_ACCESS, in_wd)})); 1838 EXPECT_THAT(out_events, Are({Event(IN_MODIFY, out_wd)})); 1839 } 1840 1841 TEST(Inotify, SpliceOnWatchTarget) { 1842 int pipefds[2]; 1843 ASSERT_THAT(pipe2(pipefds, O_NONBLOCK), SyscallSucceeds()); 1844 1845 const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1846 const FileDescriptor inotify_fd = 1847 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1848 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 1849 dir.path(), "some content", TempPath::kDefaultFileMode)); 1850 1851 const FileDescriptor fd = 1852 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); 1853 const int dir_wd = ASSERT_NO_ERRNO_AND_VALUE( 1854 InotifyAddWatch(inotify_fd.get(), dir.path(), IN_ALL_EVENTS)); 1855 const int file_wd = ASSERT_NO_ERRNO_AND_VALUE( 1856 InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS)); 1857 1858 EXPECT_THAT(splice(fd.get(), nullptr, pipefds[1], nullptr, 1, /*flags=*/0), 1859 SyscallSucceedsWithValue(1)); 1860 1861 // Surprisingly, events may not be generated in Linux if we read from a file. 1862 // fs/splice.c:generic_file_splice_read, which is used most often, does not 1863 // generate events, whereas fs/splice.c:default_file_splice_read does. 1864 std::vector<Event> events = 1865 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1866 if (IsRunningOnGvisor()) { 1867 ASSERT_THAT(events, Are({Event(IN_ACCESS, dir_wd, Basename(file.path())), 1868 Event(IN_ACCESS, file_wd)})); 1869 } 1870 1871 EXPECT_THAT(splice(pipefds[0], nullptr, fd.get(), nullptr, 1, /*flags=*/0), 1872 SyscallSucceedsWithValue(1)); 1873 1874 // On Linux, between 983652c69199 ("splice: report related fsnotify events") 1875 // and d53471ba6f7a ("splice: remove permission hook from 1876 // iter_file_splice_write()"), splice(2) generates two modification events in 1877 // many cases, by calling fsnotify_modify() in both fs/splice.c:do_splice() 1878 // and fs/splice.c:iter_file_splice_write() => 1879 // fs/read_write.c:vfs_iter_write() => do_iter_write(). 1880 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1881 if (events.size() == 4) { 1882 EXPECT_THAT(events, Are({ 1883 Event(IN_MODIFY, dir_wd, Basename(file.path())), 1884 Event(IN_MODIFY, file_wd), 1885 Event(IN_MODIFY, dir_wd, Basename(file.path())), 1886 Event(IN_MODIFY, file_wd), 1887 })); 1888 } else { 1889 EXPECT_THAT(events, Are({ 1890 Event(IN_MODIFY, dir_wd, Basename(file.path())), 1891 Event(IN_MODIFY, file_wd), 1892 })); 1893 } 1894 } 1895 1896 // Watches on a parent should not be triggered by actions on a hard link to one 1897 // of its children that has a different parent. 1898 TEST(Inotify, LinkOnOtherParent) { 1899 // Inotify does not work properly with hard links in gofer and overlay fs. 1900 SKIP_IF(IsRunningOnGvisor() && 1901 !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir()))); 1902 1903 const TempPath dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1904 const TempPath dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 1905 const TempPath file = 1906 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); 1907 std::string link_path = NewTempAbsPathInDir(dir2.path()); 1908 1909 ASSERT_THAT(link(file.path().c_str(), link_path.c_str()), SyscallSucceeds()); 1910 1911 const FileDescriptor inotify_fd = 1912 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1913 ASSERT_NO_ERRNO_AND_VALUE( 1914 InotifyAddWatch(inotify_fd.get(), dir1.path(), IN_ALL_EVENTS)); 1915 1916 // Perform various actions on the link outside of dir1, which should trigger 1917 // no inotify events. 1918 FileDescriptor fd = 1919 ASSERT_NO_ERRNO_AND_VALUE(Open(link_path.c_str(), O_RDWR)); 1920 int val = 0; 1921 ASSERT_THAT(write(fd.get(), &val, sizeof(val)), SyscallSucceeds()); 1922 ASSERT_THAT(read(fd.get(), &val, sizeof(val)), SyscallSucceeds()); 1923 ASSERT_THAT(ftruncate(fd.get(), 12345), SyscallSucceeds()); 1924 1925 // Close before unlinking; some filesystems cannot restore an open fd on an 1926 // unlinked file. 1927 fd.reset(); 1928 ASSERT_THAT(unlink(link_path.c_str()), SyscallSucceeds()); 1929 1930 const std::vector<Event> events = 1931 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1932 EXPECT_THAT(events, Are({})); 1933 } 1934 1935 TEST(Inotify, Xattr) { 1936 // TODO(gvisor.dev/issue/1636): Support extended attributes in runsc gofer. 1937 SKIP_IF(IsRunningOnGvisor()); 1938 1939 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 1940 const std::string path = file.path(); 1941 const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_RDWR)); 1942 const FileDescriptor inotify_fd = 1943 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1944 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 1945 InotifyAddWatch(inotify_fd.get(), path, IN_ALL_EVENTS)); 1946 1947 const char* cpath = path.c_str(); 1948 const char* name = "user.test"; 1949 int val = 123; 1950 ASSERT_THAT(setxattr(cpath, name, &val, sizeof(val), /*flags=*/0), 1951 SyscallSucceeds()); 1952 std::vector<Event> events = 1953 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1954 EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd)})); 1955 1956 ASSERT_THAT(getxattr(cpath, name, &val, sizeof(val)), SyscallSucceeds()); 1957 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1958 EXPECT_THAT(events, Are({})); 1959 1960 char list[100]; 1961 ASSERT_THAT(listxattr(cpath, list, sizeof(list)), SyscallSucceeds()); 1962 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1963 EXPECT_THAT(events, Are({})); 1964 1965 ASSERT_THAT(removexattr(cpath, name), SyscallSucceeds()); 1966 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1967 EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd)})); 1968 1969 ASSERT_THAT(fsetxattr(fd.get(), name, &val, sizeof(val), /*flags=*/0), 1970 SyscallSucceeds()); 1971 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1972 EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd)})); 1973 1974 ASSERT_THAT(fgetxattr(fd.get(), name, &val, sizeof(val)), SyscallSucceeds()); 1975 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1976 EXPECT_THAT(events, Are({})); 1977 1978 ASSERT_THAT(flistxattr(fd.get(), list, sizeof(list)), SyscallSucceeds()); 1979 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1980 EXPECT_THAT(events, Are({})); 1981 1982 ASSERT_THAT(fremovexattr(fd.get(), name), SyscallSucceeds()); 1983 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 1984 EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd)})); 1985 } 1986 1987 TEST(Inotify, Exec) { 1988 const FileDescriptor fd = 1989 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 1990 // Create a new executable file instead of using /bin/true directly in case 1991 // the test suite uses it at any point and generates extra events. 1992 TempPath p = ASSERT_NO_ERRNO_AND_VALUE( 1993 TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "#!/bin/true", 0755)); 1994 1995 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 1996 InotifyAddWatch(fd.get(), p.path(), IN_ALL_EVENTS)); 1997 // Perform exec. 1998 pid_t child = -1; 1999 int execve_errno = -1; 2000 auto kill = ASSERT_NO_ERRNO_AND_VALUE( 2001 ForkAndExec(p.path(), {}, {}, nullptr, &child, &execve_errno)); 2002 ASSERT_EQ(0, execve_errno); 2003 2004 int status; 2005 ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); 2006 EXPECT_EQ(0, status); 2007 2008 // Process cleanup no longer needed. 2009 kill.Release(); 2010 2011 std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 2012 EXPECT_THAT(events, Are({Event(IN_OPEN, wd), Event(IN_ACCESS, wd), 2013 Event(IN_CLOSE_NOWRITE, wd)})); 2014 } 2015 2016 // Watches without IN_EXCL_UNLINK, should continue to emit events for file 2017 // descriptors after their corresponding files have been unlinked. 2018 // 2019 // We need to disable S/R because there are filesystems where we cannot re-open 2020 // fds to an unlinked file across S/R, e.g. gofer-backed filesytems. 2021 TEST(Inotify, IncludeUnlinkedFile) { 2022 const DisableSave ds; 2023 2024 const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 2025 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE( 2026 TempPath::CreateFileWith(dir.path(), "123", TempPath::kDefaultFileMode)); 2027 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); 2028 2029 const FileDescriptor inotify_fd = 2030 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 2031 const int dir_wd = ASSERT_NO_ERRNO_AND_VALUE( 2032 InotifyAddWatch(inotify_fd.get(), dir.path(), IN_ALL_EVENTS)); 2033 const int file_wd = ASSERT_NO_ERRNO_AND_VALUE( 2034 InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS)); 2035 2036 ASSERT_THAT(unlink(file.path().c_str()), SyscallSucceeds()); 2037 int val = 0; 2038 ASSERT_THAT(read(fd.get(), &val, sizeof(val)), SyscallSucceeds()); 2039 ASSERT_THAT(write(fd.get(), &val, sizeof(val)), SyscallSucceeds()); 2040 std::vector<Event> events = 2041 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 2042 EXPECT_THAT(events, AnyOf(Are({ 2043 Event(IN_ATTRIB, file_wd), 2044 Event(IN_DELETE, dir_wd, Basename(file.path())), 2045 Event(IN_ACCESS, dir_wd, Basename(file.path())), 2046 Event(IN_ACCESS, file_wd), 2047 Event(IN_MODIFY, dir_wd, Basename(file.path())), 2048 Event(IN_MODIFY, file_wd), 2049 }), 2050 Are({ 2051 Event(IN_DELETE, dir_wd, Basename(file.path())), 2052 Event(IN_ATTRIB, file_wd), 2053 Event(IN_ACCESS, dir_wd, Basename(file.path())), 2054 Event(IN_ACCESS, file_wd), 2055 Event(IN_MODIFY, dir_wd, Basename(file.path())), 2056 Event(IN_MODIFY, file_wd), 2057 }))); 2058 2059 fd.reset(); 2060 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 2061 EXPECT_THAT(events, Are({ 2062 Event(IN_CLOSE_WRITE, dir_wd, Basename(file.path())), 2063 Event(IN_CLOSE_WRITE, file_wd), 2064 Event(IN_DELETE_SELF, file_wd), 2065 Event(IN_IGNORED, file_wd), 2066 })); 2067 } 2068 2069 // Watches created with IN_EXCL_UNLINK will stop emitting events on fds for 2070 // children that have already been unlinked. 2071 // 2072 // We need to disable S/R because there are filesystems where we cannot re-open 2073 // fds to an unlinked file across S/R, e.g. gofer-backed filesytems. 2074 TEST(Inotify, ExcludeUnlink) { 2075 const DisableSave ds; 2076 const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 2077 const TempPath file = 2078 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); 2079 2080 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); 2081 2082 const FileDescriptor inotify_fd = 2083 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 2084 const int dir_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( 2085 inotify_fd.get(), dir.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); 2086 const int file_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( 2087 inotify_fd.get(), file.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); 2088 2089 // Unlink the child, which should cause further operations on the open file 2090 // descriptor to be ignored. 2091 ASSERT_THAT(unlink(file.path().c_str()), SyscallSucceeds()); 2092 int val = 0; 2093 ASSERT_THAT(write(fd.get(), &val, sizeof(val)), SyscallSucceeds()); 2094 ASSERT_THAT(read(fd.get(), &val, sizeof(val)), SyscallSucceeds()); 2095 std::vector<Event> events = 2096 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 2097 EXPECT_THAT(events, AreUnordered({ 2098 Event(IN_ATTRIB, file_wd), 2099 Event(IN_DELETE, dir_wd, Basename(file.path())), 2100 })); 2101 2102 fd.reset(); 2103 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 2104 ASSERT_THAT(events, Are({ 2105 Event(IN_DELETE_SELF, file_wd), 2106 Event(IN_IGNORED, file_wd), 2107 })); 2108 } 2109 2110 // We need to disable S/R because there are filesystems where we cannot re-open 2111 // fds to an unlinked file across S/R, e.g. gofer-backed filesytems. 2112 TEST(Inotify, ExcludeUnlinkDirectory) { 2113 const DisableSave ds; 2114 const TempPath parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 2115 TempPath dir = 2116 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(parent.path())); 2117 std::string dirPath = dir.path(); 2118 const FileDescriptor inotify_fd = 2119 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 2120 2121 FileDescriptor fd = 2122 ASSERT_NO_ERRNO_AND_VALUE(Open(dirPath.c_str(), O_RDONLY | O_DIRECTORY)); 2123 const int parent_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( 2124 inotify_fd.get(), parent.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); 2125 const int self_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( 2126 inotify_fd.get(), dir.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); 2127 2128 // Unlink the dir, and then close the open fd. 2129 ASSERT_THAT(rmdir(dirPath.c_str()), SyscallSucceeds()); 2130 dir.reset(); 2131 2132 std::vector<Event> events = 2133 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 2134 // No close event should appear. 2135 ASSERT_THAT(events, 2136 Are({Event(IN_DELETE | IN_ISDIR, parent_wd, Basename(dirPath))})); 2137 2138 fd.reset(); 2139 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 2140 ASSERT_THAT(events, Are({ 2141 Event(IN_DELETE_SELF, self_wd), 2142 Event(IN_IGNORED, self_wd), 2143 })); 2144 } 2145 2146 // If "dir/child" and "dir/child2" are links to the same file, and "dir/child" 2147 // is unlinked, a watch on "dir" with IN_EXCL_UNLINK will exclude future events 2148 // for fds on "dir/child" but not "dir/child2". 2149 // 2150 // We need to disable S/R because there are filesystems where we cannot re-open 2151 // fds to an unlinked file across S/R, e.g. gofer-backed filesytems. 2152 TEST(Inotify, ExcludeUnlinkMultipleChildren) { 2153 // Inotify does not work properly with hard links in gofer and overlay fs. 2154 SKIP_IF(IsRunningOnGvisor() && 2155 !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir()))); 2156 2157 const DisableSave ds; 2158 2159 const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 2160 const TempPath file = 2161 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); 2162 std::string path1 = file.path(); 2163 std::string path2 = NewTempAbsPathInDir(dir.path()); 2164 ASSERT_THAT(link(path1.c_str(), path2.c_str()), SyscallSucceeds()); 2165 2166 const FileDescriptor fd1 = 2167 ASSERT_NO_ERRNO_AND_VALUE(Open(path1.c_str(), O_RDWR)); 2168 const FileDescriptor fd2 = 2169 ASSERT_NO_ERRNO_AND_VALUE(Open(path2.c_str(), O_RDWR)); 2170 2171 const FileDescriptor inotify_fd = 2172 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 2173 const int wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( 2174 inotify_fd.get(), dir.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); 2175 2176 // After unlinking path1, only events on the fd for path2 should be generated. 2177 ASSERT_THAT(unlink(path1.c_str()), SyscallSucceeds()); 2178 ASSERT_THAT(write(fd1.get(), "x", 1), SyscallSucceeds()); 2179 ASSERT_THAT(write(fd2.get(), "x", 1), SyscallSucceeds()); 2180 2181 const std::vector<Event> events = 2182 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 2183 EXPECT_THAT(events, Are({ 2184 Event(IN_DELETE, wd, Basename(path1)), 2185 Event(IN_MODIFY, wd, Basename(path2)), 2186 })); 2187 } 2188 2189 // On native Linux, actions of data type FSNOTIFY_EVENT_INODE are not affected 2190 // by IN_EXCL_UNLINK (see 2191 // fs/notify/inotify/inotify_fsnotify.c:inotify_handle_event). Inode-level 2192 // events include changes to metadata and extended attributes. 2193 // 2194 // We need to disable S/R because there are filesystems where we cannot re-open 2195 // fds to an unlinked file across S/R, e.g. gofer-backed filesytems. 2196 TEST(Inotify, ExcludeUnlinkInodeEvents) { 2197 // NOTE(gvisor.dev/issue/3654): In the gofer filesystem, we do not allow 2198 // setting attributes through an fd if the file at the open path has been 2199 // deleted. 2200 SKIP_IF(IsRunningOnGvisor() && 2201 !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir()))); 2202 2203 const DisableSave ds; 2204 2205 const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 2206 const TempPath file = 2207 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); 2208 2209 const FileDescriptor fd = 2210 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path().c_str(), O_RDWR)); 2211 2212 const FileDescriptor inotify_fd = 2213 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 2214 const int dir_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( 2215 inotify_fd.get(), dir.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); 2216 const int file_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( 2217 inotify_fd.get(), file.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); 2218 2219 // Even after unlinking, inode-level operations will trigger events regardless 2220 // of IN_EXCL_UNLINK. 2221 ASSERT_THAT(unlink(file.path().c_str()), SyscallSucceeds()); 2222 2223 // Perform various actions on fd. 2224 ASSERT_THAT(ftruncate(fd.get(), 12345), SyscallSucceeds()); 2225 std::vector<Event> events = 2226 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 2227 EXPECT_THAT(events, AnyOf(Are({ 2228 Event(IN_ATTRIB, file_wd), 2229 Event(IN_DELETE, dir_wd, Basename(file.path())), 2230 Event(IN_MODIFY, dir_wd, Basename(file.path())), 2231 Event(IN_MODIFY, file_wd), 2232 }), 2233 Are({ 2234 Event(IN_DELETE, dir_wd, Basename(file.path())), 2235 Event(IN_ATTRIB, file_wd), 2236 Event(IN_MODIFY, dir_wd, Basename(file.path())), 2237 Event(IN_MODIFY, file_wd), 2238 }))); 2239 2240 const struct timeval times[2] = {{1, 0}, {2, 0}}; 2241 ASSERT_THAT(futimes(fd.get(), times), SyscallSucceeds()); 2242 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 2243 EXPECT_THAT(events, Are({ 2244 Event(IN_ATTRIB, dir_wd, Basename(file.path())), 2245 Event(IN_ATTRIB, file_wd), 2246 })); 2247 2248 // S/R is disabled on this entire test due to behavior with unlink; it must 2249 // also be disabled after this point because of fchmod. 2250 ASSERT_THAT(fchmod(fd.get(), 0777), SyscallSucceeds()); 2251 events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 2252 EXPECT_THAT(events, Are({ 2253 Event(IN_ATTRIB, dir_wd, Basename(file.path())), 2254 Event(IN_ATTRIB, file_wd), 2255 })); 2256 } 2257 2258 TEST(Inotify, OneShot) { 2259 const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 2260 const FileDescriptor inotify_fd = 2261 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 2262 2263 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 2264 InotifyAddWatch(inotify_fd.get(), file.path(), IN_MODIFY | IN_ONESHOT)); 2265 2266 // Open an fd, write to it, and then close it. 2267 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY)); 2268 ASSERT_THAT(write(fd.get(), "x", 1), SyscallSucceedsWithValue(1)); 2269 fd.reset(); 2270 2271 // We should get a single event followed by IN_IGNORED indicating removal 2272 // of the one-shot watch. Prior activity (i.e. open) that is not in the mask 2273 // should not trigger removal, and activity after removal (i.e. close) should 2274 // not generate events. 2275 std::vector<Event> events = 2276 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); 2277 EXPECT_THAT(events, Are({ 2278 Event(IN_MODIFY, wd), 2279 Event(IN_IGNORED, wd), 2280 })); 2281 2282 // The watch should already have been removed. 2283 EXPECT_THAT(inotify_rm_watch(inotify_fd.get(), wd), 2284 SyscallFailsWithErrno(EINVAL)); 2285 } 2286 2287 // This test helps verify that the lock order of filesystem and inotify locks 2288 // is respected when inotify instances and watch targets are concurrently being 2289 // destroyed. 2290 TEST(InotifyTest, InotifyAndTargetDestructionDoNotDeadlock) { 2291 const DisableSave ds; // Too many syscalls. 2292 2293 // A file descriptor protected by a mutex. This ensures that while a 2294 // descriptor is in use, it cannot be closed and reused for a different file 2295 // description. 2296 struct atomic_fd { 2297 int fd; 2298 absl::Mutex mu; 2299 }; 2300 2301 // Set up initial inotify instances. 2302 constexpr int num_fds = 3; 2303 std::vector<atomic_fd> fds(num_fds); 2304 for (int i = 0; i < num_fds; i++) { 2305 int fd; 2306 ASSERT_THAT(fd = inotify_init1(IN_NONBLOCK), SyscallSucceeds()); 2307 fds[i].fd = fd; 2308 } 2309 2310 // Set up initial watch targets. 2311 std::vector<std::string> paths; 2312 for (int i = 0; i < 3; i++) { 2313 paths.push_back(NewTempAbsPath()); 2314 ASSERT_THAT(mknod(paths[i].c_str(), S_IFREG | 0600, 0), SyscallSucceeds()); 2315 } 2316 2317 constexpr absl::Duration runtime = absl::Seconds(4); 2318 2319 // Constantly replace each inotify instance with a new one. 2320 auto replace_fds = [&] { 2321 for (auto start = absl::Now(); absl::Now() - start < runtime;) { 2322 for (auto& afd : fds) { 2323 int new_fd; 2324 ASSERT_THAT(new_fd = inotify_init1(IN_NONBLOCK), SyscallSucceeds()); 2325 absl::MutexLock l(&afd.mu); 2326 ASSERT_THAT(close(afd.fd), SyscallSucceeds()); 2327 afd.fd = new_fd; 2328 for (auto& p : paths) { 2329 // inotify_add_watch may fail if the file at p was deleted. 2330 ASSERT_THAT(inotify_add_watch(afd.fd, p.c_str(), IN_ALL_EVENTS), 2331 AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(ENOENT))); 2332 } 2333 } 2334 sched_yield(); 2335 } 2336 }; 2337 2338 std::list<ScopedThread> ts; 2339 for (int i = 0; i < 3; i++) { 2340 ts.emplace_back(replace_fds); 2341 } 2342 2343 // Constantly replace each watch target with a new one. 2344 for (auto start = absl::Now(); absl::Now() - start < runtime;) { 2345 for (auto& p : paths) { 2346 ASSERT_THAT(unlink(p.c_str()), SyscallSucceeds()); 2347 ASSERT_THAT(mknod(p.c_str(), S_IFREG | 0600, 0), SyscallSucceeds()); 2348 } 2349 sched_yield(); 2350 } 2351 } 2352 2353 // This test helps verify that the lock order of filesystem and inotify locks 2354 // is respected when adding/removing watches occurs concurrently with the 2355 // removal of their targets. 2356 TEST(InotifyTest, AddRemoveUnlinkDoNotDeadlock) { 2357 const DisableSave ds; // Too many syscalls. 2358 2359 // Set up inotify instances. 2360 constexpr int num_fds = 3; 2361 std::vector<int> fds(num_fds); 2362 for (int i = 0; i < num_fds; i++) { 2363 ASSERT_THAT(fds[i] = inotify_init1(IN_NONBLOCK), SyscallSucceeds()); 2364 } 2365 2366 // Set up initial watch targets. 2367 std::vector<std::string> paths; 2368 for (int i = 0; i < 3; i++) { 2369 paths.push_back(NewTempAbsPath()); 2370 ASSERT_THAT(mknod(paths[i].c_str(), S_IFREG | 0600, 0), SyscallSucceeds()); 2371 } 2372 2373 constexpr absl::Duration runtime = absl::Seconds(1); 2374 2375 // Constantly add/remove watches for each inotify instance/watch target pair. 2376 auto add_remove_watches = [&] { 2377 for (auto start = absl::Now(); absl::Now() - start < runtime;) { 2378 for (int fd : fds) { 2379 for (auto& p : paths) { 2380 // Do not assert on inotify_add_watch and inotify_rm_watch. They may 2381 // fail if the file at p was deleted. inotify_add_watch may also fail 2382 // if another thread beat us to adding a watch. 2383 const int wd = inotify_add_watch(fd, p.c_str(), IN_ALL_EVENTS); 2384 if (wd > 0) { 2385 inotify_rm_watch(fd, wd); 2386 } 2387 } 2388 } 2389 sched_yield(); 2390 } 2391 }; 2392 2393 std::list<ScopedThread> ts; 2394 for (int i = 0; i < 15; i++) { 2395 ts.emplace_back(add_remove_watches); 2396 } 2397 2398 // Constantly replace each watch target with a new one. 2399 for (auto start = absl::Now(); absl::Now() - start < runtime;) { 2400 for (auto& p : paths) { 2401 ASSERT_THAT(unlink(p.c_str()), SyscallSucceeds()); 2402 ASSERT_THAT(mknod(p.c_str(), S_IFREG | 0600, 0), SyscallSucceeds()); 2403 } 2404 sched_yield(); 2405 } 2406 } 2407 2408 // This test helps verify that the lock order of filesystem and inotify locks 2409 // is respected when many inotify events and filesystem operations occur 2410 // simultaneously. 2411 TEST(InotifyTest, NotifyNoDeadlock) { 2412 const DisableSave ds; // Too many syscalls. 2413 2414 const TempPath parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); 2415 const std::string dir = parent.path(); 2416 2417 // mu protects file, which will change on rename. 2418 absl::Mutex mu; 2419 std::string file = NewTempAbsPathInDir(dir); 2420 ASSERT_THAT(mknod(file.c_str(), 0644 | S_IFREG, 0), SyscallSucceeds()); 2421 2422 const absl::Duration runtime = absl::Milliseconds(300); 2423 2424 // Add/remove watches on dir and file. 2425 ScopedThread add_remove_watches([&] { 2426 const FileDescriptor ifd = 2427 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 2428 int dir_wd = ASSERT_NO_ERRNO_AND_VALUE( 2429 InotifyAddWatch(ifd.get(), dir, IN_ALL_EVENTS)); 2430 int file_wd; 2431 { 2432 absl::ReaderMutexLock l(&mu); 2433 file_wd = ASSERT_NO_ERRNO_AND_VALUE( 2434 InotifyAddWatch(ifd.get(), file, IN_ALL_EVENTS)); 2435 } 2436 for (auto start = absl::Now(); absl::Now() - start < runtime;) { 2437 ASSERT_THAT(inotify_rm_watch(ifd.get(), file_wd), SyscallSucceeds()); 2438 ASSERT_THAT(inotify_rm_watch(ifd.get(), dir_wd), SyscallSucceeds()); 2439 dir_wd = ASSERT_NO_ERRNO_AND_VALUE( 2440 InotifyAddWatch(ifd.get(), dir, IN_ALL_EVENTS)); 2441 { 2442 absl::ReaderMutexLock l(&mu); 2443 file_wd = ASSERT_NO_ERRNO_AND_VALUE( 2444 InotifyAddWatch(ifd.get(), file, IN_ALL_EVENTS)); 2445 } 2446 sched_yield(); 2447 } 2448 }); 2449 2450 // Modify attributes on dir and file. 2451 ScopedThread stats([&] { 2452 int fd, dir_fd; 2453 { 2454 absl::ReaderMutexLock l(&mu); 2455 ASSERT_THAT(fd = open(file.c_str(), O_RDONLY), SyscallSucceeds()); 2456 } 2457 ASSERT_THAT(dir_fd = open(dir.c_str(), O_RDONLY | O_DIRECTORY), 2458 SyscallSucceeds()); 2459 const struct timeval times[2] = {{1, 0}, {2, 0}}; 2460 2461 for (auto start = absl::Now(); absl::Now() - start < runtime;) { 2462 { 2463 absl::ReaderMutexLock l(&mu); 2464 EXPECT_THAT(utimes(file.c_str(), times), SyscallSucceeds()); 2465 } 2466 EXPECT_THAT(futimes(fd, times), SyscallSucceeds()); 2467 EXPECT_THAT(utimes(dir.c_str(), times), SyscallSucceeds()); 2468 EXPECT_THAT(futimes(dir_fd, times), SyscallSucceeds()); 2469 sched_yield(); 2470 } 2471 }); 2472 2473 // Modify extended attributes on dir and file. 2474 ScopedThread xattrs([&] { 2475 // TODO(gvisor.dev/issue/1636): Support extended attributes in runsc gofer. 2476 if (!IsRunningOnGvisor()) { 2477 int fd; 2478 { 2479 absl::ReaderMutexLock l(&mu); 2480 ASSERT_THAT(fd = open(file.c_str(), O_RDONLY), SyscallSucceeds()); 2481 } 2482 2483 const char* name = "user.test"; 2484 int val = 123; 2485 for (auto start = absl::Now(); absl::Now() - start < runtime;) { 2486 { 2487 absl::ReaderMutexLock l(&mu); 2488 ASSERT_THAT( 2489 setxattr(file.c_str(), name, &val, sizeof(val), /*flags=*/0), 2490 SyscallSucceeds()); 2491 ASSERT_THAT(removexattr(file.c_str(), name), SyscallSucceeds()); 2492 } 2493 2494 ASSERT_THAT(fsetxattr(fd, name, &val, sizeof(val), /*flags=*/0), 2495 SyscallSucceeds()); 2496 ASSERT_THAT(fremovexattr(fd, name), SyscallSucceeds()); 2497 sched_yield(); 2498 } 2499 } 2500 }); 2501 2502 // Read and write file's contents. Read and write dir's entries. 2503 ScopedThread read_write([&] { 2504 int fd; 2505 { 2506 absl::ReaderMutexLock l(&mu); 2507 ASSERT_THAT(fd = open(file.c_str(), O_RDWR), SyscallSucceeds()); 2508 } 2509 for (auto start = absl::Now(); absl::Now() - start < runtime;) { 2510 int val = 123; 2511 ASSERT_THAT(write(fd, &val, sizeof(val)), SyscallSucceeds()); 2512 ASSERT_THAT(read(fd, &val, sizeof(val)), SyscallSucceeds()); 2513 TempPath new_file = 2514 ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir)); 2515 ASSERT_NO_ERRNO(ListDir(dir, false)); 2516 new_file.reset(); 2517 sched_yield(); 2518 } 2519 }); 2520 2521 // Rename file. 2522 for (auto start = absl::Now(); absl::Now() - start < runtime;) { 2523 const std::string new_path = NewTempAbsPathInDir(dir); 2524 { 2525 absl::WriterMutexLock l(&mu); 2526 ASSERT_THAT(rename(file.c_str(), new_path.c_str()), SyscallSucceeds()); 2527 file = new_path; 2528 } 2529 sched_yield(); 2530 } 2531 } 2532 2533 // NOTE(b/239215242): Regression test. 2534 TEST(Inotify, KernfsBasic) { 2535 const FileDescriptor fd = 2536 ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); 2537 const std::string procFile = "/proc/filesystems"; 2538 2539 const int wd = ASSERT_NO_ERRNO_AND_VALUE( 2540 InotifyAddWatch(fd.get(), procFile, IN_ALL_EVENTS)); 2541 const FileDescriptor file1_fd = 2542 ASSERT_NO_ERRNO_AND_VALUE(Open(procFile, O_RDONLY)); 2543 2544 char buf; 2545 EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); 2546 2547 const std::vector<Event> events = 2548 ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); 2549 ASSERT_THAT(events, Are({Event(IN_OPEN, wd), Event(IN_ACCESS, wd)})); 2550 } 2551 2552 } // namespace 2553 } // namespace testing 2554 } // namespace gvisor