Annotation of XNU/iokit/Families/IOStorage/IONeXTPartitionScheme.cpp, revision 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.