diff --git a/headers/private/kernel/fs/fd.h b/headers/private/kernel/fs/fd.h index faa5ddfb69..dd6b911b6c 100644 --- a/headers/private/kernel/fs/fd.h +++ b/headers/private/kernel/fs/fd.h @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ #ifndef _FD_H @@ -61,6 +61,8 @@ enum fd_types { FDTYPE_SOCKET }; +// additional open mode - kernel special +#define O_DISCONNECTED 0x80000000 /* Prototypes */ @@ -70,8 +72,11 @@ extern int new_fd(struct io_context *, struct file_descriptor *); extern struct file_descriptor *get_fd(struct io_context *, int); extern void close_fd(struct file_descriptor *descriptor); extern void put_fd(struct file_descriptor *descriptor); -extern status_t select_fd(int fd, uint8 event, uint32 ref, struct select_sync *sync, bool kernel); -extern status_t deselect_fd(int fd, uint8 event, struct select_sync *sync, bool kernel); +extern void disconnect_fd(struct file_descriptor *descriptor); +extern status_t select_fd(int fd, uint8 event, uint32 ref, + struct select_sync *sync, bool kernel); +extern status_t deselect_fd(int fd, uint8 event, struct select_sync *sync, + bool kernel); extern bool fd_is_valid(int fd, bool kernel); extern struct vnode *fd_vnode(struct file_descriptor *descriptor); diff --git a/src/system/kernel/fs/fd.c b/src/system/kernel/fs/fd.c index b28eb2c366..8acbea27df 100644 --- a/src/system/kernel/fs/fd.c +++ b/src/system/kernel/fs/fd.c @@ -1,6 +1,6 @@ /* Operations on file descriptors * - * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ @@ -126,15 +126,36 @@ new_fd(struct io_context *context, struct file_descriptor *descriptor) void put_fd(struct file_descriptor *descriptor) { - TRACE(("put_fd(descriptor = %p [ref = %ld, cookie = %p])\n", descriptor, descriptor->ref_count, descriptor->cookie)); + int32 previous = atomic_add(&descriptor->ref_count, -1); + + TRACE(("put_fd(descriptor = %p [ref = %ld, cookie = %p])\n", + descriptor, descriptor->ref_count, descriptor->cookie)); // free the descriptor if we don't need it anymore - if (atomic_add(&descriptor->ref_count, -1) == 1) { + if (previous == 1) { // free the underlying object if (descriptor->ops->fd_free) descriptor->ops->fd_free(descriptor); free(descriptor); + } else if ((descriptor->open_mode & O_DISCONNECTED) != 0 + && previous == descriptor->open_count) { + // the descriptor has been disconnected - it cannot + // be accessed anymore, let's close it (no one is + // currently accessing this descriptor) + + if (descriptor->ops->fd_close) + descriptor->ops->fd_close(descriptor); + if (descriptor->ops->fd_free) + descriptor->ops->fd_free(descriptor); + + // prevent this descriptor from being closed/freed again + descriptor->open_count = -1; + descriptor->ref_count = -1; + descriptor->u.vnode = NULL; + + // the file descriptor is kept intact, so that it's not + // reused until someone explicetly closes it } } @@ -153,6 +174,20 @@ close_fd(struct file_descriptor *descriptor) } +/** This descriptor's underlying object will be closed and freed + * as soon as possible (in one of the next calls to put_fd() - + * get_fd() will no longer succeed on this descriptor). + * This is useful if the underlying object is gone, for instance + * when a (mounted) volume got removed unexpectedly. + */ + +void +disconnect_fd(struct file_descriptor *descriptor) +{ + descriptor->open_mode |= O_DISCONNECTED; +} + + struct file_descriptor * get_fd(struct io_context *context, int fd) { @@ -166,6 +201,10 @@ get_fd(struct io_context *context, int fd) if ((uint32)fd < context->table_size) descriptor = context->fds[fd]; + // Disconnected descriptors cannot be accessed anymore + if (descriptor->open_mode & O_DISCONNECTED) + descriptor = NULL; + if (descriptor != NULL) // fd is valid atomic_add(&descriptor->ref_count, 1);