From e828231248036363167313f7cad872c2bb5676e4 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Mon, 19 Jul 2010 15:31:08 +0000 Subject: [PATCH] Added node monitoring support. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37601 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../system/kernel/file_corruption/fs/File.cpp | 7 +- .../system/kernel/file_corruption/fs/File.h | 3 +- .../system/kernel/file_corruption/fs/Jamfile | 1 + .../system/kernel/file_corruption/fs/Node.cpp | 3 +- .../system/kernel/file_corruption/fs/Node.h | 3 +- .../file_corruption/fs/Notifications.cpp | 117 +++++++++++++++++ .../kernel/file_corruption/fs/Notifications.h | 88 +++++++++++++ .../kernel/file_corruption/fs/Transaction.cpp | 15 ++- .../kernel/file_corruption/fs/Transaction.h | 25 +++- .../kernel/file_corruption/fs/checksumfs.cpp | 123 ++++++++++++++++-- 10 files changed, 368 insertions(+), 17 deletions(-) create mode 100644 src/tests/system/kernel/file_corruption/fs/Notifications.cpp create mode 100644 src/tests/system/kernel/file_corruption/fs/Notifications.h diff --git a/src/tests/system/kernel/file_corruption/fs/File.cpp b/src/tests/system/kernel/file_corruption/fs/File.cpp index 8a98a25e2f..3957a0ab21 100644 --- a/src/tests/system/kernel/file_corruption/fs/File.cpp +++ b/src/tests/system/kernel/file_corruption/fs/File.cpp @@ -169,8 +169,11 @@ File::Read(off_t pos, void* buffer, size_t size, size_t& _bytesRead) status_t -File::Write(off_t pos, const void* buffer, size_t size, size_t& _bytesWritten) +File::Write(off_t pos, const void* buffer, size_t size, size_t& _bytesWritten, + bool& _sizeChanged) { + _sizeChanged = false; + if (size == 0) { _bytesWritten = 0; return B_OK; @@ -208,6 +211,8 @@ File::Write(off_t pos, const void* buffer, size_t size, size_t& _bytesWritten) error = transaction.Commit(); if (error != B_OK) RETURN_ERROR(error); + + _sizeChanged = true; } // now the file has the right size -- do the write diff --git a/src/tests/system/kernel/file_corruption/fs/File.h b/src/tests/system/kernel/file_corruption/fs/File.h index 9c958722b7..189466036c 100644 --- a/src/tests/system/kernel/file_corruption/fs/File.h +++ b/src/tests/system/kernel/file_corruption/fs/File.h @@ -28,7 +28,8 @@ public: virtual status_t Read(off_t pos, void* buffer, size_t size, size_t& _bytesRead); virtual status_t Write(off_t pos, const void* buffer, - size_t size, size_t& _bytesWritten); + size_t size, size_t& _bytesWritten, + bool& _sizeChanged); virtual status_t Sync(); virtual void RevertNodeData(const checksumfs_node& nodeData); diff --git a/src/tests/system/kernel/file_corruption/fs/Jamfile b/src/tests/system/kernel/file_corruption/fs/Jamfile index 0b03d59267..a853e62397 100644 --- a/src/tests/system/kernel/file_corruption/fs/Jamfile +++ b/src/tests/system/kernel/file_corruption/fs/Jamfile @@ -22,6 +22,7 @@ HAIKU_CHECKSUM_FS_SOURCES = Directory.cpp File.cpp Node.cpp + Notifications.cpp SuperBlock.cpp SymLink.cpp Transaction.cpp diff --git a/src/tests/system/kernel/file_corruption/fs/Node.cpp b/src/tests/system/kernel/file_corruption/fs/Node.cpp index 63fb42ad5b..224bc3e562 100644 --- a/src/tests/system/kernel/file_corruption/fs/Node.cpp +++ b/src/tests/system/kernel/file_corruption/fs/Node.cpp @@ -99,7 +99,8 @@ Node::Read(off_t pos, void* buffer, size_t size, size_t& _bytesRead) status_t -Node::Write(off_t pos, const void* buffer, size_t size, size_t& _bytesWritten) +Node::Write(off_t pos, const void* buffer, size_t size, size_t& _bytesWritten, + bool& _sizeChanged) { RETURN_ERROR(B_BAD_VALUE); } diff --git a/src/tests/system/kernel/file_corruption/fs/Node.h b/src/tests/system/kernel/file_corruption/fs/Node.h index 6cfeb4efcb..0b914b0fd9 100644 --- a/src/tests/system/kernel/file_corruption/fs/Node.h +++ b/src/tests/system/kernel/file_corruption/fs/Node.h @@ -42,7 +42,8 @@ public: virtual status_t Read(off_t pos, void* buffer, size_t size, size_t& _bytesRead); virtual status_t Write(off_t pos, const void* buffer, - size_t size, size_t& _bytesWritten); + size_t size, size_t& _bytesWritten, + bool& _sizeChanged); virtual status_t Sync(); inline const checksumfs_node& NodeData() const { return fNode; } diff --git a/src/tests/system/kernel/file_corruption/fs/Notifications.cpp b/src/tests/system/kernel/file_corruption/fs/Notifications.cpp new file mode 100644 index 0000000000..e545e4acbd --- /dev/null +++ b/src/tests/system/kernel/file_corruption/fs/Notifications.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "Notifications.h" + +#include "Directory.h" +#include "Volume.h" + + +// #pragma mark - EntryCreatedNotification + + +EntryCreatedNotification::EntryCreatedNotification(Directory* directory, + const char* name, Node* node) + : + fDirectory(directory), + fName(name), + fNode(node) +{ +} + + +void +EntryCreatedNotification::NotifyPostCommit() const +{ + notify_entry_created(fDirectory->GetVolume()->ID(), + fDirectory->BlockIndex(), fName, fNode->BlockIndex()); +} + + +// #pragma mark - EntryRemovedNotification + + +EntryRemovedNotification::EntryRemovedNotification(Directory* directory, + const char* name, Node* node) + : + fDirectory(directory), + fName(name), + fNode(node) +{ +} + + +void +EntryRemovedNotification::NotifyPostCommit() const +{ + notify_entry_removed(fDirectory->GetVolume()->ID(), + fDirectory->BlockIndex(), fName, fNode->BlockIndex()); +} + + +// #pragma mark - EntryMovedNotification + + +EntryMovedNotification::EntryMovedNotification(Directory* fromDirectory, + const char* fromName, Directory* toDirectory, const char* toName, + Node* node) + : + fFromDirectory(fromDirectory), + fFromName(fromName), + fToDirectory(toDirectory), + fToName(toName), + fNode(node) +{ +} + + +void +EntryMovedNotification::NotifyPostCommit() const +{ + notify_entry_moved(fFromDirectory->GetVolume()->ID(), + fFromDirectory->BlockIndex(), fFromName, fToDirectory->BlockIndex(), + fToName, fNode->BlockIndex()); +} + + +// #pragma mark - StatChangedNotification + + +StatChangedNotification::StatChangedNotification(Node* node, uint32 statFields) + : + fNode(node), + fStatFields(statFields) +{ +} + + +void +StatChangedNotification::NotifyPostCommit() const +{ + notify_stat_changed(fNode->GetVolume()->ID(), fNode->BlockIndex(), + fStatFields); +} + + +// #pragma mark - AttributeChangedNotification + + +AttributeChangedNotification::AttributeChangedNotification(Node* node, + const char* attribute, int32 cause) + : + fNode(node), + fAttribute(attribute), + fCause(cause) +{ +} + + +void +AttributeChangedNotification::NotifyPostCommit() const +{ + notify_attribute_changed(fNode->GetVolume()->ID(), fNode->BlockIndex(), + fAttribute, fCause); +} diff --git a/src/tests/system/kernel/file_corruption/fs/Notifications.h b/src/tests/system/kernel/file_corruption/fs/Notifications.h new file mode 100644 index 0000000000..b86942e6d6 --- /dev/null +++ b/src/tests/system/kernel/file_corruption/fs/Notifications.h @@ -0,0 +1,88 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef NOTIFICATIONS_H +#define NOTIFICATIONS_H + + +#include "Transaction.h" + + +class Directory; + + +class EntryCreatedNotification : public PostCommitNotification { +public: + EntryCreatedNotification(Directory* directory, + const char* name, Node* node); + + virtual void NotifyPostCommit() const; + +private: + Directory* fDirectory; + const char* fName; + Node* fNode; +}; + + +class EntryRemovedNotification : public PostCommitNotification { +public: + EntryRemovedNotification(Directory* directory, + const char* name, Node* node); + + virtual void NotifyPostCommit() const; + +private: + Directory* fDirectory; + const char* fName; + Node* fNode; +}; + + +class EntryMovedNotification : public PostCommitNotification { +public: + EntryMovedNotification(Directory* fromDirectory, + const char* fromName, + Directory* toDirectory, const char* toName, + Node* node); + + virtual void NotifyPostCommit() const; + +private: + Directory* fFromDirectory; + const char* fFromName; + Directory* fToDirectory; + const char* fToName; + Node* fNode; +}; + + +class StatChangedNotification : public PostCommitNotification { +public: + StatChangedNotification(Node* node, + uint32 statFields); + + virtual void NotifyPostCommit() const; + +private: + Node* fNode; + uint32 fStatFields; +}; + + +class AttributeChangedNotification : public PostCommitNotification { +public: + AttributeChangedNotification(Node* node, + const char* attribute, int32 cause); + + virtual void NotifyPostCommit() const; + +private: + Node* fNode; + const char* fAttribute; + int32 fCause; +}; + + +#endif // NOTIFICATIONS_H diff --git a/src/tests/system/kernel/file_corruption/fs/Transaction.cpp b/src/tests/system/kernel/file_corruption/fs/Transaction.cpp index 17ff144b91..04993f5ef5 100644 --- a/src/tests/system/kernel/file_corruption/fs/Transaction.cpp +++ b/src/tests/system/kernel/file_corruption/fs/Transaction.cpp @@ -71,7 +71,7 @@ Transaction::StartAndAddNode(Node* node, uint32 flags = 0) status_t -Transaction::Commit() +Transaction::Commit(const PostCommitNotification* notification) { ASSERT(fID >= 0); @@ -93,6 +93,10 @@ Transaction::Commit() return error; } + // send notifications + if (notification != NULL) + notification->NotifyPostCommit(); + // clean up _DeleteNodeInfosAndUnlock(false); @@ -206,3 +210,12 @@ Transaction::_DeleteNodeInfosAndUnlock(bool failed) delete info; } } + + + +// #pragma mark - PostCommitNotification + + +PostCommitNotification::~PostCommitNotification() +{ +} diff --git a/src/tests/system/kernel/file_corruption/fs/Transaction.h b/src/tests/system/kernel/file_corruption/fs/Transaction.h index 74a9dc6b7e..36029b61a7 100644 --- a/src/tests/system/kernel/file_corruption/fs/Transaction.h +++ b/src/tests/system/kernel/file_corruption/fs/Transaction.h @@ -13,6 +13,7 @@ #include "Node.h" +class PostCommitNotification; class Volume; @@ -32,7 +33,11 @@ public: status_t Start(); status_t StartAndAddNode(Node* node, uint32 flags = 0); - status_t Commit(); + status_t Commit( + const PostCommitNotification* notification + = NULL); + inline status_t Commit( + const PostCommitNotification& notification); void Abort(); status_t AddNode(Node* node, uint32 flags = 0); @@ -62,4 +67,22 @@ private: }; +status_t +Transaction::Commit(const PostCommitNotification& notification) +{ + return Commit(¬ification); +} + + +// #pragma mark - + + +class PostCommitNotification { +public: + virtual ~PostCommitNotification(); + + virtual void NotifyPostCommit() const = 0; +}; + + #endif // TRANSACTION_H diff --git a/src/tests/system/kernel/file_corruption/fs/checksumfs.cpp b/src/tests/system/kernel/file_corruption/fs/checksumfs.cpp index 85b92995f1..481770d503 100644 --- a/src/tests/system/kernel/file_corruption/fs/checksumfs.cpp +++ b/src/tests/system/kernel/file_corruption/fs/checksumfs.cpp @@ -19,12 +19,14 @@ #include #include +#include #include "checksumfs.h" #include "checksumfs_private.h" #include "DebugSupport.h" #include "Directory.h" #include "File.h" +#include "Notifications.h" #include "SuperBlock.h" #include "SymLink.h" #include "Transaction.h" @@ -35,17 +37,99 @@ static const char* const kCheckSumFSModuleName = "file_systems/checksumfs" B_CURRENT_FS_API_VERSION; static const char* const kCheckSumFSShortName = "checksumfs"; +static const bigtime_t kModifiedInterimUpdateInterval = 500000; + // wait at least 0.5s between interim modified updates + // #pragma mark - struct FileCookie { - int openMode; + mutex lock; + int openMode; + bigtime_t lastModifiedUpdate; + bool modifiedNeedsUpdate; + bool sizeChangedSinceUpdate; + bool modifiedNeedsFinalUpdate; + bool finalSizeChanged; + FileCookie(int openMode) : - openMode(openMode) + openMode(openMode), + lastModifiedUpdate(0), + modifiedNeedsUpdate(false), + sizeChangedSinceUpdate(false), + modifiedNeedsFinalUpdate(false), + finalSizeChanged(false) { + mutex_init(&lock, "checksumfs file cookie"); + } + + ~FileCookie() + { + mutex_destroy(&lock); + } + + status_t UpdateModifiedIfNecessary(Node* node, bool finalUpdate) + { + MutexLocker locker(lock); + + return _UpdateModifiedIfNecessary(node, finalUpdate); + } + + status_t FileModified(Node* node, bool sizeChanged) + { + MutexLocker locker(lock); + + modifiedNeedsUpdate = true; + modifiedNeedsFinalUpdate = true; + sizeChangedSinceUpdate |= sizeChanged; + finalSizeChanged |= sizeChanged; + + return _UpdateModifiedIfNecessary(node, false); + } + +private: + status_t _UpdateModifiedIfNecessary(Node* node, bool finalUpdate) + { + uint32 statFlags = B_STAT_MODIFICATION_TIME | B_STAT_CHANGE_TIME; + + if (finalUpdate) { + if (!modifiedNeedsFinalUpdate) + return B_OK; + if (finalSizeChanged) + statFlags |= B_STAT_SIZE; + } else { + if (!modifiedNeedsUpdate) + return B_OK; + + if (system_time() + < lastModifiedUpdate + kModifiedInterimUpdateInterval) { + // not enough time passed -- postpone update + return B_OK; + } + + statFlags |= B_STAT_INTERIM_UPDATE + | (sizeChangedSinceUpdate ? B_STAT_SIZE : 0); + } + + // do the update -- start a transaction, lock the node, and update + Transaction transaction(node->GetVolume()); + status_t error = transaction.StartAndAddNode(node); + if (error != B_OK) + return error; + + node->Touched(NODE_MODIFIED); + + error = transaction.Commit(StatChangedNotification(node, statFlags)); + if (error != B_OK) + return error; + + modifiedNeedsUpdate = false; + lastModifiedUpdate = system_time(); + + return B_OK; } }; @@ -308,7 +392,8 @@ remove_entry(fs_volume* fsVolume, fs_vnode* parent, const char* name, } // commit the transaction - return transaction.Commit(); + return transaction.Commit(EntryRemovedNotification(directory, name, + childNode)); } @@ -723,7 +808,8 @@ checksumfs_create_symlink(fs_volume* fsVolume, fs_vnode* parent, directory->Touched(NODE_MODIFIED); // commit the transaction - return transaction.Commit(); + return transaction.Commit(EntryCreatedNotification(directory, name, + newSymLink)); } @@ -773,7 +859,7 @@ checksumfs_link(fs_volume* fsVolume, fs_vnode* dir, const char* name, directory->Touched(NODE_MODIFIED); // commit the transaction - return transaction.Commit(); + return transaction.Commit(EntryCreatedNotification(directory, name, node)); } @@ -896,7 +982,8 @@ checksumfs_rename(fs_volume* fsVolume, fs_vnode* fromDir, const char* fromName, toDirectory->Touched(NODE_MODIFIED); // commit the transaction - return transaction.Commit(); + return transaction.Commit(EntryMovedNotification(fromDirectory, fromName, + toDirectory, toName, node)); } @@ -1027,7 +1114,7 @@ checksumfs_write_stat(fs_volume* fsVolume, fs_vnode* vnode, node->Touched(NODE_ACCESSED); // commit the transaction - return transaction.Commit(); + return transaction.Commit(StatChangedNotification(node, statMask)); } @@ -1231,7 +1318,8 @@ checksumfs_create(fs_volume* fsVolume, fs_vnode* parent, const char* name, transaction.KeepNode(newFile); // commit the transaction - error = transaction.Commit(); + error = transaction.Commit(EntryCreatedNotification(directory, name, + newFile)); if (error != B_OK) { volume->RemoveNode(newFile); delete newFile; @@ -1281,6 +1369,10 @@ static status_t checksumfs_free_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie) { FileCookie* cookie = (FileCookie*)_cookie; + Node* node = (Node*)vnode->private_node; + + cookie->UpdateModifiedIfNecessary(node, true); + delete cookie; return B_OK; } @@ -1330,8 +1422,16 @@ checksumfs_write(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, off_t pos, // special value handled by Write() } - RETURN_ERROR(node->Write(pos, buffer, *_length, *_length)); -// TODO: Modification time update! + bool sizeChanged; + status_t error = node->Write(pos, buffer, *_length, *_length, sizeChanged); + if (error != B_OK) + RETURN_ERROR(error); + + // update the modification time and send out a notification from time to + // time + cookie->FileModified(node, sizeChanged); + + return B_OK; } @@ -1384,7 +1484,8 @@ checksumfs_create_dir(fs_volume* fsVolume, fs_vnode* parent, const char* name, directory->Touched(NODE_MODIFIED); // commit the transaction - return transaction.Commit(); + return transaction.Commit(EntryCreatedNotification(directory, name, + newDirectory)); }