/* Implement io_reathenticate as described in <hurd/io.defs>. */ kern_return_t diskfs_S_io_reauthenticate (struct protid *cred, mach_port_t rend_port) { struct protid *newcred; error_t err; mach_port_t newright; struct iouser *user; if (cred == 0) return EOPNOTSUPP; /* This routine must carefully ignore EINTR because we are a simpleroutine, so callers won't know to restart. */ pthread_mutex_lock (&cred->po->np->lock); refcount_ref (&cred->po->refcnt); do err = diskfs_start_protid (cred->po, &newcred); while (err == EINTR); if (err) { refcount_deref (&cred->po->refcnt); pthread_mutex_unlock (&cred->po->np->lock); return err; } newright = ports_get_send_right (newcred); assert (newright != MACH_PORT_NULL); err = iohelp_reauth (&user, diskfs_auth_server_port, rend_port, newright, 1); if (! err) { diskfs_finish_protid (newcred, user); iohelp_free_iouser (user); mach_port_deallocate (mach_task_self (), rend_port); } mach_port_deallocate (mach_task_self (), newright); pthread_mutex_unlock (&cred->po->np->lock); ports_port_deref (newcred); return err; }
error_t fshelp_get_identity (struct port_bucket *bucket, ino_t fileno, mach_port_t *pt) { struct idspec *i; error_t err = 0; error_t check_port (void *arg) { struct idspec *i = arg; if (i->fileno == fileno) { *pt = ports_get_right (i); return 1; } else return 0; } mutex_lock (&idlock); if (!idclass) id_initialize (); *pt = MACH_PORT_NULL; ports_class_iterate (idclass, check_port); if (*pt != MACH_PORT_NULL) { mutex_unlock (&idlock); return 0; } err = ports_create_port (idclass, bucket, sizeof (struct idspec), &i); if (err) { mutex_unlock (&idlock); return err; } i->fileno = fileno; *pt = ports_get_right (i); ports_port_deref (i); mutex_unlock (&idlock); return 0; }
/* Return a new connection from a socket previously listened. */ error_t S_socket_accept (struct sock_user *user, mach_port_t *port, mach_msg_type_name_t *port_type, mach_port_t *peer_addr_port, mach_msg_type_name_t *peer_addr_port_type) { error_t err; struct sock *sock; if (!user) return EOPNOTSUPP; sock = user->sock; err = ensure_connq (sock); if (!err) { struct timespec noblock = {0, 0}; struct sock *peer_sock; err = connq_listen (sock->listen_queue, (sock->flags & PFLOCAL_SOCK_NONBLOCK) ? &noblock : NULL, &peer_sock); if (!err) { struct addr *peer_addr; *port_type = MACH_MSG_TYPE_MAKE_SEND; err = sock_create_port (peer_sock, port); if (!err) err = sock_get_addr (peer_sock, &peer_addr); if (!err) { *peer_addr_port = ports_get_right (peer_addr); *peer_addr_port_type = MACH_MSG_TYPE_MAKE_SEND; ports_port_deref (peer_addr); } else { /* TEAR DOWN THE CONNECTION XXX */ } } } return err; }
void ports_no_senders (void *portstruct, mach_port_mscount_t mscount) { struct port_info *pi = portstruct; int dealloc; mach_port_t old; pthread_mutex_lock (&_ports_lock); if ((pi->flags & PORT_HAS_SENDRIGHTS) == 0) { pthread_mutex_unlock (&_ports_lock); return; } if (mscount >= pi->mscount) { dealloc = 1; pi->flags &= ~PORT_HAS_SENDRIGHTS; } else { /* Request a new notification. The sync value is because we might have accounted for a new sender but not actually made the send right yet. */ mach_port_request_notification (mach_task_self (), pi->port_right, MACH_NOTIFY_NO_SENDERS, pi->mscount, pi->port_right, MACH_MSG_TYPE_MAKE_SEND_ONCE, &old); if (old) mach_port_deallocate (mach_task_self (), old); dealloc = 0; } pthread_mutex_unlock (&_ports_lock); if (dealloc) { ports_interrupt_notified_rpcs (portstruct, pi->port_right, MACH_NOTIFY_NO_SENDERS); ports_interrupt_rpcs (pi); ports_port_deref (pi); } }
/* Find out the name of a socket. */ error_t S_socket_name (struct sock_user *user, mach_port_t *addr_port, mach_msg_type_name_t *addr_port_type) { error_t err; struct addr *addr; if (!user) return EOPNOTSUPP; err = sock_get_addr (user->sock, &addr); if (err) return err; *addr_port = ports_get_right (addr); *addr_port_type = MACH_MSG_TYPE_MAKE_SEND; ports_port_deref (addr); return 0; }
error_t S_ifsock_getsockaddr (file_t sockfile, mach_port_t *address) { struct trivfs_protid *cred = ports_lookup_port (port_bucket, sockfile, node_class); int perms; error_t err; if (!cred) return EOPNOTSUPP; err = file_check_access (cred->realnode, &perms); if (!err && !(perms & O_READ)) err = EACCES; if (!err) *address = address_port; ports_port_deref (cred); return err; }
error_t netfs_S_dir_mkfile (struct protid *diruser, int flags, mode_t mode, mach_port_t *newfile, mach_msg_type_name_t *newfiletype) { error_t err; struct node *np; struct iouser *user; struct protid *newpi; pthread_mutex_lock (&diruser->po->np->lock); err = netfs_attempt_mkfile (diruser->user, diruser->po->np, mode, &np); if (!err) { /* the dir is now unlocked and NP is locked */ flags &= OPENONLY_STATE_MODES; err = iohelp_dup_iouser (&user, diruser->user); if (! err) { newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po), user); if (newpi) { *newfile = ports_get_right (newpi); *newfiletype = MACH_MSG_TYPE_MAKE_SEND; ports_port_deref (newpi); } else { err = errno; iohelp_free_iouser (user); } } netfs_nput (np); } return err; }
error_t netfs_S_io_restrict_auth (struct protid *user, mach_port_t *newport, mach_msg_type_name_t *newporttype, uid_t *uids, mach_msg_type_number_t nuids, gid_t *gids, mach_msg_type_number_t ngids) { error_t err; struct protid *newpi; struct iouser *new_user; if (!user) return EOPNOTSUPP; err = iohelp_restrict_iouser (&new_user, user->user, uids, nuids, gids, ngids); if (err) return err; refcount_ref (&user->po->refcnt); newpi = netfs_make_protid (user->po, new_user); if (newpi) { *newport = ports_get_right (newpi); *newporttype = MACH_MSG_TYPE_MAKE_SEND; } else { refcount_deref (&user->po->refcnt); iohelp_free_iouser (new_user); err = ENOMEM; } ports_port_deref (newpi); return err; }
error_t ports_transfer_right (void *tostruct, void *fromstruct) { struct port_info *topi = tostruct; struct port_info *frompi = fromstruct; mach_port_t port; int dereffrompi = 0; int dereftopi = 0; int hassendrights = 0; error_t err; mutex_lock (&_ports_lock); /* Fetch the port in FROMPI and clear its use */ port = frompi->port_right; if (port != MACH_PORT_NULL) { hurd_ihash_locp_remove (&frompi->bucket->htable, frompi->hentry); frompi->port_right = MACH_PORT_NULL; if (frompi->flags & PORT_HAS_SENDRIGHTS) { frompi->flags &= ~PORT_HAS_SENDRIGHTS; hassendrights = 1; dereffrompi = 1; } } /* Destroy the existing right in TOPI. */ if (topi->port_right != MACH_PORT_NULL) { hurd_ihash_locp_remove (&topi->bucket->htable, topi->hentry); err = mach_port_mod_refs (mach_task_self (), topi->port_right, MACH_PORT_RIGHT_RECEIVE, -1); assert_perror (err); if ((topi->flags & PORT_HAS_SENDRIGHTS) && !hassendrights) { dereftopi = 1; topi->flags &= ~PORT_HAS_SENDRIGHTS; } else if (((topi->flags & PORT_HAS_SENDRIGHTS) == 0) && hassendrights) { topi->flags |= PORT_HAS_SENDRIGHTS; topi->refcnt++; } } /* Install the new right in TOPI. */ topi->port_right = port; topi->cancel_threshold = frompi->cancel_threshold; topi->mscount = frompi->mscount; if (port) { hurd_ihash_add (&topi->bucket->htable, port, topi); if (topi->bucket != frompi->bucket) { err = mach_port_move_member (mach_task_self (), port, topi->bucket->portset); assert_perror (err); } } mutex_unlock (&_ports_lock); /* Take care of any lowered reference counts. */ if (dereffrompi) ports_port_deref (frompi); if (dereftopi) ports_port_deref (topi); return 0; }
kern_return_t trivfs_S_dir_lookup (struct trivfs_protid *cred, mach_port_t reply, mach_msg_type_name_t reply_type, char *filename, int flags, mode_t mode, retry_type *retry_type, char *retry_name, mach_port_t *retrypt, mach_msg_type_name_t *retrypt_type) { int perms; error_t err; struct trivfs_protid *newcred; if (!cred) return EOPNOTSUPP; if (filename[0]) return ENOTDIR; /* This is a null-pathname "reopen" call; do the right thing. */ /* Burn off flags we don't actually implement */ flags &= O_HURD; flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS); /* Validate permissions */ if (! trivfs_check_access_hook) file_check_access (cred->realnode, &perms); else (*trivfs_check_access_hook) (cred->po->cntl, cred->user, cred->realnode, &perms); if ((flags & (O_READ|O_WRITE|O_EXEC) & perms) != (flags & (O_READ|O_WRITE|O_EXEC))) return EACCES; /* Execute the open */ err = 0; if (trivfs_check_open_hook) err = (*trivfs_check_open_hook) (cred->po->cntl, cred->user, flags); if (!err) { struct iouser *user; err = iohelp_dup_iouser (&user, cred->user); if (err) return err; err = trivfs_open (cred->po->cntl, user, flags, cred->realnode, &newcred); if (err) iohelp_free_iouser (user); else mach_port_mod_refs (mach_task_self (), cred->realnode, MACH_PORT_RIGHT_SEND, +1); } if (err) return err; *retry_type = FS_RETRY_NORMAL; *retry_name = '\0'; *retrypt = ports_get_right (newcred); *retrypt_type = MACH_MSG_TYPE_MAKE_SEND; ports_port_deref (newcred); return 0; }
/* Return in FILE & FILE_TYPE the file in FSYS corresponding to the NFS file handle HANDLE & HANDLE_LEN. */ error_t diskfs_S_fsys_getfile (mach_port_t fsys, mach_port_t reply, mach_msg_type_name_t reply_type, uid_t *uids, mach_msg_type_number_t nuids, gid_t *gids, mach_msg_type_number_t ngids, char *handle, mach_msg_type_number_t handle_len, mach_port_t *file, mach_msg_type_name_t *file_type) { int flags; error_t err; struct node *node; const union diskfs_fhandle *f; struct protid *new_cred; struct peropen *new_po; struct iouser *user; struct port_info *pt = ports_lookup_port (diskfs_port_bucket, fsys, diskfs_control_class); if (!pt) return EOPNOTSUPP; if (handle_len != sizeof *f) { ports_port_deref (pt); return EINVAL; } f = (const union diskfs_fhandle *) handle; err = diskfs_cached_lookup (f->data.cache_id, &node); if (err) { ports_port_deref (pt); return err; } if (node->dn_stat.st_gen != f->data.gen) { diskfs_nput (node); ports_port_deref (pt); return ESTALE; } err = iohelp_create_complex_iouser (&user, uids, nuids, gids, ngids); if (err) { diskfs_nput (node); ports_port_deref (pt); return err; } flags = 0; if (! fshelp_access (&node->dn_stat, S_IREAD, user)) flags |= O_READ; if (! fshelp_access (&node->dn_stat, S_IEXEC, user)) flags |= O_EXEC; if (! fshelp_access (&node->dn_stat, S_IWRITE, user) && ! S_ISDIR (node->dn_stat.st_mode) && ! diskfs_check_readonly ()) flags |= O_WRITE; err = diskfs_make_peropen (node, flags, 0, &new_po); if (! err) { err = diskfs_create_protid (new_po, user, &new_cred); if (err) diskfs_release_peropen (new_po); } iohelp_free_iouser (user); diskfs_nput (node); ports_port_deref (pt); if (! err) { *file = ports_get_right (new_cred); *file_type = MACH_MSG_TYPE_MAKE_SEND; } return err; }
/* Send data over a socket, possibly including Mach ports. */ error_t S_socket_send (struct sock_user *user, struct addr *dest_addr, int flags, char *data, size_t data_len, mach_port_t *ports, size_t num_ports, char *control, size_t control_len, size_t *amount) { error_t err = 0; struct pipe *pipe; struct sock *sock, *dest_sock; struct addr *source_addr; if (!user) return EOPNOTSUPP; sock = user->sock; if (flags & MSG_OOB) /* BSD local sockets don't support OOB data. */ return EOPNOTSUPP; if (dest_addr) { err = addr_get_sock (dest_addr, &dest_sock); if (err == EADDRNOTAVAIL) /* The server went away. */ err = ECONNREFUSED; if (err) return err; if (sock->pipe_class != dest_sock->pipe_class) /* Sending to a different type of socket! */ err = EINVAL; /* ? XXX */ } else dest_sock = 0; /* We could provide a source address for all writes, but we only do so for connectionless sockets because that's the only place it's required, and it's more efficient not to. */ if (!err && sock->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS) err = sock_get_addr (sock, &source_addr); else source_addr = NULL; if (!err) { if (dest_sock) /* Grab the destination socket's read pipe directly, and stuff data into it. This is not quite the usage sock_acquire_read_pipe was intended for, but it will work, as the only inappropriate errors occur on a broken pipe, which shouldn't be possible with the sort of sockets with which we can use socket_send... XXXX */ err = sock_acquire_read_pipe (dest_sock, &pipe); else /* No address, must be a connected socket... */ err = sock_acquire_write_pipe (sock, &pipe); if (!err) { err = pipe_send (pipe, sock->flags & PFLOCAL_SOCK_NONBLOCK, source_addr, data, data_len, control, control_len, ports, num_ports, amount); if (dest_sock) pipe_release_reader (pipe); else pipe_release_writer (pipe); } if (err) /* The send failed, so free any resources it would have consumed (mig gets rid of memory, but we have to do everything else). */ { if (source_addr) ports_port_deref (source_addr); while (num_ports-- > 0) mach_port_deallocate (mach_task_self (), *ports++); } } if (dest_sock) sock_deref (dest_sock); return err; }
void ports_manage_port_operations_one_thread (struct port_bucket *bucket, ports_demuxer_type demuxer, int timeout) { error_t err; int internal_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outheadp) { struct port_info *pi; struct rpc_info link; int status; error_t err; register mig_reply_header_t *outp = (mig_reply_header_t *) outheadp; static const mach_msg_type_t RetCodeType = { /* msgt_name = */ MACH_MSG_TYPE_INTEGER_32, /* msgt_size = */ 32, /* msgt_number = */ 1, /* msgt_inline = */ TRUE, /* msgt_longform = */ FALSE, /* msgt_deallocate = */ FALSE, /* msgt_unused = */ 0 }; /* Fill in default response. */ outp->Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(inp->msgh_bits), 0); outp->Head.msgh_size = sizeof *outp; outp->Head.msgh_remote_port = inp->msgh_remote_port; outp->Head.msgh_local_port = MACH_PORT_NULL; outp->Head.msgh_seqno = 0; outp->Head.msgh_id = inp->msgh_id + 100; outp->RetCodeType = RetCodeType; outp->RetCode = MIG_BAD_ID; pi = ports_lookup_port (bucket, inp->msgh_local_port, 0); if (pi) { err = ports_begin_rpc (pi, inp->msgh_id, &link); if (err) { mach_port_deallocate (mach_task_self (), inp->msgh_remote_port); outp->RetCode = err; status = 1; } else { /* No need to check cancel threshhold here, because in a single threaded server the cancel is always handled in order. */ status = demuxer (inp, outheadp); ports_end_rpc (pi, &link); } ports_port_deref (pi); } else { outp->RetCode = EOPNOTSUPP; status = 1; } return status; } do err = mach_msg_server_timeout (internal_demuxer, 0, bucket->portset, timeout ? MACH_RCV_TIMEOUT : 0, timeout); while (err != MACH_RCV_TIMED_OUT); }
void ports_manage_port_operations_one_thread (struct port_bucket *bucket, ports_demuxer_type demuxer, int timeout) { struct ports_thread thread; error_t err; int internal_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outheadp) { struct port_info *pi; struct rpc_info link; int status; error_t err; register mig_reply_header_t *outp = (mig_reply_header_t *) outheadp; static const mach_msg_type_t RetCodeType = { /* msgt_name = */ MACH_MSG_TYPE_INTEGER_32, /* msgt_size = */ 32, /* msgt_number = */ 1, /* msgt_inline = */ TRUE, /* msgt_longform = */ FALSE, /* msgt_deallocate = */ FALSE, /* msgt_unused = */ 0 }; /* Fill in default response. */ outp->Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(inp->msgh_bits), 0); outp->Head.msgh_size = sizeof *outp; outp->Head.msgh_remote_port = inp->msgh_remote_port; outp->Head.msgh_local_port = MACH_PORT_NULL; outp->Head.msgh_seqno = 0; outp->Head.msgh_id = inp->msgh_id + 100; outp->RetCodeType = RetCodeType; outp->RetCode = MIG_BAD_ID; if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) == MACH_MSG_TYPE_PROTECTED_PAYLOAD) pi = ports_lookup_payload (bucket, inp->msgh_protected_payload, NULL); else { pi = ports_lookup_port (bucket, inp->msgh_local_port, 0); if (pi) { /* Store the objects address as the payload and set the message type accordingly. This prevents us from having to do another hash table lookup in the intran functions if protected payloads are not supported by the kernel. */ inp->msgh_bits = MACH_MSGH_BITS_OTHER (inp->msgh_bits) | MACH_MSGH_BITS (MACH_MSGH_BITS_REMOTE (inp->msgh_bits), MACH_MSG_TYPE_PROTECTED_PAYLOAD); inp->msgh_protected_payload = (unsigned long) pi; } } if (pi) { err = ports_begin_rpc (pi, inp->msgh_id, &link); if (err) { mach_port_deallocate (mach_task_self (), inp->msgh_remote_port); outp->RetCode = err; status = 1; } else { /* No need to check cancel threshold here, because in a single threaded server the cancel is always handled in order. */ status = demuxer (inp, outheadp); ports_end_rpc (pi, &link); } ports_port_deref (pi); } else { outp->RetCode = EOPNOTSUPP; status = 1; } _ports_thread_quiescent (&bucket->threadpool, &thread); return status; } /* XXX It is currently unsafe for most servers to terminate based on inactivity because a request may arrive after a server has started shutting down, causing the client to receive an error. Prevent the service loop from terminating by setting TIMEOUT to zero. */ timeout = 0; _ports_thread_online (&bucket->threadpool, &thread); do err = mach_msg_server_timeout (internal_demuxer, 0, bucket->portset, timeout ? MACH_RCV_TIMEOUT : 0, timeout); while (err != MACH_RCV_TIMED_OUT); _ports_thread_offline (&bucket->threadpool, &thread); }
/* Receive data from a socket, possibly including Mach ports. */ error_t S_socket_recv (struct sock_user *user, mach_port_t *addr, mach_msg_type_name_t *addr_type, int in_flags, char **data, size_t *data_len, mach_port_t **ports, mach_msg_type_name_t *ports_type, size_t *num_ports, char **control, size_t *control_len, int *out_flags, size_t amount) { error_t err; unsigned flags; struct pipe *pipe; void *source_addr = NULL; if (!user) return EOPNOTSUPP; if (in_flags & MSG_OOB) /* BSD local sockets don't support OOB data. */ return EINVAL; /* XXX */ /* Fill in the pipe FLAGS from any corresponding ones in IN_FLAGS. */ flags = in_flags & MSG_PEEK; err = sock_acquire_read_pipe (user->sock, &pipe); if (err == EPIPE) /* EOF */ { *data_len = 0; if (num_ports) *num_ports = 0; if (control_len) *control_len = 0; } else if (!err) { err = pipe_recv (pipe, user->sock->flags & PFLOCAL_SOCK_NONBLOCK, &flags, &source_addr, data, data_len, amount, control, control_len, ports, num_ports); pipe_release_reader (pipe); } if (!err) /* Setup mach ports for return. */ { *addr_type = MACH_MSG_TYPE_MAKE_SEND; *ports_type = MACH_MSG_TYPE_MOVE_SEND; if (source_addr) { *addr = ports_get_right (source_addr); ports_port_deref (source_addr); /* since get_right has one too. */ } else *addr = MACH_PORT_NULL; } *out_flags = 0; return err; }
kern_return_t trivfs_S_fsys_getroot (struct trivfs_control *cntl, mach_port_t reply_port, mach_msg_type_name_t reply_port_type, mach_port_t dotdot, uid_t *uids, size_t nuids, uid_t *gids, size_t ngids, int flags, retry_type *do_retry, char *retry_name, mach_port_t *newpt, mach_msg_type_name_t *newpttype) { int perms; error_t err = 0; mach_port_t new_realnode; struct trivfs_protid *cred; struct iouser *user; if (!cntl) return EOPNOTSUPP; if (trivfs_getroot_hook) { err = (*trivfs_getroot_hook) (cntl, reply_port, reply_port_type, dotdot, uids, nuids, gids, ngids, flags, do_retry, retry_name, newpt, newpttype); if (err != EAGAIN) return err; } if ((flags & O_WRITE & trivfs_allow_open) != (flags & O_WRITE)) return EROFS; if ((flags & (O_READ|O_WRITE|O_EXEC) & trivfs_allow_open) != (flags & (O_READ|O_WRITE|O_EXEC))) return EACCES; /* O_CREAT and O_EXCL are not meaningful here; O_NOLINK and O_NOTRANS will only be useful when trivfs supports translators (which it doesn't now). */ flags &= O_HURD; flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS); struct idvec idvec = { .ids = uids, .num = nuids, .alloced = nuids, }; if (_is_privileged (&idvec)) /* Privileged users should be given all our rights. */ err = io_duplicate (cntl->underlying, &new_realnode); else /* Non-privileged, restrict rights. */ err = io_restrict_auth (cntl->underlying, &new_realnode, uids, nuids, gids, ngids); if (err) return err; err = iohelp_create_complex_iouser (&user, uids, nuids, gids, ngids); if (err) return err; /* Validate permissions. */ if (! trivfs_check_access_hook) file_check_access (new_realnode, &perms); else (*trivfs_check_access_hook) (cntl, user, new_realnode, &perms); if ((flags & (O_READ|O_WRITE|O_EXEC) & perms) != (flags & (O_READ|O_WRITE|O_EXEC))) err = EACCES; if (!err && trivfs_check_open_hook) err = (*trivfs_check_open_hook) (cntl, user, flags); if (!err) { if (! trivfs_open_hook) { err = trivfs_open (cntl, user, flags, new_realnode, &cred); if (!err) mach_port_deallocate (mach_task_self (), dotdot); } else err = (*trivfs_open_hook) (cntl, user, dotdot, flags, new_realnode, &cred); } if (err) { mach_port_deallocate (mach_task_self (), new_realnode); iohelp_free_iouser (user); } else { *do_retry = FS_RETRY_NORMAL; *retry_name = '\0'; *newpt = ports_get_right (cred); *newpttype = MACH_MSG_TYPE_MAKE_SEND; ports_port_deref (cred); } return err; }
/* Implement pagein callback as described in <mach/memory_object.defs>. */ kern_return_t _pager_seqnos_memory_object_data_request (mach_port_t object, mach_port_seqno_t seqno, mach_port_t control, vm_offset_t offset, vm_size_t length, vm_prot_t access) { struct pager *p; short *pm_entry; int doread, doerror; error_t err; vm_address_t page; int write_lock; p = ports_lookup_port (0, object, _pager_class); if (!p) return EOPNOTSUPP; /* Acquire the right to meddle with the pagemap */ mutex_lock (&p->interlock); _pager_wait_for_seqno (p, seqno); /* sanity checks -- we don't do multi-page requests yet. */ if (control != p->memobjcntl) { printf ("incg data request: wrong control port\n"); goto release_out; } if (length != __vm_page_size) { printf ("incg data request: bad length size %zd\n", length); goto release_out; } if (offset % __vm_page_size) { printf ("incg data request: misaligned request\n"); goto release_out; } _pager_block_termination (p); /* prevent termination until mark_object_error is done */ if (p->pager_state != NORMAL) { printf ("pager in wrong state for read\n"); goto allow_release_out; } err = _pager_pagemap_resize (p, offset + length); if (err) goto allow_release_out; /* Can't do much about the actual error. */ /* If someone is paging this out right now, the disk contents are unreliable, so we have to wait. It is too expensive (right now) to find the data and return it, and then interrupt the write, so we just mark the page and have the writing thread do m_o_data_supply when it gets around to it. */ pm_entry = &p->pagemap[offset / __vm_page_size]; if (*pm_entry & PM_PAGINGOUT) { doread = 0; *pm_entry |= PM_PAGEINWAIT; } else doread = 1; if (*pm_entry & PM_INVALID) doerror = 1; else doerror = 0; *pm_entry |= PM_INCORE; if (PM_NEXTERROR (*pm_entry) != PAGE_NOERR && (access & VM_PROT_WRITE)) { memory_object_data_error (control, offset, length, _pager_page_errors[PM_NEXTERROR (*pm_entry)]); _pager_mark_object_error (p, offset, length, _pager_page_errors[PM_NEXTERROR (*pm_entry)]); *pm_entry = SET_PM_NEXTERROR (*pm_entry, PAGE_NOERR); doread = 0; } /* Let someone else in. */ _pager_release_seqno (p, seqno); mutex_unlock (&p->interlock); if (!doread) goto allow_term_out; if (doerror) goto error_read; err = pager_read_page (p->upi, offset, &page, &write_lock); if (err) goto error_read; memory_object_data_supply (p->memobjcntl, offset, page, length, 1, write_lock ? VM_PROT_WRITE : VM_PROT_NONE, 0, MACH_PORT_NULL); mutex_lock (&p->interlock); _pager_mark_object_error (p, offset, length, 0); _pager_allow_termination (p); mutex_unlock (&p->interlock); ports_port_deref (p); return 0; error_read: memory_object_data_error (p->memobjcntl, offset, length, EIO); _pager_mark_object_error (p, offset, length, EIO); allow_term_out: mutex_lock (&p->interlock); _pager_allow_termination (p); mutex_unlock (&p->interlock); ports_port_deref (p); return 0; allow_release_out: _pager_allow_termination (p); release_out: _pager_release_seqno (p, seqno); mutex_unlock (&p->interlock); ports_port_deref (p); return 0; }
/* Create a new trivfs control port, with underlying node UNDERLYING, and return it in CONTROL. CONTROL_CLASS & CONTROL_BUCKET are passed to the ports library to create the control port, and PROTID_CLASS & PROTID_BUCKET are used when creating ports representing opens of this node. */ error_t trivfs_create_control (mach_port_t underlying, struct port_class *control_class, struct port_bucket *control_bucket, struct port_class *protid_class, struct port_bucket *protid_bucket, struct trivfs_control **control) { error_t err; /* Perhaps allocate, and perhaps add the specified port classes the ones recognized by trivfs. */ err = trivfs_add_control_port_class (&control_class); if (! err) err = trivfs_add_protid_port_class (&protid_class); else protid_class = 0; /* Perhaps allocate new port buckets. */ if (! err) err = trivfs_add_port_bucket (&control_bucket); else control_bucket = 0; if (! err) { if (! protid_bucket) /* By default, use the same port bucket for both. */ protid_bucket = control_bucket; err = trivfs_add_port_bucket (&protid_bucket); } else protid_bucket = 0; if (! err) err = ports_create_port (control_class, control_bucket, sizeof (struct trivfs_control), control); if (! err) { (*control)->underlying = underlying; (*control)->protid_class = protid_class; (*control)->protid_bucket = protid_bucket; err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &(*control)->filesys_id); if (err) { ports_port_deref (*control); goto out; } err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &(*control)->file_id); if (err) { mach_port_destroy (mach_task_self (), (*control)->filesys_id); ports_port_deref (*control); goto out; } (*control)->hook = 0; mutex_init (&(*control)->lock); } out: if (err) { trivfs_remove_control_port_class (control_class); trivfs_remove_protid_port_class (protid_class); trivfs_remove_port_bucket (control_bucket); trivfs_remove_port_bucket (protid_bucket); } return err; }
/* Implement the object termination call from the kernel as described in <mach/memory_object.defs>. */ kern_return_t _pager_seqnos_memory_object_terminate (mach_port_t object, mach_port_seqno_t seqno, mach_port_t control, mach_port_t name) { struct pager *p; p = ports_lookup_port (0, object, _pager_class); if (!p) return EOPNOTSUPP; mutex_lock (&p->interlock); _pager_wait_for_seqno (p, seqno); if (control != p->memobjcntl) { printf ("incg terminate: wrong control port"); goto out; } if (name != p->memobjname) { printf ("incg terminate: wrong name port"); goto out; } while (p->noterm) { p->termwaiting = 1; condition_wait (&p->wakeup, &p->interlock); } /* Destry the ports we received; mark that in P so that it doesn't bother doing it again. */ mach_port_destroy (mach_task_self (), control); mach_port_destroy (mach_task_self (), name); p->memobjcntl = p->memobjname = MACH_PORT_NULL; _pager_free_structure (p); #ifdef KERNEL_INIT_RACE if (p->init_head) { struct pending_init *i = p->init_head; p->init_head = i->next; if (!i->next) p->init_tail = 0; p->memobjcntl = i->control; p->memobjname = i->name; memory_object_ready (i->control, p->may_cache, p->copy_strategy); p->pager_state = NORMAL; free (i); } #endif out: _pager_release_seqno (p, seqno); mutex_unlock (&p->interlock); ports_port_deref (p); return 0; }
int ethernet_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp) { struct net_rcv_msg *msg = (struct net_rcv_msg *) inp; struct sk_buff *skb; int datalen; struct ether_device *edev; struct device *dev = 0; mach_port_t local_port; if (inp->msgh_id != NET_RCV_MSG_ID) return 0; if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) == MACH_MSG_TYPE_PROTECTED_PAYLOAD) { struct port_info *pi = ports_lookup_payload (NULL, inp->msgh_protected_payload, NULL); if (pi) { local_port = pi->port_right; ports_port_deref (pi); } else local_port = MACH_PORT_NULL; } else local_port = inp->msgh_local_port; for (edev = ether_dev; edev; edev = edev->next) if (local_port == edev->readptname) dev = &edev->dev; if (! dev) { if (inp->msgh_remote_port != MACH_PORT_NULL) mach_port_deallocate (mach_task_self (), inp->msgh_remote_port); return 1; } datalen = ETH_HLEN + msg->packet_type.msgt_number - sizeof (struct packet_header); pthread_mutex_lock (&net_bh_lock); skb = alloc_skb (datalen, GFP_ATOMIC); skb_put (skb, datalen); skb->dev = dev; /* Copy the two parts of the frame into the buffer. */ memcpy (skb->data, msg->header, ETH_HLEN); memcpy (skb->data + ETH_HLEN, msg->packet + sizeof (struct packet_header), datalen - ETH_HLEN); /* Drop it on the queue. */ skb->protocol = eth_type_trans (skb, dev); netif_rx (skb); pthread_mutex_unlock (&net_bh_lock); return 1; }
kern_return_t trivfs_S_io_restrict_auth (struct trivfs_protid *cred, mach_port_t reply, mach_msg_type_name_t replytype, mach_port_t *newport, mach_msg_type_name_t *newporttype, uid_t *uids, size_t nuids, uid_t *gids, size_t ngids) { int i; error_t err; struct trivfs_protid *newcred; struct idvec *uvec, *gvec; struct iouser *user; if (!cred) return EOPNOTSUPP; if (cred->isroot) /* CRED has root access, and so may use any ids. */ { err = iohelp_create_complex_iouser (&user, uids, nuids, gids, ngids); if (err) return err; } else { uvec = make_idvec (); if (! uvec) return ENOMEM; gvec = make_idvec (); if (! gvec) { idvec_free (uvec); return ENOMEM; } /* Otherwise, use any of the requested ids that CRED already has. */ for (i = 0; i < cred->user->uids->num; i++) if (listmember (uids, cred->user->uids->ids[i], nuids)) { err = idvec_add (uvec, cred->user->uids->ids[i]); if (err) goto out; } for (i = 0; i < cred->user->gids->num; i++) if (listmember (gids, cred->user->gids->ids[i], ngids)) { err = idvec_add (gvec, cred->user->gids->ids[i]); if (err) goto out; } err = iohelp_create_iouser (&user, uvec, gvec); if (err) { out: idvec_free (uvec); idvec_free (gvec); return err; } } err = ports_create_port (cred->po->cntl->protid_class, cred->po->cntl->protid_bucket, sizeof (struct trivfs_protid), &newcred); if (err) { iohelp_free_iouser (user); return err; } newcred->isroot = 0; mutex_lock (&cred->po->cntl->lock); newcred->po = cred->po; newcred->po->refcnt++; mutex_unlock (&cred->po->cntl->lock); if (cred->isroot && idvec_contains (user->uids, 0)) newcred->isroot = 1; newcred->user = user; newcred->hook = cred->hook; err = io_restrict_auth (cred->realnode, &newcred->realnode, user->uids->ids, user->uids->num, user->gids->ids, user->gids->num); if (!err && trivfs_protid_create_hook) { err = (*trivfs_protid_create_hook) (newcred); if (err) mach_port_deallocate (mach_task_self (), newcred->realnode); } if (err) /* Signal that the user destroy hook shouldn't be called on NEWCRED. */ newcred->realnode = MACH_PORT_NULL; else { *newport = ports_get_right (newcred); *newporttype = MACH_MSG_TYPE_MAKE_SEND; } /* This will destroy NEWCRED if we got an error and didn't do the ports_get_right above. */ ports_port_deref (newcred); return 0; }
/* Implement dir_lookup as described in <hurd/fs.defs>. */ kern_return_t diskfs_S_dir_lookup (struct protid *dircred, char *path, int flags, mode_t mode, enum retry_type *retry, char *retryname, file_t *returned_port, mach_msg_type_name_t *returned_port_poly) { struct node *dnp; struct node *np; int nsymlink = 0; char *nextname; char *relpath; int nextnamelen; error_t error = 0; char *pathbuf = 0; int pathbuflen = 0; int newnamelen; int create, excl; int lastcomp = 0; int newnode = 0; struct dirstat *ds = 0; int mustbedir = 0; size_t amt; int type; struct protid *newpi = 0; struct peropen *newpo = 0; if (!dircred) return EOPNOTSUPP; flags &= O_HURD; create = (flags & O_CREAT); excl = (flags & O_EXCL); /* Skip leading slashes */ while (path[0] == '/') path++; /* Preserve the path relative to diruser->po->path. */ relpath = strdup (path); if (! relpath) return ENOMEM; /* Keep a pointer to the start of the path for length calculations. */ char *path_start = path; *returned_port_poly = MACH_MSG_TYPE_MAKE_SEND; *retry = FS_RETRY_NORMAL; retryname[0] = '\0'; if (path[0] == '\0') { /* Set things up in the state expected by the code from gotit: on. */ dnp = 0; np = dircred->po->np; pthread_mutex_lock (&np->lock); diskfs_nref (np); goto gotit; } dnp = dircred->po->np; pthread_mutex_lock (&dnp->lock); np = 0; diskfs_nref (dnp); /* acquire a reference for later diskfs_nput */ do { assert (!lastcomp); /* Find the name of the next pathname component */ nextname = index (path, '/'); if (nextname) { *nextname++ = '\0'; while (*nextname == '/') nextname++; if (*nextname == '\0') { /* These are the rules for filenames ending in /. */ nextname = 0; lastcomp = 1; mustbedir = 1; create = 0; } else lastcomp = 0; } else lastcomp = 1; np = 0; /* diskfs_lookup the next pathname component */ if (lastcomp && create) { if (!ds) ds = alloca (diskfs_dirstat_size); error = diskfs_lookup (dnp, path, CREATE, &np, ds, dircred); } else error = diskfs_lookup (dnp, path, LOOKUP, &np, 0, dircred); if (lastcomp && create && excl && (!error || error == EAGAIN)) error = EEXIST; /* If we get an error we're done */ if (error == EAGAIN) { if (dnp == dircred->po->shadow_root) /* We're at the root of a shadow tree. */ { if (dircred->po->shadow_root_parent == MACH_PORT_NULL) { /* This is a shadow root with no parent, meaning we should treat it as a virtual root disconnected from its real .. directory. */ error = 0; np = dnp; diskfs_nref (np); } else { /* Punt the client up to the shadow root parent. */ *retry = FS_RETRY_REAUTH; *returned_port = dircred->po->shadow_root_parent; *returned_port_poly = MACH_MSG_TYPE_COPY_SEND; if (! lastcomp) strcpy (retryname, nextname); error = 0; goto out; } } else if (dircred->po->root_parent != MACH_PORT_NULL) /* We're at a real translator root; even if DIRCRED->po has a shadow root, we can get here if its in a directory that was renamed out from under it... */ { *retry = FS_RETRY_REAUTH; *returned_port = dircred->po->root_parent; *returned_port_poly = MACH_MSG_TYPE_COPY_SEND; if (!lastcomp) strcpy (retryname, nextname); error = 0; goto out; } else /* We're at a REAL root, as in there's no way up from here. */ { error = 0; np = dnp; diskfs_nref (np); } } /* Create the new node if necessary */ if (lastcomp && create) { if (error == ENOENT) { mode &= ~(S_IFMT | S_ISPARE | S_ISVTX | S_ITRANS); mode |= S_IFREG; error = diskfs_create_node (dnp, path, mode, &np, dircred, ds); if (diskfs_synchronous) { diskfs_file_update (dnp, 1); diskfs_file_update (np, 1); } newnode = 1; } else diskfs_drop_dirstat (dnp, ds); } if (error) goto out; /* If this is translated, start the translator (if necessary) and return. */ if ((((flags & O_NOTRANS) == 0) || !lastcomp) && ((np->dn_stat.st_mode & S_IPTRANS) || S_ISFIFO (np->dn_stat.st_mode) || S_ISCHR (np->dn_stat.st_mode) || S_ISBLK (np->dn_stat.st_mode) || fshelp_translated (&np->transbox))) { mach_port_t dirport; struct iouser *user; /* A callback function for short-circuited translators. Symlink & ifsock are handled elsewhere. */ error_t short_circuited_callback1 (void *cookie1, void *cookie2, uid_t *uid, gid_t *gid, char **argz, size_t *argz_len) { struct node *node = cookie1; switch (node->dn_stat.st_mode & S_IFMT) { case S_IFCHR: case S_IFBLK: asprintf (argz, "%s%c%d%c%d", (S_ISCHR (node->dn_stat.st_mode) ? _HURD_CHRDEV : _HURD_BLKDEV), 0, major (node->dn_stat.st_rdev), 0, minor (node->dn_stat.st_rdev)); *argz_len = strlen (*argz) + 1; *argz_len += strlen (*argz + *argz_len) + 1; *argz_len += strlen (*argz + *argz_len) + 1; break; case S_IFIFO: asprintf (argz, "%s", _HURD_FIFO); *argz_len = strlen (*argz) + 1; break; default: return ENOENT; } *uid = node->dn_stat.st_uid; *gid = node->dn_stat.st_gid; return 0; } /* Create an unauthenticated port for DNP, and then unlock it. */ error = iohelp_create_empty_iouser (&user); if (! error) { error = diskfs_make_peropen (dnp, 0, dircred->po, &newpo); if (! error) { error = diskfs_create_protid (newpo, user, &newpi); if (! error) newpo = 0; } iohelp_free_iouser (user); } if (error) goto out; dirport = ports_get_send_right (newpi); if (np != dnp) pthread_mutex_unlock (&dnp->lock); /* Check if an active translator is currently running. If not, fshelp_fetch_root will start one. In that case, we need to register it in the list of active translators. */ boolean_t register_translator = np->transbox.active == MACH_PORT_NULL; error = fshelp_fetch_root (&np->transbox, dircred->po, dirport, dircred->user, lastcomp ? flags : 0, ((np->dn_stat.st_mode & S_IPTRANS) ? _diskfs_translator_callback1 : short_circuited_callback1), _diskfs_translator_callback2, retry, retryname, returned_port); /* fetch_root copies DIRPORT for success, so we always should deallocate our send right. */ mach_port_deallocate (mach_task_self (), dirport); if (error != ENOENT) { *returned_port_poly = MACH_MSG_TYPE_MOVE_SEND; if (!lastcomp && !error) { char *end = strchr (retryname, '\0'); *end++ = '/'; strcpy (end, nextname); } if (register_translator) { char *translator_path = strdupa (relpath); char *complete_path; if (nextname != NULL) { /* This was not the last path component. NEXTNAME points to the next component, locate the end of the current component and use it to trim TRANSLATOR_PATH. */ char *end = nextname; while (*end != 0) end--; translator_path[end - path_start] = '\0'; } if (dircred->po->path == NULL || !strcmp (dircred->po->path,".")) /* dircred is the root directory. */ complete_path = translator_path; else asprintf (&complete_path, "%s/%s", dircred->po->path, translator_path); error = fshelp_set_active_translator (&newpi->pi, complete_path, np->transbox.active); if (complete_path != translator_path) free(complete_path); if (error) goto out; } goto out; } ports_port_deref (newpi); newpi = NULL; /* ENOENT means there was a hiccup, and the translator vanished while NP was unlocked inside fshelp_fetch_root. Reacquire the locks, and continue as normal. */ error = 0; if (np != dnp) { if (!strcmp (path, "..")) pthread_mutex_lock (&dnp->lock); else { if (pthread_mutex_trylock (&dnp->lock)) { pthread_mutex_unlock (&np->lock); pthread_mutex_lock (&dnp->lock); pthread_mutex_lock (&np->lock); } } } } if (S_ISLNK (np->dn_stat.st_mode) && (!lastcomp || mustbedir /* "foo/" must see that foo points to a dir */ || !(flags & (O_NOLINK|O_NOTRANS)))) { /* Handle symlink interpretation */ if (nsymlink++ > diskfs_maxsymlinks) { error = ELOOP; goto out; } nextnamelen = nextname ? strlen (nextname) + 1 : 0; newnamelen = nextnamelen + np->dn_stat.st_size + 1 + 1; if (pathbuflen < newnamelen) { pathbuf = alloca (newnamelen); pathbuflen = newnamelen; } if (diskfs_read_symlink_hook) error = (*diskfs_read_symlink_hook)(np, pathbuf); if (!diskfs_read_symlink_hook || error == EINVAL) { error = diskfs_node_rdwr (np, pathbuf, 0, np->dn_stat.st_size, 0, dircred, &amt); if (!error) assert (amt == np->dn_stat.st_size); } if (error) goto out; if (np->dn_stat.st_size == 0) /* symlink to "" */ path = nextname; else { if (nextname) { pathbuf[np->dn_stat.st_size] = '/'; memcpy (pathbuf + np->dn_stat.st_size + 1, nextname, nextnamelen - 1); } pathbuf[nextnamelen + np->dn_stat.st_size] = '\0'; if (pathbuf[0] == '/') { /* Punt to the caller. */ *retry = FS_RETRY_MAGICAL; *returned_port = MACH_PORT_NULL; memcpy (retryname, pathbuf, nextnamelen + np->dn_stat.st_size + 1); if (mustbedir) { retryname[nextnamelen + np->dn_stat.st_size] = '/'; retryname[nextnamelen + np->dn_stat.st_size + 1] = '\0'; } goto out; } path = pathbuf; } if (lastcomp) lastcomp = 0; diskfs_nput (np); np = 0; if (path == 0) /* symlink to "" was the last component */ { np = dnp; dnp = 0; break; } } else { /* Handle normal nodes */ path = nextname; if (np == dnp) diskfs_nrele (dnp); else diskfs_nput (dnp); if (!lastcomp) { dnp = np; np = 0; } else dnp = 0; } } while (path && *path);