/*
 * Copyright (c) 1998-2005 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#include <IOKit/IODeviceTreeSupport.h> // (gIODTPlane, ...)
#include <IOKit/IOLib.h>               // (IONew, ...)
#include <IOKit/IOMessage.h>           // (kIOMessageServicePropertyChange, ...)
#include <IOKit/storage/IOMedia.h>

#define super IOStorage
OSDefineMetaClassAndStructors(IOMedia, IOStorage)

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

enum
{
    kIOStorageAccessWriter       = 0x00000002,
    kIOStorageAccessInvalid      = 0x0000000C,
    kIOStorageAccessReservedMask = 0xFFFFFFF0
};

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

static UInt8 gIOMediaAccessTable[8][8] =
{            /* Rea, Wri, R|S, W|S, R|E, W|E, Inv, Non */
    /* Rea */ { 000, 001, 002, 003, 006, 006, 006, 000 },
    /* Wri */ { 011, 006, 011, 006, 006, 006, 006, 011 },
    /* R|S */ { 002, 001, 002, 003, 006, 006, 006, 002 },
    /* W|S */ { 003, 006, 003, 003, 006, 006, 006, 003 },
    /* R|E */ { 006, 006, 006, 006, 006, 006, 006, 004 },
    /* W|E */ { 006, 006, 006, 006, 006, 006, 006, 015 },
    /* Inv */ { 006, 006, 006, 006, 006, 006, 006, 006 },
    /* Inv */ { 006, 006, 006, 006, 006, 006, 006, 006 }
};

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

class IOMediaAccess
{
protected:

    IOStorageAccess _access;

public:

    inline void operator=( IOStorageAccess access )
    {
        _access = access;
    }

    inline void operator=( OSObject * access )
    {
        if ( access )
        {
            operator=( ( ( OSNumber * ) access )->unsigned32BitValue( ) );
        }
        else
        {
            operator=( kIOStorageAccessNone );
        }
    }

    inline void operator+=( IOStorageAccess access )
    {
        _access = ( ( _access - 1 ) >> 1 ) & 7;
        _access = gIOMediaAccessTable[ ( ( access - 1 ) >> 1 ) & 7 ][ _access ];
        _access = ( ( _access & 7 ) << 1 ) + 1;
    }

    inline void operator+=( OSObject * access )
    {
        if ( access )
        {
            operator+=( ( ( OSNumber * ) access )->unsigned32BitValue( ) );
        }
    }

