diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index b8e16c3ebfc..332cad51613 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -851,6 +851,18 @@ struct unlock_file_reply +struct unmount_device_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct unmount_device_reply +{ + struct reply_header __header; +}; + + + struct create_socket_request { struct request_header __header; @@ -3592,6 +3604,7 @@ enum request REQ_flush_file, REQ_lock_file, REQ_unlock_file, + REQ_unmount_device, REQ_create_socket, REQ_accept_socket, REQ_set_socket_event, @@ -3802,6 +3815,7 @@ union generic_request struct flush_file_request flush_file_request; struct lock_file_request lock_file_request; struct unlock_file_request unlock_file_request; + struct unmount_device_request unmount_device_request; struct create_socket_request create_socket_request; struct accept_socket_request accept_socket_request; struct set_socket_event_request set_socket_event_request; @@ -4010,6 +4024,7 @@ union generic_reply struct flush_file_reply flush_file_reply; struct lock_file_reply lock_file_reply; struct unlock_file_reply unlock_file_reply; + struct unmount_device_reply unmount_device_reply; struct create_socket_reply create_socket_reply; struct accept_socket_reply accept_socket_reply; struct set_socket_event_reply set_socket_event_reply; @@ -4175,6 +4190,6 @@ union generic_reply struct set_mailslot_info_reply set_mailslot_info_reply; }; -#define SERVER_PROTOCOL_VERSION 188 +#define SERVER_PROTOCOL_VERSION 189 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/change.c b/server/change.c index 3d227b05d0b..b76cf060bc2 100644 --- a/server/change.c +++ b/server/change.c @@ -137,6 +137,8 @@ static struct change *create_change_notification( struct fd *fd, int subtree, un struct stat st; int unix_fd = get_unix_fd( fd ); + if (unix_fd == -1) return NULL; + if (fstat( unix_fd, &st ) == -1 || !S_ISDIR(st.st_mode)) { set_error( STATUS_NOT_A_DIRECTORY ); diff --git a/server/fd.c b/server/fd.c index f6017ee5770..79148867cbf 100644 --- a/server/fd.c +++ b/server/fd.c @@ -625,7 +625,7 @@ static void device_destroy( struct object *obj ) /* inode functions */ /* close all pending file descriptors in the closed list */ -static void inode_close_pending( struct inode *inode ) +static void inode_close_pending( struct inode *inode, int keep_unlinks ) { struct list *ptr = list_head( &inode->closed ); @@ -639,7 +639,7 @@ static void inode_close_pending( struct inode *inode ) close( fd->unix_fd ); fd->unix_fd = -1; } - if (!fd->unlink) /* get rid of it unless there's an unlink pending on that file */ + if (!keep_unlinks || !fd->unlink[0]) /* get rid of it unless there's an unlink pending on that file */ { list_remove( ptr ); free( fd ); @@ -968,7 +968,7 @@ static void remove_lock( struct file_lock *lock, int remove_unix ) list_remove( &lock->inode_entry ); list_remove( &lock->proc_entry ); if (remove_unix) remove_unix_locks( lock->fd, lock->start, lock->end ); - if (list_empty( &inode->locks )) inode_close_pending( inode ); + if (list_empty( &inode->locks )) inode_close_pending( inode, 1 ); lock->process = NULL; wake_up( &lock->obj, 0 ); release_object( lock ); @@ -1187,6 +1187,26 @@ void set_fd_events( struct fd *fd, int events ) } } +/* prepare an fd for unmounting its corresponding device */ +static inline void unmount_fd( struct fd *fd ) +{ + assert( fd->inode ); + + async_terminate_queue( &fd->read_q, STATUS_VOLUME_DISMOUNTED ); + async_terminate_queue( &fd->write_q, STATUS_VOLUME_DISMOUNTED ); + + if (fd->poll_index != -1) set_fd_events( fd, -1 ); + + if (fd->unix_fd != -1) close( fd->unix_fd ); + + fd->unix_fd = -1; + fd->closed->unix_fd = -1; + fd->closed->unlink[0] = 0; + + /* stop using Unix locks on this fd (existing locks have been removed by close) */ + fd->fs_locks = 0; +} + /* allocate an fd object, without setting the unix fd yet */ struct fd *alloc_fd( const struct fd_ops *fd_user_ops, struct object *user ) { @@ -1379,6 +1399,7 @@ void *get_fd_user( struct fd *fd ) /* retrieve the unix fd for an object */ int get_unix_fd( struct fd *fd ) { + if (fd->unix_fd == -1) set_error( STATUS_VOLUME_DISMOUNTED ); return fd->unix_fd; } @@ -1399,6 +1420,8 @@ int check_fd_events( struct fd *fd, int events ) { struct pollfd pfd; + if (fd->unix_fd == -1) return POLLERR; + pfd.fd = fd->unix_fd; pfd.events = events; if (poll( &pfd, 1, 0 ) <= 0) return 0; @@ -1554,6 +1577,29 @@ void no_cancel_async( struct fd *fd ) set_error( STATUS_OBJECT_TYPE_MISMATCH ); } +/* close all Unix file descriptors on a device to allow unmounting it */ +static void unmount_device( struct device *device ) +{ + unsigned int i; + struct inode *inode; + struct fd *fd; + + for (i = 0; i < INODE_HASH_SIZE; i++) + { + LIST_FOR_EACH_ENTRY( inode, &device->inode_hash[i], struct inode, entry ) + { + LIST_FOR_EACH_ENTRY( fd, &inode->open, struct fd, inode_entry ) + { + unmount_fd( fd ); + } + inode_close_pending( inode, 0 ); + } + } + /* remove it from the hash table */ + list_remove( &device->entry ); + list_init( &device->entry ); +} + /* same as get_handle_obj but retrieve the struct fd associated to the object */ static struct fd *get_handle_fd_obj( struct process *process, obj_handle_t handle, unsigned int access ) @@ -1595,18 +1641,31 @@ DECL_HANDLER(get_handle_fd) if ((fd = get_handle_fd_obj( current->process, req->handle, req->access ))) { - int unix_fd = get_handle_unix_fd( current->process, req->handle, req->access ); - if (unix_fd != -1) reply->fd = unix_fd; - else if (!get_error()) + int unix_fd = get_unix_fd( fd ); + if (unix_fd != -1) { - assert( fd->unix_fd != -1 ); - send_client_fd( current->process, fd->unix_fd, req->handle ); + int cached_fd = get_handle_unix_fd( current->process, req->handle, req->access ); + if (cached_fd != -1) reply->fd = cached_fd; + else if (!get_error()) send_client_fd( current->process, unix_fd, req->handle ); } reply->flags = fd->fd_ops->get_file_info( fd ); release_object( fd ); } } +/* get ready to unmount a Unix device */ +DECL_HANDLER(unmount_device) +{ + struct fd *fd; + + if ((fd = get_handle_fd_obj( current->process, req->handle, 0 ))) + { + if (fd->inode) unmount_device( fd->inode->device ); + else set_error( STATUS_OBJECT_TYPE_MISMATCH ); + release_object( fd ); + } +} + /* create / reschedule an async I/O */ DECL_HANDLER(register_async) { diff --git a/server/file.c b/server/file.c index f73196a4d2f..a5e4d768b90 100644 --- a/server/file.c +++ b/server/file.c @@ -226,8 +226,13 @@ static int file_get_poll_events( struct fd *fd ) static int file_flush( struct fd *fd, struct event **event ) { - int ret = (fsync( get_unix_fd(fd) ) != -1); - if (!ret) file_set_error(); + int ret = 0, unix_fd = get_unix_fd( fd ); + + if (unix_fd != -1) + { + ret = (fsync( unix_fd ) != -1); + if (!ret) file_set_error(); + } return ret; } @@ -304,6 +309,8 @@ static int extend_file( struct file *file, file_pos_t new_size ) int unix_fd = get_file_unix_fd( file ); off_t size = new_size; + if (unix_fd == -1) return 0; + if (sizeof(new_size) > sizeof(size) && size != new_size) { set_error( STATUS_INVALID_PARAMETER ); @@ -326,6 +333,8 @@ int grow_file( struct file *file, file_pos_t size ) struct stat st; int unix_fd = get_file_unix_fd( file ); + if (unix_fd == -1) return 0; + if (fstat( unix_fd, &st ) == -1) { file_set_error(); diff --git a/server/mapping.c b/server/mapping.c index 4c9a323993b..5826fef839d 100644 --- a/server/mapping.c +++ b/server/mapping.c @@ -196,7 +196,7 @@ static int get_image_params( struct mapping *mapping ) /* load the headers */ if (!(fd = mapping_get_fd( &mapping->obj ))) return 0; - unix_fd = get_unix_fd( fd ); + if ((unix_fd = get_unix_fd( fd )) == -1) goto error; if (pread( unix_fd, &dos, sizeof(dos), 0 ) != sizeof(dos)) goto error; if (dos.e_magic != IMAGE_DOS_SIGNATURE) goto error; pos = dos.e_lfanew; @@ -250,7 +250,7 @@ inline static int get_file_size( struct file *file, file_pos_t *size ) struct stat st; int unix_fd = get_file_unix_fd( file ); - if (fstat( unix_fd, &st ) == -1) return 0; + if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return 0; *size = st.st_size; return 1; } diff --git a/server/protocol.def b/server/protocol.def index b18782d4d2f..d4b52ccd83a 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -657,6 +657,12 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT }; @END +/* Get ready to unmount a Unix device */ +@REQ(unmount_device) + obj_handle_t handle; /* handle to a file on the device */ +@END + + /* Create a socket */ @REQ(create_socket) unsigned int access; /* wanted access rights */ diff --git a/server/request.h b/server/request.h index 71dbc7d0078..740a4175039 100644 --- a/server/request.h +++ b/server/request.h @@ -143,6 +143,7 @@ DECL_HANDLER(get_handle_fd); DECL_HANDLER(flush_file); DECL_HANDLER(lock_file); DECL_HANDLER(unlock_file); +DECL_HANDLER(unmount_device); DECL_HANDLER(create_socket); DECL_HANDLER(accept_socket); DECL_HANDLER(set_socket_event); @@ -352,6 +353,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_flush_file, (req_handler)req_lock_file, (req_handler)req_unlock_file, + (req_handler)req_unmount_device, (req_handler)req_create_socket, (req_handler)req_accept_socket, (req_handler)req_set_socket_event, diff --git a/server/trace.c b/server/trace.c index dce6195b2ad..17b2544ad47 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1077,6 +1077,11 @@ static void dump_unlock_file_request( const struct unlock_file_request *req ) fprintf( stderr, " count_high=%08x", req->count_high ); } +static void dump_unmount_device_request( const struct unmount_device_request *req ) +{ + fprintf( stderr, " handle=%p", req->handle ); +} + static void dump_create_socket_request( const struct create_socket_request *req ) { fprintf( stderr, " access=%08x,", req->access ); @@ -3114,6 +3119,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_flush_file_request, (dump_func)dump_lock_file_request, (dump_func)dump_unlock_file_request, + (dump_func)dump_unmount_device_request, (dump_func)dump_create_socket_request, (dump_func)dump_accept_socket_request, (dump_func)dump_set_socket_event_request, @@ -3320,6 +3326,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_flush_file_reply, (dump_func)dump_lock_file_reply, (dump_func)0, + (dump_func)0, (dump_func)dump_create_socket_reply, (dump_func)dump_accept_socket_reply, (dump_func)0, @@ -3526,6 +3533,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "flush_file", "lock_file", "unlock_file", + "unmount_device", "create_socket", "accept_socket", "set_socket_event", @@ -3755,6 +3763,7 @@ static const struct { "SUSPEND_COUNT_EXCEEDED", STATUS_SUSPEND_COUNT_EXCEEDED }, { "TIMEOUT", STATUS_TIMEOUT }, { "UNSUCCESSFUL", STATUS_UNSUCCESSFUL }, + { "VOLUME_DISMOUNTED", STATUS_VOLUME_DISMOUNTED }, { "WAS_LOCKED", STATUS_WAS_LOCKED }, { NULL, 0 } };