* Factored out the vnode disconnection code from fs_unmount() to a separate
function, and added a vfs_disconnect_vnode() for other kernel components. * devfs_unpublish_device() can now optionally make use of this call. * Fixed the type check of devfs' unpublish_node(). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16907 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -24,7 +24,7 @@ status_t devfs_publish_file_device(const char *path, const char *filePath);
|
||||
status_t devfs_unpublish_partition(const char *path);
|
||||
status_t devfs_publish_partition(const char *path, const partition_info *info);
|
||||
|
||||
status_t devfs_unpublish_device(const char *path);
|
||||
status_t devfs_unpublish_device(const char *path, bool disconnect);
|
||||
status_t devfs_publish_device(const char *path, void *ident, device_hooks *calls);
|
||||
status_t devfs_publish_directory(const char *path);
|
||||
|
||||
|
||||
@@ -5,24 +5,26 @@
|
||||
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
||||
* Distributed under the terms of the NewOS License.
|
||||
*/
|
||||
|
||||
#ifndef _KERNEL_VFS_H
|
||||
#define _KERNEL_VFS_H
|
||||
|
||||
|
||||
#include <kernel.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/select.h>
|
||||
#include <signal.h>
|
||||
#include <dirent.h>
|
||||
#include <fs_interface.h>
|
||||
#include <lock.h>
|
||||
#include <util/list.h>
|
||||
|
||||
#include <fs_interface.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
|
||||
#define DEFAULT_FD_TABLE_SIZE 128
|
||||
#define MAX_FD_TABLE_SIZE 2048
|
||||
#define MAX_NODE_MONITORS 4096
|
||||
|
||||
|
||||
struct kernel_args;
|
||||
struct vm_cache_ref;
|
||||
struct file_descriptor;
|
||||
@@ -96,6 +98,7 @@ status_t vfs_get_fs_node_from_path(mount_id mountID, const char *path,
|
||||
status_t vfs_stat_vnode(void *_vnode, struct stat *stat);
|
||||
status_t vfs_get_vnode_name(void *vnode, char *name, size_t nameSize);
|
||||
status_t vfs_get_cwd(mount_id *_mountID, vnode_id *_vnodeID);
|
||||
status_t vfs_disconnect_vnode(mount_id mountID, vnode_id vnodeID);
|
||||
void vfs_free_unused_vnodes(int32 level);
|
||||
|
||||
/* special module convenience call */
|
||||
|
||||
@@ -641,29 +641,35 @@ get_node_for_path(struct devfs *fs, const char *path, struct devfs_vnode **_node
|
||||
|
||||
|
||||
static status_t
|
||||
unpublish_node(struct devfs *fs, const char *path, int type)
|
||||
unpublish_node(struct devfs *fs, devfs_vnode *node, mode_t type)
|
||||
{
|
||||
if ((node->stream.type & S_IFMT) != type)
|
||||
return B_BAD_TYPE;
|
||||
|
||||
recursive_lock_lock(&fs->lock);
|
||||
|
||||
status_t status = devfs_remove_from_dir(node->parent, node);
|
||||
if (status < B_OK)
|
||||
goto out;
|
||||
|
||||
status = remove_vnode(fs->id, node->id);
|
||||
|
||||
out:
|
||||
recursive_lock_unlock(&fs->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
unpublish_node(struct devfs *fs, const char *path, mode_t type)
|
||||
{
|
||||
devfs_vnode *node;
|
||||
status_t status = get_node_for_path(fs, path, &node);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
if ((type & S_IFMT) != type) {
|
||||
status = B_BAD_TYPE;
|
||||
goto err1;
|
||||
}
|
||||
status = unpublish_node(fs, node, type);
|
||||
|
||||
recursive_lock_lock(&fs->lock);
|
||||
|
||||
status = devfs_remove_from_dir(node->parent, node);
|
||||
if (status < B_OK)
|
||||
goto err2;
|
||||
|
||||
status = remove_vnode(fs->id, node->id);
|
||||
|
||||
err2:
|
||||
recursive_lock_unlock(&fs->lock);
|
||||
err1:
|
||||
put_vnode(fs->id, node->id);
|
||||
return status;
|
||||
}
|
||||
@@ -2117,10 +2123,20 @@ devfs_publish_partition(const char *path, const partition_info *info)
|
||||
|
||||
|
||||
extern "C" status_t
|
||||
devfs_unpublish_device(const char *path)
|
||||
devfs_unpublish_device(const char *path, bool disconnect)
|
||||
{
|
||||
// TODO: disconnect any open file handles!
|
||||
return unpublish_node(sDeviceFileSystem, path, S_IFCHR);
|
||||
devfs_vnode *node;
|
||||
status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
status = unpublish_node(sDeviceFileSystem, node, S_IFCHR);
|
||||
|
||||
if (status == B_OK && disconnect)
|
||||
vfs_disconnect_vnode(sDeviceFileSystem->id, node->id);
|
||||
|
||||
put_vnode(sDeviceFileSystem->id, node->id);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+112
-77
@@ -1236,6 +1236,102 @@ normalize_flock(struct file_descriptor *descriptor, struct flock *flock)
|
||||
}
|
||||
|
||||
|
||||
/** Disconnects all file descriptors that go the \a vnodeToDisconnect, or
|
||||
* if this is NULL, all vnodes of the specified \a mount object.
|
||||
*
|
||||
* Note, after you've called this function, there might still be ongoing
|
||||
* accesses - they won't be interrupted if they already happened before.
|
||||
* However, any subsequent access will fail.
|
||||
*
|
||||
* This is not a cheap function and should be used with care and rarely.
|
||||
* TODO: there is currently no means to stop a blocking read/write!
|
||||
*/
|
||||
|
||||
void
|
||||
disconnect_mount_or_vnode_fds(struct fs_mount *mount,
|
||||
struct vnode *vnodeToDisconnect)
|
||||
{
|
||||
// iterate over all teams and peek into their file descriptors
|
||||
int32 nextTeamID = 0;
|
||||
|
||||
while (true) {
|
||||
struct io_context *context = NULL;
|
||||
sem_id contextMutex = -1;
|
||||
struct team *team = NULL;
|
||||
team_id lastTeamID;
|
||||
|
||||
cpu_status state = disable_interrupts();
|
||||
GRAB_TEAM_LOCK();
|
||||
|
||||
lastTeamID = peek_next_thread_id();
|
||||
if (nextTeamID < lastTeamID) {
|
||||
// get next valid team
|
||||
while (nextTeamID < lastTeamID
|
||||
&& !(team = team_get_team_struct_locked(nextTeamID))) {
|
||||
nextTeamID++;
|
||||
}
|
||||
|
||||
if (team) {
|
||||
context = (io_context *)team->io_context;
|
||||
contextMutex = context->io_mutex.sem;
|
||||
nextTeamID++;
|
||||
}
|
||||
}
|
||||
|
||||
RELEASE_TEAM_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
if (context == NULL)
|
||||
break;
|
||||
|
||||
// we now have a context - since we couldn't lock it while having
|
||||
// safe access to the team structure, we now need to lock the mutex
|
||||
// manually
|
||||
|
||||
if (acquire_sem(contextMutex) != B_OK) {
|
||||
// team seems to be gone, go over to the next team
|
||||
continue;
|
||||
}
|
||||
|
||||
// the team cannot be deleted completely while we're owning its
|
||||
// io_context mutex, so we can safely play with it now
|
||||
|
||||
context->io_mutex.holder = thread_get_current_thread_id();
|
||||
|
||||
if (context->cwd != NULL && context->cwd->mount == mount) {
|
||||
put_vnode(context->cwd);
|
||||
|
||||
if (context->cwd == mount->root_vnode) {
|
||||
// redirect the current working directory to the covered vnode
|
||||
context->cwd = mount->covers_vnode;
|
||||
inc_vnode_ref_count(context->cwd);
|
||||
} else
|
||||
context->cwd = NULL;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < context->table_size; i++) {
|
||||
if (struct file_descriptor *descriptor = context->fds[i]) {
|
||||
inc_fd_ref_count(descriptor);
|
||||
|
||||
// if this descriptor points at this mount, we
|
||||
// need to disconnect it to be able to unmount
|
||||
struct vnode *vnode = fd_vnode(descriptor);
|
||||
if (vnodeToDisconnect != NULL) {
|
||||
if (vnode == vnodeToDisconnect)
|
||||
disconnect_fd(descriptor);
|
||||
} else if (vnode != NULL && vnode->mount == mount
|
||||
|| vnode == NULL && descriptor->u.mount == mount)
|
||||
disconnect_fd(descriptor);
|
||||
|
||||
put_fd(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&context->io_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** \brief Resolves a mount point vnode to the volume root vnode it is covered
|
||||
* by.
|
||||
*
|
||||
@@ -2872,6 +2968,20 @@ vfs_get_cwd(mount_id *_mountID, vnode_id *_vnodeID)
|
||||
}
|
||||
|
||||
|
||||
extern "C" status_t
|
||||
vfs_disconnect_vnode(mount_id mountID, vnode_id vnodeID)
|
||||
{
|
||||
struct vnode *vnode;
|
||||
|
||||
status_t status = get_vnode(mountID, vnodeID, &vnode, true);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
disconnect_mount_or_vnode_fds(vnode->mount, vnode);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
extern "C" void
|
||||
vfs_free_unused_vnodes(int32 level)
|
||||
{
|
||||
@@ -5300,84 +5410,9 @@ fs_unmount(char *path, uint32 flags, bool kernel)
|
||||
|
||||
mutex_unlock(&sVnodeMutex);
|
||||
|
||||
// iterate over all teams and peek into their file descriptors
|
||||
|
||||
int32 nextTeamID = 0;
|
||||
|
||||
while (true) {
|
||||
struct io_context *context = NULL;
|
||||
sem_id contextMutex = -1;
|
||||
struct team *team = NULL;
|
||||
team_id lastTeamID;
|
||||
|
||||
cpu_status state = disable_interrupts();
|
||||
GRAB_TEAM_LOCK();
|
||||
|
||||
lastTeamID = peek_next_thread_id();
|
||||
if (nextTeamID < lastTeamID) {
|
||||
// get next valid team
|
||||
while (nextTeamID < lastTeamID
|
||||
&& !(team = team_get_team_struct_locked(nextTeamID))) {
|
||||
nextTeamID++;
|
||||
}
|
||||
|
||||
if (team) {
|
||||
context = (io_context *)team->io_context;
|
||||
contextMutex = context->io_mutex.sem;
|
||||
nextTeamID++;
|
||||
}
|
||||
}
|
||||
|
||||
RELEASE_TEAM_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
if (context == NULL)
|
||||
break;
|
||||
|
||||
// we now have a context - since we couldn't lock it while having
|
||||
// safe access to the team structure, we now need to lock the mutex
|
||||
// manually
|
||||
|
||||
if (acquire_sem(contextMutex) != B_OK) {
|
||||
// team seems to be gone, go over to the next team
|
||||
continue;
|
||||
}
|
||||
|
||||
// the team cannot be deleted completely while we're owning its
|
||||
// io_context mutex, so we can safely play with it now
|
||||
|
||||
context->io_mutex.holder = thread_get_current_thread_id();
|
||||
|
||||
if (context->cwd != NULL && context->cwd->mount == mount) {
|
||||
put_vnode(context->cwd);
|
||||
|
||||
if (context->cwd == mount->root_vnode) {
|
||||
// redirect the current working directory to the covered vnode
|
||||
context->cwd = mount->covers_vnode;
|
||||
inc_vnode_ref_count(context->cwd);
|
||||
} else
|
||||
context->cwd = NULL;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < context->table_size; i++) {
|
||||
if (struct file_descriptor *descriptor = context->fds[i]) {
|
||||
inc_fd_ref_count(descriptor);
|
||||
|
||||
// if this descriptor points at this mount, we
|
||||
// need to disconnect it to be able to unmount
|
||||
vnode = fd_vnode(descriptor);
|
||||
if (vnode != NULL && vnode->mount == mount
|
||||
|| vnode == NULL && descriptor->u.mount == mount)
|
||||
disconnect_fd(descriptor);
|
||||
|
||||
put_fd(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&context->io_mutex);
|
||||
}
|
||||
|
||||
disconnect_mount_or_vnode_fds(mount, NULL);
|
||||
disconnectedDescriptors = true;
|
||||
|
||||
mutex_lock(&sVnodeMutex);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user