From 67988f501a67260d2dd434d517d08dcef29807e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Mon, 21 Mar 2016 20:12:44 +0100 Subject: [PATCH] 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. --- headers/private/kernel/vfs.h | 4 +- src/system/kernel/fs/node_monitor.cpp | 44 ++++++++++++-- src/system/kernel/fs/vfs.cpp | 84 +++++++++++++++++---------- 3 files changed, 97 insertions(+), 35 deletions(-) diff --git a/headers/private/kernel/vfs.h b/headers/private/kernel/vfs.h index f108cd0d28..fc818ba68d 100644 --- a/headers/private/kernel/vfs.h +++ b/headers/private/kernel/vfs.h @@ -1,5 +1,5 @@ /* - * 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. @@ -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, diff --git a/src/system/kernel/fs/node_monitor.cpp b/src/system/kernel/fs/node_monitor.cpp index a953e755c6..092f217596 100644 --- a/src/system/kernel/fs/node_monitor.cpp +++ b/src/system/kernel/fs/node_monitor.cpp @@ -26,6 +26,7 @@ #include #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); } diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index 7e23512c8f..0482459785 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -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;