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