9#include "inotify-syscalls.h"
10#include "pedigree/kernel/LockGuard.h"
11#include "pedigree/kernel/errors.h"
12#include "pedigree/kernel/process/ConditionVariable.h"
13#include "pedigree/kernel/process/Mutex.h"
14#include "pedigree/kernel/process/Process.h"
15#include "pedigree/kernel/processor/Processor.h"
16#include "pedigree/kernel/processor/ProcessorInformation.h"
17#include "pedigree/kernel/syscallError.h"
18#include "pedigree/kernel/utilities/List.h"
19#include "pedigree/kernel/utilities/SharedPointer.h"
20#include "pedigree/kernel/utilities/String.h"
21#include "pedigree/kernel/utilities/Vector.h"
22#include "pedigree/kernel/utilities/utility.h"
27#include "modules/subsys/posix/FileDescriptor.h"
28#include "modules/subsys/posix/PosixSubsystem.h"
29#include "modules/subsys/posix/ResolvedPath.h"
30#include "modules/subsys/posix/file-syscalls.h"
31#include "modules/system/vfs/Directory.h"
32#include "modules/system/vfs/File.h"
33#include "modules/system/vfs/FileEvent.h"
34#include "modules/system/vfs/VFS.h"
37constexpr size_t MaxQueuedEvents = 16384;
38constexpr FileEventMask AllFileEvents =
39 FileEvents::Access | FileEvents::Modify | FileEvents::Attributes | FileEvents::CloseWrite |
40 FileEvents::CloseNoWrite | FileEvents::Open | FileEvents::Created | FileEvents::Removed |
41 FileEvents::DeletedSelf;
42constexpr uint32_t AcceptedMask = LinuxInotify::AllBits;
46 InotifyResult(
int result)
48 error(result < 0 ?
Processor::information().getCurrentThread()->getErrno() : 0) {}
53size_t paddedNameLength(
const String& name) {
60uint32_t linuxMaskFor(FileEventMask mask) {
62 if (mask & FileEvents::Access)
63 result |= LinuxInotify::Access;
64 if (mask & FileEvents::Modify)
65 result |= LinuxInotify::Modify;
66 if (mask & FileEvents::Attributes)
67 result |= LinuxInotify::Attributes;
68 if (mask & FileEvents::CloseWrite)
69 result |= LinuxInotify::CloseWrite;
70 if (mask & FileEvents::CloseNoWrite)
71 result |= LinuxInotify::CloseNoWrite;
72 if (mask & FileEvents::Open)
73 result |= LinuxInotify::Open;
74 if (mask & FileEvents::Created)
75 result |= LinuxInotify::Create;
76 if (mask & FileEvents::Removed)
77 result |= LinuxInotify::Delete;
78 if (mask & FileEvents::DeletedSelf)
79 result |= LinuxInotify::DeleteSelf;
84 QueuedEvent(
int descriptor, uint32_t eventMask, uint32_t eventCookie,
const StringView& eventName)
85 : wd(descriptor), mask(eventMask), cookie(eventCookie), name(eventName.toString()) {}
96 : owner(readinessOwner),
102 overflowQueued(false) {}
105 while (events.count()) {
106 delete events.popFront();
110 bool enqueue(
int wd, uint32_t mask, uint32_t cookie,
const StringView& name) {
111 bool becameReadable =
false;
118 if (events.count() >= MaxQueuedEvents) {
119 if (!overflowQueued) {
120 becameReadable = !events.count();
121 events.pushBack(
new QueuedEvent(-1, LinuxInotify::QueueOverflow, 0,
StringView()));
122 overflowQueued =
true;
123 if (becameReadable) {
129 owner->eventsQueued();
130 return becameReadable;
133 if (events.count()) {
134 QueuedEvent* last = *events.rbegin();
135 if (last && !(last->mask & LinuxInotify::Ignored) && last->wd == wd && last->mask == mask &&
136 last->name.view() == name) {
142 becameReadable = !events.count();
143 events.pushBack(
new QueuedEvent(wd, mask, cookie, name));
144 if (becameReadable) {
149 owner->eventsQueued();
150 return becameReadable;
153 int readOne(uint8_t* buffer,
size_t length,
bool canBlock) {
155 while (!events.count()) {
158 SYSCALL_ERROR(BadFileDescriptor);
163 SYSCALL_ERROR(NoMoreProcesses);
166 ConditionVariable::Error error = ConditionVariable::NoError;
167 if (!changed.wait(lock, error)) {
171 if (error == ConditionVariable::Interrupted ||
172 error == ConditionVariable::TerminationDeferred) {
173 SYSCALL_ERROR(Interrupted);
175 SYSCALL_ERROR(BadFileDescriptor);
183 const size_t firstSize =
sizeof(
LinuxInotifyEvent) + paddedNameLength((*events.begin())->name);
184 if (firstSize > length) {
186 SYSCALL_ERROR(InvalidArgument);
190 QueuedEvent*
event = *events.begin();
191 const size_t nameLength = paddedNameLength(event->name);
194 static_cast<uint32_t
>(nameLength)};
195 MemoryCopy(buffer, &header,
sizeof(header));
197 ByteSet(buffer +
sizeof(header), 0, nameLength);
198 MemoryCopy(buffer +
sizeof(header), event->name.cstr(), event->name.length());
200 event = events.popFront();
201 if (event->mask & LinuxInotify::QueueOverflow) {
202 overflowQueued =
false;
206 return static_cast<int>(recordSize);
209 ReadyMask queryReady() {
211 return events.count() ? ReadyRead : ReadyNone;
226 while (events.count()) {
227 delete events.popFront();
229 overflowQueued =
false;
247 uint32_t eventMask,
bool directory)
252 isDirectory(directory),
255 void updateMask(uint32_t eventMask,
bool add) {
258 mask |= eventMask & ~LinuxInotify::MaskAdd;
266 const bool wasActive = active;
281 uint32_t selected = linuxMaskFor(event.mask) & mask;
282 const bool deleted =
event.mask & FileEvents::DeletedSelf;
283 if (!selected && !deleted) {
286 if (!(selected & (LinuxInotify::DeleteSelf | LinuxInotify::MoveSelf)) &&
287 ((event.
name.length() && event.targetIsDirectory) ||
288 (!event.
name.length() && isDirectory))) {
289 selected |= LinuxInotify::IsDirectory;
291 const bool retire = deleted || ((mask & LinuxInotify::OneShot) && selected);
296 queue->enqueue(wd, selected, 0, event.
name);
299 queue->enqueue(wd, LinuxInotify::Ignored, 0,
StringView());
313 InotifyWatch(
File* watchedTarget,
bool retainedTarget,
315 InotifyWatchObserver* concreteObserver,
int descriptor)
316 : target(watchedTarget),
317 retained(retainedTarget),
318 observer(watchObserver),
319 observerImpl(concreteObserver),
326 InotifyWatchObserver* observerImpl;
331void retireWatch(InotifyWatch* watch) {
335 watch->subscription.reset();
336 if (watch->retained) {
337 watch->target->releaseVfsReference();
347 : lock(), watches(), queue(
new InotifyQueue(owner)), nextWd(1), descriptorOpen(
true) {}
358InotifyInstance::~InotifyInstance() {
359 lastDescriptorClosed();
364void InotifyInstance::reapInactiveWatches() {
367 for (
auto it = m_State->watches.
begin(); it != m_State->watches.
end();) {
368 InotifyWatch* watch = *it;
369 if (!watch->observerImpl->isActive()) {
370 it = m_State->watches.
erase(it);
378 while (retiring.
count()) {
383int InotifyInstance::addWatch(
File* target, uint32_t mask) {
384 if (!target || (mask & ~AcceptedMask) || !(mask & AcceptedMask) ||
385 ((mask & LinuxInotify::MaskAdd) && (mask & LinuxInotify::MaskCreate))) {
386 SYSCALL_ERROR(InvalidArgument);
389 if ((mask & LinuxInotify::OnlyDirectory) && !target->
isDirectory()) {
390 SYSCALL_ERROR(NotADirectory);
394 reapInactiveWatches();
397 if (!m_State->descriptorOpen) {
399 SYSCALL_ERROR(BadFileDescriptor);
402 for (
auto watch : m_State->watches) {
403 if (watch->target != target || !watch->observerImpl->isActive()) {
406 if (mask & LinuxInotify::MaskCreate) {
408 SYSCALL_ERROR(FileExists);
411 watch->observerImpl->updateMask(mask, mask & LinuxInotify::MaskAdd);
412 const int wd = watch->wd;
417 int wd = m_State->nextWd++;
425 SYSCALL_ERROR(DoesNotExist);
429 InotifyWatchObserver* observerImpl =
430 new InotifyWatchObserver(m_State->queue, wd, mask, target->
isDirectory());
432 InotifyWatch* watch =
new InotifyWatch(target, retained, observer, observerImpl, wd);
433 if (!target->subscribeFileEvents(AllFileEvents, observer, watch->subscription)) {
439 SYSCALL_ERROR(DoesNotExist);
447int InotifyInstance::removeWatch(
int wd) {
448 reapInactiveWatches();
450 InotifyWatch* retiring =
nullptr;
451 bool wasActive =
false;
453 for (
auto it = m_State->watches.
begin(); it != m_State->watches.
end(); ++it) {
454 if ((*it)->wd == wd) {
456 retiring->subscription.reset();
457 wasActive = retiring->observerImpl->deactivate();
458 m_State->watches.
erase(it);
460 m_State->queue->enqueue(wd, LinuxInotify::Ignored, 0,
StringView());
467 SYSCALL_ERROR(InvalidArgument);
471 if (retiring->retained) {
472 retiring->target->releaseVfsReference();
476 SYSCALL_ERROR(InvalidArgument);
482int InotifyInstance::readEvents(uint8_t* buffer,
size_t length,
bool canBlock) {
485 bool attempted =
false;
486 while (!attempted || copied < length) {
488 uint8_t* destination = copied ? buffer + copied : buffer;
489 const int result = m_State->queue->readOne(destination, length - copied, canBlock && !copied);
493 outcome =
static_cast<int>(copied);
499 copied +=
static_cast<size_t>(result);
500 outcome =
static_cast<int>(copied);
502 reapInactiveWatches();
506int InotifyInstance::readEventsToUser(uint8_t* buffer,
size_t length,
bool canBlock) {
507 constexpr size_t MaximumInotifyRecord =
510 const size_t bounceCapacity = length < MaximumInotifyRecord ? length : MaximumInotifyRecord;
515 bool attempted =
false;
516 while (!attempted || copied < length) {
518 const size_t remaining = length - copied;
519 const size_t eventCapacity = remaining < bounceCapacity ? remaining : bounceCapacity;
520 const int result = m_State->queue->readOne(bounce.get(), eventCapacity, canBlock && !copied);
524 outcome =
static_cast<int>(copied);
531 const uintptr_t base =
reinterpret_cast<uintptr_t
>(buffer);
532 if (copied > ~
static_cast<uintptr_t
>(0) - base ||
534 static_cast<size_t>(result))) {
535 SYSCALL_ERROR(BadAddress);
539 copied +=
static_cast<size_t>(result);
540 outcome =
static_cast<int>(copied);
542 reapInactiveWatches();
546ReadyMask InotifyInstance::queryReady() {
547 return m_State->queue->queryReady();
551 return m_State->queue->readinessGenerations();
558void InotifyInstance::lastDescriptorClosed() {
561 if (!m_State->descriptorOpen) {
565 m_State->descriptorOpen =
false;
566 while (m_State->watches.
count()) {
571 while (retiring.
count()) {
574 m_State->queue->close();
578int posix_inotify_init() {
579 return posix_inotify_init1(0);
582int posix_inotify_init1(
int flags) {
583 constexpr int AllowedFlags = LinuxInotify::NonBlock | LinuxInotify::CloseOnExec;
584 if (flags & ~AllowedFlags) {
585 SYSCALL_ERROR(InvalidArgument);
589 const size_t fd = getAvailableDescriptor();
590 const int descriptorFlags = flags & LinuxInotify::CloseOnExec ? FD_CLOEXEC : 0;
591 const int statusFlags = O_RDONLY | (flags & LinuxInotify::NonBlock ? O_NONBLOCK : 0);
594 addDescriptor(
static_cast<int>(fd), descriptor);
595 return static_cast<int>(fd);
598static InotifyResult addWatch(
int fd,
const char* pathname, uint32_t mask) {
602 if ((mask & ~AcceptedMask) || !(mask & AcceptedMask)) {
603 SYSCALL_ERROR(InvalidArgument);
611 SYSCALL_ERROR(BadFileDescriptor);
614 if ((mask & LinuxInotify::MaskAdd) && (mask & LinuxInotify::MaskCreate)) {
615 SYSCALL_ERROR(InvalidArgument);
620 SYSCALL_ERROR(InvalidArgument);
625 const PosixSubsystem::UserStringResult copied =
627 if (copied == PosixSubsystem::UserStringBadAddress) {
628 SYSCALL_ERROR(BadAddress);
631 if (copied == PosixSubsystem::UserStringTooLong) {
632 SYSCALL_ERROR(NameTooLong);
635 if (!path.length()) {
636 SYSCALL_ERROR(DoesNotExist);
640 normalisePath(normalised, path.cstr());
641 const bool requireDirectory = path[path.length() - 1] ==
'/';
644 !(mask & LinuxInotify::DontFollow) || requireDirectory);
647 SYSCALL_ERROR(DoesNotExist);
650 if (((mask & LinuxInotify::OnlyDirectory) || requireDirectory) && !target->
isDirectory()) {
651 SYSCALL_ERROR(NotADirectory);
657 return instance->addWatch(target, mask);
660int posix_inotify_add_watch(
int fd,
const char* pathname, uint32_t mask) {
661 const InotifyResult result = addWatch(fd, pathname, mask);
662 syscallError(result.error);
666int posix_inotify_rm_watch(
int fd,
int wd) {
671 SYSCALL_ERROR(BadFileDescriptor);
676 SYSCALL_ERROR(InvalidArgument);
679 return instance->removeWatch(wd);
static bool mutexAcquired(Error error)
SharedPointer< InotifyInstance > getInotifyImpl() const
void setInotifyImpl(const SharedPointer< InotifyInstance > &implementation)
virtual void fileEvent(const FileEvent &event)=0
virtual bool retainVfsReference()
virtual void releaseVfsReference()
virtual bool isStableVfsRoot() const
virtual bool isDirectory()
ReadinessGenerations readinessGenerations() override
bool acquireFileDescriptor(size_t fd, DescriptorLease &descriptor)
static UserStringResult copyUserString(const char *userString, String ©, size_t maxLength)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static ProcessorInformation & information()
void notifyReadiness(ReadyMask mask)
void closeReadiness(ReadyMask mask=ReadyInvalid|ReadyHangup)
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
static bool checkAccess(File *pFile, bool bRead, bool bWrite, bool bExecute)
Iterator erase(Iterator &Iter)
void pushBack(const T &value)