diff --git a/Tests/LibCore/TestLibCoreFileWatcher.cpp b/Tests/LibCore/TestLibCoreFileWatcher.cpp index 98f623c6e2a..2a35afc269a 100644 --- a/Tests/LibCore/TestLibCoreFileWatcher.cpp +++ b/Tests/LibCore/TestLibCoreFileWatcher.cpp @@ -107,3 +107,54 @@ TEST_CASE(contents_changed) event_loop.exec(); } + +TEST_CASE(symbolic_link) +{ + auto event_loop = Core::EventLoop(); + + auto temp_path = MUST(FileSystem::real_path("/tmp"sv)); + auto test_file = LexicalPath::join(temp_path, "testfile"sv); + auto test_link1 = LexicalPath::join(temp_path, "testlink1"sv); + auto test_link2 = LexicalPath::join(temp_path, "testlink2"sv); + + (void)MUST(Core::File::open(test_link1.string(), Core::File::OpenMode::ReadWrite)); + (void)MUST(Core::File::open(test_link2.string(), Core::File::OpenMode::ReadWrite)); + MUST(Core::System::symlink(test_link1.string(), test_file.string())); + + auto file_watcher = MUST(Core::FileWatcher::create()); + MUST(file_watcher->add_watch(test_file.string(), Core::FileWatcherEvent::Type::Deleted | Core::FileWatcherEvent::Type::DoNotFollowLink)); + + int event_count = 0; + file_watcher->on_change = [&](Core::FileWatcherEvent const& event) { + EXPECT_EQ(event.event_path, test_file.string()); + EXPECT(has_flag(event.type, Core::FileWatcherEvent::Type::Deleted)); + + MUST(file_watcher->add_watch(test_file.string(), Core::FileWatcherEvent::Type::Deleted | Core::FileWatcherEvent::Type::DoNotFollowLink)); + + if (++event_count == 2) { + MUST(Core::System::unlink(test_file.string())); + MUST(Core::System::unlink(test_link1.string())); + MUST(Core::System::unlink(test_link2.string())); + event_loop.quit(0); + } + }; + + auto timer1 = Core::Timer::create_single_shot(500, [&] { + MUST(Core::System::unlink(test_file.string())); + MUST(Core::System::symlink(test_link1.string(), test_file.string())); + }); + timer1->start(); + + auto timer2 = Core::Timer::create_single_shot(1000, [&] { + MUST(Core::System::unlink(test_file.string())); + MUST(Core::System::symlink(test_link2.string(), test_file.string())); + }); + timer2->start(); + + auto catchall_timer = Core::Timer::create_single_shot(2000, [&] { + VERIFY_NOT_REACHED(); + }); + catchall_timer->start(); + + event_loop.exec(); +} diff --git a/Userland/Libraries/LibCore/FileWatcher.h b/Userland/Libraries/LibCore/FileWatcher.h index 42bb783131f..ea05ad0b841 100644 --- a/Userland/Libraries/LibCore/FileWatcher.h +++ b/Userland/Libraries/LibCore/FileWatcher.h @@ -25,6 +25,7 @@ struct FileWatcherEvent { Deleted = 1 << 2, ChildCreated = 1 << 3, ChildDeleted = 1 << 4, + DoNotFollowLink = 1 << 5, }; Type type { Type::Invalid }; ByteString event_path; diff --git a/Userland/Libraries/LibCore/FileWatcherLinux.cpp b/Userland/Libraries/LibCore/FileWatcherLinux.cpp index 12319a302bc..97de7c6a8ed 100644 --- a/Userland/Libraries/LibCore/FileWatcherLinux.cpp +++ b/Userland/Libraries/LibCore/FileWatcherLinux.cpp @@ -145,6 +145,8 @@ ErrorOr FileWatcherBase::add_watch(ByteString path, FileWatcherEvent::Type inotify_mask |= IN_MODIFY; if (has_flag(event_mask, FileWatcherEvent::Type::MetadataModified)) inotify_mask |= IN_ATTRIB; + if (has_flag(event_mask, FileWatcherEvent::Type::DoNotFollowLink)) + inotify_mask |= IN_DONT_FOLLOW; int watch_descriptor = ::inotify_add_watch(m_watcher_fd, path.characters(), inotify_mask); if (watch_descriptor < 0)