Annotation of driverkit/libDriver/IODisk.m, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
                      3:  *
                      4:  * @APPLE_LICENSE_HEADER_START@
                      5:  * 
                      6:  * Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
                      7:  * Reserved.  This file contains Original Code and/or Modifications of
                      8:  * Original Code as defined in and that are subject to the Apple Public
                      9:  * Source License Version 1.1 (the "License").  You may not use this file
                     10:  * except in compliance with the License.  Please obtain a copy of the
                     11:  * License at http://www.apple.com/publicsource and read it before using
                     12:  * this file.
                     13:  * 
                     14:  * The Original Code and all software distributed under the License are
                     15:  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
                     16:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
                     17:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
                     18:  * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT.  Please see the
                     19:  * License for the specific language governing rights and limitations
                     20:  * under the License.
                     21:  * 
                     22:  * @APPLE_LICENSE_HEADER_END@
                     23:  */
                     24: /*     Copyright (c) 1991 NeXT Computer, Inc.  All rights reserved. 
                     25:  *
                     26:  * IODisk.m - implementation of generic disk object.
                     27:  *
                     28:  * HISTORY
                     29:  * 31-Jan-91    Doug Mitchell at NeXT
                     30:  *      Created.
                     31:  */
                     32: 
                     33: #import <bsd/sys/types.h>
                     34: #import <driverkit/return.h>
                     35: #import <mach/kern_return.h>
                     36: #import <driverkit/IODisk.h>
                     37: #import <driverkit/Device_ddm.h>
                     38: #ifdef KERNEL
                     39: #import <kernserv/prototypes.h>
                     40: #import <mach/mach_interface.h>
                     41: #else  KERNEL
                     42: #import <mach/mach.h>
                     43: #import <bsd/libc.h>
                     44: #endif KERNEL
                     45: #import <driverkit/IOLogicalDisk.h> 
                     46: #import <driverkit/IODiskPartition.h>
                     47: #import <driverkit/volCheck.h>
                     48: #import <machkit/NXLock.h>
                     49: #import <driverkit/generalFuncs.h>
                     50: #import <bsd/sys/errno.h>
                     51: #import <driverkit/IODeviceDescription.h>
                     52: 
                     53: #ifdef ppc //bknight - 12/16/97 - Radar #2004660
                     54: #define GROK_APPLE 1
                     55: #endif //bknight - 12/16/97 - Radar #2004660
                     56: 
                     57: /*
                     58:  * For stringFromReturn:.
                     59:  */
                     60: static IONamedValue diskIoReturnValues[] = { 
                     61:         {IO_R_NO_LABEL,                "No Label"              },
                     62:         {IO_R_UNFORMATTED,     "Disk Not Formatted"    },
                     63:        {IO_R_NO_DISK,          "Disk Not Present"      },
                     64:        {IO_R_NO_BLOCK_ZERO,    "Can\'t Read Block 0"   },
                     65:        {IO_R_NO_NEXT_PART,     "No NeXT partition"     },
                     66:        {0,                     NULL                    }
                     67: };
                     68: 
                     69: #ifdef DDM_DEBUG
                     70: /*
                     71:  * For xpr's.
                     72:  */
                     73: IONamedValue readyStateValues[] = { 
                     74:         {IO_Ready,             "Ready"         },
                     75:        {IO_NotReady,           "Not Ready"     },
                     76:        {IO_NoDisk,             "No Disk"       },
                     77:        {IO_Ejecting,           "Ejecting"      },
                     78:        {0,                     NULL            }
                     79: };
                     80: #endif DDM_DEBUG
                     81: 
                     82: @implementation IODisk
                     83: 
                     84: /*
                     85:  * Public methods.
                     86:  */
                     87: 
                     88: /*
                     89:  * Public methods to get and set disk parameters. May be overridden by
                     90:  * subclass.
                     91:  */
                     92:  
                     93: - (unsigned)diskSize
                     94: {
                     95:        return(_diskSize);
                     96: }
                     97: 
                     98: - (unsigned)blockSize
                     99: {
                    100:        return(_blockSize);
                    101: }
                    102: 
                    103: - (IOReturn)setFormatted : (BOOL)formattedFlag
                    104: {
                    105:        
                    106:        /*
                    107:         * This is illegal if any attached logicalDisks are open. 
                    108:         * In the Unix world, this is normally only done on the raw
                    109:         * device, an IODiskPartition, which overrides this method. 
                    110:         */
                    111: #ifdef KERNEL
                    112:        IOPanic("setFormatted: on IODisk");
                    113: #else  KERNEL
                    114:        id targ = (id)self;
                    115: 
                    116:        xpr_disk("DiskObject %s: setFormatted flag = %d\n",
                    117:                [self name], formattedFlag, 3,4,5);
                    118:                
                    119:        if(_nextLogicalDisk && [_nextLogicalDisk isOpen]) {
                    120:                xpr_err("%s: setFormatted with logicalDisk open\n",
                    121:                        [self name], 2,3,4,5);
                    122:                return(IO_R_BUSY);
                    123:        }
                    124:        if(formattedFlag) {
                    125:                /*
                    126:                 * Update our internal device parameters.
                    127:                 * FIXME - maybe check for respondsTo:.
                    128:                 */
                    129:                [targ updatePhysicalParameters];
                    130:                _formatted = 1;
                    131:        }
                    132:        else
                    133:                _formatted = 0;
                    134:        /*
                    135:         * Let any attached logical disks know about this, using the internal
                    136:         * version of setFormatted to avoid having our setFormattedInternal:
                    137:         * being called back..
                    138:         */
                    139:        if(_nextLogicalDisk)
                    140:                [_nextLogicalDisk setFormattedInternal:formattedFlag];
                    141: #endif KERNEL
                    142:        return(IO_R_SUCCESS);
                    143: }
                    144: 
                    145: - (BOOL)isFormatted
                    146: {
                    147:        return(_formatted);
                    148: }
                    149: 
                    150: - (BOOL)isRemovable
                    151: {
                    152:        return(_removable);
                    153: }
                    154: 
                    155: - (const char *)driveName
                    156: {
                    157:        return(_driveName);
                    158: }
                    159: 
                    160: - (BOOL)isPhysical
                    161: {
                    162:        return(_isPhysical);
                    163: }
                    164: 
                    165: - (BOOL)isWriteProtected
                    166: {
                    167:        return(_writeProtected);
                    168: }
                    169: 
                    170: /*
                    171:  * Eject current disk.
                    172:  */
                    173: - (IOReturn) eject
                    174: {
                    175:        
                    176: #ifdef KERNEL
                    177:        IOPanic("IODisk eject in kernel illegal\n");
                    178:        return(IO_R_UNSUPPORTED);
                    179: #else  KERNEL
                    180: 
                    181:        id targ = (id)self;
                    182: 
                    183:        if(!_removable)
                    184:                return(IO_R_UNSUPPORTED);
                    185:        xpr_disk("DiskObject %s eject:\n", [self name], 2,3,4,5);
                    186:        
                    187:        /*
                    188:         * We can't allow this if any logical disks are attached. 
                    189:         * Normally in the Unix world, eject is invoked on an IODiskPartition,
                    190:         * which overrides this method and ends up calling physDev's
                    191:         * ejectPhysical. If we're here, that means we must be a
                    192:         * physical device.
                    193:         */
                    194:        if(_nextLogicalDisk) {
                    195:                xpr_disk("DiskObject eject: Logical Disk(s)"
                    196:                        " Attached\n", 1,2,3,4,5);
                    197:                IOLog("%s: Eject attempt with attached logical disks", 
                    198:                        [self name]);
                    199:                return(IO_R_OPEN);
                    200:        }
                    201:        
                    202:        /*
                    203:         * We know nothing about this disk...
                    204:         */
                    205:        _formatted = 0;
                    206:        
                    207:        /* 
                    208:         * Can't send this method to self, it's in IOPhysicalDiskMethods 
                    209:         * protocol.
                    210:         * FIXME - probably should check for respondsTo:.
                    211:         */
                    212:        return([targ ejectPhysical]);
                    213: #endif KERNEL
                    214: }
                    215: 
                    216: /*
                    217:  * Get/set parameters used only by subclasses.
                    218:  */                    
                    219: - (void)setDiskSize    : (unsigned)size
                    220: {
                    221:        _diskSize = size;
                    222: }
                    223: 
                    224: - (void)setBlockSize : (unsigned)size
                    225: {
                    226:        _blockSize = size;
                    227: }
                    228: 
                    229: - (void)setIsPhysical : (BOOL)isPhysFlag
                    230: {
                    231:        _isPhysical = isPhysFlag ? YES : NO;
                    232: }
                    233: 
                    234: - nextLogicalDisk
                    235: {
                    236:        return(_nextLogicalDisk);
                    237: }
                    238: 
                    239: - (void) setRemovable : (BOOL)removableFlag
                    240: {
                    241:        _removable = removableFlag ? YES : NO;
                    242:        return;
                    243: }
                    244: 
                    245: - (void)setDriveName   : (const char *)name
                    246: {
                    247:        int len;
                    248:        
                    249:        len = strlen((char *)name);
                    250:        if(len >= MAXDNMLEN)
                    251:                len = MAXDNMLEN - 1;
                    252:        strncpy(_driveName, name, len);
                    253:        _driveName[MAXDNMLEN - 1] = '\0';               
                    254: }
                    255: 
                    256: - (IODiskReadyState)lastReadyState
                    257: {
                    258:        return(_lastReadyState);
                    259: }
                    260: 
                    261: - (void)setLastReadyState : (IODiskReadyState)readyState
                    262: {
                    263:        _lastReadyState = readyState;
                    264: }
                    265: 
                    266: - (void)setWriteProtected      : (BOOL)writeProtectedFlag
                    267: {
                    268:        _writeProtected = writeProtectedFlag ? YES : NO;
                    269: }
                    270: 
                    271: 
                    272: /*
                    273:  * internal setFormatted - avoids logical disk interaction.
                    274:  */
                    275: - (void)setFormattedInternal:(BOOL)formattedFlag
                    276: {
                    277:        xpr_disk("DiskObject %s: setFormattedInternal flag = %d\n",
                    278:                [self name], formattedFlag, 3,4,5);
                    279:        _formatted = formattedFlag ? YES : NO;
                    280: }
                    281: 
                    282: /*
                    283:  * Statistics support.
                    284:  */
                    285: - (void)addToBytesRead  : (unsigned)bytesRead
                    286:                    totalTime : (ns_time_t)totalTime
                    287:                    latentTime : (ns_time_t)latentTime
                    288: {
                    289:        unsigned long long ms_ll;
                    290:        
                    291:        _readOps++;
                    292:        _bytesRead += bytesRead;
                    293:        ms_ll = totalTime / (1000 * 1000);
                    294:        _readTotalTime += ms_ll;
                    295:        ms_ll = latentTime / (1000 * 1000);
                    296:        _readLatentTime += ms_ll;
                    297: }
                    298: 
                    299: - (void)addToBytesWritten : (unsigned)bytesWritten
                    300:                     totalTime : (ns_time_t)totalTime
                    301:                     latentTime : (ns_time_t)latentTime
                    302: {
                    303:        unsigned long long ms_ll;
                    304:        
                    305:        _writeOps++;
                    306:        _bytesWritten += bytesWritten;
                    307:        ms_ll = totalTime / (1000 * 1000);
                    308:        _writeTotalTime += ms_ll;
                    309:        ms_ll = latentTime / (1000 * 1000);
                    310:        _writeLatentTime += ms_ll;
                    311: }
                    312: 
                    313: - (void)incrementReadRetries
                    314: {
                    315:        _readRetries++;
                    316: }
                    317: 
                    318: - (void)incrementReadErrors
                    319: {      
                    320:        _readErrors++;
                    321: }
                    322: 
                    323: - (void)incrementWriteRetries
                    324: {
                    325:        _writeRetries++;
                    326: }
                    327: 
                    328: - (void)incrementWriteErrors
                    329: {      
                    330:        _writeErrors++;
                    331: }
                    332: 
                    333: - (void)incrementOtherRetries
                    334: {
                    335:        _otherRetries++;
                    336: }
                    337: 
                    338: - (void)incrementOtherErrors
                    339: {      
                    340:        _otherErrors++;
                    341: }
                    342: 
                    343: 
                    344: /*
                    345:  * For gathering cumulative statistics.
                    346:  */
                    347: - (IOReturn)getIntValues               : (unsigned *)parameterArray
                    348:                           forParameter : (IOParameterName)parameterName
                    349:                                  count : (unsigned *)count;    // in/out
                    350: {
                    351:        unsigned int paramArray[IO_DISK_STAT_ARRAY_SIZE];
                    352:        int i;
                    353:        int maxCount = *count;
                    354:        
                    355:        if(maxCount == 0) {
                    356:                maxCount = IO_MAX_PARAMETER_ARRAY_LENGTH;
                    357:        }
                    358:        if(strcmp(parameterName, IO_DISK_STATS) == 0) {
                    359:                paramArray[IO_Reads]            = _readOps;
                    360:                paramArray[IO_BytesRead]        = _bytesRead;
                    361:                paramArray[IO_TotalReadTime]    = _readTotalTime;
                    362:                paramArray[IO_LatentReadTime]   = _readLatentTime;
                    363:                paramArray[IO_ReadRetries]      = _readRetries;
                    364:                paramArray[IO_ReadErrors]       = _readErrors;
                    365:                paramArray[IO_Writes]           = _writeOps;
                    366:                paramArray[IO_BytesWritten]     = _bytesWritten;
                    367:                paramArray[IO_TotalWriteTime]   = _writeTotalTime;
                    368:                paramArray[IO_LatentWriteTime]  = _writeLatentTime;
                    369:                paramArray[IO_WriteRetries]     = _writeRetries;
                    370:                paramArray[IO_WriteErrors]      = _writeErrors;
                    371:                paramArray[IO_OtherRetries]     = _otherRetries;
                    372:                paramArray[IO_OtherErrors]      = _otherErrors;
                    373:                
                    374:                *count = 0;
                    375:                for(i=0; i<IO_DISK_STAT_ARRAY_SIZE; i++) {
                    376:                        if(*count == maxCount)
                    377:                                break;
                    378:                        parameterArray[i] = paramArray[i];
                    379:                        (*count)++;
                    380:                }
                    381:                return IO_R_SUCCESS;
                    382:        }
                    383:        else if(strcmp(parameterName, IO_IS_A_DISK) == 0) {
                    384:                /*
                    385:                 * No data; just let caller know we're a disk.
                    386:                 */
                    387:                *count = 0;
                    388:                return IO_R_SUCCESS;
                    389:        }
                    390:        else if(strcmp(parameterName, IO_IS_A_PHYSICAL_DISK) == 0) {
                    391:                *count = 1;
                    392:                parameterArray[0] = _isPhysical ? 1 : 0;
                    393:                return IO_R_SUCCESS;
                    394:        }
                    395:        else {
                    396:                return [super getIntValues : parameterArray
                    397:                        forParameter : parameterName
                    398:                        count : count];
                    399: 
                    400:        }
                    401: }                                      
                    402: 
                    403: /*
                    404:  * Register a connection with LogicalDisk. If we already have a logicalDisk,
                    405:  * this is a nop - subclasses might override this method to perform 
                    406:  * other operations in that case. 
                    407:  */
                    408: - (void) setLogicalDisk        : diskId
                    409: {
                    410:        xpr_disk("setLogicalDisk: log %s self %s oldLog %s\n",
                    411:                (diskId ? [diskId name] : "nil"), 
                    412:                [self name], 
                    413:                (_nextLogicalDisk ? [_nextLogicalDisk name] : "nil"), 4,5);
                    414: #ifdef GROK_APPLE //bknight - 12/16/97 - Radar #2004660
                    415:        /*
                    416:         * bknight - 12/16/97 - This was changed in order to prevent a
                    417:         * space leak caused by IODiskPartition instances for HFS partitions
                    418:         * on disks that get ejected.  It changes the meaning of this call
                    419:         * to be either "set to nil" or "insert at the end of the list beginning
                    420:         * at", depending on the value of the argument.  With this change,
                    421:         * it becomes important that the _nextLogicalDisk field gets set to nil
                    422:         * before being used - including being used by this routine, which means
                    423:         * that it must be set to nil before setting it to a non-nil value.
                    424:         * Otherwise, there would be a use-before-definition bug.  Anyhow, it
                    425:         * it appears that all the callers do a setLogicalDisk:nil before
                    426:         * ever calling it with a non-nil argument, so this is safe.
                    427:         * However, if new call sites are added, this needs to be taken into
                    428:         * account, or we need to split it into two methods each having the
                    429:         * appropriate functionality: either "set the field" or "add to the list".
                    430:         */
                    431:        if( ! diskId ) {
                    432:                _nextLogicalDisk = diskId;
                    433:        }
                    434:        else if ( ! _nextLogicalDisk && self != diskId ) {
                    435:                _nextLogicalDisk = diskId;
                    436:        }
                    437:        else if ( _nextLogicalDisk ) {
                    438:                [_nextLogicalDisk setLogicalDisk: diskId];
                    439:        }
                    440: #else GROK_APPLE //bknight - 12/16/97 - Radar #2004660
                    441:        if(_nextLogicalDisk == nil) {
                    442:                _nextLogicalDisk = diskId;
                    443:        }
                    444: #endif GROK_APPLE //bknight - 12/16/97 - Radar #2004660
                    445: 
                    446: }
                    447: 
                    448: /*
                    449:  * Invoked by instance of subclass upon completion of device initialization.
                    450:  * The code here is only executed for physical disks.
                    451:  *
                    452:  * All device parameters (_diskSize, _blockSize, _formatted, _removable,
                    453:  * _lastReadyState) must be valid at this time.
                    454:  */
                    455: #define ALWAYS_VCREGISTER      1
                    456: 
                    457: - registerDevice
                    458: {
                    459:        id targ = (id)self;     // IODisk doesn't implement updateReadyState
                    460:        id ret;
                    461:        
                    462:        xpr_disk("DiskObject registerDisk\n", 1,2,3,4,5);
                    463:        if(!_isPhysical) {
                    464:                 ret = [super registerDevice];
                    465:                goto out;
                    466:        }
                    467:        
                    468:        /*
                    469:         * Do some trivial initialization.
                    470:         */
                    471: 
                    472:        _nextLogicalDisk = nil;
                    473:        _LogicalDiskLock = [NXLock new];
                    474:                
                    475:        _readOps         = 0;
                    476:        _bytesRead       = 0;
                    477:        _readTotalTime   = 0;
                    478:        _readLatentTime  = 0;
                    479:        _readRetries     = 0;
                    480:        _readErrors      = 0;
                    481:        _writeOps        = 0;
                    482:        _bytesWritten    = 0;
                    483:        _writeTotalTime  = 0;
                    484:        _writeLatentTime = 0;
                    485:        _writeRetries    = 0;
                    486:        _writeErrors     = 0;
                    487:        _otherRetries    = 0;
                    488:        _otherErrors     = 0;
                    489:        
                    490:        /*
                    491:         * For physical disks, this will cause probes of IODiskPartition.
                    492:         */
                    493:        ret = [super registerDevice];
                    494: 
                    495:        /*
                    496:         * sign up for disk insertion/ready detect notification.
                    497:         */
                    498:        if(ret != nil && (
                    499:           ALWAYS_VCREGISTER || 
                    500:           _removable || 
                    501:           ([targ updateReadyState] != IO_Ready))) {
                    502:                /*
                    503:                 * Make sure the disk conforms to the appropriate protocol.
                    504:                 */
                    505:                if(! [[self class] 
                    506:                    conformsTo:@protocol(IOPhysicalDiskMethods)] ) {
                    507:                        IOLog("Warning: %s, class %s, does not conform to "
                    508:                                "IOPhysicalDiskMethods\n",
                    509:                                [self name], [[self class] name]);
                    510:                        goto out;       
                    511:                } 
                    512: #ifdef KERNEL
                    513:                volCheckRegister(self,
                    514:                        _devAndIdInfo->blockDev,
                    515:                        _devAndIdInfo->rawDev);
                    516: #else  KERNEL
                    517:                volCheckRegister(self, 0, 0);
                    518: #endif KERNEL
                    519:        }
                    520:        
                    521: out:
                    522:        return ret;
                    523: }
                    524: 
                    525: /*
                    526:  * Lock/Unlock device for LogicalDisk-specific methods. Invoked only by
                    527:  * LogicalDisks which are attached to this device.
                    528:  */
                    529: - (void)lockLogicalDisks
                    530: {
                    531:        [_LogicalDiskLock lock];
                    532: }
                    533: 
                    534: - (void)unlockLogicalDisks
                    535: {
                    536:        [_LogicalDiskLock unlock];
                    537: }
                    538: 
                    539: /*
                    540:  * Convert an IOReturn to text. Overrides superclass's method of same name
                    541:  * to allow for additional IOReturn's defined in IODisk.h.
                    542:  */
                    543: - (const char *)stringFromReturn       : (IOReturn)rtn
                    544: {
                    545:        IONamedValue *valArray = diskIoReturnValues;
                    546:        
                    547:        for( ; valArray->name; valArray++) {
                    548:                if(valArray->value == rtn)
                    549:                        return(valArray->name);
                    550:        }
                    551:        /* 
                    552:         * Not found here. Pass up to superclass.
                    553:         */
                    554:        return([super stringFromReturn:rtn]);
                    555: }
                    556: 
                    557: /*
                    558:  * Convert an IOReturn to an errno.
                    559:  */
                    560: - (int)errnoFromReturn : (IOReturn)rtn
                    561: {
                    562:        switch(rtn) {
                    563:            case IO_R_NO_DISK:
                    564:                return(ENXIO);
                    565:            case IO_R_NO_LABEL:
                    566:                return(ENXIO);          // ???
                    567:            case IO_R_UNFORMATTED:
                    568:                return(EINVAL);
                    569:            default:
                    570:                /* 
                    571:                 * Not found here. Pass up to superclass.
                    572:                 */
                    573:                return([super errnoFromReturn:rtn]);
                    574: 
                    575:        }
                    576: }
                    577: 
                    578: /* 
                    579:  * volCheck module support.
                    580:  */
                    581:  
                    582: static inline int 
                    583: diskTypeToPR(IODiskType diskType)
                    584: {
                    585:        switch(diskType) {
                    586:            case IO_SCSI:
                    587:                return PR_DRIVE_SCSI;
                    588:            case IO_Floppy:
                    589:                return PR_DRIVE_FLOPPY;
                    590:            case IO_Other:
                    591:                return -1;
                    592:        }
                    593: }
                    594: 
                    595: /*
                    596:  * Request a "please insert disk" panel.
                    597:  */
                    598: - (void)requestInsertionPanelForDiskType : (IODiskType)diskType
                    599: {
                    600:        volCheckRequest(self, diskTypeToPR(diskType));
                    601: }
                    602: 
                    603: /*
                    604:  * Notify volCheck thread that specified device is in a "disk ejecting" state.
                    605:  */
                    606: - (void)diskIsEjecting : (IODiskType)diskType
                    607: {
                    608:        volCheckEjecting(self, diskTypeToPR(diskType));
                    609: 
                    610: }
                    611: 
                    612: /*
                    613:  * Notify volCheck that disk has gone not ready. Typically called on
                    614:  * gross error detection.
                    615:  */
                    616: - (void)diskNotReady
                    617: {
                    618:        volCheckNotReady(self);
                    619: }
                    620: 
                    621: /*
                    622:  * To be optionally overridden by subclass. IODisk version returns NO.
                    623:  * If subclass's version returns NO, and the drive is a removable media 
                    624:  * drive, the drive will not be polled once per second while ready state
                    625:  * is IO_NotReady or IO_NoDisk; instead; polling will only occur when
                    626:  * an DKIOCCHECKINSERT ioctl is executed on the vol driver.
                    627:  */
                    628: - (BOOL)needsManualPolling
                    629: {
                    630:        return NO;
                    631: }      
                    632: 
                    633: - property_IODeviceType:(char *)types length:(unsigned int *)maxLen
                    634: {
                    635:     if( [self isRemovable])
                    636:         strcat( types, " "IOTypeRemovableDisk);
                    637:     if( [self isWriteProtected])
                    638:         strcat( types, " "IOTypeWriteProtectedDisk);
                    639: 
                    640:     return( self);
                    641: }
                    642: 
                    643: - property_IODeviceClass:(char *)classes length:(unsigned int *)maxLen
                    644: {
                    645:     strcpy( classes, IOClassBlock);
                    646:     if( [self isPhysical])
                    647:         strcat( classes, " "IOClassDisk);
                    648:     return( self);
                    649: }
                    650: 
                    651: @end

unix.superglobalmegacorp.com

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