|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * The contents of this file constitute Original Code as defined in and ! 7: * are subject to the Apple Public Source License Version 1.1 (the ! 8: * "License"). You may not use this file except in compliance with the ! 9: * License. Please obtain a copy of the License at ! 10: * http://www.apple.com/publicsource and read it before using this file. ! 11: * ! 12: * This Original Code and all software distributed under the License are ! 13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 17: * License for the specific language governing rights and limitations ! 18: * under the License. ! 19: * ! 20: * @APPLE_LICENSE_HEADER_END@ ! 21: */ ! 22: ! 23: // ! 24: // Notes: ! 25: // ----- ! 26: // o the on-disk dpme structure is packed for PowerPC and has big endian fields ! 27: // ! 28: // o the dpme_pblock_start block value is relative to the containing media's ! 29: // boundary, and the implicit block size is hardcoded to 512 bytes ! 30: // ! 31: // o the dpme_pblocks block value's implicit block size is 512 bytes ! 32: // ! 33: // Policies: ! 34: // -------- ! 35: // o the apple partition scheme accepts probes only on "whole" media objects -- ! 36: // this is done in the spirit of minimizing reads, since in general practice, ! 37: // subpartitions of the apple type do not exist ! 38: // ! 39: // o the default probe score of the apple partition scheme is 1200 ! 40: // ! 41: // o the "Apple_partition_map" partition is always published as a non-writable ! 42: // media -- this is because its contents are nobody's business but ours ! 43: // ! 44: // o the "Apple_Free" partition is always skipped -- this is because it would ! 45: // never be used by a serious user application, and it cries out to harbour ! 46: // viruses; the partition also often exceeds the confines of the media for ! 47: // CDs, which would arguably require ugly truncation logic to be consistent ! 48: // ! 49: // o the checksum for each partition entry is not validated -- this is done in ! 50: // the spirit of non-necessity and perhaps a bit of laziness ! 51: // ! 52: ! 53: #include <IOKit/assert.h> ! 54: #include <IOKit/IOLib.h> ! 55: #include <IOKit/storage/IOApplePartitionScheme.h> ! 56: #include <libkern/OSByteOrder.h> ! 57: ! 58: #define super IOPartitionScheme ! 59: OSDefineMetaClassAndStructors(IOApplePartitionScheme, IOPartitionScheme); ! 60: ! 61: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 62: ! 63: bool IOApplePartitionScheme::init(OSDictionary * properties = 0) ! 64: { ! 65: // ! 66: // Initialize this object's minimal state. ! 67: // ! 68: ! 69: if (super::init(properties) == false) return false; ! 70: ! 71: _buffer = 0; ! 72: _bufferSize = 0; ! 73: _masteredAt512 = false; ! 74: ! 75: // Validate the compiled size of our important fixed-size structures. ! 76: ! 77: assert(sizeof(dpme) == 512); ! 78: assert(sizeof(DDMap) == 8); ! 79: assert(sizeof(Block0) == 512); ! 80: ! 81: return true; ! 82: } ! 83: ! 84: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 85: ! 86: void IOApplePartitionScheme::free() ! 87: { ! 88: // ! 89: // Free all of this object's outstanding resources. ! 90: // ! 91: ! 92: if (_buffer) _buffer->release(); ! 93: ! 94: super::free(); ! 95: } ! 96: ! 97: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 98: ! 99: IOService * IOApplePartitionScheme::probe(IOService * provider, SInt32 * score) ! 100: { ! 101: // ! 102: // Determine whether the provider media contains an apple partition map. If ! 103: // it does, we return "this" to indicate success, otherwise we return zero. ! 104: // ! 105: ! 106: IOMedia * media = (IOMedia *) provider; ! 107: ! 108: // State our assumptions. ! 109: ! 110: assert(OSDynamicCast(IOMedia, provider)); ! 111: ! 112: // Ask superclass' opinion about this probe. ! 113: ! 114: if (super::probe(provider, score) == 0) return 0; ! 115: ! 116: // Determine whether this media object is unformatted. ! 117: ! 118: if (media->isFormatted() == false) return 0; ! 119: ! 120: // Determine whether this media's block size is below our assumed minimum. ! 121: ! 122: if (media->getPreferredBlockSize() < sizeof(dpme)) return 0; ! 123: ! 124: // Determine whether we can rule out the media object as an apple partition ! 125: // map container without reading actual data from the media. We rule out ! 126: // all non-whole media objects. ! 127: ! 128: if (media->isWhole() == false) return 0; ! 129: ! 130: // Allocate a buffer large enough to hold one media block. ! 131: ! 132: _bufferSize = media->getPreferredBlockSize(); ! 133: _buffer = IOBufferMemoryDescriptor::withCapacity(_bufferSize, ! 134: kIODirectionIn); ! 135: ! 136: if (_buffer == 0) return 0; ! 137: ! 138: // Search for a valid apple partition on the provider media. ! 139: ! 140: return identify(media) ? this : 0; ! 141: } ! 142: ! 143: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 144: ! 145: bool IOApplePartitionScheme::start(IOService * provider) ! 146: { ! 147: // ! 148: // This method is called once we have been attached to the media object. We ! 149: // generate the new media objects that will represent our partitions here. ! 150: // ! 151: ! 152: UInt64 block; ! 153: UInt32 entriesCount; ! 154: IOMedia * media = (IOMedia *) provider; ! 155: dpme * partition; ! 156: UInt32 partitionsPerBlock; ! 157: UInt32 partitionsLeftOnThisBlock; ! 158: bool success = false; ! 159: ! 160: // State our assumptions. ! 161: ! 162: assert(_buffer); ! 163: ! 164: // Ask our superclass' opinion. ! 165: ! 166: if ( !super::start(provider) ) return false; ! 167: ! 168: // Open the media object for access. ! 169: ! 170: if ( media->open(this, 0, kAccessReader) == false ) return false; ! 171: ! 172: // Set up our iteration variables, based on whether the media was mastered ! 173: // on a 512-byte-block medium or at the media's preferred block size. ! 174: ! 175: if ( _masteredAt512 ) ! 176: { ! 177: block = 0; ! 178: partition = (dpme*)((UInt8*)_buffer->getBytesNoCopy() + sizeof(Block0)); ! 179: partitionsPerBlock = _bufferSize / sizeof(dpme); ! 180: partitionsLeftOnThisBlock = partitionsPerBlock - 1; ! 181: } ! 182: else ! 183: { ! 184: block = 1; ! 185: partition = (dpme *) _buffer->getBytesNoCopy(); ! 186: partitionsPerBlock = 1; ! 187: partitionsLeftOnThisBlock = 1; ! 188: } ! 189: ! 190: // Scan through all the partition entries. ! 191: ! 192: entriesCount = OSSwapBigToHostInt32(partition->dpme_map_entries); ! 193: ! 194: for ( unsigned index = 0; index < entriesCount; index++, partition += 1 ) ! 195: { ! 196: // Determine whether we've exhausted the current buffer of partitions. ! 197: ! 198: if ( partitionsLeftOnThisBlock == 0 ) ! 199: { ! 200: // Read the next block into our buffer. ! 201: ! 202: block++; ! 203: _buffer->setDirection(kIODirectionIn); // (a read) ! 204: _buffer->setLength(_bufferSize); // (transfer one full block) ! 205: ! 206: if ( media->read( /* client */ this, ! 207: /* byteStart */ block * _bufferSize, ! 208: /* buffer */ _buffer ) != kIOReturnSuccess ) ! 209: { ! 210: IOLog("%s: %s: Unable to read block %d.\n", ! 211: getName(), media->getName(), (int) block); ! 212: break; // (failure) ! 213: } ! 214: ! 215: partition = (dpme *) _buffer->getBytesNoCopy(); ! 216: partitionsLeftOnThisBlock = partitionsPerBlock; ! 217: } ! 218: ! 219: partitionsLeftOnThisBlock--; ! 220: ! 221: // Determine whether this partition has a valid 'PM' signature. ! 222: ! 223: if ( OSSwapBigToHostInt16(partition->dpme_signature) != DPME_SIGNATURE ) ! 224: { ! 225: IOLog("%s on %s: Partition %d has an invalid signature.\n", ! 226: getName(), media->getName(), index + 1); ! 227: continue; // (skip) ! 228: } ! 229: ! 230: // Determine whether this partition's type is 'Apple_Free'. ! 231: ! 232: if ( !strcmp(partition->dpme_type, "Apple_Free") ) ! 233: continue; // (skip) ! 234: ! 235: // Determine whether this partition's type is 'Apple_partition_map'. ! 236: ! 237: bool isWritable = media->isWritable(); ! 238: ! 239: if ( !strcmp(partition->dpme_type, "Apple_partition_map") ) ! 240: isWritable = false; ! 241: ! 242: // Compute the relative position and size of the new partition. The ! 243: // block values are always in terms of 512-byte blocks. ! 244: ! 245: UInt64 base = OSSwapBigToHostInt32(partition->dpme_pblock_start)*512ULL; ! 246: UInt64 size = OSSwapBigToHostInt32(partition->dpme_pblocks) * 512ULL; ! 247: ! 248: if ( base == 0 || size == 0 ) ! 249: continue; // (skip) ! 250: ! 251: // Look up a name and a type for this partition. ! 252: ! 253: const char * aName = partition->dpme_name; ! 254: const char * aHint = partition->dpme_type; ! 255: ! 256: // Ensure the partition definition does not leave the confines of the ! 257: // containing media. Note the "Apple_Free" partition often leaves its ! 258: // confines on CD media, however it is our policy to ignore Apple_Free ! 259: // partitions anyway. ! 260: ! 261: if ( base + size > media->getSize() ) ! 262: { ! 263: IOLog("%s on %s: \"%s\" (partition %d) exceeds confines of " ! 264: "containing media.\n", ! 265: getName(), media->getName(), aName, index+1); ! 266: continue; // (skip) ! 267: } ! 268: ! 269: // The partition base may be unaligned with respect the parent media's ! 270: // block boundaries. We warn the user in this event since every read ! 271: // or write is going to cause deblocking. We presume, of course, that ! 272: // the parent media's base is aligned in making this determination. ! 273: ! 274: if ( base % media->getPreferredBlockSize() ) ! 275: { ! 276: IOLog("%s on %s: Access to \"%s\" (partition %d) may be slow due " ! 277: "to a misaligned block boundary.\n", ! 278: getName(), media->getName(), aName, index + 1); ! 279: } ! 280: ! 281: // Create the new media object. ! 282: ! 283: IOMedia * newMedia = new IOMedia; ! 284: ! 285: if ( !newMedia || ! 286: !newMedia->init( // base (bytes; relative to provider media) ! 287: base, ! 288: // size (bytes) ! 289: size, ! 290: // natural block size (bytes) ! 291: ///m:2361246:workaround:added:start ! 292: (!strcmp(aHint, "Apple_HFS")) ? (512) : ! 293: ///m:2361246:workaround:added:stop ! 294: media->getPreferredBlockSize(), ! 295: // is ejectable ! 296: media->isEjectable(), ! 297: // is whole ! 298: false, ! 299: // is writable ! 300: isWritable, ! 301: // content hint ! 302: aHint ) || ! 303: !newMedia->attach(this) ) ! 304: { ! 305: IOLog("%s on %s: Unable to create media object for \"%s\" " ! 306: "(partition %d).\n", ! 307: getName(), media->getName(), aName, index + 1); ! 308: ! 309: if ( newMedia ) newMedia->release(); ! 310: continue; // (skip) ! 311: } ! 312: ! 313: newMedia->setName(aName); ! 314: ! 315: // Set a location value (the partition number) for this partition. ! 316: ! 317: char location[12]; ! 318: sprintf(location, "%d", index + 1); ! 319: newMedia->setLocation(location); ! 320: ! 321: // Create the "Partition ID" key. ! 322: ! 323: newMedia->setProperty(kIOMediaPartitionID, index + 1, 32); ! 324: ! 325: // Release our retain on the new media object (in registry). ! 326: ! 327: newMedia->release(); ! 328: ! 329: // We don't register the new media object with the matching system ! 330: // until we've closed our open on the media object below us. ! 331: ! 332: success = true; ! 333: } // (for all partition entries) ! 334: ! 335: // Release our buffer now that we no longer need it. ! 336: ! 337: _buffer->release(); ! 338: ! 339: _buffer = 0; ! 340: _bufferSize = 0; ! 341: ! 342: // Close the media object we opened earlier. ! 343: ! 344: media->close(this); ! 345: ! 346: // We now register the new media objects with the matching system, now ! 347: // that we've closed our open on the media object below us. ! 348: ! 349: if ( success ) ! 350: { ! 351: OSIterator * clients = getClientIterator(); ! 352: ! 353: if (clients) ! 354: { ! 355: IOService * client; ! 356: while ( (client = (IOService *) clients->getNextObject()) ) ! 357: client->registerService(); ! 358: clients->release(); ! 359: } ! 360: } ! 361: ! 362: // Return success if we found at least one partition. ! 363: ! 364: return success; ! 365: } ! 366: ! 367: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! 368: ! 369: bool IOApplePartitionScheme::identify(IOMedia * media) ! 370: { ! 371: // ! 372: // Searches for the existence of an apple partition map on the given media. ! 373: // ! 374: ! 375: bool success = false; ! 376: ! 377: // State our assumptions. ! 378: ! 379: assert(_buffer); ! 380: ! 381: // Open the media object for access. ! 382: ! 383: if ( media->open(this, 0, kAccessReader) == false ) return false; ! 384: ! 385: // Determine whether this is a larger-than-512-byte-block media that was ! 386: // mastered from a 512-byte-block media -- the mastering of 2048-byte- ! 387: // block CDs is the prime example, from a 512-byte-block hard drive. ! 388: ! 389: if ( _bufferSize > sizeof(Block0) ) ! 390: { ! 391: // State our assumptions. ! 392: ! 393: assert(_bufferSize >= sizeof(Block0) + sizeof(dpme)); ! 394: ! 395: // Read the first block into our buffer. ! 396: ! 397: _buffer->setDirection(kIODirectionIn); // (a read) ! 398: _buffer->setLength(_bufferSize); // (transfer one full block) ! 399: ! 400: if ( media->read(this, 0, _buffer) != kIOReturnSuccess ) ! 401: { ! 402: media->close(this); ! 403: return false; // (failure) ! 404: } ! 405: ! 406: // Determine whether the partition signature 'PM' is present at a 512 ! 407: // byte offset into the block. ! 408: ! 409: dpme * partition; ! 410: partition = (dpme*)((UInt8*)_buffer->getBytesNoCopy() + sizeof(Block0)); ! 411: ! 412: if ( OSSwapBigToHostInt16(partition->dpme_signature) == DPME_SIGNATURE ) ! 413: { ! 414: _masteredAt512 = true; ! 415: success = true; ! 416: } ! 417: } ! 418: ! 419: // Determine whether there is a valid apple partition map on the media; ! 420: // note if the masteredAt512 flag is set, we already confirmed it does. ! 421: ! 422: if ( _masteredAt512 == false ) ! 423: { ! 424: // Read the second block into our buffer. ! 425: ! 426: _buffer->setDirection(kIODirectionIn); // (a read) ! 427: _buffer->setLength(_bufferSize); // (transfer one full block) ! 428: ! 429: if ( media->read(this, _bufferSize, _buffer) == kIOReturnSuccess ) ! 430: { ! 431: // Determine whether the partition map signature 'PM' is present. ! 432: ! 433: dpme * partition = (dpme *) _buffer->getBytesNoCopy(); ! 434: ! 435: if (OSSwapBigToHostInt16(partition->dpme_signature)==DPME_SIGNATURE) ! 436: success = true; ! 437: } ! 438: } ! 439: ! 440: // Close the media object we opened earlier. ! 441: ! 442: media->close(this); ! 443: ! 444: return success; ! 445: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.