/* * Copyright (c) 2008, 2010 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include extern void fileport_releasefg(struct fileglob *); /* * fileport_alloc * * Description: Obtain a send right for the given fileglob, which must be * referenced. * * Parameters: fg A fileglob. * * Returns: Port of type IKOT_FILEPORT with fileglob set as its kobject. * Port is returned with a send right. */ ipc_port_t fileport_alloc(struct fileglob *fg) { ipc_port_t fileport; ipc_port_t sendport; ipc_port_t notifyport; fileport = ipc_port_alloc_kernel(); if (fileport == IP_NULL) { goto out; } ipc_kobject_set(fileport, (ipc_kobject_t)fg, IKOT_FILEPORT); ip_lock(fileport); /* unlocked by ipc_port_nsrequest */ notifyport = ipc_port_make_sonce_locked(fileport); ipc_port_nsrequest(fileport, 1, notifyport, ¬ifyport); sendport = ipc_port_make_send(fileport); if (!IP_VALID(sendport)) { panic("Couldn't allocate send right for fileport!\n"); } out: return fileport; } /* * fileport_get_fileglob * * Description: Obtain the fileglob associated with a given port. * * Parameters: port A Mach port of type IKOT_FILEPORT. * * Returns: NULL The given Mach port did not reference a * fileglob. * !NULL The fileglob that is associated with the * Mach port. * * Notes: The caller must have a reference on the fileport. */ struct fileglob * fileport_port_to_fileglob(ipc_port_t port) { struct fileglob *fg = NULL; if (!IP_VALID(port)) return NULL; ip_lock(port); if (ip_active(port) && IKOT_FILEPORT == ip_kotype(port)) fg = (void *)port->ip_kobject; ip_unlock(port); return fg; } /* * fileport_notify * * Description: Handle a no-senders notification for a fileport. Unless * the message is spoofed, destroys the port and releases * its reference on the fileglob. * * Parameters: msg A Mach no-senders notification message. */ void fileport_notify(mach_msg_header_t *msg) { mach_no_senders_notification_t *notification = (void *)msg; ipc_port_t port = notification->not_header.msgh_remote_port; struct fileglob *fg = NULL; if (!IP_VALID(port)) panic("Invalid port passed to fileport_notify()\n"); ip_lock(port); fg = (struct fileglob *)port->ip_kobject; if (!ip_active(port)) panic("Inactive port passed to fileport_notify()\n"); if (ip_kotype(port) != IKOT_FILEPORT) panic("Port of type other than IKOT_FILEPORT passed to fileport_notify()\n"); if (fg == NULL) panic("fileport without an assocated fileglob\n"); if (port->ip_srights == 0) { ip_unlock(port); fileport_releasefg(fg); ipc_port_dealloc_kernel(port); } else { ip_unlock(port); } } /* * fileport_invoke * * Description: Invoke a function with the fileglob underlying the fileport. * Returns the error code related to the fileglob lookup. * * Parameters: task The target task * action The function to invoke with the fileglob * arg Anonymous pointer to caller state * rval The value returned from calling 'action' */ kern_return_t fileport_invoke(task_t task, mach_port_name_t name, int (*action)(mach_port_name_t, struct fileglob *, void *), void *arg, int *rval) { kern_return_t kr; ipc_port_t fileport; struct fileglob *fg; kr = ipc_object_copyin(task->itk_space, name, MACH_MSG_TYPE_COPY_SEND, (ipc_object_t *)&fileport); if (kr != KERN_SUCCESS) return (kr); if ((fg = fileport_port_to_fileglob(fileport)) != NULL) *rval = (*action)(name, fg, arg); else kr = KERN_FAILURE; ipc_port_release_send(fileport); return (kr); } /* * fileport_walk * * Description: Invoke the action function on every fileport in the task. * * This could be more efficient if we refactored mach_port_names() * so that (a) it didn't compute the type information unless asked * and (b) it could be asked to -not- unwire/copyout the memory * and (c) if we could ask for port names by kobject type. Not * clear that it's worth all that complexity, though. * * Parameters: task The target task * action The function to invoke on each fileport * arg Anonymous pointer to caller state. */ kern_return_t fileport_walk(task_t task, int (*action)(mach_port_name_t, struct fileglob *, void *arg), void *arg) { mach_port_name_t *names; mach_msg_type_number_t ncnt, tcnt; vm_map_copy_t map_copy_names, map_copy_types; vm_map_address_t map_names; kern_return_t kr; uint_t i; int rval; /* * mach_port_names returns the 'name' and 'types' in copied-in * form. Discard 'types' immediately, then copyout 'names' * back into the kernel before walking the array. */ kr = mach_port_names(task->itk_space, (mach_port_name_t **)&map_copy_names, &ncnt, (mach_port_type_t **)&map_copy_types, &tcnt); if (kr != KERN_SUCCESS) return (kr); vm_map_copy_discard(map_copy_types); kr = vm_map_copyout(ipc_kernel_map, &map_names, map_copy_names); if (kr != KERN_SUCCESS) { vm_map_copy_discard(map_copy_names); return (kr); } names = (mach_port_name_t *)(uintptr_t)map_names; for (rval = 0, i = 0; i < ncnt; i++) if (fileport_invoke(task, names[i], action, arg, &rval) == KERN_SUCCESS && -1 == rval) break; /* early termination clause */ vm_deallocate(ipc_kernel_map, (vm_address_t)names, ncnt * sizeof (*names)); return (KERN_SUCCESS); }