Implemented force unmounting - by passing the "-f" option to "unmount" you
can now safely unmount volumes that are still in use by some applications. Minor fixes to the FD disconnection implementation: * put_fd() checked the condition for being able to disconnect a descriptor incorrectly (causing the FD to never be disconnected). * remove_fd() would hand out disconnected descriptors (but should have returned NULL for them). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15952 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
60e5ea42e0
commit
ddb7d0b6a6
@ -73,6 +73,7 @@ extern struct file_descriptor *get_fd(struct io_context *, int);
|
|||||||
extern void close_fd(struct file_descriptor *descriptor);
|
extern void close_fd(struct file_descriptor *descriptor);
|
||||||
extern void put_fd(struct file_descriptor *descriptor);
|
extern void put_fd(struct file_descriptor *descriptor);
|
||||||
extern void disconnect_fd(struct file_descriptor *descriptor);
|
extern void disconnect_fd(struct file_descriptor *descriptor);
|
||||||
|
extern void inc_fd_ref_count(struct file_descriptor *descriptor);
|
||||||
extern status_t select_fd(int fd, uint8 event, uint32 ref,
|
extern status_t select_fd(int fd, uint8 event, uint32 ref,
|
||||||
struct select_sync *sync, bool kernel);
|
struct select_sync *sync, bool kernel);
|
||||||
extern status_t deselect_fd(int fd, uint8 event, struct select_sync *sync,
|
extern status_t deselect_fd(int fd, uint8 event, struct select_sync *sync,
|
||||||
|
@ -139,7 +139,7 @@ put_fd(struct file_descriptor *descriptor)
|
|||||||
|
|
||||||
free(descriptor);
|
free(descriptor);
|
||||||
} else if ((descriptor->open_mode & O_DISCONNECTED) != 0
|
} else if ((descriptor->open_mode & O_DISCONNECTED) != 0
|
||||||
&& previous == descriptor->open_count) {
|
&& previous - 1 == descriptor->open_count) {
|
||||||
// the descriptor has been disconnected - it cannot
|
// the descriptor has been disconnected - it cannot
|
||||||
// be accessed anymore, let's close it (no one is
|
// be accessed anymore, let's close it (no one is
|
||||||
// currently accessing this descriptor)
|
// currently accessing this descriptor)
|
||||||
@ -188,6 +188,13 @@ disconnect_fd(struct file_descriptor *descriptor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
inc_fd_ref_count(struct file_descriptor *descriptor)
|
||||||
|
{
|
||||||
|
atomic_add(&descriptor->ref_count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct file_descriptor *
|
struct file_descriptor *
|
||||||
get_fd(struct io_context *context, int fd)
|
get_fd(struct io_context *context, int fd)
|
||||||
{
|
{
|
||||||
@ -206,7 +213,7 @@ get_fd(struct io_context *context, int fd)
|
|||||||
if (descriptor->open_mode & O_DISCONNECTED)
|
if (descriptor->open_mode & O_DISCONNECTED)
|
||||||
descriptor = NULL;
|
descriptor = NULL;
|
||||||
else
|
else
|
||||||
atomic_add(&descriptor->ref_count, 1);
|
inc_fd_ref_count(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&context->io_mutex);
|
mutex_unlock(&context->io_mutex);
|
||||||
@ -231,10 +238,14 @@ remove_fd(struct io_context *context, int fd)
|
|||||||
if ((uint32)fd < context->table_size)
|
if ((uint32)fd < context->table_size)
|
||||||
descriptor = context->fds[fd];
|
descriptor = context->fds[fd];
|
||||||
|
|
||||||
if (descriptor) { // fd is valid
|
if (descriptor) {
|
||||||
|
// fd is valid
|
||||||
context->fds[fd] = NULL;
|
context->fds[fd] = NULL;
|
||||||
fd_set_close_on_exec(context, fd, false);
|
fd_set_close_on_exec(context, fd, false);
|
||||||
context->num_used_fds--;
|
context->num_used_fds--;
|
||||||
|
|
||||||
|
if (descriptor->open_mode & O_DISCONNECTED)
|
||||||
|
descriptor = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&context->io_mutex);
|
mutex_unlock(&context->io_mutex);
|
||||||
|
@ -1249,7 +1249,6 @@ resolve_mount_point_to_volume_root(mount_id mountID, vnode_id nodeID,
|
|||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
|
||||||
// resolve the node
|
// resolve the node
|
||||||
struct vnode *resolvedNode = resolve_mount_point_to_volume_root(node);
|
struct vnode *resolvedNode = resolve_mount_point_to_volume_root(node);
|
||||||
if (resolvedNode) {
|
if (resolvedNode) {
|
||||||
@ -1538,7 +1537,7 @@ static status_t
|
|||||||
path_to_vnode(char *path, bool traverseLink, struct vnode **_vnode,
|
path_to_vnode(char *path, bool traverseLink, struct vnode **_vnode,
|
||||||
vnode_id *_parentID, bool kernel)
|
vnode_id *_parentID, bool kernel)
|
||||||
{
|
{
|
||||||
struct vnode *start;
|
struct vnode *start = NULL;
|
||||||
|
|
||||||
FUNCTION(("path_to_vnode(path = \"%s\")\n", path));
|
FUNCTION(("path_to_vnode(path = \"%s\")\n", path));
|
||||||
|
|
||||||
@ -1561,8 +1560,12 @@ path_to_vnode(char *path, bool traverseLink, struct vnode **_vnode,
|
|||||||
|
|
||||||
mutex_lock(&context->io_mutex);
|
mutex_lock(&context->io_mutex);
|
||||||
start = context->cwd;
|
start = context->cwd;
|
||||||
inc_vnode_ref_count(start);
|
if (start != NULL)
|
||||||
|
inc_vnode_ref_count(start);
|
||||||
mutex_unlock(&context->io_mutex);
|
mutex_unlock(&context->io_mutex);
|
||||||
|
|
||||||
|
if (start == NULL)
|
||||||
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return vnode_path_to_vnode(start, path, traverseLink, 0, _vnode, _parentID, NULL);
|
return vnode_path_to_vnode(start, path, traverseLink, 0, _vnode, _parentID, NULL);
|
||||||
@ -3015,8 +3018,6 @@ vfs_free_io_context(void *_ioContext)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&context->io_mutex);
|
|
||||||
|
|
||||||
mutex_destroy(&context->io_mutex);
|
mutex_destroy(&context->io_mutex);
|
||||||
|
|
||||||
remove_node_monitors(context);
|
remove_node_monitors(context);
|
||||||
@ -3826,10 +3827,12 @@ common_fcntl(int fd, int op, uint32 argument, bool kernel)
|
|||||||
// we only accept changes to O_APPEND and O_NONBLOCK
|
// we only accept changes to O_APPEND and O_NONBLOCK
|
||||||
argument &= O_APPEND | O_NONBLOCK;
|
argument &= O_APPEND | O_NONBLOCK;
|
||||||
|
|
||||||
status = FS_CALL(vnode, set_flags)(vnode->mount->cookie, vnode->private_node, descriptor->cookie, (int)argument);
|
status = FS_CALL(vnode, set_flags)(vnode->mount->cookie,
|
||||||
|
vnode->private_node, descriptor->cookie, (int)argument);
|
||||||
if (status == B_OK) {
|
if (status == B_OK) {
|
||||||
// update this descriptor's open_mode field
|
// update this descriptor's open_mode field
|
||||||
descriptor->open_mode = (descriptor->open_mode & ~(O_APPEND | O_NONBLOCK)) | argument;
|
descriptor->open_mode = (descriptor->open_mode & ~(O_APPEND | O_NONBLOCK))
|
||||||
|
| argument;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
status = EOPNOTSUPP;
|
status = EOPNOTSUPP;
|
||||||
@ -5138,19 +5141,129 @@ fs_unmount(char *path, uint32 flags, bool kernel)
|
|||||||
// from the path_to_vnode() call above
|
// from the path_to_vnode() call above
|
||||||
mount->root_vnode->ref_count -= 2;
|
mount->root_vnode->ref_count -= 2;
|
||||||
|
|
||||||
// cycle through the list of vnodes associated with this mount and
|
bool disconnectedDescriptors = false;
|
||||||
// make sure all of them are not busy or have refs on them
|
|
||||||
vnode = NULL;
|
while (true) {
|
||||||
while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) {
|
bool busy = false;
|
||||||
if (vnode->busy || vnode->ref_count != 0) {
|
|
||||||
// there are still vnodes in use on this mount, so we cannot unmount yet
|
// cycle through the list of vnodes associated with this mount and
|
||||||
// ToDo: cut read/write access file descriptors, depending on the B_FORCE_UNMOUNT flag
|
// make sure all of them are not busy or have refs on them
|
||||||
|
vnode = NULL;
|
||||||
|
while ((vnode = (struct vnode *)list_get_next_item(&mount->vnodes, vnode)) != NULL) {
|
||||||
|
if (vnode->busy || vnode->ref_count != 0) {
|
||||||
|
// there are still vnodes in use on this mount, so we cannot
|
||||||
|
// unmount yet
|
||||||
|
busy = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!busy)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ((flags & B_FORCE_UNMOUNT) == 0) {
|
||||||
mount->root_vnode->ref_count += 2;
|
mount->root_vnode->ref_count += 2;
|
||||||
mutex_unlock(&sVnodeMutex);
|
mutex_unlock(&sVnodeMutex);
|
||||||
put_vnode(mount->root_vnode);
|
put_vnode(mount->root_vnode);
|
||||||
|
|
||||||
return B_BUSY;
|
return B_BUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (disconnectedDescriptors) {
|
||||||
|
// wait a bit until the last access is finished, and then try again
|
||||||
|
mutex_unlock(&sVnodeMutex);
|
||||||
|
snooze(100000);
|
||||||
|
mutex_lock(&sVnodeMutex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the file system is still busy - but we're forced to unmount it,
|
||||||
|
// so let's disconnect all open file descriptors
|
||||||
|
|
||||||
|
mount->unmounting = true;
|
||||||
|
// prevent new vnodes from being created
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedDescriptors = true;
|
||||||
|
mutex_lock(&sVnodeMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we can safely continue, mark all of the vnodes busy and this mount
|
// we can safely continue, mark all of the vnodes busy and this mount
|
||||||
|
Loading…
x
Reference in New Issue
Block a user