mirror of
https://github.com/SerenityOS/serenity.git
synced 2026-11-12 02:01:45 -10:00
This prevents use-after-frees when the watcher tries to access the inode after it has been freed. Beforefe5ca6ca27, the inode was correctly stored as a WeakPtr. For some reason that commit changed it to a raw reference instead. (This was before11eee67b85, so before the old WeakPtr was renamed to LockWeakPtr. Inode derives from LockWeakable, so we unfortunately have to use LockWeakPtr here.) We have to add a new WatchDescription member to store the InodeIdentifier for the inode so that we can still remove the inode_to_watches entry for inodes that have already been freed.
91 lines
3.0 KiB
C++
91 lines
3.0 KiB
C++
/*
|
|
* Copyright (c) 2018-2020, Andreas Kling <[email protected]>
|
|
* Copyright (c) 2021, sin-ack <[email protected]>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/Badge.h>
|
|
#include <AK/Checked.h>
|
|
#include <AK/CircularQueue.h>
|
|
#include <AK/HashMap.h>
|
|
#include <AK/NonnullOwnPtr.h>
|
|
#include <Kernel/API/InodeWatcherEvent.h>
|
|
#include <Kernel/FileSystem/File.h>
|
|
#include <Kernel/FileSystem/Inode.h>
|
|
#include <Kernel/FileSystem/InodeIdentifier.h>
|
|
#include <Kernel/Forward.h>
|
|
#include <Kernel/Locking/MutexProtected.h>
|
|
#include <Kernel/Locking/SpinlockProtected.h>
|
|
|
|
namespace Kernel {
|
|
|
|
// A specific description of a watch.
|
|
struct WatchDescription {
|
|
int wd;
|
|
LockWeakPtr<Inode> inode;
|
|
InodeIdentifier inode_identifier;
|
|
unsigned event_mask;
|
|
|
|
static ErrorOr<NonnullOwnPtr<WatchDescription>> create(int wd, Inode& inode, unsigned event_mask)
|
|
{
|
|
return adopt_nonnull_own_or_enomem(new (nothrow) WatchDescription(wd, inode, event_mask));
|
|
}
|
|
|
|
private:
|
|
WatchDescription(int wd, Inode& inode, unsigned event_mask)
|
|
: wd(wd)
|
|
, inode(inode)
|
|
, inode_identifier(inode.identifier())
|
|
, event_mask(event_mask)
|
|
{
|
|
}
|
|
};
|
|
|
|
class InodeWatcher final : public File {
|
|
public:
|
|
static ErrorOr<NonnullRefPtr<InodeWatcher>> try_create();
|
|
virtual ~InodeWatcher() override;
|
|
|
|
virtual bool can_read(OpenFileDescription const&, u64) const override;
|
|
virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override;
|
|
// Can't write to an inode watcher.
|
|
virtual bool can_write(OpenFileDescription const&, u64) const override { return true; }
|
|
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return EIO; }
|
|
virtual ErrorOr<void> close() override;
|
|
|
|
virtual ErrorOr<NonnullOwnPtr<KString>> pseudo_path(OpenFileDescription const&) const override;
|
|
virtual StringView class_name() const override { return "InodeWatcher"sv; }
|
|
virtual bool is_inode_watcher() const override { return true; }
|
|
|
|
void notify_inode_event(Badge<Inode>, InodeIdentifier, InodeWatcherEvent::Type, StringView name = {});
|
|
|
|
ErrorOr<int> register_inode(Inode&, unsigned event_mask);
|
|
ErrorOr<void> unregister_by_wd(int);
|
|
void unregister_by_inode(Badge<Inode>, InodeIdentifier);
|
|
|
|
private:
|
|
explicit InodeWatcher() { }
|
|
|
|
struct Event {
|
|
int wd { 0 };
|
|
InodeWatcherEvent::Type type { InodeWatcherEvent::Type::Invalid };
|
|
OwnPtr<KString> path;
|
|
};
|
|
SpinlockProtected<CircularQueue<Event, 32>, LockRank::None> m_queue;
|
|
Checked<int> m_wd_counter { 1 };
|
|
|
|
// NOTE: These two hashmaps provide two different ways of reaching the same
|
|
// watch description, so they will overlap.
|
|
struct WatchMaps {
|
|
HashMap<int, NonnullOwnPtr<WatchDescription>> wd_to_watches;
|
|
HashMap<InodeIdentifier, WatchDescription*> inode_to_watches;
|
|
};
|
|
|
|
mutable SpinlockProtected<WatchMaps, LockRank::None> m_watch_maps;
|
|
};
|
|
|
|
}
|