Annotation of XNU/iokit/Families/IOStorage/IONeXTPartitionScheme.cpp, revision 1.1.1.1

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 label structure is packed for M68xxx and has big endian fields
                     27: //
                     28: // o the on-disk label is stored four times in succession, each label taking up
                     29: //   round(sizeof(disk_label_t), drive's hard block size) bytes; CD's suffer of
                     30: //   the mastering-with-a-different-hard-block-size problem, however we presume
                     31: //   that all CDs produced that exhibit this problem have a valid first label
                     32: //   at relative byte zero, and hence the next label increment is irrelevant.
                     33: //
                     34: // o the dl_label_blkno block value is absolute with respect to the whole disk,
                     35: //   and the implicit block size is the drive's hard block size (eg. hard drive
                     36: //   is typically 512 bytes, CD is typically 2048 bytes)
                     37: //
                     38: // o the dl_dt.d_front block value is relative to the containing partition's
                     39: //   boundary (ie. relative to the very first label's dl_label_blkno value),
                     40: //   and the implicit block size is dl_dt.d_secsize bytes (eg. hd=1024, cd=2048)
                     41: //
                     42: // o the dl_dt.d_partitions[].p_base block value is absolute with respect to the
                     43: //   whole disk, and the implicit block size is dl_dt.d_secsize bytes; an offset
                     44: //   of dl_dt.d_front is not factored into the p_base value, but should be in
                     45: //   order to calculate the actual start position of the partition on the disk
                     46: //
                     47: // o the dl_dt.d_partitions[].p_size block value's implicit block size is
                     48: //   dl_dt.d_secsize bytes
                     49: //
                     50: // Policies:
                     51: // --------
                     52: // o the NeXT partition scheme accepts probes only on "whole" media objects
                     53: //   and non-whole media objects with a content hint of 'Apple_Rhapsody_UFS'
                     54: //
                     55: // o the first two out of the four possible on-disk labels are read while the
                     56: //   last two are ignored --  this is done in the spirit of minimizing reads;
                     57: //   we forfeit some redundancy, however that is acceptable; the second label
                     58: //   MUST be checked due to the fact that i386 machines commonly have the 1st
                     59: //   label missing (due to the FDISK boot structure), but the second is valid
                     60: //
                     61: // o the default probe score of the NeXT partition scheme is 1000
                     62: //
                     63: // o the eigth partition is always skipped -- this is due to the many varying
                     64: //   preconceptions associated with the 8th partition in unix land:   we just
                     65: //   ignore it altogether (+ permits us a significant coding simplification)
                     66: //
                     67: // o the checksum for each NeXT label is not validated -- this is done in the
                     68: //   spirit of minimizing reads and perhaps a bit of laziness
                     69: //
                     70: 
                     71: #include <IOKit/assert.h>
                     72: #include <IOKit/IOLib.h>
                     73: #include <IOKit/storage/IONeXTPartitionScheme.h>
                     74: #include <libkern/OSByteOrder.h>
                     75: 
                     76: #define super IOPartitionScheme
                     77: OSDefineMetaClassAndStructors(IONeXTPartitionScheme, IOPartitionScheme);
                     78: 
                     79: #define kMinimumBlockSize 512
                     80: 
                     81: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                     82: // NeXT Partition Types
                     83: 
                     84: static struct { char * type; char * name; } partitionTypes[] =
                     85: {
                     86:     { "4.4BSD", "Apple_UFS" },
                     87: //  { "4.1BSD", ... },          // V7 with 1K blocks (4.1, 2.9)
                     88: //  { "4.2BSD", ... },          // 4.2BSD fast file system
                     89: //  { "4.4LFS", ... },          // 4.4BSD log-structured file system
                     90:     { 0, 0 }
                     91: };
                     92: 
                     93: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                     94: 
                     95: bool IONeXTPartitionScheme::init(OSDictionary * properties = 0)
                     96: {
                     97:     //
                     98:     // Initialize this object's minimal state.
                     99:     //
                    100: 
                    101:     if (super::init(properties) == false)  return false;
                    102: 
                    103:     _absoluteBase = 0;
                    104:     _buffer       = 0;
                    105:     _bufferSize   = 0;
                    106: 
                    107:     // Validate the compiled size of our important fixed-size structures.
                    108: 
                    109:     assert(sizeof(disktab_t)    ==  514);
                    110:     assert(sizeof(partition_t)  ==   46);
                    111:     assert(sizeof(disk_label_t) == 7240);
                    112: 
                    113:     return true;
                    114: }
                    115: 
                    116: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                    117: 
                    118: void IONeXTPartitionScheme::free()
                    119: {
                    120:     //
                    121:     // Free all of this object's outstanding resources.
                    122:     //
                    123: 
                    124:     if (_buffer)  _buffer->release();
                    125: 
                    126:     super::free();
                    127: }
                    128: 
                    129: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                    130: 
                    131: IOService * IONeXTPartitionScheme::probe(IOService * provider, SInt32 * score)
                    132: {
                    133:     //
                    134:     // Determine whether the provider media contains an NeXT partition map.  If
                    135:     // it does, we return "this" to indicate success, otherwise we return zero.
                    136:     //
                    137: 
                    138:     IOMedia * media = (IOMedia *) provider;
                    139: 
                    140:     // State our assumptions.
                    141: 
                    142:     assert(OSDynamicCast(IOMedia, provider));
                    143: 
                    144:     // Ask superclass' opinion about this probe.
                    145: 
                    146:     if (super::probe(provider, score) == 0)  return 0;
                    147: 
                    148:     // Determine whether this media object is unformatted.
                    149: 
                    150:     if (media->isFormatted() == false)  return 0;
                    151: 
                    152:     // Determine whether this media's block size is below our assumed minimum.
                    153: 
                    154:     if (media->getPreferredBlockSize() < kMinimumBlockSize)  return 0;
                    155: 
                    156:     // Determine whether we can rule out the media object as a NeXT partition
                    157:     // map container without reading actual data from the media.  We rule out
                    158:     // all non-whole media objects without an 'Apple_Rhapsody_UFS' hint.
                    159: 
                    160:     if ( media->isWhole() == false )
                    161:     {
                    162:         if ( strcmp(media->getContentHint(), "Apple_Rhapsody_UFS") )  return 0;
                    163:     }
                    164: 
                    165:     // Compute this partition's absolute offset with respect to the whole
                    166:     // media, since the disk_label structure requires this information --
                    167:     // we go down the service hierarchy until we reach the whole media
                    168:     // object (or run off the end :-).
                    169: 
                    170:     for (IOService * service = media; service; service = service->getProvider())
                    171:     {
                    172:         if ( OSDynamicCast(IOMedia, service) ) // (is this a media object?)
                    173:         {
                    174:             _absoluteBase += ((IOMedia *)service)->getBase();
                    175:             if (((IOMedia *)service)->isWhole())  break;
                    176:         }
                    177:     }
                    178: 
                    179:     // Allocate a buffer large enough to hold one media block.
                    180: 
                    181:     _bufferSize = media->getPreferredBlockSize();
                    182:     _buffer     = IOBufferMemoryDescriptor::withCapacity(_bufferSize,
                    183:                                                          kIODirectionIn);
                    184: 
                    185:     if (_buffer == 0)  return 0;
                    186: 
                    187:     // Search for a valid NeXT label on the provider media.
                    188: 
                    189:     return identify(media) ? this : 0;
                    190: }
                    191: 
                    192: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                    193: 
                    194: bool IONeXTPartitionScheme::start(IOService * provider)
                    195: {
                    196:     //
                    197:     // This method is called once we have been attached to the media object.  We
                    198:     // generate the new media objects that will represent our partitions here.
                    199:     //
                    200: 
                    201:     disk_label_t * label;
                    202:     IOMedia *      media   = (IOMedia *) provider;
                    203:     bool           success = false;
                    204: 
                    205:     // State our assumptions.
                    206: 
                    207:     assert(_buffer);
                    208:     assert(_bufferSize >= kMinimumBlockSize);
                    209: 
                    210:     // Ask our superclass' opinion.
                    211: 
                    212:     if ( !super::start(provider) )  return false;
                    213: 
                    214:     // The block size we calculate for the partitions is important to
                    215:     // BSD and its filesystems  -- if we get it wrong, the filesystem
                    216:     // will end up reading and writing the wrong blocks.   We use the
                    217:     // NeXT label's sector size, NOT the partition's fragment size or
                    218:     // it won't work (speaking out of experience).  Period.
                    219: 
                    220:     label = (disk_label_t *) _buffer->getBytesNoCopy();
                    221: 
                    222:     UInt64 fsBlockSize = OSSwapBigToHostInt32(label->dl_secsize);
                    223: 
                    224:     if (fsBlockSize % media->getPreferredBlockSize())
                    225:     {
                    226:         IOLog("%s on %s: Bad block size is defined.\n",
                    227:               getName(), media->getName());
                    228:         return false;
                    229:     }
                    230: 
                    231:     // Scan through all the partition entries.
                    232:     //
                    233:     // Due to the different preconceptions associated with the eigth partition,
                    234:     // our policy is to ignore the eigth partition altogether.  This also makes
                    235:     // for a cool coding simplification  -- since the first 7 partition entries
                    236:     // completely fit in the first 512-byte block, a read of the next block and
                    237:     // caching useful global information from the first block is not necessary
                    238:     // just to parse the eigth partition entry.
                    239: 
                    240:     assert( kMinimumBlockSize >= ( (UInt8 *) &(label->dl_part[NPART-1]) -
                    241:                                    (UInt8 *) label) );
                    242: 
                    243:     for ( unsigned index = 0; index < NPART - 1; index++ )
                    244:     {
                    245:         // Skip all null partition entries (have base of -1 and size of -1).
                    246: 
                    247:         if ( (SInt32) OSSwapBigToHostInt32(label->dl_part[index].p_base) <  0 ||
                    248:              (SInt32) OSSwapBigToHostInt32(label->dl_part[index].p_size) <= 0 )
                    249:             continue; // (skip)
                    250: 
                    251:         // Compute the absolute position and size of the new partition.
                    252: 
                    253:         UInt64 base;
                    254:         UInt64 size;
                    255: 
                    256:         base = ( (UInt64) OSSwapBigToHostInt32(label->dl_part[index].p_base) +
                    257:                  (UInt64) OSSwapBigToHostInt16(label->dl_front) ) *
                    258:                (UInt64) OSSwapBigToHostInt32(label->dl_secsize);
                    259:         size = (UInt64) OSSwapBigToHostInt32(label->dl_part[index].p_size) *
                    260:                (UInt64) OSSwapBigToHostInt32(label->dl_secsize);
                    261: 
                    262:         // Look up a name and a type for this partition.
                    263: 
                    264:         const char * aName = "Untitled";
                    265:         const char * aHint = 0;
                    266: 
                    267:         if ( label->dl_part[index].p_mountpt[0] )
                    268:             aName = label->dl_part[index].p_mountpt;
                    269:         else if ( label->dl_label[0] )
                    270:             aName = label->dl_label;
                    271: 
                    272:         for (unsigned n = 0; partitionTypes[n].type; n++)
                    273:         {
                    274:             if ( !strcmp(label->dl_part[index].p_type, partitionTypes[n].type) )
                    275:             {
                    276:                 aHint = partitionTypes[n].name;
                    277:                 break;
                    278:             }
                    279:         }
                    280: 
                    281:         if (aHint == 0)  aHint = label->dl_part[index].p_type;
                    282: 
                    283:         // Ensure the partition definition does not leave the confines of the
                    284:         // containing media (especially that base is an absolute position).
                    285: 
                    286:         if ( base < _absoluteBase ||
                    287:              base - _absoluteBase + size > media->getSize() )
                    288:         {
                    289:             IOLog("%s on %s: \"%s\" (partition %d) exceeds confines of "
                    290:                   "containing media.\n",
                    291:                   getName(), media->getName(), aName, index+1);
                    292:             continue; // (skip)
                    293:         }
                    294: 
                    295:         // The partition base may be unaligned with respect the whole media's
                    296:         // block boundaries.  We warn the user in this event since every read
                    297:         // or write is going to cause deblocking.
                    298: 
                    299:         if ( (_absoluteBase + base) % media->getPreferredBlockSize() )
                    300:         {
                    301:             IOLog("%s on %s: Access to \"%s\" (partition %d) may be slow due "
                    302:                   "to a misaligned block boundary.\n",
                    303:                   getName(), media->getName(), aName, index + 1);
                    304:         }        
                    305: 
                    306:         // Create the new media object.
                    307: 
                    308:         IOMedia * newMedia = new IOMedia;
                    309: 
                    310:         if ( !newMedia ||
                    311:              !newMedia->init( // base (bytes; relative to provider media)
                    312:                               base - _absoluteBase,
                    313:                               // size (bytes)
                    314:                               size,
                    315:                               // natural block size (bytes)
                    316:                               fsBlockSize,
                    317:                               // is ejectable
                    318:                               media->isEjectable(),
                    319:                               // is whole
                    320:                               false,
                    321:                               // is writable
                    322:                               media->isWritable(),
                    323:                               // content hint
                    324:                               aHint ) ||
                    325:              !newMedia->attach(this) )
                    326:         {
                    327:             IOLog("%s on %s: Unable to create media object for \"%s\" "
                    328:                   "(partition %d).\n",
                    329:                   getName(), media->getName(), aName, index + 1);
                    330: 
                    331:             if (newMedia)  newMedia->release();
                    332:             continue; // (skip)
                    333:         }
                    334: 
                    335:         newMedia->setName(aName);
                    336: 
                    337:         // Set a location value (the partition number) for this partition.
                    338: 
                    339:         char location[12];
                    340:         sprintf(location, "%d", index + 1);
                    341:         newMedia->setLocation(location);
                    342: 
                    343:         // Create the "Partition ID" key.
                    344: 
                    345:         newMedia->setProperty(kIOMediaPartitionID, index + 1, 32);
                    346: 
                    347:         // Register the media object with the matching system.
                    348: 
                    349:         newMedia->registerService();
                    350: 
                    351:         // Release our retain on the new media object (in registry).
                    352: 
                    353:         newMedia->release();
                    354: 
                    355:         success = true;
                    356:     } // (for all partition entries)
                    357: 
                    358:     // Release our buffer now that we no longer need it.
                    359: 
                    360:     _buffer->release();
                    361: 
                    362:     _buffer     = 0;
                    363:     _bufferSize = 0;
                    364: 
                    365:     // Return success if we found at least one partition.
                    366: 
                    367:     return success;
                    368: }
                    369: 
                    370: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                    371: 
                    372: bool IONeXTPartitionScheme::identify(IOMedia * media)
                    373: {
                    374:     //
                    375:     // Searches for the existence of a NeXT partition map on the given media.
                    376:     //
                    377: 
                    378:     UInt32 labelIncrement; // (in bytes)
                    379:     bool   success = false;
                    380: 
                    381:     // State our assumptions.
                    382: 
                    383:     assert(_buffer);
                    384:     assert(_bufferSize >= kMinimumBlockSize);
                    385: 
                    386:     // Open the media object for access.
                    387: 
                    388:     if ( media->open(this, 0, kAccessReader) == false )  return false;
                    389: 
                    390:     // Search through the four (NLABELS) possible label positions.
                    391:     //
                    392:     // In the spirit of minimizing reads, however, we cheat and only check the
                    393:     // first two of the four possible label positions.  Note that it is common
                    394:     // on i386 machines that the first label is non-existent, while the second
                    395:     // is valid, hence why we check the first TWO labels.
                    396: 
                    397:     labelIncrement=IORound(sizeof(disk_label_t),media->getPreferredBlockSize());
                    398: 
                    399:     for (unsigned index = 0; index < 2; index++)  // ("2" was "NLABELS")
                    400:     {
                    401:         // Read the appropriate block into our buffer.
                    402: 
                    403:         _buffer->setDirection(kIODirectionIn);   // (a read)
                    404:         _buffer->setLength(_bufferSize);         // (transfer one full block)
                    405: 
                    406:         if ( media->read( /* client    */ this,
                    407:                           /* byteStart */ labelIncrement * index,
                    408:                           /* buffer    */ _buffer ) == kIOReturnSuccess )
                    409:         {
                    410:             // Determine whether this buffer contains a valid NeXT label.  We
                    411:             // validate the version signature and the label's block position.
                    412: 
                    413:             disk_label_t * label = (disk_label_t *) _buffer->getBytesNoCopy();
                    414: 
                    415:             if ( OSSwapBigToHostInt32(label->dl_version) == DL_V3 ||
                    416:                  OSSwapBigToHostInt32(label->dl_version) == DL_V2 ||
                    417:                  OSSwapBigToHostInt32(label->dl_version) == DL_V1 )
                    418:             {
                    419:                 if ( OSSwapBigToHostInt32(label->dl_label_blkno) ==
                    420:                      (UInt32) ( (_absoluteBase + labelIncrement * index) /
                    421:                                 media->getPreferredBlockSize() ) )
                    422:                 {
                    423:                     success = true; // (valid version and block position)
                    424:                     break;
                    425:                 }
                    426:             }
                    427:         }
                    428:     }
                    429: 
                    430:     // Close the media object we opened earlier.
                    431: 
                    432:     media->close(this);
                    433: 
                    434:     return success;
                    435: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.