    inline operator IOStorageAccess( )
    {
        return _access;
    }
};

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IOStorage * IOMedia::getProvider() const
{
    //
    // Obtain this object's provider.  We override the superclass's method to
    // return a more specific subclass of OSObject -- IOStorage.  This method
    // serves simply as a convenience to subclass developers.
    //

    return (IOStorage *) IOService::getProvider();
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

bool IOMedia::init(UInt64         base,
                   UInt64         size,
                   UInt64         preferredBlockSize,
                   bool           isEjectable,
                   bool           isWhole,
                   bool           isWritable,
                   const char *   contentHint,
                   OSDictionary * properties)
{
    //
    // Initialize this object's minimal state.
    //

    IOMediaAttributeMask attributes = 0;

    attributes |= isEjectable ? kIOMediaAttributeEjectableMask : 0;
    attributes |= isEjectable ? kIOMediaAttributeRemovableMask : 0;

    return init( /* base               */ base,
                 /* size               */ size,
                 /* preferredBlockSize */ preferredBlockSize,
                 /* attributes         */ attributes,
                 /* isWhole            */ isWhole,
                 /* isWritable         */ isWritable,
                 /* contentHint        */ contentHint,
                 /* properties         */ properties );
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void IOMedia::free(void)
{
    //
    // Free all of this object's outstanding resources.
    //

    if (_openClients)  _openClients->release();

    super::free();
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

bool IOMedia::attachToChild(IORegistryEntry *       client,
                            const IORegistryPlane * plane)
{
    //
    // This method is called for each client interested in the services we
    // provide.  The superclass links us as a parent to this client in the
    // I/O Kit registry on success.
    //

    OSString * s;

    // Ask our superclass' opinion.

    if (super::attachToChild(client, plane) == false)  return false;

    //
    // Determine whether the client is a storage driver, which we consider
    // to be a consumer of this storage object's content and a producer of
    // new content. A storage driver need not be an IOStorage subclass, so
    // long as it identifies itself with a match category of "IOStorage".
    //
    // If the client is indeed a storage driver, we reset the media's Leaf
    // property to false and replace the media's Content property with the
    // client's Content Mask property, if any.
    //

    s = OSDynamicCast(OSString, client->getProperty(gIOMatchCategoryKey));
 
    if (s && !strcmp(s->getCStringNoCopy(), kIOStorageCategory))
    {
        setProperty(kIOMediaLeafKey, false);

        s = OSDynamicCast(OSString,client->getProperty(kIOMediaContentMaskKey));
        if (s)  setProperty(kIOMediaContentKey, s->getCStringNoCopy());
    }

    return true;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void IOMedia::detachFromChild(IORegistryEntry *       client,
                              const IORegistryPlane * plane)
{
    //
    // This method is called for each client that loses interest in the
    // services we provide.  The superclass unlinks us from this client
    // in the I/O Kit registry on success.
    //
    // Note that this method is called at a nondeterministic time after
    // our client is terminated, which means another client may already
    // have arrived and attached in the meantime.  This is not an issue
    // should the termination be issued synchrnously, however, which we
    // take advantage of when this media needs to  eliminate one of its
    // clients.  If the termination was issued on this media or farther
    // below in the hierarchy, we don't really care that the properties
    // would not  be consistent since this media object is going to die
    // anyway.
    //

    OSString * s;

    //
    // Determine whether the client is a storage driver, which we consider
    // to be a consumer of this storage object's content and a producer of
    // new content. A storage driver need not be an IOStorage subclass, so
    // long as it identifies itself with a match category of "IOStorage".
    //
    // If the client is indeed a storage driver, we reset the media's Leaf
    // property to true and reset the media's Content property to the hint
    // we obtained when this media was initialized.
    //

    s = OSDynamicCast(OSString, client->getProperty(gIOMatchCategoryKey));
 
    if (s && !strcmp(s->getCStringNoCopy(), kIOStorageCategory))
    {
        setProperty(kIOMediaContentKey, getContentHint());
        setProperty(kIOMediaLeafKey, true);
    }

    // Pass the call onto our superclass.

    super::detachFromChild(client, plane);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

bool IOMedia::handleOpen(IOService *  client,
                         IOOptionBits options,
                         void *       argument)
{
    //
    // The handleOpen method grants or denies permission to access this object
    // to an interested client.  The argument is an IOStorageAccess value that
    // specifies the level of access desired -- reader or reader-writer.
    //
    // This method can be invoked to upgrade or downgrade the access level for
    // an existing client as well.  The previous access level will prevail for
    // upgrades that fail, of course.   A downgrade should never fail.  If the
    // new access level should be the same as the old for a given client, this
    // method will do nothing and return success.  In all cases, one, singular
    // close-per-client is expected for all opens-per-client received.
    //
    // This method will work even when the media is in the terminated state.
    //
    // We are guaranteed that no other opens or closes will be processed until
    // we make our decision, change our state, and return from this method.
    //

    IOMediaAccess   access;
    IOStorageAccess accessIn;
    IOService *     driver;
    IOMediaAccess   level;
    OSObject *      object;
    OSIterator *    objects;
    bool            rebuild;
    bool            teardown;

    //
    // State our assumptions.
    //

    assert( client );

    //
    // Initialize our minimal state.
    //

    access   = _openClients->getObject( ( OSSymbol * ) client );
    accessIn = ( IOStorageAccess ) argument;

    rebuild  = false;
    teardown = false;

    //
    // Determine whether one of our clients is a storage driver.
    //

    object = ( OSObject * ) OSSymbol::withCString( kIOStorageCategory );

    if ( object == 0 )
    {
        return false;
    }

    driver = getClientWithCategory( ( OSSymbol * ) object );

    object->release( );

    //
    // Reevaluate the open we have on the level below us.
    //

    objects = OSCollectionIterator::withCollection( _openClients );

    if ( objects == 0 )
    {
        return false;
    }

    level = kIOStorageAccessNone;

    while ( ( object = objects->getNextObject( ) ) )
    {
        if ( object != client )
        {
            level += _openClients->getObject( ( OSSymbol * ) object );
        }
    }

    objects->release( );

    //
    // Evaluate the open we have from the level above us.
    //

    level += accessIn;

    if ( level == kIOStorageAccessInvalid )
    {
        return false;
    }

    if ( ( accessIn & kIOStorageAccessWriter ) )
    {
        if ( _isWritable == false )
        {
            return false;
        }

        if ( ( accessIn & kIOStorageAccessSharedLock ) == false )
        {
            if ( driver )
            {
                if ( driver != client )
                {
                    teardown = true;
                }
            }
        }
    }
    else
    {
        if ( ( access & kIOStorageAccessWriter ) )
        {
            rebuild = true;
        }
    }

    //
    // If we are in the terminated state, we only accept downgrades.
    //

    if ( isInactive( ) )
    {
        if ( access == kIOStorageAccessNone )
        {
            return false;
        }

        if ( ( accessIn & kIOStorageAccessWriter ) )
        {
            return false;
        }
    }

    //
    // Determine whether the storage driver above us can be torn down, if
    // this is a new exclusive writer open, or an upgrade to an exclusive
    // writer open (and if the client issuing the open is not the storage
    // driver itself).
    //

    if ( teardown )
    {
        if ( _openClients->getObject( ( OSSymbol * ) driver ) )
        {
            return false;
        }

        if ( driver->terminate( kIOServiceSynchronous ) == false )
        {
            return false;
        }
    }

    //
    // Determine whether the storage object below us accepts the open at this
    // multiplexed level of access -- new opens, upgrades and downgrades (and
    // no changes in access) all enter through the same method.
    //

    level = ( level & kIOStorageAccessReaderWriter );

    if ( _openLevel != level )
    {
        IOStorage * provider;

        provider = OSDynamicCast( IOStorage, getProvider( ) );

        if ( provider )
        {
            bool success;

            success = provider->open( this, options, level );

            if ( success == false )
            {
                //
                // We were unable to open the storage object below us.  We
                // must recover from the terminate we invoked above before
                // bailing out, if applicable, by re-registering the media
                // object for matching.
                //

                if ( teardown )
                {
                    registerService( kIOServiceAsynchronous );
                }

                return false;
            }
        }
    }

    //
    // Process the open.
    //

    object = OSNumber::withNumber( level, 32 );

    assert( object );

    _openClients->setObject( ( OSSymbol * ) client, object );

    _openLevel = level;

    object->release( );

    //
    // If a writer just closed, re-register the media so that I/O Kit will
    // attempt to match storage drivers that may now be interested in this
    // media.
    //

    if ( rebuild )
    {
        if ( isInactive( ) == false )
        {
            if ( driver )
            {
                if ( driver != client )
                {
                    driver->requestProbe( 0 );
                }
            }
            else
            {
                registerService( kIOServiceAsynchronous );
            }
        }
    }

    return true;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

bool IOMedia::handleIsOpen(const IOService * client) const
{
    //
    // The handleIsOpen method determines whether the specified client, or any
    // client if none is specificed, presently has an open on this object.
    //
    // This method will work even when the media is in the terminated state.
    //
    // We are guaranteed that no other opens or closes will be processed until
    // we return from this method.
    //

    if ( client )
    {
        return _openClients->getObject( ( OSSymbol * ) client ) ? true : false;
    }
    else
    {
        return _openClients->getCount( ) ? true : false;
    }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void IOMedia::handleClose(IOService * client, IOOptionBits options)
{
    //
    // A client is informing us that it is giving up access to our contents.
    //
    // This method will work even when the media is in the terminated state.
    //
    // We are guaranteed that no other opens or closes will be processed until
    // we change our state and return from this method.
    //

    IOMediaAccess access;
    IOService *   driver;
    IOMediaAccess level;
    OSObject *    object;
    OSIterator *  objects;

    //
    // State our assumptions.
    //

    assert( client );

    assert( _openClients->getObject( ( OSSymbol * ) client ) );

    //
    // Initialize our minimal state.
    //

    access = _openClients->getObject( ( OSSymbol * ) client );

    //
    // Determine whether one of our clients is a storage driver.
    //

    object = ( OSObject * ) OSSymbol::withCString( kIOStorageCategory );

    if ( object == 0 )
    {
        return;
    }

    driver = getClientWithCategory( ( OSSymbol * ) object );

    object->release( );

    //
    // Reevaluate the open we have on the level below us.
    //

    objects = OSCollectionIterator::withCollection( _openClients );

    if ( objects == 0 )
    {
        return;
    }

    level = kIOStorageAccessNone;

    while ( ( object = objects->getNextObject( ) ) )
    {
        if ( object != client )
        {
            level += _openClients->getObject( ( OSSymbol * ) object );
        }
    }

    objects->release( );

    //
    // If no opens remain, we close, or if no writers remain, but readers do,
    // we downgrade.
    //

    level = ( level & kIOStorageAccessReaderWriter );

    if ( _openLevel != level )
    {
        IOStorage * provider;

        provider = OSDynamicCast( IOStorage, getProvider( ) );

        if ( provider )
        {
            if ( level == kIOStorageAccessNone )
            {
                provider->close( this, options );
            }
            else
            {
                bool success;

                success = provider->open( this, 0, level );

                assert( success );
            }
        }
    }

    //
    // Process the close.
    //

    _openClients->removeObject( ( OSSymbol * ) client );

    _openLevel = level;

    //
    // If a writer just closed, re-register the media so that I/O Kit will
    // attempt to match storage drivers that may now be interested in this
    // media.
    //

    if ( ( access & kIOStorageAccessWriter ) )
    {
        if ( isInactive( ) == false )
        {
            if ( driver )
            {
                if ( driver != client )
                {
                    driver->requestProbe( 0 );
                }
            }
            else
            {
                registerService( kIOServiceAsynchronous );
            }
        }
    }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void IOMedia::read(IOService *          /* client */,
                   UInt64               byteStart,
                   IOMemoryDescriptor * buffer,
                   IOStorageCompletion  completion)
{
    //
    // Read data from the storage object at the specified byte offset into the
    // specified buffer, asynchronously.   When the read completes, the caller
    // will be notified via the specified completion action.
    //
    // The buffer will be retained for the duration of the read.
    //
    // This method will work even when the media is in the terminated state.
    //

    if (isInactive())
    {
        complete(completion, kIOReturnNoMedia);
        return;
    }

    if (_openLevel == kIOStorageAccessNone)    // (instantaneous value, no lock)
    {
        complete(completion, kIOReturnNotOpen);
        return;
    }

    if (_mediaSize == 0 || _preferredBlockSize == 0)
    {
        complete(completion, kIOReturnUnformattedMedia);
        return;
    }

    if (buffer == 0)
    {
        complete(completion, kIOReturnBadArgument);
        return;
    }

    if (_mediaSize < byteStart + buffer->getLength())
    {
        complete(completion, kIOReturnBadArgument);
        return;
    }

    byteStart += _mediaBase;
    getProvider()->read(this, byteStart, buffer, completion);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void IOMedia::write(IOService *          client,
                    UInt64               byteStart,
                    IOMemoryDescriptor * buffer,
                    IOStorageCompletion  completion)
{
    //
    // Write data into the storage object at the specified byte offset from the
    // specified buffer, asynchronously.   When the write completes, the caller
    // will be notified via the specified completion action.
    //
    // The buffer will be retained for the duration of the write.
    //
    // This method will work even when the media is in the terminated state.
    //

    if (isInactive())
    {
        complete(completion, kIOReturnNoMedia);
        return;
    }

    if (_openLevel == kIOStorageAccessNone)    // (instantaneous value, no lock)
    {
        complete(completion, kIOReturnNotOpen);
        return;
    }

    if (_openLevel == kIOStorageAccessReader)  // (instantaneous value, no lock)
    {
///m:2425148:workaround:commented:start
//        complete(completion, kIOReturnNotPrivileged);
//        return;
///m:2425148:workaround:commented:stop
    }

    if (_isWritable == 0)
    {
        complete(completion, kIOReturnLockedWrite);
        return;
    }

    if (_mediaSize == 0 || _preferredBlockSize == 0)
    {
        complete(completion, kIOReturnUnformattedMedia);
        return;
    }

    if (buffer == 0)
    {
        complete(completion, kIOReturnBadArgument);
        return;
    }

    if (_mediaSize < byteStart + buffer->getLength())
    {
        complete(completion, kIOReturnBadArgument);
        return;
    }

    byteStart += _mediaBase;
    getProvider()->write(this, byteStart, buffer, completion);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IOReturn IOMedia::synchronizeCache(IOService * client)
{
    //
    // Flush the cached data in the storage object, if any, synchronously.
    //

    if (isInactive())
    {
        return kIOReturnNoMedia;
    }

    if (_openLevel == kIOStorageAccessNone)    // (instantaneous value, no lock)
    {
        return kIOReturnNotOpen;
    }

    if (_openLevel == kIOStorageAccessReader)  // (instantaneous value, no lock)
    {
///m:2425148:workaround:commented:start
//        return kIOReturnNotPrivileged;
///m:2425148:workaround:commented:stop
    }

    if (_isWritable == 0)
    {
        return kIOReturnLockedWrite;
    }

    if (_mediaSize == 0 || _preferredBlockSize == 0)
    {
        return kIOReturnUnformattedMedia;
    }

    return getProvider()->synchronizeCache(this);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

UInt64 IOMedia::getPreferredBlockSize() const
{
    //
    // Ask the media object for its natural block size.  This information
    // is useful to clients that want to optimize access to the media.
    //

    return _preferredBlockSize;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

UInt64 IOMedia::getSize() const
{
    //
    // Ask the media object for its total length in bytes.
    //

    return _mediaSize;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

UInt64 IOMedia::getBase() const
{
    //
    // Ask the media object for its byte offset relative to its provider media
    // object below it in the storage hierarchy.
    //

    return _mediaBase;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

bool IOMedia::isEjectable() const
{
    //
    // Ask the media object whether it is ejectable.
    //

    return (_attributes & kIOMediaAttributeEjectableMask) ? true : false;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

bool IOMedia::isFormatted() const
{
    //
    // Ask the media object whether it is formatted.
    //

    return (_mediaSize && _preferredBlockSize);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

bool IOMedia::isWritable() const
{
    //
    // Ask the media object whether it is writable.
    //

    return _isWritable;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

bool IOMedia::isWhole() const
{
    //
    // Ask the media object whether it represents the whole disk.
    //

    return _isWhole;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

const char * IOMedia::getContent() const
{
    //
    // Ask the media object for a description of its contents.  The description
    // is the same as the hint at the time of the object's creation,  but it is
    // possible that the description be overrided by a client (which has probed
    // the media and identified the content correctly) of the media object.  It
    // is more accurate than the hint for this reason.  The string is formed in
    // the likeness of Apple's "Apple_HFS" strings or in the likeness of a UUID.
    //
    // The content description can be overrided by any client that matches onto
    // this media object with a match category of kIOStorageCategory.  The media
    // object checks for a kIOMediaContentMaskKey property in the client, and if
    // it finds one, it copies it into kIOMediaContentKey property.
    //

    OSString * string;

    string = OSDynamicCast(OSString, getProperty(kIOMediaContentKey));
    if (string == 0)  return "";
    return string->getCStringNoCopy();
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

const char * IOMedia::getContentHint() const
{
    //
    // Ask the media object for a hint of its contents.  The hint is set at the
    // time of the object's creation, should the creator have a clue as to what
    // it may contain.  The hint string does not change for the lifetime of the
    // object and is also formed in the likeness of Apple's "Apple_HFS" strings
    // or in the likeness of a UUID.
    //

    OSString * string;

    string = OSDynamicCast(OSString, getProperty(kIOMediaContentHintKey));
    if (string == 0)  return "";
    return string->getCStringNoCopy();
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

bool IOMedia::matchPropertyTable(OSDictionary * table, SInt32 * score)
{
    //
    // Compare the properties in the supplied table to this object's properties.
    //

    OSCollectionIterator * properties;
    bool                   success = true;

    // Ask our superclass' opinion.

    if (super::matchPropertyTable(table, score) == false)  return false;

    // Compare properties.

    properties = OSCollectionIterator::withCollection(table);

    if (properties)
    {
        OSSymbol * property;

        while ((property = (OSSymbol *) properties->getNextObject()))
        {
///m:2922205:workaround:added:start
            extern const OSSymbol * gIOMatchCategoryKey;
            extern const OSSymbol * gIOProbeScoreKey;

            if ( property->isEqualTo(gIOMatchCategoryKey) ||
                 property->isEqualTo(gIOProbeScoreKey   ) )
            {
                continue;
            }
///m:2922205:workaround:added:stop
            OSObject * valueFromMedia = copyProperty(property);
            OSObject * valueFromTable = table->getObject(property);

            if (valueFromMedia)
            {
                success = valueFromMedia->isEqualTo(valueFromTable);

                valueFromMedia->release();

                if (success == false)  break;
            }
        }

        properties->release();
    }

    return success;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

bool IOMedia::init(UInt64               base,
                   UInt64               size,
                   UInt64               preferredBlockSize,
                   IOMediaAttributeMask attributes,
                   bool                 isWhole,
                   bool                 isWritable,
                   const char *         contentHint,
                   OSDictionary *       properties)
{
    //
    // Initialize this object's minimal state.
    //

    bool isEjectable;
    bool isRemovable;

    // Ask our superclass' opinion.

    if (_openClients == 0)
    {
        if (super::init(properties) == false)  return false;
    }

    // Initialize our state.

    isEjectable = (attributes & kIOMediaAttributeEjectableMask) ? true : false;
    isRemovable = (attributes & kIOMediaAttributeRemovableMask) ? true : false;

    if (isEjectable)
    {
        attributes |= kIOMediaAttributeRemovableMask;
        isRemovable = true;
    }

    _attributes         = attributes;
    _mediaBase          = base;
    _isWhole            = isWhole;
    _isWritable         = isWritable;
    _preferredBlockSize = preferredBlockSize;

///m:3879984:workaround:added:start
    if (size > _mediaSize)
    {
        *((volatile UInt64 *) &_mediaSize) = (size & (UINT64_MAX ^ UINT32_MAX)) | (_mediaSize & UINT32_MAX);
    }
    else
    {
        *((volatile UInt64 *) &_mediaSize) = (size & UINT32_MAX) | (_mediaSize & (UINT64_MAX ^ UINT32_MAX));
    }
///m:3879984:workaround:added:stop
    *((volatile UInt64 *) &_mediaSize) = size;

    if (_openClients == 0)
    {
        _openClients = OSDictionary::withCapacity(2);
        _openLevel   = kIOStorageAccessNone;
    }

    if (_openClients == 0)  return false;

    // Create our registry properties.

    setProperty(kIOMediaContentKey,            contentHint ? contentHint : "");
    setProperty(kIOMediaContentHintKey,        contentHint ? contentHint : "");
    setProperty(kIOMediaEjectableKey,          isEjectable);
    setProperty(kIOMediaLeafKey,               true);
    setProperty(kIOMediaPreferredBlockSizeKey, preferredBlockSize, 64);
    setProperty(kIOMediaRemovableKey,          isRemovable);
    setProperty(kIOMediaSizeKey,               size, 64);
    setProperty(kIOMediaWholeKey,              isWhole);
    setProperty(kIOMediaWritableKey,           isWritable);

    messageClients(kIOMessageServicePropertyChange);

    return true;
}

OSMetaClassDefineReservedUsed(IOMedia, 0);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IOMediaAttributeMask IOMedia::getAttributes() const
{
    //
    // Ask the media object for its attributes.
    //

    return _attributes;
}

OSMetaClassDefineReservedUsed(IOMedia, 1);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 2);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 3);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 4);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 5);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 6);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 7);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 8);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 9);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 10);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 11);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 12);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 13);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 14);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OSMetaClassDefineReservedUnused(IOMedia, 15);
