+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef kIOMediaPreferredBlockSizeKey
+#define kIOMediaPreferredBlockSizeKey "Preferred Block Size"
+#endif
+
+enum { kDefaultIOSize = 128*1024 };
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+class IOPolledFilePollers : public OSObject
+{
+ OSDeclareDefaultStructors(IOPolledFilePollers)
+
+public:
+ IOService * media;
+ OSArray * pollers;
+ IOBufferMemoryDescriptor * ioBuffer;
+ bool abortable;
+ bool io;
+ IOReturn ioStatus;
+ uint32_t openCount;
+ uint32_t openState;
+
+ static IOPolledFilePollers * copyPollers(IOService * media);
+};
+
+OSDefineMetaClassAndStructors(IOPolledFilePollers, OSObject)
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+IOPolledFilePollers *
+IOPolledFilePollers::copyPollers(IOService * media)
+{
+ IOPolledFilePollers * vars;
+ IOReturn err;
+ IOService * service;
+ OSObject * obj;
+ IORegistryEntry * next;
+ IORegistryEntry * child;
+
+ if ((obj = media->copyProperty(kIOPolledInterfaceStackKey)))
+ {
+ return (OSDynamicCast(IOPolledFilePollers, obj));
+ }
+
+ do
+ {
+ vars = OSTypeAlloc(IOPolledFilePollers);
+ vars->init();
+
+ vars->pollers = OSArray::withCapacity(4);
+ if (!vars->pollers)
+ {
+ err = kIOReturnNoMemory;
+ break;
+ }
+
+ next = vars->media = media;
+ do
+ {
+ IOPolledInterface * poller;
+ OSObject * obj;
+
+ obj = next->getProperty(kIOPolledInterfaceSupportKey);
+ if (kOSBooleanFalse == obj)
+ {
+ vars->pollers->flushCollection();
+ break;
+ }
+ else if ((poller = OSDynamicCast(IOPolledInterface, obj)))
+ vars->pollers->setObject(poller);
+
+ if ((service = OSDynamicCast(IOService, next))
+ && service->getDeviceMemory()
+ && !vars->pollers->getCount()) break;
+
+ child = next;
+ }
+ while ((next = child->getParentEntry(gIOServicePlane))
+ && child->isParent(next, gIOServicePlane, true));
+
+ if (!vars->pollers->getCount())
+ {
+ err = kIOReturnUnsupported;
+ break;
+ }
+ }
+ while (false);
+
+ media->setProperty(kIOPolledInterfaceStackKey, vars);
+
+ return (vars);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static IOReturn
+IOPolledFilePollersIODone(IOPolledFilePollers * vars, bool abortable);
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static IOReturn
+IOPolledFilePollersProbe(IOPolledFilePollers * vars)
+{
+ IOReturn err = kIOReturnError;
+ int32_t idx;
+ IOPolledInterface * poller;
+
+ for (idx = vars->pollers->getCount() - 1; idx >= 0; idx--)
+ {
+ poller = (IOPolledInterface *) vars->pollers->getObject(idx);
+ err = poller->probe(vars->media);
+ if (err)
+ {
+ HIBLOG("IOPolledInterface::probe[%d] 0x%x\n", idx, err);
+ break;
+ }
+ }
+
+ return (err);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+IOReturn
+IOPolledFilePollersOpen(IOPolledFileIOVars * filevars, uint32_t state, bool abortable)
+{
+
+ IOPolledFilePollers * vars = filevars->pollers;
+ IOBufferMemoryDescriptor * ioBuffer;
+ IOPolledInterface * poller;
+ IOService * next;
+ IOReturn err = kIOReturnError;
+ int32_t idx;
+
+ vars->abortable = abortable;
+ ioBuffer = 0;
+
+ if (kIOPolledAfterSleepState == state)
+ {
+ vars->ioStatus = 0;
+ vars->io = false;
+ }
+ (void) IOPolledFilePollersIODone(vars, false);
+
+ if ((kIOPolledPreflightState == state) || (kIOPolledPreflightCoreDumpState == state))
+ {
+ ioBuffer = vars->ioBuffer;
+ if (!ioBuffer)
+ {
+ vars->ioBuffer = ioBuffer = IOBufferMemoryDescriptor::withOptions(kIODirectionInOut,
+ 2 * kDefaultIOSize, page_size);
+ if (!ioBuffer) return (kIOReturnNoMemory);
+ }
+ }
+
+ for (idx = vars->pollers->getCount() - 1; idx >= 0; idx--)
+ {
+ poller = (IOPolledInterface *) vars->pollers->getObject(idx);
+ err = poller->open(state, ioBuffer);
+ if ((kIOReturnSuccess != err) && (kIOPolledPreflightCoreDumpState == state))
+ {
+ err = poller->open(kIOPolledPreflightState, ioBuffer);
+ }
+ if (kIOReturnSuccess != err)
+ {
+ HIBLOG("IOPolledInterface::open[%d] 0x%x\n", idx, err);
+ break;
+ }
+ }
+ if (kIOReturnSuccess == err)
+ {
+ next = vars->media;
+ while (next)
+ {
+ next->setProperty(kIOPolledInterfaceActiveKey, kOSBooleanTrue);
+ next = next->getProvider();
+ }
+ }
+
+ return (err);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+IOReturn
+IOPolledFilePollersClose(IOPolledFileIOVars * filevars, uint32_t state)
+{
+ IOPolledFilePollers * vars = filevars->pollers;
+ IOPolledInterface * poller;
+ IORegistryEntry * next;
+ IOReturn err;
+ int32_t idx;
+
+ (void) IOPolledFilePollersIODone(vars, false);
+
+ if (kIOPolledPostflightState == state)
+ {
+ vars->openCount--;
+ if (vars->openCount)
+ {
+ // 21207427
+ IOPolledFilePollersOpen(filevars, vars->openState, vars->abortable);
+ return (kIOReturnSuccess);
+ }
+ }
+
+ for (idx = 0, err = kIOReturnSuccess;
+ (poller = (IOPolledInterface *) vars->pollers->getObject(idx));
+ idx++)
+ {
+ err = poller->close(state);
+ if (err) HIBLOG("IOPolledInterface::close[%d] 0x%x\n", idx, err);
+ }
+
+ if (kIOPolledPostflightState == state)
+ {
+ next = vars->media;
+ while (next)
+ {
+ next->removeProperty(kIOPolledInterfaceActiveKey);
+ next = next->getParentEntry(gIOServicePlane);
+ }
+
+ if (vars->ioBuffer)
+ {
+ vars->ioBuffer->release();
+ vars->ioBuffer = 0;
+ }
+ }
+ return (err);
+}
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+IOMemoryDescriptor *
+IOPolledFileGetIOBuffer(IOPolledFileIOVars * vars)
+{
+ return (vars->pollers->ioBuffer);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static void
+IOPolledIOComplete(void * target,
+ void * parameter,
+ IOReturn status,
+ UInt64 actualByteCount)
+{
+ IOPolledFilePollers * vars = (IOPolledFilePollers *) parameter;
+
+ vars->ioStatus = status;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static IOReturn
+IOStartPolledIO(IOPolledFilePollers * vars,
+ uint32_t operation, uint32_t bufferOffset,
+ uint64_t deviceOffset, uint64_t length)
+{
+ IOReturn err;
+ IOPolledInterface * poller;
+ IOPolledCompletion completion;
+
+ err = vars->ioStatus;
+ if (kIOReturnSuccess != err) return (err);
+
+ completion.target = 0;
+ completion.action = &IOPolledIOComplete;
+ completion.parameter = vars;
+
+ vars->ioStatus = -1;
+
+ poller = (IOPolledInterface *) vars->pollers->getObject(0);
+ err = poller->startIO(operation, bufferOffset, deviceOffset, length, completion);
+ if (err)
+ HIBLOG("IOPolledInterface::startIO[%d] 0x%x\n", 0, err);
+
+ return (err);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static IOReturn
+IOPolledFilePollersIODone(IOPolledFilePollers * vars, bool abortable)
+{
+ IOReturn err = kIOReturnSuccess;
+ int32_t idx = 0;
+ IOPolledInterface * poller;
+ AbsoluteTime deadline;
+
+ if (!vars->io) return (kIOReturnSuccess);
+
+ abortable &= vars->abortable;
+
+ clock_interval_to_deadline(2000, kMillisecondScale, &deadline);
+
+ while (-1 == vars->ioStatus)
+ {
+ for (idx = 0;
+ (poller = (IOPolledInterface *) vars->pollers->getObject(idx));
+ idx++)
+ {
+ IOReturn newErr;
+ newErr = poller->checkForWork();
+ if ((newErr == kIOReturnAborted) && !abortable)
+ newErr = kIOReturnSuccess;
+ if (kIOReturnSuccess == err)
+ err = newErr;
+ }
+ if ((false) && (kIOReturnSuccess == err) && (mach_absolute_time() > AbsoluteTime_to_scalar(&deadline)))
+ {
+ HIBLOG("IOPolledInterface::forced timeout\n");
+ vars->ioStatus = kIOReturnTimeout;
+ }
+ }
+ vars->io = false;
+
+#if HIBERNATION
+ if ((kIOReturnSuccess == err) && abortable && hibernate_should_abort())
+ {
+ err = kIOReturnAborted;
+ HIBLOG("IOPolledInterface::checkForWork sw abort\n");
+ }
+#endif
+
+ if (err)
+ {
+ HIBLOG("IOPolledInterface::checkForWork[%d] 0x%x\n", idx, err);
+ }
+ else
+ {
+ err = vars->ioStatus;
+ if (kIOReturnSuccess != err) HIBLOG("IOPolledInterface::ioStatus 0x%x\n", err);
+ }
+
+ return (err);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct _OpenFileContext
+{
+ OSData * extents;
+ uint64_t size;
+};
+
+static void
+file_extent_callback(void * ref, uint64_t start, uint64_t length)
+{
+ _OpenFileContext * ctx = (_OpenFileContext *) ref;
+ IOPolledFileExtent extent;
+
+ extent.start = start;
+ extent.length = length;
+ ctx->extents->appendBytes(&extent, sizeof(extent));
+ ctx->size += length;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static IOService *
+IOCopyMediaForDev(dev_t device)
+{
+ OSDictionary * matching;
+ OSNumber * num;
+ OSIterator * iter;
+ IOService * result = 0;
+
+ matching = IOService::serviceMatching("IOMedia");
+ if (!matching)
+ return (0);
+ do
+ {
+ num = OSNumber::withNumber(major(device), 32);
+ if (!num)
+ break;
+ matching->setObject(kIOBSDMajorKey, num);
+ num->release();
+ num = OSNumber::withNumber(minor(device), 32);
+ if (!num)
+ break;
+ matching->setObject(kIOBSDMinorKey, num);
+ num->release();
+ if (!num)
+ break;
+ iter = IOService::getMatchingServices(matching);
+ if (iter)
+ {
+ result = (IOService *) iter->getNextObject();
+ result->retain();
+ iter->release();
+ }
+ }
+ while (false);
+ matching->release();
+
+ return (result);
+}
+
+static IOReturn
+IOGetVolumeCryptKey(dev_t block_dev, OSString ** pKeyUUID,
+ uint8_t * volumeCryptKey, size_t keySize)
+{
+ IOReturn err;
+ IOService * part;
+ OSString * keyUUID = 0;
+ OSString * keyStoreUUID = 0;
+ uuid_t volumeKeyUUID;
+ aks_volume_key_t vek;
+
+ static IOService * sKeyStore;
+
+ part = IOCopyMediaForDev(block_dev);
+ if (!part) return (kIOReturnNotFound);
+
+ err = part->callPlatformFunction(PLATFORM_FUNCTION_GET_MEDIA_ENCRYPTION_KEY_UUID, false,
+ (void *) &keyUUID, (void *) &keyStoreUUID, NULL, NULL);
+ if ((kIOReturnSuccess == err) && keyUUID && keyStoreUUID)
+ {
+// IOLog("got volume key %s\n", keyStoreUUID->getCStringNoCopy());
+
+ if (!sKeyStore)
+ sKeyStore = (IOService *) IORegistryEntry::fromPath(AKS_SERVICE_PATH, gIOServicePlane);
+ if (sKeyStore)
+ err = uuid_parse(keyStoreUUID->getCStringNoCopy(), volumeKeyUUID);
+ else
+ err = kIOReturnNoResources;
+ if (kIOReturnSuccess == err)
+ err = sKeyStore->callPlatformFunction(gAKSGetKey, true, volumeKeyUUID, &vek, NULL, NULL);
+ if (kIOReturnSuccess != err)
+ IOLog("volume key err 0x%x\n", err);
+ else
+ {
+ if (vek.key.keybytecount < keySize) keySize = vek.key.keybytecount;
+ bcopy(&vek.key.keybytes[0], volumeCryptKey, keySize);
+ }
+ bzero(&vek, sizeof(vek));
+
+ }
+ part->release();
+ if (pKeyUUID) *pKeyUUID = keyUUID;
+
+ return (err);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+