* 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_unpublish_partition(const char *path);
|
||||||
status_t devfs_publish_partition(const char *path, const partition_info *info);
|
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_device(const char *path, void *ident, device_hooks *calls);
|
||||||
status_t devfs_publish_directory(const char *path);
|
status_t devfs_publish_directory(const char *path);
|
||||||
|
|
||||||
|
|||||||
@@ -5,24 +5,26 @@
|
|||||||
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
||||||
* Distributed under the terms of the NewOS License.
|
* Distributed under the terms of the NewOS License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _KERNEL_VFS_H
|
#ifndef _KERNEL_VFS_H
|
||||||
#define _KERNEL_VFS_H
|
#define _KERNEL_VFS_H
|
||||||
|
|
||||||
|
|
||||||
#include <kernel.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 <lock.h>
|
||||||
#include <util/list.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 DEFAULT_FD_TABLE_SIZE 128
|
||||||
#define MAX_FD_TABLE_SIZE 2048
|
#define MAX_FD_TABLE_SIZE 2048
|
||||||
#define MAX_NODE_MONITORS 4096
|
#define MAX_NODE_MONITORS 4096
|
||||||
|
|
||||||
|
|
||||||
struct kernel_args;
|
struct kernel_args;
|
||||||
struct vm_cache_ref;
|
struct vm_cache_ref;
|
||||||
struct file_descriptor;
|
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_stat_vnode(void *_vnode, struct stat *stat);
|
||||||
status_t vfs_get_vnode_name(void *vnode, char *name, size_t nameSize);
|
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_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);
|
void vfs_free_unused_vnodes(int32 level);
|
||||||
|
|
||||||
/* special module convenience call */
|
/* 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
|
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;
|
devfs_vnode *node;
|
||||||
status_t status = get_node_for_path(fs, path, &node);
|
status_t status = get_node_for_path(fs, path, &node);
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
if ((type & S_IFMT) != type) {
|
status = unpublish_node(fs, node, type);
|
||||||
status = B_BAD_TYPE;
|
|
||||||
goto err1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
put_vnode(fs->id, node->id);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -2117,10 +2123,20 @@ devfs_publish_partition(const char *path, const partition_info *info)
|
|||||||
|
|
||||||
|
|
||||||
extern "C" status_t
|
extern "C" status_t
|
||||||
devfs_unpublish_device(const char *path)
|
devfs_unpublish_device(const char *path, bool disconnect)
|
||||||
{
|
{
|
||||||
// TODO: disconnect any open file handles!
|
devfs_vnode *node;
|
||||||
return unpublish_node(sDeviceFileSystem, path, S_IFCHR);
|
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
|
/** \brief Resolves a mount point vnode to the volume root vnode it is covered
|
||||||
* by.
|
* 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
|
extern "C" void
|
||||||
vfs_free_unused_vnodes(int32 level)
|
vfs_free_unused_vnodes(int32 level)
|
||||||
{
|
{
|
||||||
@@ -5300,84 +5410,9 @@ fs_unmount(char *path, uint32 flags, bool kernel)
|
|||||||
|
|
||||||
mutex_unlock(&sVnodeMutex);
|
mutex_unlock(&sVnodeMutex);
|
||||||
|
|
||||||
// iterate over all teams and peek into their file descriptors
|
disconnect_mount_or_vnode_fds(mount, NULL);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedDescriptors = true;
|
disconnectedDescriptors = true;
|
||||||
|
|
||||||
mutex_lock(&sVnodeMutex);
|
mutex_lock(&sVnodeMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user