NodeMonitor: Resolve mount points for B_WATCH_CHILDREN.

* When a watched directory contains a mount point, we need to resolve
  the actual parent directory of the mount point in the file system to
  serve the monitor.
This commit is contained in:
Axel Dörfler
2016-03-28 14:31:27 +02:00
parent 6f7fc2204b
commit 67988f501a
3 changed files with 97 additions and 35 deletions
+3 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015, Axel Dörfler, [email protected].
* Copyright 2002-2016, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
@@ -126,6 +126,8 @@ status_t vfs_get_cwd(dev_t *_mountID, ino_t *_vnodeID);
void vfs_unlock_vnode_if_locked(struct file_descriptor *descriptor);
status_t vfs_unmount(dev_t mountID, uint32 flags);
status_t vfs_disconnect_vnode(dev_t mountID, ino_t vnodeID);
status_t vfs_resolve_parent(struct vnode* parent, dev_t* device,
ino_t* node);
void vfs_free_unused_vnodes(int32 level);
status_t vfs_read_stat(int fd, const char *path, bool traverseLeafLink,
+40 -4
View File
@@ -26,6 +26,7 @@
#include <util/list.h>
#include "node_monitor_private.h"
#include "Vnode.h"
//#define TRACE_MONITOR
@@ -153,6 +154,8 @@ class NodeMonitorService : public NotificationService {
status_t _SendNotificationMessage(KMessage &message,
interested_monitor_listener_list *interestedListeners,
int32 interestedListenerCount);
void _ResolveMountPoint(dev_t device, ino_t directory,
dev_t& parentDevice, ino_t& parentDirectory);
struct monitor_hash_key {
dev_t device;
@@ -623,6 +626,25 @@ NodeMonitorService::_SendNotificationMessage(KMessage &message,
}
/*! \brief Resolves the device/directory node pair to the node it's covered
by, if any.
*/
void
NodeMonitorService::_ResolveMountPoint(dev_t device, ino_t directory,
dev_t& parentDevice, ino_t& parentDirectory)
{
struct vnode* vnode;
status_t status = vfs_get_vnode(device, directory, true, &vnode);
if (status == B_OK) {
if (vnode->covers != NULL)
status = vfs_resolve_parent(vnode, &parentDevice, &parentDirectory);
vfs_put_vnode(vnode);
}
if (status != B_OK)
dprintf("Resolving mount point %ld:%lld failed!\n", device, directory);
}
/*! \brief Notifies all interested listeners that an entry has been created
or removed.
\param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED.
@@ -747,8 +769,15 @@ NodeMonitorService::NotifyStatChanged(dev_t device, ino_t directory, ino_t node,
_GetInterestedVolumeListeners(device, watchFlag, interestedListeners,
interestedListenerCount);
// ... for the directory
if (directory >= 0) {
_GetInterestedMonitorListeners(device, directory,
if (directory > 0) {
dev_t parentDevice = device;
ino_t parentDirectory = directory;
if (directory == node) {
// This is a mount point -- get its file system parent
_ResolveMountPoint(device, directory, parentDevice,
parentDirectory);
}
_GetInterestedMonitorListeners(parentDevice, parentDirectory,
B_WATCH_CHILDREN | watchFlag,
interestedListeners, interestedListenerCount);
}
@@ -799,8 +828,15 @@ NodeMonitorService::NotifyAttributeChanged(dev_t device, ino_t directory,
_GetInterestedVolumeListeners(device, B_WATCH_ATTR,
interestedListeners, interestedListenerCount);
// ... for the directory
if (directory >= 0) {
_GetInterestedMonitorListeners(device, directory,
if (directory > 0) {
dev_t parentDevice = device;
ino_t parentDirectory = directory;
if (directory == node) {
// This is a mount point -- get its file system parent
_ResolveMountPoint(device, directory, parentDevice,
parentDirectory);
}
_GetInterestedMonitorListeners(parentDevice, parentDirectory,
B_WATCH_CHILDREN | B_WATCH_ATTR,
interestedListeners, interestedListenerCount);
}
+54 -30
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2005-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2002-2015, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2002-2016, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
@@ -2914,6 +2914,34 @@ normalize_path(char* path, size_t pathSize, bool traverseLink, bool kernel)
}
static status_t
resolve_covered_parent(struct vnode* parent, dev_t* _device, ino_t* _node,
struct io_context* ioContext)
{
// Make sure the IO context root is not bypassed.
if (parent == ioContext->root) {
*_device = parent->device;
*_node = parent->id;
return B_OK;
}
inc_vnode_ref_count(parent);
// vnode_path_to_vnode() puts the node
// ".." is guaranteed not to be clobbered by this call
struct vnode* vnode;
status_t status = vnode_path_to_vnode(parent, (char*)"..", false, 0,
ioContext, &vnode, NULL);
if (status == B_OK) {
*_device = vnode->device;
*_node = vnode->id;
put_vnode(vnode);
}
return status;
}
#ifdef ADD_DEBUGGER_COMMANDS
@@ -4425,6 +4453,19 @@ vfs_normalize_path(const char* path, char* buffer, size_t bufferSize,
}
/*! \brief Gets the parent of the passed in node.
Gets the parent of the passed in node, and correctly resolves covered
nodes.
*/
extern "C" status_t
vfs_resolve_parent(struct vnode* parent, dev_t* device, ino_t* node)
{
return resolve_covered_parent(parent, device, node,
get_current_io_context(true));
}
/*! \brief Creates a special node in the file system.
The caller gets a reference to the newly created node (which is passed
@@ -5863,38 +5904,21 @@ fix_dirent(struct vnode* parent, struct dirent* entry,
// If this is the ".." entry and the directory covering another vnode,
// we need to replace d_dev and d_ino with the actual values.
if (strcmp(entry->d_name, "..") == 0 && parent->IsCovering()) {
// Make sure the IO context root is not bypassed.
if (parent == ioContext->root) {
entry->d_dev = parent->device;
entry->d_ino = parent->id;
} else {
inc_vnode_ref_count(parent);
// vnode_path_to_vnode() puts the node
return resolve_covered_parent(parent, &entry->d_dev, &entry->d_ino,
ioContext);
}
// ".." is guaranteed not to be clobbered by this call
struct vnode* vnode;
status_t status = vnode_path_to_vnode(parent, (char*)"..", false, 0,
ioContext, &vnode, NULL);
// resolve covered vnodes
ReadLocker _(&sVnodeLock);
if (status == B_OK) {
entry->d_dev = vnode->device;
entry->d_ino = vnode->id;
put_vnode(vnode);
}
}
} else {
// resolve covered vnodes
ReadLocker _(&sVnodeLock);
struct vnode* vnode = lookup_vnode(entry->d_dev, entry->d_ino);
if (vnode != NULL && vnode->covered_by != NULL) {
do {
vnode = vnode->covered_by;
} while (vnode->covered_by != NULL);
struct vnode* vnode = lookup_vnode(entry->d_dev, entry->d_ino);
if (vnode != NULL && vnode->covered_by != NULL) {
do {
vnode = vnode->covered_by;
} while (vnode->covered_by != NULL);
entry->d_dev = vnode->device;
entry->d_ino = vnode->id;
}
entry->d_dev = vnode->device;
entry->d_ino = vnode->id;
}
return B_OK;