ramfs: Rework NodeMTimeUpdater into NodeStatChangeNotifier.

This now does what notify_if_stat_changed used to, fixing a
race: if some thread was modifying a file, and some other thread
(or the same thread, in some other mtime-updating operation)
updated that file and this called MarkUnmodified() before the
first thread close()d it, then when the first thread called close(),
no node monitor notification would've been sent, as fModified
would've already been unset.

So, now every time we are to mark a file "unmodified", we also
send the node monitor notification at the same time.

Fixes the remainder of #19910.
This commit is contained in:
Augustin Cavalier
2026-02-26 16:11:50 -05:00
parent 0c5895120d
commit b43289d544
3 changed files with 60 additions and 72 deletions
@@ -135,6 +135,19 @@ Node::SetMTime(time_t mTime)
} }
uint32
Node::MarkUnmodified()
{
uint32 modified = fModified;
if (modified) {
fCTime = time(NULL);
SetMTime(fCTime);
fModified = 0;
}
return modified;
}
status_t status_t
Node::CheckPermissions(int mode) const Node::CheckPermissions(int mode) const
{ {
@@ -317,3 +330,20 @@ Node::GetAllocationInfo(AllocationInfo &info)
while (GetNextAttribute(&attribute) == B_OK) while (GetNextAttribute(&attribute) == B_OK)
attribute->GetAllocationInfo(info); attribute->GetAllocationInfo(info);
} }
NodeStatChangeNotifier::~NodeStatChangeNotifier()
{
if (fNode == NULL || !fNode->IsModified())
return;
uint32 statFields = fNode->MarkUnmodified();
for (Entry* entry = fNode->GetFirstReferrer(); entry != NULL;
entry = fNode->GetNextReferrer(entry)) {
ino_t parentID = -1;
if (entry->GetParent() != NULL)
parentID = ((Node*)entry->GetParent())->GetID();
notify_stat_changed(fNode->GetVolume()->GetID(), parentID,
fNode->GetID(), statFields);
}
}
+4 -21
View File
@@ -129,32 +129,15 @@ protected:
DoublyLinkedList<Entry, GetNodeReferrerLink> fReferrers; DoublyLinkedList<Entry, GetNodeReferrerLink> fReferrers;
}; };
// MarkUnmodified
inline
uint32
Node::MarkUnmodified()
{
uint32 modified = fModified;
if (modified) {
fCTime = time(NULL);
SetMTime(fCTime);
fModified = 0;
}
return modified;
}
// NodeMTimeUpdater class NodeStatChangeNotifier {
class NodeMTimeUpdater {
public: public:
NodeMTimeUpdater(Node *node) : fNode(node) {} NodeStatChangeNotifier(Node *node) : fNode(node) {}
~NodeMTimeUpdater() ~NodeStatChangeNotifier();
{
if (fNode && fNode->IsModified())
fNode->MarkUnmodified();
}
private: private:
Node *fNode; Node *fNode;
}; };
#endif // NODE_H #endif // NODE_H
@@ -68,23 +68,6 @@ static const size_t kOptimalIOSize = 65536;
static const bigtime_t kNotificationInterval = 1000000LL; static const bigtime_t kNotificationInterval = 1000000LL;
static void
notify_if_stat_changed(Volume *volume, Node *node)
{
if (volume == NULL || node == NULL || !node->IsModified())
return;
uint32 statFields = node->MarkUnmodified();
for (Entry* entry = node->GetFirstReferrer(); entry != NULL;
entry = node->GetNextReferrer(entry)) {
ino_t parentID = -1;
if (entry->GetParent() != NULL)
parentID = entry->GetParent()->GetID();
notify_stat_changed(volume->GetID(), parentID, node->GetID(), statFields);
}
}
// #pragma mark - FS // #pragma mark - FS
@@ -418,7 +401,7 @@ ramfs_create_symlink(fs_volume* _volume, fs_vnode* _dir, const char *name,
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
status_t error = B_OK; status_t error = B_OK;
NodeMTimeUpdater mTimeUpdater(dir); NodeStatChangeNotifier statNotifier(dir);
// directory deleted? // directory deleted?
bool removed; bool removed;
if (get_vnode_removed(volume->FSVolume(), dir->GetID(), &removed) if (get_vnode_removed(volume->FSVolume(), dir->GetID(), &removed)
@@ -448,7 +431,7 @@ ramfs_create_symlink(fs_volume* _volume, fs_vnode* _dir, const char *name,
} }
} }
} }
NodeMTimeUpdater mTimeUpdater2(node); NodeStatChangeNotifier statNotifier2(node);
// notify listeners // notify listeners
if (error == B_OK) { if (error == B_OK) {
notify_entry_created(volume->GetID(), dir->GetID(), name, notify_entry_created(volume->GetID(), dir->GetID(), name,
@@ -476,7 +459,7 @@ ramfs_link(fs_volume* _volume, fs_vnode* _dir, const char *name,
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
status_t error = B_OK; status_t error = B_OK;
NodeMTimeUpdater mTimeUpdater(dir); NodeStatChangeNotifier statNotifier(dir);
// directory deleted? // directory deleted?
bool removed; bool removed;
if (get_vnode_removed(volume->FSVolume(), dir->GetID(), &removed) if (get_vnode_removed(volume->FSVolume(), dir->GetID(), &removed)
@@ -522,7 +505,7 @@ ramfs_unlink(fs_volume* _volume, fs_vnode* _dir, const char *name)
if (!locker.IsLocked()) if (!locker.IsLocked())
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
NodeMTimeUpdater mTimeUpdater(dir); NodeStatChangeNotifier statNotifier(dir);
// check directory write permissions // check directory write permissions
error = dir->CheckPermissions(W_OK); error = dir->CheckPermissions(W_OK);
ino_t nodeID = -1; ino_t nodeID = -1;
@@ -568,8 +551,8 @@ ramfs_rename(fs_volume* _volume, fs_vnode* _oldDir, const char *oldName,
status_t error = B_OK; status_t error = B_OK;
NodeMTimeUpdater mTimeUpdater1(oldDir); NodeStatChangeNotifier statNotifier1(oldDir);
NodeMTimeUpdater mTimeUpdater2(newDir); NodeStatChangeNotifier statNotifier2(newDir);
// target directory deleted? // target directory deleted?
bool removed; bool removed;
@@ -733,7 +716,7 @@ ramfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat *st,
mask, st) != B_OK) mask, st) != B_OK)
RETURN_ERROR(B_NOT_ALLOWED); RETURN_ERROR(B_NOT_ALLOWED);
NodeMTimeUpdater mTimeUpdater(node); NodeStatChangeNotifier statNotifier(node);
status_t error = B_OK; status_t error = B_OK;
if ((mask & B_STAT_SIZE) != 0) if ((mask & B_STAT_SIZE) != 0)
@@ -759,10 +742,6 @@ ramfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat *st,
node->SetCrTime(st->st_crtime); node->SetCrTime(st->st_crtime);
} }
// notify listeners
if (error == B_OK)
notify_if_stat_changed(volume, node);
RETURN_ERROR(error); RETURN_ERROR(error);
} }
@@ -814,7 +793,7 @@ ramfs_create(fs_volume* _volume, fs_vnode* _dir, const char *name, int openMode,
if (!locker.IsLocked()) if (!locker.IsLocked())
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
NodeMTimeUpdater mTimeUpdater(dir); NodeStatChangeNotifier statNotifier(dir);
status_t error = B_OK; status_t error = B_OK;
// directory deleted? // directory deleted?
@@ -879,7 +858,7 @@ ramfs_create(fs_volume* _volume, fs_vnode* _dir, const char *name, int openMode,
else if (cookie) else if (cookie)
delete cookie; delete cookie;
} }
NodeMTimeUpdater mTimeUpdater2(node); NodeStatChangeNotifier statNotifier2(node);
// notify listeners // notify listeners
if (error == B_OK) if (error == B_OK)
notify_entry_created(volume->GetID(), dir->GetID(), name, *vnid); notify_entry_created(volume->GetID(), dir->GetID(), name, *vnid);
@@ -906,7 +885,7 @@ ramfs_create_special_node(fs_volume *_volume, fs_vnode *_dir, const char *name,
if (!locker.IsLocked()) if (!locker.IsLocked())
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
NodeMTimeUpdater mTimeUpdater(dir); NodeStatChangeNotifier statNotifier(dir);
status_t error = B_OK; status_t error = B_OK;
// directory deleted? // directory deleted?
@@ -944,7 +923,7 @@ ramfs_create_special_node(fs_volume *_volume, fs_vnode *_dir, const char *name,
RETURN_ERROR(error); RETURN_ERROR(error);
} }
NodeMTimeUpdater mTimeUpdater2(node); NodeStatChangeNotifier statNotifier2(node);
// notify listeners // notify listeners
notify_entry_created(volume->GetID(), dir->GetID(), name, *vnid); notify_entry_created(volume->GetID(), dir->GetID(), name, *vnid);
@@ -989,7 +968,7 @@ ramfs_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
VolumeWriteLocker writeLocker(volume); VolumeWriteLocker writeLocker(volume);
error = node->SetSize(0); error = node->SetSize(0);
NodeMTimeUpdater mTimeUpdater(node); NodeStatChangeNotifier statNotifier(node);
} }
// set result / cleanup on failure // set result / cleanup on failure
@@ -1011,15 +990,12 @@ ramfs_close(fs_volume* _volume, fs_vnode* _node, void* /*cookie*/)
FUNCTION(("node: %lld\n", node->GetID())); FUNCTION(("node: %lld\n", node->GetID()));
VolumeReadLocker locker(volume); VolumeWriteLocker locker(volume);
if (!locker.IsLocked()) if (!locker.IsLocked())
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
status_t error = B_OK; NodeStatChangeNotifier _(node);
// notify listeners return B_OK;
notify_if_stat_changed(volume, node);
return error;
} }
@@ -1103,9 +1079,9 @@ ramfs_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
} }
} }
// notify listeners // notify listeners
if (error == B_OK && cookie->NotificationIntervalElapsed(true)) if (error == B_OK && cookie->NotificationIntervalElapsed(true)) {
notify_if_stat_changed(volume, node); NodeStatChangeNotifier _(node);
NodeMTimeUpdater mTimeUpdater(node); }
RETURN_ERROR(error); RETURN_ERROR(error);
} }
@@ -1210,7 +1186,7 @@ ramfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char *name, int mode)
if (!locker.IsLocked()) if (!locker.IsLocked())
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
NodeMTimeUpdater mTimeUpdater(dir); NodeStatChangeNotifier statNotifier(dir);
// directory deleted? // directory deleted?
bool removed; bool removed;
status_t error = B_OK; status_t error = B_OK;
@@ -1241,7 +1217,7 @@ ramfs_create_dir(fs_volume* _volume, fs_vnode* _dir, const char *name, int mode)
} }
} }
} }
NodeMTimeUpdater mTimeUpdater2(node); NodeStatChangeNotifier statNotifier2(node);
// notify listeners // notify listeners
if (error == B_OK) { if (error == B_OK) {
notify_entry_created(volume->GetID(), dir->GetID(), name, notify_entry_created(volume->GetID(), dir->GetID(), name,
@@ -1268,7 +1244,7 @@ ramfs_remove_dir(fs_volume* _volume, fs_vnode* _dir, const char *name)
if (!locker.IsLocked()) if (!locker.IsLocked())
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
NodeMTimeUpdater mTimeUpdater(dir); NodeStatChangeNotifier statNotifier(dir);
// check directory write permissions // check directory write permissions
status_t error = dir->CheckPermissions(W_OK); status_t error = dir->CheckPermissions(W_OK);
ino_t nodeID = -1; ino_t nodeID = -1;
@@ -1638,7 +1614,7 @@ ramfs_create_attr(fs_volume* _volume, fs_vnode* _node, const char *name,
if (error != B_OK) if (error != B_OK)
return error; return error;
} }
NodeMTimeUpdater mTimeUpdater(node); NodeStatChangeNotifier statNotifier(node);
// success // success
cookieDeleter.Detach(); cookieDeleter.Detach();
@@ -1689,7 +1665,7 @@ ramfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char *name,
// truncate if requested // truncate if requested
if (error == B_OK && (openMode & O_TRUNC)) if (error == B_OK && (openMode & O_TRUNC))
error = attribute->SetSize(0); error = attribute->SetSize(0);
NodeMTimeUpdater mTimeUpdater(node); NodeStatChangeNotifier statNotifier(node);
// set result / cleanup on failure // set result / cleanup on failure
if (error == B_OK) if (error == B_OK)
@@ -1713,8 +1689,7 @@ ramfs_close_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie)
if (!locker.IsLocked()) if (!locker.IsLocked())
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
// notify listeners NodeStatChangeNotifier _(node);
notify_if_stat_changed(volume, node);
return B_OK; return B_OK;
} }
@@ -1788,7 +1763,7 @@ ramfs_write_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
if (!locker.IsLocked()) if (!locker.IsLocked())
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
NodeMTimeUpdater mTimeUpdater(node); NodeStatChangeNotifier statNotifier(node);
// find the attribute // find the attribute
Attribute *attribute = NULL; Attribute *attribute = NULL;
@@ -1864,7 +1839,7 @@ ramfs_remove_attr(fs_volume* _volume, fs_vnode* _node, const char *name)
if (!locker.IsLocked()) if (!locker.IsLocked())
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
NodeMTimeUpdater mTimeUpdater(node); NodeStatChangeNotifier statNotifier(node);
// check permissions // check permissions
error = node->CheckPermissions(W_OK); error = node->CheckPermissions(W_OK);