struct security_descriptor *get_file_sd( struct object *obj, struct fd *fd, mode_t *mode, uid_t *uid ) { int unix_fd = get_unix_fd( fd ); struct stat st; struct security_descriptor *sd; const SID *user, *group; if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return obj->sd; /* mode and uid the same? if so, no need to re-generate security descriptor */ if (obj->sd && (st.st_mode & (S_IRWXU|S_IRWXO)) == (*mode & (S_IRWXU|S_IRWXO)) && (st.st_uid == *uid)) return obj->sd; user = security_unix_uid_to_sid( st.st_uid ); group = token_get_primary_group( current->process->token ); sd = get_xattr_sd( unix_fd ); if (!sd) sd = get_xattr_acls( unix_fd, user, group ); if (sd) convert_generic_sd( sd ); if (!sd) sd = mode_to_sd( st.st_mode, user, group ); if (!sd) return obj->sd; *mode = st.st_mode; *uid = st.st_uid; free( obj->sd ); obj->sd = sd; return sd; }
/* handle a socket event */ static void master_socket_poll_event( struct fd *fd, int event ) { struct master_socket *sock = get_fd_user( fd ); assert( master_socket->obj.ops == &master_socket_ops ); assert( sock == master_socket ); /* there is only one master socket */ if (event & (POLLERR | POLLHUP)) { /* this is not supposed to happen */ fprintf( stderr, "wineserver: Error on master socket\n" ); release_object( sock ); } else if (event & POLLIN) { struct sockaddr_un dummy; unsigned int len = sizeof(dummy); int client = accept( get_unix_fd( master_socket->fd ), (struct sockaddr *) &dummy, &len ); if (client == -1) return; if (sock->timeout) { remove_timeout_user( sock->timeout ); sock->timeout = NULL; } fcntl( client, F_SETFL, O_NONBLOCK ); create_process( client, NULL, 0 ); } }
/* read a request from a thread */ void read_request( struct thread *thread ) { int ret; if (!thread->req_toread) /* no pending request */ { if ((ret = read( get_unix_fd( thread->request_fd ), &thread->req, sizeof(thread->req) )) != sizeof(thread->req)) goto error; if (!(thread->req_toread = thread->req.request_header.request_size)) { /* no data, handle request at once */ call_req_handler( thread ); return; } if (!(thread->req_data = malloc( thread->req_toread ))) { fatal_protocol_error( thread, "no memory for %u bytes request %d\n", thread->req_toread, thread->req.request_header.req ); return; } } /* read the variable sized data */ for (;;) { ret = read( get_unix_fd( thread->request_fd ), (char *)thread->req_data + thread->req.request_header.request_size - thread->req_toread, thread->req_toread ); if (ret <= 0) break; if (!(thread->req_toread -= ret)) { call_req_handler( thread ); free( thread->req_data ); thread->req_data = NULL; return; } } error: if (!ret) /* closed pipe */ kill_thread( thread, 0 ); else if (ret > 0) fatal_protocol_error( thread, "partial read %d\n", ret ); else if (errno != EWOULDBLOCK && errno != EAGAIN) fatal_protocol_perror( thread, "read" ); }
static inline int sock_error( struct fd *fd ) { unsigned int optval = 0; socklen_t optlen = sizeof(optval); getsockopt( get_unix_fd(fd), SOL_SOCKET, SO_ERROR, (void *) &optval, &optlen); return optval; }
/* retrieve the mapping parameters for an executable (PE) image */ static int get_image_params( struct mapping *mapping ) { IMAGE_DOS_HEADER dos; IMAGE_NT_HEADERS nt; IMAGE_SECTION_HEADER *sec = NULL; struct fd *fd; off_t pos; int unix_fd, size, toread; /* load the headers */ if (!(fd = mapping_get_fd( &mapping->obj ))) return 0; 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; if (pread( unix_fd, &nt.Signature, sizeof(nt.Signature), pos ) != sizeof(nt.Signature)) goto error; pos += sizeof(nt.Signature); if (nt.Signature != IMAGE_NT_SIGNATURE) goto error; if (pread( unix_fd, &nt.FileHeader, sizeof(nt.FileHeader), pos ) != sizeof(nt.FileHeader)) goto error; pos += sizeof(nt.FileHeader); /* zero out Optional header in the case it's not present or partial */ memset(&nt.OptionalHeader, 0, sizeof(nt.OptionalHeader)); toread = min( sizeof(nt.OptionalHeader), nt.FileHeader.SizeOfOptionalHeader ); if (pread( unix_fd, &nt.OptionalHeader, toread, pos ) != toread) goto error; pos += nt.FileHeader.SizeOfOptionalHeader; /* load the section headers */ size = sizeof(*sec) * nt.FileHeader.NumberOfSections; if (!(sec = malloc( size ))) goto error; if (pread( unix_fd, sec, size, pos ) != size) goto error; if (!build_shared_mapping( mapping, unix_fd, sec, nt.FileHeader.NumberOfSections )) goto error; if (mapping->shared_file) list_add_head( &shared_list, &mapping->shared_entry ); mapping->size = ROUND_SIZE( nt.OptionalHeader.SizeOfImage ); mapping->base = (void *)nt.OptionalHeader.ImageBase; mapping->header_size = pos + size; mapping->protect = VPROT_IMAGE; /* sanity check */ if (mapping->header_size > mapping->size) goto error; free( sec ); release_object( fd ); return 1; error: if (sec) free( sec ); release_object( fd ); set_error( STATUS_INVALID_FILE_FOR_SECTION ); return 0; }
static int file_flush( struct fd *fd, struct async *async ) { int unix_fd = get_unix_fd( fd ); if (unix_fd != -1 && fsync( unix_fd ) == -1) { file_set_error(); return 0; } return 1; }
/* send a reply to the current thread */ static void send_reply( union generic_reply *reply ) { int ret; if (!current->reply_size) { if ((ret = write( get_unix_fd( current->reply_fd ), reply, sizeof(*reply) )) != sizeof(*reply)) goto error; } else { struct iovec vec[2]; vec[0].iov_base = (void *)reply; vec[0].iov_len = sizeof(*reply); vec[1].iov_base = current->reply_data; vec[1].iov_len = current->reply_size; if ((ret = writev( get_unix_fd( current->reply_fd ), vec, 2 )) < sizeof(*reply)) goto error; if ((current->reply_towrite = current->reply_size - (ret - sizeof(*reply)))) { /* couldn't write it all, wait for POLLOUT */ set_fd_events( current->reply_fd, POLLOUT ); set_fd_events( current->request_fd, 0 ); return; } } if (current->reply_data) { free( current->reply_data ); current->reply_data = NULL; } return; error: if (ret >= 0) fatal_protocol_error( current, "partial write %d\n", ret ); else if (errno == EPIPE) kill_thread( current, 0 ); /* normal death */ else fatal_protocol_perror( current, "reply write" ); }
/* send an fd to a client */ int send_client_fd( struct process *process, int fd, obj_handle_t handle ) { struct iovec vec; struct msghdr msghdr; int ret; #ifdef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS msghdr.msg_accrightslen = sizeof(fd); msghdr.msg_accrights = (void *)&fd; #else /* HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */ char cmsg_buffer[256]; struct cmsghdr *cmsg; msghdr.msg_control = cmsg_buffer; msghdr.msg_controllen = sizeof(cmsg_buffer); msghdr.msg_flags = 0; cmsg = CMSG_FIRSTHDR( &msghdr ); cmsg->cmsg_len = CMSG_LEN( sizeof(fd) ); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; *(int *)CMSG_DATA(cmsg) = fd; msghdr.msg_controllen = cmsg->cmsg_len; #endif /* HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */ msghdr.msg_name = NULL; msghdr.msg_namelen = 0; msghdr.msg_iov = &vec; msghdr.msg_iovlen = 1; vec.iov_base = (void *)&handle; vec.iov_len = sizeof(handle); if (debug_level) fprintf( stderr, "%04x: *fd* %04x -> %d\n", current ? current->id : process->id, handle, fd ); ret = sendmsg( get_unix_fd( process->msg_fd ), &msghdr, 0 ); if (ret == sizeof(handle)) return 0; if (ret >= 0) { fprintf( stderr, "Protocol error: process %04x: partial sendmsg %d\n", process->id, ret ); kill_process( process, 1 ); } else if (errno == EPIPE) { kill_process( process, 0 ); } else { fprintf( stderr, "Protocol error: process %04x: ", process->id ); perror( "sendmsg" ); kill_process( process, 1 ); } return -1; }
static int file_flush( struct fd *fd, struct event **event ) { 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; }
static struct security_descriptor *file_get_parent_sd( struct fd *root, const char *child_name, int child_len, int is_dir ) { struct security_descriptor *parent_sd, *sd = NULL; mode_t parent_mode = 0555; char *p, *parent_name; struct fd *parent_fd; struct stat st; int unix_fd; if (!(parent_name = mem_alloc( child_len + 1 ))) return NULL; memcpy( parent_name, child_name, child_len ); parent_name[child_len] = 0; /* skip trailing slashes */ p = parent_name + strlen( parent_name ) - 1; while (p > parent_name && *p == '/') p--; /* remove last path component */ if (p == parent_name && *p == '/') { free(parent_name); return NULL; } while (p > parent_name && *p != '/') p--; while (p > parent_name && *p == '/') p--; p[1] = 0; parent_fd = open_fd( root, parent_name, O_NONBLOCK | O_LARGEFILE, &parent_mode, READ_CONTROL|ACCESS_SYSTEM_SECURITY, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_FOR_BACKUP_INTENT ); free( parent_name ); if (parent_fd) { unix_fd = get_unix_fd( parent_fd ); if (unix_fd != -1) { parent_sd = get_xattr_sd( unix_fd ); if (!parent_sd && fstat( unix_fd, &st ) != -1) parent_sd = get_xattr_acls( unix_fd, security_unix_uid_to_sid( st.st_uid ), token_get_primary_group( current->process->token ) ); if (parent_sd) { sd = inherit_sd( parent_sd, is_dir ); free( parent_sd ); } } release_object( parent_fd ); } return sd; }
static void handler_poll_event( struct fd *fd, int event ) { struct handler *handler = get_fd_user( fd ); if (event & (POLLERR | POLLHUP)) { /* this is not supposed to happen */ fprintf( stderr, "wineserver: Error on signal handler pipe\n" ); release_object( handler ); } else if (event & POLLIN) { char dummy; handler->pending = 0; read( get_unix_fd( handler->fd ), &dummy, 1 ); handler->callback(); } }
static void sock_destroy( struct object *obj ) { struct sock *sock = (struct sock *)obj; assert( obj->ops == &sock_ops ); /* FIXME: special socket shutdown stuff? */ if ( sock->deferred ) release_object( sock->deferred ); free_async_queue( sock->read_q ); free_async_queue( sock->write_q ); if (sock->event) release_object( sock->event ); if (sock->fd) { /* shut the socket down to force pending poll() calls in the client to return */ shutdown( get_unix_fd(sock->fd), SHUT_RDWR ); release_object( sock->fd ); } }
/* accepts a socket and inits it */ static int accept_new_fd( struct sock *sock ) { /* Try to accept(2). We can't be safe that this an already connected socket * or that accept() is allowed on it. In those cases we will get -1/errno * return. */ int acceptfd; struct sockaddr saddr; socklen_t slen = sizeof(saddr); acceptfd = accept( get_unix_fd(sock->fd), &saddr, &slen); if (acceptfd == -1) { sock_set_error(); return acceptfd; } fcntl(acceptfd, F_SETFL, O_NONBLOCK); /* make socket nonblocking */ return acceptfd; }
/* send an fd to a client */ int send_client_fd( struct process *process, int fd, obj_handle_t handle ) { int ret; if (debug_level) fprintf( stderr, "%04x: *fd* %p -> %d\n", current ? current->id : process->id, handle, fd ); #ifdef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS msghdr.msg_accrightslen = sizeof(fd); msghdr.msg_accrights = (void *)&fd; #else /* HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */ msghdr.msg_control = &cmsg; msghdr.msg_controllen = sizeof(cmsg.header) + sizeof(fd); cmsg.fd = fd; #endif /* HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */ myiovec.iov_base = (void *)&handle; myiovec.iov_len = sizeof(handle); ret = sendmsg( get_unix_fd( process->msg_fd ), &msghdr, 0 ); if (ret == sizeof(handle)) return 0; if (ret >= 0) { fprintf( stderr, "Protocol error: process %04x: partial sendmsg %d\n", process->id, ret ); kill_process( process, NULL, 1 ); } else if (errno == EPIPE) { kill_process( process, NULL, 0 ); } else { fprintf( stderr, "Protocol error: process %04x: ", process->id ); perror( "sendmsg" ); kill_process( process, NULL, 1 ); } return -1; }
/* write the remaining part of the reply */ void write_reply( struct thread *thread ) { int ret; if ((ret = write( get_unix_fd( thread->reply_fd ), (char *)thread->reply_data + thread->reply_size - thread->reply_towrite, thread->reply_towrite )) >= 0) { if (!(thread->reply_towrite -= ret)) { free( thread->reply_data ); thread->reply_data = NULL; /* sent everything, can go back to waiting for requests */ set_fd_events( thread->request_fd, POLLIN ); set_fd_events( thread->reply_fd, 0 ); } return; } if (errno == EPIPE) kill_thread( thread, 0 ); /* normal death */ else if (errno != EWOULDBLOCK && errno != EAGAIN) fatal_protocol_perror( thread, "reply write" ); }
/* create a file by duplicating an fd object */ struct file *create_file_for_fd_obj( struct fd *fd, unsigned int access, unsigned int sharing ) { struct file *file; struct stat st; if (fstat( get_unix_fd(fd), &st ) == -1) { file_set_error(); return NULL; } if ((file = alloc_object( &file_ops ))) { file->mode = st.st_mode; file->access = default_fd_map_access( &file->obj, access ); if (!(file->fd = dup_fd_object( fd, access, sharing, FILE_SYNCHRONOUS_IO_NONALERT ))) { release_object( file ); return NULL; } set_fd_user( file->fd, &file_fd_ops, &file->obj ); } return file; }
static obj_handle_t file_flush( struct fd *fd, const async_data_t *async, int blocking ) { int unix_fd = get_unix_fd( fd ); if (unix_fd != -1 && fsync( unix_fd ) == -1) file_set_error(); return 0; }
int set_file_sd( struct object *obj, struct fd *fd, const struct security_descriptor *sd, unsigned int set_info ) { struct security_descriptor *tmp_sd = NULL; int unix_fd = get_unix_fd( fd ); const SID *owner, *group; struct stat st; mode_t mode; int ret = 1; if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return 1; if (!(set_info & PROTECTED_DACL_SECURITY_INFORMATION)) { char *child_name = fd_get_unix_name( fd ); if (child_name) { struct security_descriptor *parent_sd; parent_sd = file_get_parent_sd( NULL, child_name, strlen(child_name), S_ISDIR(st.st_mode) ); free( child_name ); if (parent_sd) { tmp_sd = file_combine_sds( parent_sd, sd ); if (tmp_sd) sd = tmp_sd; /* only used combined sd if successful */ free( parent_sd ); } } } if (set_info & OWNER_SECURITY_INFORMATION) { owner = sd_get_owner( sd ); if (!owner) { set_error( STATUS_INVALID_SECURITY_DESCR ); ret = 0; goto err; } if (!obj->sd || !security_equal_sid( owner, sd_get_owner( obj->sd ) )) { /* FIXME: get Unix uid and call fchown */ } } else if (obj->sd) owner = sd_get_owner( obj->sd ); else owner = token_get_user( current->process->token ); if (set_info & GROUP_SECURITY_INFORMATION) { group = sd_get_group( sd ); if (!group) { set_error( STATUS_INVALID_SECURITY_DESCR ); ret = 0; goto err; } if (!obj->sd || !security_equal_sid( group, sd_get_group( obj->sd ) )) { /* FIXME: get Unix uid and call fchown */ } } else if (obj->sd) group = sd_get_group( obj->sd ); else group = token_get_primary_group( current->process->token ); /* group and sacl not supported */ if (set_info & DACL_SECURITY_INFORMATION) { /* keep the bits that we don't map to access rights in the ACL */ mode = st.st_mode & (S_ISUID|S_ISGID|S_ISVTX); mode |= sd_to_mode( sd, owner ); set_xattr_sd( unix_fd, sd, owner, group ); if (((st.st_mode ^ mode) & (S_IRWXU|S_IRWXG|S_IRWXO)) && fchmod( unix_fd, mode ) == -1) { file_set_error(); ret = 0; } } err: free( tmp_sd ); return ret; }
/* receive a file descriptor on the process socket */ int receive_fd( struct process *process ) { struct send_fd data; int fd, ret; #ifdef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS msghdr.msg_accrightslen = sizeof(int); msghdr.msg_accrights = (void *)&fd; #else /* HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */ msghdr.msg_control = &cmsg; msghdr.msg_controllen = sizeof(cmsg.header) + sizeof(fd); cmsg.fd = -1; #endif /* HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */ myiovec.iov_base = (void *)&data; myiovec.iov_len = sizeof(data); ret = recvmsg( get_unix_fd( process->msg_fd ), &msghdr, 0 ); #ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS fd = cmsg.fd; #endif if (ret == sizeof(data)) { struct thread *thread; if (data.tid) thread = get_thread_from_id( data.tid ); else thread = (struct thread *)grab_object( get_process_first_thread( process )); if (!thread || thread->process != process || thread->state == TERMINATED) { if (debug_level) fprintf( stderr, "%04x: *fd* %d <- %d bad thread id\n", data.tid, data.fd, fd ); close( fd ); } else { if (debug_level) fprintf( stderr, "%04x: *fd* %d <- %d\n", thread->id, data.fd, fd ); thread_add_inflight_fd( thread, data.fd, fd ); } if (thread) release_object( thread ); return 0; } if (!ret) { kill_process( process, NULL, 0 ); } else if (ret > 0) { fprintf( stderr, "Protocol error: process %04x: partial recvmsg %d for fd\n", process->id, ret ); kill_process( process, NULL, 1 ); } else { if (errno != EWOULDBLOCK && errno != EAGAIN) { fprintf( stderr, "Protocol error: process %04x: ", process->id ); perror( "recvmsg" ); kill_process( process, NULL, 1 ); } } return -1; }
static void sock_poll_event( struct fd *fd, int event ) { struct sock *sock = get_fd_user( fd ); int hangup_seen = 0; int prevstate = sock->state; int error = 0; assert( sock->obj.ops == &sock_ops ); if (debug_level) fprintf(stderr, "socket %p select event: %x\n", sock, event); /* we may change event later, remove from loop here */ if (event & (POLLERR|POLLHUP)) set_fd_events( sock->fd, -1 ); if (sock->state & FD_CONNECT) { if (event & (POLLERR|POLLHUP)) { /* we didn't get connected? */ sock->state &= ~FD_CONNECT; event &= ~POLLOUT; error = sock_error( fd ); } else if (event & POLLOUT) { /* we got connected */ sock->state |= FD_WINE_CONNECTED|FD_READ|FD_WRITE; sock->state &= ~FD_CONNECT; } } else if (sock->state & FD_WINE_LISTENING) { /* listening */ if (event & (POLLERR|POLLHUP)) error = sock_error( fd ); } else { /* normal data flow */ if ( sock->type == SOCK_STREAM && ( event & POLLIN ) ) { char dummy; int nr; /* Linux 2.4 doesn't report POLLHUP if only one side of the socket * has been closed, so we need to check for it explicitly here */ nr = recv( get_unix_fd( fd ), &dummy, 1, MSG_PEEK ); if ( nr == 0 ) { hangup_seen = 1; event &= ~POLLIN; } else if ( nr < 0 ) { event &= ~POLLIN; /* EAGAIN can happen if an async recv() falls between the server's poll() call and the invocation of this routine */ if ( errno != EAGAIN ) { error = errno; event |= POLLERR; if ( debug_level ) fprintf( stderr, "recv error on socket %p: %d\n", sock, errno ); } } } if ( (hangup_seen || event & (POLLHUP|POLLERR)) && (sock->state & (FD_READ|FD_WRITE)) ) { error = error ? error : sock_error( fd ); if ( (event & POLLERR) || ( sock_shutdown_type == SOCK_SHUTDOWN_EOF && (event & POLLHUP) )) sock->state &= ~FD_WRITE; sock->state &= ~FD_READ; if (debug_level) fprintf(stderr, "socket %p aborted by error %d, event: %x\n", sock, error, event); } if (hangup_seen) event |= POLLHUP; } event = sock_dispatch_asyncs( sock, event, error ); sock_dispatch_events( sock, prevstate, event, error ); /* if anyone is stupid enough to wait on the socket object itself, * maybe we should wake them up too, just in case? */ wake_up( &sock->obj, 0 ); sock_reselect( sock ); }
static struct object *create_file( struct fd *root, const char *nameptr, data_size_t len, unsigned int access, unsigned int sharing, int create, unsigned int options, unsigned int attrs, const struct security_descriptor *sd ) { struct security_descriptor *temp_sd = NULL; const SID *owner = NULL, *group = NULL; struct object *obj = NULL; struct fd *fd; int flags; char *name; mode_t mode; if (!len || ((nameptr[0] == '/') ^ !root)) { set_error( STATUS_OBJECT_PATH_SYNTAX_BAD ); return NULL; } if (!(name = mem_alloc( len + 1 ))) return NULL; memcpy( name, nameptr, len ); name[len] = 0; switch(create) { case FILE_CREATE: flags = O_CREAT | O_EXCL; break; case FILE_OVERWRITE_IF: /* FIXME: the difference is whether we trash existing attr or not */ access |= FILE_WRITE_ATTRIBUTES; case FILE_SUPERSEDE: flags = O_CREAT | O_TRUNC; break; case FILE_OPEN: flags = 0; break; case FILE_OPEN_IF: flags = O_CREAT; break; case FILE_OVERWRITE: flags = O_TRUNC; access |= FILE_WRITE_ATTRIBUTES; break; default: set_error( STATUS_INVALID_PARAMETER ); goto done; } /* Note: inheritance of security descriptors only occurs on creation when sd is NULL */ if (!sd && (create == FILE_CREATE || create == FILE_OVERWRITE_IF)) sd = temp_sd = file_get_parent_sd( root, nameptr, len, options & FILE_DIRECTORY_FILE ); if (sd) { owner = sd_get_owner( sd ); if (!owner) owner = token_get_user( current->process->token ); group = sd_get_group( sd ); if (!group) group = token_get_primary_group( current->process->token ); mode = sd_to_mode( sd, owner ); } else if (options & FILE_DIRECTORY_FILE) mode = (attrs & FILE_ATTRIBUTE_READONLY) ? 0555 : 0777; else mode = (attrs & FILE_ATTRIBUTE_READONLY) ? 0444 : 0666; if (len >= 4 && (!strcasecmp( name + len - 4, ".exe" ) || !strcasecmp( name + len - 4, ".com" ))) { if (mode & S_IRUSR) mode |= S_IXUSR; if (mode & S_IRGRP) mode |= S_IXGRP; if (mode & S_IROTH) mode |= S_IXOTH; } access = generic_file_map_access( access ); /* FIXME: should set error to STATUS_OBJECT_NAME_COLLISION if file existed before */ fd = open_fd( root, name, flags | O_NONBLOCK | O_LARGEFILE, &mode, access, sharing, options ); if (!fd) goto done; set_xattr_sd( get_unix_fd( fd ), sd, owner, group ); if (S_ISDIR(mode)) obj = create_dir_obj( fd, access, mode ); else if (S_ISCHR(mode) && is_serial_fd( fd )) obj = create_serial( fd ); else obj = create_file_obj( fd, access, mode ); release_object( fd ); done: free( temp_sd ); free( name ); return obj; }
static void file_flush( struct fd *fd, struct event **event ) { int unix_fd = get_unix_fd( fd ); if (unix_fd != -1 && fsync( unix_fd ) == -1) file_set_error(); }
/* receive a file descriptor on the process socket */ int receive_fd( struct process *process ) { struct iovec vec; struct send_fd data; struct msghdr msghdr; int fd = -1, ret; #ifdef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS msghdr.msg_accrightslen = sizeof(int); msghdr.msg_accrights = (void *)&fd; #else /* HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */ char cmsg_buffer[256]; msghdr.msg_control = cmsg_buffer; msghdr.msg_controllen = sizeof(cmsg_buffer); msghdr.msg_flags = 0; #endif /* HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */ msghdr.msg_name = NULL; msghdr.msg_namelen = 0; msghdr.msg_iov = &vec; msghdr.msg_iovlen = 1; vec.iov_base = (void *)&data; vec.iov_len = sizeof(data); ret = recvmsg( get_unix_fd( process->msg_fd ), &msghdr, 0 ); #ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS if (ret > 0) { struct cmsghdr *cmsg; for (cmsg = CMSG_FIRSTHDR( &msghdr ); cmsg; cmsg = CMSG_NXTHDR( &msghdr, cmsg )) { if (cmsg->cmsg_level != SOL_SOCKET) continue; if (cmsg->cmsg_type == SCM_RIGHTS) fd = *(int *)CMSG_DATA(cmsg); } } #endif /* HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */ if (ret == sizeof(data)) { struct thread *thread; if (data.tid) thread = get_thread_from_id( data.tid ); else thread = (struct thread *)grab_object( get_process_first_thread( process )); if (!thread || thread->process != process || thread->state == TERMINATED) { if (debug_level) fprintf( stderr, "%04x: *fd* %d <- %d bad thread id\n", data.tid, data.fd, fd ); close( fd ); } else { if (debug_level) fprintf( stderr, "%04x: *fd* %d <- %d\n", thread->id, data.fd, fd ); thread_add_inflight_fd( thread, data.fd, fd ); } if (thread) release_object( thread ); return 0; } if (!ret) { kill_process( process, 0 ); } else if (ret > 0) { fprintf( stderr, "Protocol error: process %04x: partial recvmsg %d for fd\n", process->id, ret ); if (fd != -1) close( fd ); kill_process( process, 1 ); } else { if (errno != EWOULDBLOCK && errno != EAGAIN) { fprintf( stderr, "Protocol error: process %04x: ", process->id ); perror( "recvmsg" ); kill_process( process, 1 ); } } return -1; }
static struct object *create_mapping( struct object *root, const struct unicode_str *name, unsigned int attr, mem_size_t size, unsigned int flags, obj_handle_t handle, unsigned int file_access, const struct security_descriptor *sd ) { struct mapping *mapping; struct file *file; struct fd *fd; int unix_fd; struct stat st; if (!page_mask) page_mask = sysconf( _SC_PAGESIZE ) - 1; if (!(mapping = create_named_object( root, &mapping_ops, name, attr, sd ))) return NULL; if (get_error() == STATUS_OBJECT_NAME_EXISTS) return &mapping->obj; /* Nothing else to do */ mapping->size = size; mapping->fd = NULL; mapping->shared = NULL; mapping->committed = NULL; if (!(mapping->flags = get_mapping_flags( handle, flags ))) goto error; if (handle) { const unsigned int sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; unsigned int mapping_access = FILE_MAPPING_ACCESS; if (!(file = get_file_obj( current->process, handle, file_access ))) goto error; fd = get_obj_fd( (struct object *)file ); /* file sharing rules for mappings are different so we use magic the access rights */ if (flags & SEC_IMAGE) mapping_access |= FILE_MAPPING_IMAGE; else if (file_access & FILE_WRITE_DATA) mapping_access |= FILE_MAPPING_WRITE; if (!(mapping->fd = get_fd_object_for_mapping( fd, mapping_access, sharing ))) { mapping->fd = dup_fd_object( fd, mapping_access, sharing, FILE_SYNCHRONOUS_IO_NONALERT ); if (mapping->fd) set_fd_user( mapping->fd, &mapping_fd_ops, NULL ); } release_object( file ); release_object( fd ); if (!mapping->fd) goto error; if ((unix_fd = get_unix_fd( mapping->fd )) == -1) goto error; if (fstat( unix_fd, &st ) == -1) { file_set_error(); goto error; } if (flags & SEC_IMAGE) { unsigned int err = get_image_params( mapping, st.st_size, unix_fd ); if (!err) return &mapping->obj; set_error( err ); goto error; } if (!mapping->size) { if (!(mapping->size = st.st_size)) { set_error( STATUS_MAPPED_FILE_SIZE_ZERO ); goto error; } } else if (st.st_size < mapping->size) { if (!(file_access & FILE_WRITE_DATA)) { set_error( STATUS_SECTION_TOO_BIG ); goto error; } if (!grow_file( unix_fd, mapping->size )) goto error; } } else /* Anonymous mapping (no associated file) */ { if (!mapping->size) { set_error( STATUS_INVALID_PARAMETER ); goto error; } if ((flags & SEC_RESERVE) && !(mapping->committed = create_ranges())) goto error; mapping->size = (mapping->size + page_mask) & ~((mem_size_t)page_mask); if ((unix_fd = create_temp_file( mapping->size )) == -1) goto error; if (!(mapping->fd = create_anonymous_fd( &mapping_fd_ops, unix_fd, &mapping->obj, FILE_SYNCHRONOUS_IO_NONALERT ))) goto error; allow_fd_caching( mapping->fd ); } return &mapping->obj; error: release_object( mapping ); return NULL; }
int get_file_unix_fd( struct file *file ) { return get_unix_fd( file->fd ); }
/* accept a socket (creates a new fd) */ static struct sock *accept_socket( obj_handle_t handle ) { struct sock *acceptsock; struct sock *sock; int acceptfd; struct sockaddr saddr; sock = (struct sock *)get_handle_obj( current->process, handle, FILE_READ_DATA, &sock_ops ); if (!sock) return NULL; if ( sock->deferred ) { acceptsock = sock->deferred; sock->deferred = NULL; } else { /* Try to accept(2). We can't be safe that this an already connected socket * or that accept() is allowed on it. In those cases we will get -1/errno * return. */ unsigned int slen = sizeof(saddr); acceptfd = accept( get_unix_fd(sock->fd), &saddr, &slen); if (acceptfd==-1) { sock_set_error(); release_object( sock ); return NULL; } if (!(acceptsock = alloc_object( &sock_ops ))) { close( acceptfd ); release_object( sock ); return NULL; } /* newly created socket gets the same properties of the listening socket */ fcntl(acceptfd, F_SETFL, O_NONBLOCK); /* make socket nonblocking */ acceptsock->state = FD_WINE_CONNECTED|FD_READ|FD_WRITE; if (sock->state & FD_WINE_NONBLOCKING) acceptsock->state |= FD_WINE_NONBLOCKING; acceptsock->mask = sock->mask; acceptsock->hmask = 0; acceptsock->pmask = 0; acceptsock->polling = 0; acceptsock->type = sock->type; acceptsock->family = sock->family; acceptsock->event = NULL; acceptsock->window = sock->window; acceptsock->message = sock->message; acceptsock->wparam = 0; if (sock->event) acceptsock->event = (struct event *)grab_object( sock->event ); acceptsock->flags = sock->flags; acceptsock->deferred = NULL; acceptsock->read_q = NULL; acceptsock->write_q = NULL; memset( acceptsock->errors, 0, sizeof(acceptsock->errors) ); if (!(acceptsock->fd = create_anonymous_fd( &sock_fd_ops, acceptfd, &acceptsock->obj, get_fd_options( sock->fd ) ))) { release_object( acceptsock ); release_object( sock ); return NULL; } } clear_error(); sock->pmask &= ~FD_ACCEPT; sock->hmask &= ~FD_ACCEPT; sock_reselect( sock ); release_object( sock ); return acceptsock; }