bool DldIODataQueue::waitForData()
{
    
    assert( preemption_enabled() );
    
    bool wait;
    
    IOLockLock( this->lock );
    {// start of the lock
        
        //
        // wait if the queue is empty
        //
        wait = ( this->dataQueue->head == this->dataQueue->tail );

        if( wait && THREAD_WAITING != assert_wait( this->dataQueue, THREAD_UNINT ) )
            wait = false;
        
    }// end of the lock
    IOLockUnlock( this->lock );
    
    if( wait )
        thread_block( THREAD_CONTINUE_NULL );
    
    return true;
}
void
DldIOShadowFile::UnLockShared()
{   assert( this->rwLock );
    assert( preemption_enabled() );
    
    IORWLockUnlock( this->rwLock );
};
bool DldIODataQueue::initWithCapacity( __in UInt32 size )
{
    assert( 0x0 != size );
    assert( preemption_enabled() );
    
    if( !super::init() ){
        return false;
    }
    
    this->lock = IOLockAlloc();
    assert( this->lock );
    if( !this->lock )
        return false;
    
    this->dataQueue = (DldIODataQueueMemory*)IOMallocAligned(round_page(size + DLD_DATA_QUEUE_MEMORY_HEADER_SIZE), PAGE_SIZE);
    assert( this->dataQueue );
    if( !this->dataQueue ){
        
        IOLockFree( this->lock );
        return false;
    }
    
    bzero( this->dataQueue, sizeof( *this->dataQueue ) );
    this->dataQueue->queueSize = size;
    
    return true;
}
DldIOShadowFile* DldIOShadowFile::withFileName( __in char* name, __in size_t nameLength )
{
    assert( preemption_enabled() );
    
    //
    // check for the correct file format, the name must be a full path
    //
    if( 0x0 == nameLength || L'\0' != name[ nameLength - 0x1 ] || L'/' != name[0] ){
        
        DBG_PRINT_ERROR(("an invalid file name format\n"));
        return NULL;
    }
    
    
    DldIOShadowFile*  newFile = new DldIOShadowFile();
    assert( newFile );
    if( !newFile )
        return NULL;
    
    
    if( !newFile->initWithFileName( name, nameLength ) ){
        
        newFile->release();
        return NULL;
    }
    
    return newFile;
}
IOReturn  DldIPCUserClient::waitForCompletion( __in unsigned int waitBlockIndex )
{
    IOReturn       error = kIOReturnTimeout;
    wait_result_t  wait = THREAD_AWAKENED;
    
    assert( preemption_enabled() );
    assert( 0x0 != waitBlockIndex && waitBlockIndex < DLD_STATIC_ARRAY_SIZE(DldIPCUserClient::WaitBlocks) );
    
    //
    // wait for a response from the service
    //
    IOSimpleLockLock( DldIPCUserClient::WaitLock );
    { // start of the locked region
        
        if( !DldIPCUserClient::WaitBlocks[waitBlockIndex].completed ){
            //
            // wait for response with 2 minutes timeout
            //
            wait = assert_wait_timeout((event_t)&DldIPCUserClient::WaitBlocks[waitBlockIndex],
                                       THREAD_UNINT,
                                       120000, /*120secs*/
                                       1000*NSEC_PER_USEC);
        }
    } // end of the locked region
    IOSimpleLockUnlock( DldIPCUserClient::WaitLock );
    
    if( THREAD_WAITING == wait ){
        
        wait = thread_block( THREAD_CONTINUE_NULL );
    }
    
    assert( 0x0 != DldIPCUserClient::WaitBlocks[waitBlockIndex].inUse );
    
    //
    // if the service has not responded then this is an error as the service is probably dead
    //
    if( THREAD_TIMED_OUT == wait ){
        
        error = kIOReturnTimeout;
        
    } else {
        
        assert( DldIPCUserClient::WaitBlocks[waitBlockIndex].completed );
        error = DldIPCUserClient::WaitBlocks[waitBlockIndex].operationCompletionStatus;
    }
    
    //
    // release the block
    //
    this->releaseWaitBlock( waitBlockIndex );
    
    return error;
}
void
DldIOShadowFile::UnLockExclusive()
{
    assert( this->rwLock );
    assert( preemption_enabled() );
    
#if defined(DBG)
    assert( current_thread() == this->exclusiveThread );
    this->exclusiveThread = NULL;
#endif//DBG
    
    IORWLockUnlock( this->rwLock );
};
void DldIODataQueue::free()
{
    assert( preemption_enabled() );
    
    if( this->dataQueue )
        IOFreeAligned( this->dataQueue, round_page(dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE) );
        
    if( this->lock )
        IOLockFree( this->lock );
    
    super::free();
    
    return;
}
void
DldIOShadowFile::LockExclusive()
{
    assert( this->rwLock );
    assert( preemption_enabled() );
    
#if defined(DBG)
    assert( current_thread() != this->exclusiveThread );
#endif//DBG
    
    IORWLockWrite( this->rwLock );
    
#if defined(DBG)
    assert( NULL == this->exclusiveThread );
    this->exclusiveThread = current_thread();
#endif//DBG
    
};
DldMacPolicy*  DldMacPolicy::createPolicyObject( __in struct mac_policy_conf *mpc, __in void* xd )
{
    assert( preemption_enabled() );
    
    DldMacPolicy* newObject = new DldMacPolicy();
    assert( newObject );
    if( !newObject ){
        DBG_PRINT_ERROR(("new DldMacPolicy() failed\n"));
        return NULL;
    }
    
    if( !newObject->initWithPolicyConf( mpc, xd ) ){
        
        DBG_PRINT_ERROR(("newObject->initWithPolicyConf( mpc, xd ) failed\n"));
        newObject->release();
        return NULL;
    }
    
    return newObject;
}
Exemplo n.º 10
0
kern_return_t DldMacPolicy::unRegisterMacPolicy()
{
    kern_return_t  err = ENOENT;
    
    assert( preemption_enabled() );
    
    if( this->registered ){
        
        err = mac_policy_unregister( this->handlep );
        this->registered = !( KERN_SUCCESS == err );
        
        assert( KERN_SUCCESS == err );
        if( KERN_SUCCESS != err ){
            
            DBG_PRINT_ERROR(("unRegisterMacPolicy failed with err = %d\n", err));
        }
    }
    
    return err;
}
Exemplo n.º 11
0
/* Handles timer interrupt.
 * By default, the timer fires at 18.222hz
 */
void timer_handler(struct regs *r)
{
    (void)r;    /* prevent 'unused' parameter warning */
    g_num_ticks++;

    if (get_current_thread() && get_current_thread()->id == 5) {
        DEBUGF("%s\n", "timer_handler in user!");
    }

    /* if the current thread has outlived the quantum, add it to the
     * run queue and schedule a new thread */
    thread_t* current = get_current_thread();
    if (current) {
        if (++current->num_ticks > THREAD_QUANTUM && preemption_enabled()) {
            /* DEBUGF("preempting thread %d\n", current->id); */
            make_runnable(current);
            g_need_reschedule = true;
        }
    }
}
DldIODataQueue* DldIODataQueue::withCapacity( __in UInt32 size )
{
    DldIODataQueue *dataQueue = new DldIODataQueue();
    
    assert( preemption_enabled() );
    
    if( dataQueue ){
        
        if( !dataQueue->initWithCapacity( size ) ){
            
            assert( !"dataQueue->initWithCapacity(size) failed" );
            
            dataQueue->release();
            dataQueue = 0;
        }
        
    }
    
    return dataQueue;
}
Exemplo n.º 13
0
kern_return_t DldMacPolicy::registerMacPolicy()
{
    kern_return_t  err;
    
    assert( preemption_enabled() );
    assert( !this->registered );
    
    if( this->registered )
        return KERN_SUCCESS;
    
    err = mac_policy_register( this->mpc, &this->handlep, this->xd );
    this->registered = ( KERN_SUCCESS == err );
    
    assert( KERN_SUCCESS == err );
    if( KERN_SUCCESS != err ){
        
        DBG_PRINT_ERROR(("registerMacPolicy failed with err = %d\n", err));
    }
    
    return err;
}
void DldIOShadowFile::free()
{
    assert( preemption_enabled() );
    
    if( NULLVP != this->vnode ){
        
        //
        // write a terminator
        //
        UInt64  terminator = DLD_SHADOW_FILE_TERMINATOR;
        this->write( &terminator, sizeof( terminator ), DLD_IGNR_FSIZE );
     
        //
        // TO DO - ponder the vfs context retrieval as it seems vfs_context_current might be 
        // an arbitrary one which differs from the open context
        //
        vfs_context_t   vfs_context;
        vfs_context = vfs_context_create( NULL );
        assert( vfs_context );
        if( vfs_context ){
        
            VNOP_FSYNC( this->vnode, MNT_WAIT, vfs_context );
            vnode_close( this->vnode, ( this->bytesWritten )? FWASWRITTEN: 0x0, vfs_context );
            vfs_context_rele( vfs_context );
            
        }// end if( vfs_context )
    }
    
    if( this->path ){
        
        assert( this->pathLength );
        IOFree( this->path, this->pathLength );
    }
    
    if( this->rwLock )
        IORWLockFree( this->rwLock );
    
}
IOReturn DldIOShadowFile::write( __in void* data, __in off_t length, __in_opt off_t offsetForShadowFile )
{
    /*!
     @function vn_rdwr
     @abstract Read from or write to a file.
     @discussion vn_rdwr() abstracts the details of constructing a uio and picking a vnode operation to allow
     simple in-kernel file I/O.
     @param rw UIO_READ for a read, UIO_WRITE for a write.
     @param vp The vnode on which to perform I/O.
     @param base Start of buffer into which to read or from which to write data.
     @param len Length of buffer.
     @param offset Offset within the file at which to start I/O.
     @param segflg What kind of address "base" is.   See uio_seg definition in sys/uio.h.  UIO_SYSSPACE for kernelspace, UIO_USERSPACE for userspace.
     UIO_USERSPACE32 and UIO_USERSPACE64 are in general preferred, but vn_rdwr will make sure that has the correct address sizes.
     @param ioflg Defined in vnode.h, e.g. IO_NOAUTH, IO_NOCACHE.
     @param cred Credential to pass down to filesystem for authentication.
     @param aresid Destination for amount of requested I/O which was not completed, as with uio_resid().
     @param p Process requesting I/O.
     @return 0 for success; errors from filesystem, and EIO if did not perform all requested I/O and the "aresid" parameter is NULL.
     */
    
    assert( preemption_enabled() );
    assert( length < 0x0FFFFFFF00000000ll );
    
    off_t  offset = offsetForShadowFile;
    
    if( DLD_IGNR_FSIZE == offsetForShadowFile && !this->reserveOffset( length, &offset ) ){
        
        DBG_PRINT_ERROR(("A quota has been exceeded for the %s file, the quota is %llu \n", this->path, this->maxSize ));
        return kIOReturnNoResources;
    }
    
#if defined( DBG )
    static UInt32 gWriteCount = 0x0;
    UInt32 writeCount = OSIncrementAtomic( &gWriteCount ) + 0x1;
#endif//DBG
    
    int   error = 0x0;
    off_t bytesWritten = 0x0;
    
    while( !error && length != bytesWritten ){
        
        unsigned int bytesToWrite;
        
#if defined( DBG )
        //
        // write by 32МВ chunks for each 2nd function invocation, and 1GB for others
        //
        if( (length - bytesWritten) > ((writeCount%2)? 0x40000000ll : 0x2000000ll) )
            bytesToWrite = (writeCount%2)? 0x40000000ll : 0x2000000ll;
        else
            bytesToWrite = (int)(length - bytesWritten);
#else
        //
        // write by 1GB chunks
        //
        if( (length - bytesWritten) > 0x40000000ll )
            bytesToWrite = 0x40000000;
        else
            bytesToWrite = (int)(length - bytesWritten);
#endif//!DBG
        
        
        error = vn_rdwr( UIO_WRITE,
                         this->vnode,
                         (char*)data,
                         bytesToWrite,
                         offset + bytesWritten,
                         UIO_SYSSPACE,
                         IO_NOAUTH | IO_SYNC,//IO_UNIT,
                         kauth_cred_get(),
                         NULL,
                         current_proc() );
        
        if( !error )
            bytesWritten = bytesWritten + bytesToWrite;
        else {
            DBG_PRINT_ERROR(("vn_rdwr( %s, %u ) failed with the 0x%X error\n", this->path, bytesToWrite, error));
        }

        
    }
    
    return error;
}
bool DldIOShadowFile::reserveOffset( __in off_t sizeToWrite, __out off_t*  reservedOffset_t )
{
    
    assert( preemption_enabled() );
    
    //
    // Make sure that we are root.  Growing a file
    // without zero filling the data is a security hole 
    // root would have access anyway so we'll allow it
    //
/*
#if defined(__i386__)
    assert( is_suser() );
#elif defined(__x86_64__)
#endif//defined(__x86_64__)
 */
    
    off_t  newOffset;
    off_t  reservedOffset;
    off_t  currentLastExpansion;
    bool   quotaExceeded = false;
    bool   expansionRequired = false;
    
    if( 0x0 == sizeToWrite ){
        
        this->LockShared();
        {// start of the lock
            *reservedOffset_t = this->offset;
        }// end of the lock
        this->UnLockShared();
        
        return true;
    }
    
    this->LockExclusive();
    {// start of the lock
        
        currentLastExpansion = this->lastExpansion;
        reservedOffset = this->offset;
        
        newOffset = this->offset + sizeToWrite;
        
        //
        // check for quota
        //
        if( DLD_IGNR_FSIZE != this->maxSize && newOffset > this->maxSize ){
            
            quotaExceeded = true;
        }
        
        if( !quotaExceeded ){
            
            this->offset = newOffset;
            
            expansionRequired =  this->offset > this->lastExpansion ||  // we crossed the border and the next write will extend the file OR
               ( this->lastExpansion - this->offset ) < 0x1000*0x1000;  // we are on the brink of the free space depletion
            
        }// end if( !quotaExceeded )

    }// end of the lock
    this->UnLockExclusive();
    
    
    if( quotaExceeded ){
        
        //
        // remeber that we failed at least once to shadow data due to quota exceeding
        //
        gShadow->quotaExceedingDetected();
        
        return false;
    }
    
    
    *reservedOffset_t = reservedOffset;
    
    //
    // extend the file,
    // only one expansion at any given time is allowed,
    // access fields w/o lock as concurrency is tolerable here
    //
    if( expansionRequired ){

        currentLastExpansion = this->expandFile( reservedOffset + sizeToWrite );
    }
    
    //
    // return true if there is a place for the following write
    //
    return ( reservedOffset + sizeToWrite ) <= currentLastExpansion;
}
off_t DldIOShadowFile::expandFile( __in off_t lowTargetSize )
{
    assert( preemption_enabled() );
    
    off_t             target;
    off_t             currentLastExpansion;
#if defined( DBG )
    currentLastExpansion = DLD_IGNR_FSIZE;
#endif//DBG
    
    //
    // Make sure that we are root.  Growing a file
    // without zero filling the data is a security hole 
    // root would have access anyway so we'll allow it
    //
    
    //
    // TO DO - is_suser is not supported for 64 bit kext
    //

/*
#if defined(__i386__)
    assert( is_suser() );
    if( !is_suser() )
        return this->lastExpansion;// an unsafe unprotected access to 64b value
#elif defined(__x86_64__)
#endif//defined(__x86_64__)
*/

    
    //
    // check that the limit has not been reached
    //
    if( DLD_IGNR_FSIZE != this->maxSize  && this->lastExpansion == this->maxSize )
        return this->maxSize;// a safe access to 64b value as the value can't change
    
    
    this->LockExclusive();
    {// start of the lock
        
        off_t   valueToAdd;
        
        currentLastExpansion = this->lastExpansion;
        
        //
        // try to extend the file on 128 MB
        // ( the same value is used as a maximum value for a mapping
        //   range in DldIOShadow::processFileWriteWQ )
        //
        valueToAdd = 0x8*0x1000*0x1000;
        
        
        if( this->offset > this->lastExpansion )
            target = this->offset + valueToAdd;
        else
            target = this->lastExpansion + valueToAdd;
        
        if( target < lowTargetSize )
            target  = lowTargetSize;
        
        if( DLD_IGNR_FSIZE != this->maxSize && target > this->maxSize )
            target = this->maxSize;
        
    }// end of the lock
    this->UnLockExclusive();
    
    
    if( target < lowTargetSize ){
        
        //
        // there is no point for extension as the goal won't be reached, fail the request
        //
        return currentLastExpansion;
    }
    
    assert( DLD_IGNR_FSIZE != currentLastExpansion && currentLastExpansion <= target );
    
    //
    // extend the file
    //
    vfs_context_t       vfs_context;
    int                 error;
    
    error = vnode_getwithref( this->vnode );
    assert( !error );
    if( !error ){
        
        struct vnode_attr	va;
        
        VATTR_INIT(&va);
        VATTR_SET(&va, va_data_size, target);
        va.va_vaflags =(IO_NOZEROFILL) & 0xffff;
        
        vfs_context = vfs_context_create( NULL );
        assert( vfs_context );
        if( vfs_context ){
            
            this->LockExclusive();
            {// start of the lock
                
                assert( currentLastExpansion <= this->lastExpansion );
                //
                // do not truncate the file incidentally!
                //
                if( target > this->offset ){
                    
                    error = vnode_setattr( this->vnode, &va, vfs_context );
                    if( !error ){
                        
                        this->lastExpansion = target;
                        currentLastExpansion = target;
                    }
                    
                } else {
                  
                    //
                    // the file has been extended by the write operation
                    //
                    this->lastExpansion = this->offset;
                    currentLastExpansion = this->offset;

                }// end if( target < this->offset )
                
            }// end of the lock
            this->UnLockExclusive();
            
            vfs_context_rele( vfs_context );
            DLD_DBG_MAKE_POINTER_INVALID( vfs_context );
            
        }// end if( vfs_context )
        
        vnode_put( this->vnode );
        
    }// end if( !error )
    
    assert( DLD_IGNR_FSIZE != currentLastExpansion );
    
    return currentLastExpansion;
}
bool DldIOShadowFile::initWithFileName( __in char* name, __in size_t nameLength )
{
    assert( preemption_enabled() );
    
    
    if( nameLength > (MAXPATHLEN + sizeof( L'\0' )) ){
        
        DBG_PRINT_ERROR(("length > (MAXPATHLEN + sizeof( L'\\0' ))\n"));
        return false;
    }
    
    
    if( !super::init() ){
        
        DBG_PRINT_ERROR(("super::init() failed\n"));
        return false;
    }
    
    this->rwLock = IORWLockAlloc();
    assert( this->rwLock );
    if( !this->rwLock ){
        
        DBG_PRINT_ERROR(("this->rwLock = IORWLockAlloc() failed\n" ));
        return false;
    }
    
    //
    // set a maximum file size to infinity
    //
    this->maxSize = DLD_IGNR_FSIZE;
    
    //
    // set switch size to 512 MB
    //
    this->switchSize = 0x20000000ll;
    
    this->path = (char*)IOMalloc( nameLength );
    assert( this->path );
    if( !this->path ){
        
        DBG_PRINT_ERROR(("this->path = IOMalloc( %u ) failed\n", (int)nameLength ));
        return false;
    }
    
    this->pathLength = nameLength;
    
    memcpy( this->path, name, nameLength );
    assert( L'\0' == this->path[ this->pathLength - 0x1 ] );
    
    //
    // open or create the file
    //
    errno_t  error;
    vfs_context_t   vfs_context;
    
    vfs_context = vfs_context_create( NULL );
    assert( vfs_context );
    if( !vfs_context )
        return false;
    
    //
    // open or create a file, truncate if the file exists
    //
    error = vnode_open( this->path,
                        O_EXLOCK | O_RDWR | O_CREAT | O_TRUNC | O_SYNC, // fmode
                        0644,// cmode
                        0x0,// flags
                        &this->vnode,
                        vfs_context );
    vfs_context_rele( vfs_context );
    if( error ){
        
        DBG_PRINT_ERROR(("vnode_open() failed with the %u error\n", error));
        return false;
    }
    
    //
    // vn_open returns with both a use_count
    // and an io_count on the found vnode
    //
    
    //
    // mark as an internal non shadowed vnode, use the CreateAndAddIOVnodByBSDVnode
    // as the kauth callback might have not been registered so the vnode open
    // was not seen by the DL driver
    //
    DldIOVnode*  dldVnode;
    dldVnode = DldVnodeHashTable::sVnodesHashTable->CreateAndAddIOVnodByBSDVnode( this->vnode );
    assert( dldVnode );
    if( !dldVnode ){
        
        //
        // we can't use this vnode as will be unable to distinguish
        // the DL internal writes from the user writes this will result
        // in an endless loop
        //
        DBG_PRINT_ERROR(("RetrieveReferencedIOVnodByBSDVnode() failed\n"));
        
        this->release();
        return NULL;
    }
    
    //
    // mark as skipped for log, shadowing and any other operations
    //
    dldVnode->flags.internal = 0x1;
    
    dldVnode->release();
    DLD_DBG_MAKE_POINTER_INVALID( dldVnode );
    
    this->expandFile();
    
    //
    // mark as noncached vnode, writes must be sector aligned!
    //
    //vnode_setnocache( this->vnode );
    
    return true;
}
void DldIODataQueue::removeEntry( __in DldIODataQueueEntry* entry )
{
    assert( preemption_enabled() );
    assert( DLD_QUEUE_SIGNATURE == entry->signature );
    assert( false == entry->intermediate );
    
    bool wakeupWaiter;
    DldIODataQueueEntry* nextEntry;
    
    IOLockLock( this->lock );
    {// start of the lock
        
        bool waitForHeadPropagation = false;
        
        //
        // check whether the head points to the entry this means that there
        // are no unprocessed entries before this one and the head can be moved
        // beyound this entry in the direction to the validDataHead
        //
        if( (DldIODataQueueEntry *)((char *)dataQueue->queue + dataQueue->head ) != entry ){
            
            //
            // check that that there was no wrap around, because of which the head does not point to the entry
            //
            bool                   wrapAround = false;
            DldIODataQueueEntry*   head;
            UInt32                 headOffset;
            
            headOffset = dataQueue->head;
            head = (DldIODataQueueEntry *)((char *)dataQueue->queue + headOffset);
            
            //
            // we wraped around to beginning, so read from there
            // either there was not even room for the header
            //
            if( (dataQueue->head +  DLD_DATA_QUEUE_ENTRY_HEADER_SIZE > dataQueue->queueSize) ||
                // or there was room for the header, but not for the data
                ((dataQueue->head + head->dataSize + DLD_DATA_QUEUE_ENTRY_HEADER_SIZE) > dataQueue->queueSize) ){
                
                //
                // check whether the entry is the entry which wrapped around the queue
                //
                if( entry == dataQueue->queue )
                    wrapAround = true;
                else
                    wrapAround = false;
            }
            
            //
            // wait if there was not due to the wrap around
            //
            waitForHeadPropagation = !wrapAround;
            
        }
        
        if( waitForHeadPropagation ){
            
            wait_result_t  wresult;
            
            //
            // a concurrent consumer holds the previous entry,
            // back off for the concurrent consumer to complete its processing
            // 
            
            dataQueue->waitersForHeadPropogation += 0x1;
            entry->waitingForHeadPropogation = true;
            
            wresult = assert_wait( entry, THREAD_UNINT );
            
            //
            // reschedule
            //
            if( THREAD_WAITING == wresult ){
                
                //
                // free the lock before rescheduling
                //
                IOLockUnlock( this->lock );
                
                assert( preemption_enabled() );
                thread_block(THREAD_CONTINUE_NULL);
                
                //
                // reacquire the lock
                //
                IOLockLock( this->lock );
                
            }
            
            assert( dataQueue->waitersForHeadPropogation > 0x0 );
            
            dataQueue->waitersForHeadPropogation -= 0x1;
            entry->waitingForHeadPropogation = false;
            
            //
            // there must be no more than one waiter
            //
            assert( entry == (DldIODataQueueEntry *)((char *)dataQueue->queue + dataQueue->head ) );
            
        }
        
        //
        // propagate the head moving it closer to a valid data head
        //
        {
            
            assert( ((char *)dataQueue->queue + dataQueue->head ) == (char*)entry || (char*)entry == (char *)dataQueue->queue);
            DldIODataQueueEntry*   head;
            UInt32                 headOffset;
            
            headOffset = dataQueue->head;
            head = (DldIODataQueueEntry *)((char *)dataQueue->queue + headOffset);
            
            //
            // check whether we wraped around to beginning
            //
            if( (dataQueue->head +  DLD_DATA_QUEUE_ENTRY_HEADER_SIZE > dataQueue->queueSize) ||
                // or there was room for the header, but not for the data
                ((dataQueue->head + head->dataSize + DLD_DATA_QUEUE_ENTRY_HEADER_SIZE) > dataQueue->queueSize) ){
                
                //
                // wrapped around
                //
                assert( (char*)entry == (char *)dataQueue->queue );
                dataQueue->head = entry->dataSize + DLD_DATA_QUEUE_ENTRY_HEADER_SIZE;
                
            } else { 
                
                assert( ((char *)dataQueue->queue + dataQueue->head ) == (char*)entry );
                dataQueue->head += entry->dataSize + DLD_DATA_QUEUE_ENTRY_HEADER_SIZE;
            }
            
        }
        
        //
        // check for waiters
        //
        wakeupWaiter = ( 0x0 != dataQueue->waitersForHeadPropogation );
        if( wakeupWaiter ){
            
            //
            // get the entry next to the current, it must exist as somebody
            // is waiting for the head propogation
            //
            
            DldIODataQueueEntry*   head;
            UInt32                 headOffset;
            
            headOffset = dataQueue->head;
            head = (DldIODataQueueEntry *)((char *)dataQueue->queue + headOffset);
            
            //
            // we wraped around to beginning, so read from there
            // either there was not even room for the header
            //
            if( (dataQueue->head +  DLD_DATA_QUEUE_ENTRY_HEADER_SIZE > dataQueue->queueSize) ||
               // or there was room for the header, but not for the data
               ((dataQueue->head + head->dataSize + DLD_DATA_QUEUE_ENTRY_HEADER_SIZE) > dataQueue->queueSize)) {
                
                nextEntry = dataQueue->queue;
                // else it is at the end
                
            } else {
                
                nextEntry = head;
            }
            
            assert( DLD_QUEUE_SIGNATURE == nextEntry->signature );
            assert( false == nextEntry->intermediate );
            
            //
            // rechek whether the next entry consumer is really waiting,
            // this is required to avoid using a dequeued entry
            // after the lock is released
            //
            wakeupWaiter = nextEntry->waitingForHeadPropogation;
            
        }// else if( wakeupWaiter )
        
        
    }// end of the lock
    IOLockUnlock( this->lock );
    
    if( wakeupWaiter ){
        
        //
        // there must be exactly one waiter
        //
        
        assert( nextEntry->waitingForHeadPropogation );
        assert( DLD_QUEUE_SIGNATURE == nextEntry->signature );
        assert( false == nextEntry->intermediate );
        
        thread_wakeup( nextEntry );
    }
    
}
bool DldIODataQueue::enqueueScatterGather(void * dataArray[], UInt32 dataSizeArray[], UInt32 entries )
{
    UInt32    newTail;
    UInt32    oldTail;
    bool      wakeupWaiter;
    DldIODataQueueEntry*  entry = NULL;
    UInt32    i;
    vm_size_t dataSize = 0x0;
    
    assert( preemption_enabled() );
    
    for( i = 0x0; i < entries; ++i ){
        
        dataSize += dataSizeArray[ i ];
        assert( dataArray[ i ] );
    }// end for
    
    assert( 0x0 != dataSize );
    
    IOLockLock( this->lock );
    {// start of the lock
        
        UInt32                head      = dataQueue->head;
        UInt32                tail      = dataQueue->tail;
        vm_size_t             entrySize = dataSize + DLD_DATA_QUEUE_ENTRY_HEADER_SIZE;
        
        newTail = tail;
        oldTail = tail;
        
        if ( tail >= head )
        {
            //
            // Is there enough room at the end for the entry?
            //
            if ( (tail + entrySize) <= dataQueue->queueSize )
            {
                entry = (DldIODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
                
                entry->dataSize = dataSize;
                
                //
                // The tail can be out of bound when the size of the new entry
                // exactly matches the available space at the end of the queue.
                // The tail can range from 0 to dataQueue->queueSize inclusive.
                //
                
                dataQueue->tail += entrySize;
            }
            else if ( head > entrySize ) 	// Is there enough room at the beginning?
            {
                //
                // Wrap around to the beginning, but do not allow the tail to catch
                // up to the head.
                //
                
                dataQueue->queue->dataSize = dataSize;
                
                //
                // We need to make sure that there is enough room to set the size before
                // doing this. The consumer checks for this and will look for the size
                // at the beginning if there isn't room for it at the end.
                //
                
                if ( ( dataQueue->queueSize - tail ) >= DLD_DATA_QUEUE_ENTRY_HEADER_SIZE )
                {
                    //
                    // set an intermediate entry, the entry has only a valid header and
                    // used as a trampoline to the entry at the begining of the queue
                    //
                    
                    DldIODataQueueEntry* intermediateEntry = (DldIODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
                    
                    intermediateEntry->dataSize = dataSize;
                    intermediateEntry->waitingForHeadPropogation = false;
                    intermediateEntry->waitingForTailPropogation = false;
                    #if defined( DBG )
                    intermediateEntry->signature = DLD_QUEUE_SIGNATURE;
                    intermediateEntry->intermediate = true;
                    #endif//#if defined( DBG )
                }
                
                entry = &dataQueue->queue[0];
                
                dataQueue->tail = entrySize;
            }
            else
            {
                //return false;	// queue is full
            }
        }
        else
        {
            //
            // Do not allow the tail to catch up to the head when the queue is full.
            // That's why the comparison uses a '>' rather than '>='.
            //
            
            if ( (head - tail) > entrySize )
            {
                entry = (DldIODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
                
                entry->dataSize = dataSize;
                dataQueue->tail += entrySize;
            }
            else
            {
                //return false;	// queue is full
            }
        }
        
        
        if( entry ){
            
            entry->waitingForHeadPropogation = false;
            entry->waitingForTailPropogation = false;
            
#if defined( DBG )
            entry->signature = DLD_QUEUE_SIGNATURE;
            entry->intermediate = false;
#endif//DBG
        }
        
        newTail = dataQueue->tail;
        
    }// end of the lock
    IOLockUnlock( this->lock );
    
    if( !entry ){
        
        //
        // the queue is full
        //
        return false;
    }
    
    assert( newTail != oldTail );
    
    //
    // copy the data in he reserved queue space
    //
    UInt32 accumulatedSize = 0x0;
    
    for( i = 0x0; i < entries; ++i ){
        
        memcpy( (char*)entry->data + accumulatedSize, dataArray[ i ], dataSizeArray[ i ] );
        
        accumulatedSize += dataSizeArray[ i ];
        
    }// end for
    
    assert( accumulatedSize == dataSize );
    
    IOLockLock( this->lock );
    {// start of the lock
        
        //
        // propagate a valid data pointers
        //
        
        if( oldTail != dataQueue->validDataTail ){
            
            wait_result_t wresult;
            
            //
            // back off and give concurrent producers to complete the data fill in
            //
            
            dataQueue->waitersForTailPropogation += 0x1;
            entry->waitingForTailPropogation = true;
            
            wresult = assert_wait( ((char*)dataQueue->queue + oldTail), THREAD_UNINT );
            
            //
            // reschedule
            //
            if( THREAD_WAITING == wresult ){
                
                //
                // free the lock before rescheduling
                //
                IOLockUnlock( this->lock );
                
                //
                // reschedule
                //
                assert( preemption_enabled() );
                thread_block( THREAD_CONTINUE_NULL );
                
                //
                // reacquire the lock
                //
                IOLockLock( this->lock );
                
            }// end if( THREAD_WAITING == wresult )
            
            assert( dataQueue->waitersForTailPropogation > 0x0 );
            
            dataQueue->waitersForTailPropogation -= 0x1;
            
            //
            // there must be no more than one waiter
            //
            assert( oldTail == dataQueue->validDataTail );
            
            
        }// end if
        
        assert( oldTail == dataQueue->validDataTail );
        
        //
        // all data up to this one have been filled in
        // so propagate the valid data tail
        //
        dataQueue->validDataTail = newTail;
        
        assert( DLD_QUEUE_SIGNATURE == entry->signature );
        
        //
        // check for waiters
        //
        wakeupWaiter = ( 0x0 != dataQueue->waitersForTailPropogation );
        if( wakeupWaiter ){
            
            //
            // get the entry next to the current, it must exists as somebody
            // is waiting for the tail propogation
            //
            
            //DldIODataQueueEntry*   nextEntry;
            DldIODataQueueEntry*   nextEntry;
            UInt32                 nextEntryOffset;
            
            nextEntryOffset = newTail;
            nextEntry = (DldIODataQueueEntry *)((char *)dataQueue->queue + nextEntryOffset);
            
            assert( DLD_QUEUE_SIGNATURE == nextEntry->signature );
            assert( false == nextEntry->intermediate );
            
            //
            // rechek whether the next entry producer is really waiting,
            // this is required to avoid using a dequeued entry
            // after the lock is released
            //
            wakeupWaiter = nextEntry->waitingForTailPropogation;
        }
        
    }// end of the lock
    IOLockUnlock( this->lock );
    
    
    if( wakeupWaiter ){
        
        DldIODataQueueEntry* nextEntry = (DldIODataQueueEntry*)( (char*)dataQueue->queue + newTail );
        
        //
        // there must be exactly one waiter, but not necesary at the
        // newTail offset which might be for an intermadiate entry
        //
        assert( DLD_QUEUE_SIGNATURE == nextEntry->signature );
        assert( nextEntry->waitingForTailPropogation );
        
        thread_wakeup( nextEntry );
    }
    
    return true;
}
Exemplo n.º 21
0
RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread)
{
    Assert(hThread == NIL_RTTHREAD);
    return preemption_enabled();
}
//
// if fase is returned the request has been completed as the access is denied
//
bool
DldIOSCSIProtocolInterface::ExecuteCommand(
    __in IOSCSIProtocolInterface* service,
    __in SCSITaskIdentifier request
    )
{
    //SCSICommandDescriptorBlock   cdb;
    
    //assert( sizeof( cdb ) == kSCSICDBSize_Maximum );
    
    //DldSCSITaskGetCommandDescriptorBlock( request, &cdb );
    
    ///////
    /// start of the test
    /*
      DldSCSITaskCompleteAccessDenied( request );
      return false;
     */
    /// end of the test
    ////////
    /*
    dld_classic_rights_t   access;
    access = DldSCSITaskGetCdbRequestedAccess( request );
    if( 0x0 != ( DEVICE_WRITE & access ) ){
        
        DldSCSITaskCompleteAccessDenied( request );
        return false;
    }*/
    
    
    assert( preemption_enabled() );
    
    DldIOService*             dldSCSIDev;
    DldObjectPropertyEntry*   property;
    dld_classic_rights_t      requestedAccess;
    DldAccessCheckParam       param;
    bool                      disable        = false;
    bool                      isShadowedUser = false;
    
    requestedAccess = DldSCSITaskGetCdbRequestedAccess( request );
    if( 0x0 == requestedAccess )
        return true;
    
    dldSCSIDev = DldIOService::RetrieveDldIOServiceForIOService( service );
    assert( dldSCSIDev );
    if( !dldSCSIDev ){
        
        DBG_PRINT_ERROR(("DldIOService::RetrieveDldIOServiceForIOService( 0x%p ) returned NULL\n", service ));
        return true;
    }
    
    //
    // do not check read operations if there is a CD or DVD object in the stack,
    // as read requests are generated by every client including a mounted FSD( i.e. BSD susbsystem ),
    // but if there is no CD or DVD object then a user mode client dismantled them and
    // attached directly to the SCSI object, in that case the read requests must be 
    // checked and audited
    //
    if( DEVICE_READ == requestedAccess ){
        
        DldObjectPropertyEntry*   media;
        
        //
        // look for actual CD/DVD properties
        //

        media = dldSCSIDev->retrievePropertyByTypeRef( DldObjectPopertyType_IODVDMedia );
        if( NULL == media )
            media = dldSCSIDev->retrievePropertyByTypeRef( DldObjectPopertyType_IOCDMedia );
        
        if( media ){
            
            //
            // a request from the BSD subssytem, the request has been checked by the BSD KAUTH callbacks
            //
            media->release();
            goto __exit;
        }
        
    } // end if( DEVICE_READ == requestedAccess )
    
    property = dldSCSIDev->getObjectProperty();
    if( !property ){
        
        //
        // we are not interested in this device, this means that
        // there is another IOSCSIProtocolInterface device attached
        // to this one and this last is visible for user apps
        //
        goto __exit;
    }
    
    if( DldObjectPopertyType_SCSIPeripheralDeviceType05 != property->dataU.property->typeDsc.type ){
     
        DBG_PRINT_ERROR(("It seems you added a new SCSI type %i but forgot to add its processing\n", property->dataU.property->typeDsc.type ));
        assert( !"It seems you added a new SCSI type but forgot to add its processing" );
        goto __exit;
    }
        
    bzero( &param, sizeof( param ) );
    
    param.userSelectionFlavor = kDefaultUserSelectionFlavor;
    param.aclType             = kDldAclTypeSecurity;
    param.checkParentType     = true;
    param.dldRequestedAccess.winRequestedAccess   = requestedAccess;
    //param.dldRequestedAccess.kauthRequestedAccess = requestedAccess; // TO DO remove when the daemon will be able to proide ACL
    param.credential          = NULL;// current thread's user
    param.dldIOService        = dldSCSIDev;
#if defined(LOG_ACCESS)
    param.sourceFunction      = __PRETTY_FUNCTION__;
    param.sourceFile          = __FILE__;
    param.sourceLine          = __LINE__;
#endif//#if defined(LOG_ACCESS)
    
    /*if( dldIOService->getObjectProperty() &&
     DLD_DEVICE_TYPE_REMOVABLE == dldIOService->getObjectProperty()->dataU.property->deviceType.type.major ){}*/
    
    ::DldAcquireResources( &param );
    {
        ::isAccessAllowed( &param );
        
        disable = ( param.output.access.result[ DldFullTypeFlavor ].disable || 
                    param.output.access.result[ DldMajorTypeFlavor ].disable || 
                    param.output.access.result[ DldParentTypeFlavor ].disable );
        
#if defined( DBG )
        /*if( disable ){
         __asm__ volatile( "int $0x3" );
         }*/
#endif
        
        //
        // should we log?
        //
        if( param.output.access.result[ DldFullTypeFlavor ].log ||
            param.output.access.result[ DldMajorTypeFlavor ].log ){
            
            DldDriverDataLogInt intData;
            DldDriverDataLog    data;
            bool                logDataValid;
            kauth_cred_t        credentials;
            
            intData.logDataSize = sizeof( data );
            intData.logData = &data;
            
            credentials = kauth_cred_proc_ref( current_proc() ); // just like in isAccessAllowed()
            assert( credentials );
            if( credentials ){
                
                logDataValid = dldSCSIDev->initDeviceLogData( &param.dldRequestedAccess,
                                                              (ALL_WRITE & requestedAccess) ? DldFileOperationWrite : DldFileOperationRead,
                                                              proc_pid(current_proc()),
                                                              credentials,
                                                              ( param.output.access.result[ DldFullTypeFlavor ].disable || 
                                                                param.output.access.result[ DldMajorTypeFlavor ].disable ),
                                                              &intData );
                
                assert( logDataValid );
                if( logDataValid ){
                    
                    gLog->logData( &intData );
                    
                } else {
                    
                    DBG_PRINT_ERROR(("device log data is invalid\n"));
                }
                
                kauth_cred_unref( &credentials );
            } else {
                
                DBG_PRINT_ERROR(("kauth_cred_proc_ref failed\n"));
            }
            
        }// end log
    }
    ::DldReleaseResources( &param );
    
    if( !disable ){
        
        //
        // check for ejection
        //
        if( DldSCSITaskIsMediaEjectionRequest( request ) ){
            
            /*
             a media is ejected by calling IOSCSIMultimediaCommandsDevice::EjectTheMedia which in turn
             calls IOSCSIProtocolInterface::ExecuteCommand with two commands - the first unlocks the tray
             and the second is kSCSICmd_START_STOP_UNIT, the stack is as follows
             
             #0  0x015eae43 in IOSCSIMultimediaCommandsDevice::EjectTheMedia (this=0x57c2d00) at /SourceCache/IOSCSIArchitectureModelFamily/IOSCSIArchitectureModelFamily-265.0.3/IOSCSIMultimediaCommands/IOSCSIMultimediaCommandsDevice.cpp:722
             #1  0x015f0cb3 in IODVDServices::doEjectMedia (this=0x57c6380) at /SourceCache/IOSCSIArchitectureModelFamily/IOSCSIArchitectureModelFamily-265.0.3/IOSCSIMultimediaCommands/IODVDServices.cpp:594
             #2  0x011edfd8 in IOBlockStorageDriver::ejectMedia (this=0x57c2200) at /SourceCache/IOStorageFamily/IOStorageFamily-116.1/IOBlockStorageDriver.cpp:1191
             #3  0x011f70ef in dkioctl (dev=234881028, cmd=536896533, data=0x31d93e94 "", flags=1, proc=0x5bf4d20) at /SourceCache/IOStorageFamily/IOStorageFamily-116.1/IOMediaBSDClient.cpp:1382
             #4  0x003662a1 in spec_ioctl (ap=0x31d93d9c) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/miscfs/specfs/spec_vnops.c:529
             #5  0x00357488 in VNOP_IOCTL (vp=0x8f612e4, command=536896533, data=0x31d93e94 "", fflag=1, ctx=0x31d93e8c) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/vfs/kpi_vfs.c:3606
             #6  0x0034ca2f in vn_ioctl (fp=0x63504a0, com=536896533, data=0x31d93e94 "", ctx=0x31d93e8c) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/vfs/vfs_vnops.c:1147
             #7  0x00515c35 in fo_ioctl (fp=0x63504a0, com=536896533, data=0x31d93e94 "", ctx=0x31d93e8c) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/kern/kern_descrip.c:4826
             #8  0x005458ff in ioctl (p=0x5bf4d20, uap=0x54bea28, retval=0x5680324) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/kern/sys_generic.c:914
             #9  0x005b19b5 in unix_syscall64 (state=0x54bea24) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/dev/i386/systemcalls.c:365
             
             a description for the START_STOP_UNIT command can be found here
             http://en.wikipedia.org/wiki/SCSI_Start_Stop_Unit_Command
             we are interested in the following field
             LoEj (load/eject) and Start - these two bits are used together:
             00 - Stop motor
             01 - Start motor
             10 - Eject media
             11 - Load media
             */
            
            //
            // the media is being removed, invalidate the CD/DVD white list state,
            // the FSD has been already unmounted as the tray is locked when FSD
            // is mounted, if the white list results will be removed prematurely
            // when they will be restored on the next request
            //
            
            DldAclObject*  wlACLToRemove;
            
            property->dataU.property->LockExclusive();
            { // start of the lock
                
                wlACLToRemove = property->dataU.property->whiteListState.acl;
                property->dataU.property->whiteListState.acl              = NULL;
                property->dataU.property->whiteListState.inWhiteList      = false;
                property->dataU.property->whiteListState.currentWLApplied = false;// this value is a main driver to start reinitialization
                
                //
                // meaningless for DVD/CD
                //
                assert( false == property->dataU.property->whiteListState.propagateUp );
                
                if( DldObjectPopertyType_SCSIPeripheralDeviceType05 == property->dataU.property->typeDsc.type ){
                    
                    //
                    // the media is being removed
                    //
                    
                    property->dataU.ioSCSIPeripheralType05Property->uidValid = false;
                    
                    bzero( &property->dataU.ioSCSIPeripheralType05Property->mediaUID,
                           sizeof( property->dataU.ioSCSIPeripheralType05Property->mediaUID ) );
                }
                
            }// end of the lock
            property->dataU.property->UnLockExclusive();
            
            if( wlACLToRemove )
                wlACLToRemove->release();
            
            property->setUIDProperty();
            
        }// end if( DldSCSITaskIsMediaEjectionRequest( request )
        
        
        //
        // check for shadowing
        //
        DldAccessCheckParam param;
        
        bzero( &param, sizeof( param ) );
        
        param.userSelectionFlavor = kDefaultUserSelectionFlavor;
        param.aclType             = kDldAclTypeShadow;
        param.checkParentType     = true;
        param.dldRequestedAccess.kauthRequestedAccess = requestedAccess;// actually the value is ignored
        param.credential          = NULL;// current thread's user
        param.service             = NULL;
        param.dldIOService        = dldSCSIDev;
        
        ::DldAcquireResources( &param );
        ::DldIsShadowedUser( &param );
        ::DldReleaseResources( &param );
        
        isShadowedUser = ( param.output.shadow.result[ DldFullTypeFlavor ].shadow || 
                           param.output.shadow.result[ DldMajorTypeFlavor ].shadow || 
                           param.output.shadow.result[ DldParentTypeFlavor ].shadow );
        
    }// end if( !disable )
    
    
    //
    // shadow the write request
    //
    if( isShadowedUser && 0x0 != ( DEVICE_WRITE & requestedAccess ) ){
        
        assert( !disable );
        
        DldCommonShadowParams commonParams;
        UInt32       completionEvent    = DLD_INVALID_EVENT_VALUE;
        IOReturn     shadowCompletionRC = kIOReturnInvalid;
        bool         synchronousShadow  = false;
        bool         shadowed           = false;
        
        if( gSecuritySettings.securitySettings.disableOnShadowErrors ){
            
            //
            // the shadow operation must be synchronous
            //
            synchronousShadow = true;
        }
        
        bzero( &commonParams, sizeof( commonParams ) );
        
        commonParams.operationID = gShadow->generateUniqueID();
        if( synchronousShadow ){
            
            //
            // for synchronous processing we need an event to wait for shadow completion
            //
            DldInitNotificationEvent( &completionEvent );
            commonParams.completionEvent    = &completionEvent;
            commonParams.shadowCompletionRC = &shadowCompletionRC;
        }
        
        //
        // shadow the request
        //
        shadowed = gShadow->shadowSCSIOperation( dldSCSIDev, request, &commonParams );        
        if( shadowed && synchronousShadow ){
            
            //
            // wait for shadow completion
            //
            DldWaitForNotificationEvent( &completionEvent );
            
            //
            // the status code must be valid
            //
            assert( kIOReturnInvalid != completionEvent );
            
        } else if( shadowed ){
            
            //
            // an asynchronous shadowing, the first phase was successful
            //
            assert( !synchronousShadow );
            assert( DLD_INVALID_EVENT_VALUE == completionEvent );
            
            shadowCompletionRC = KERN_SUCCESS;
            
        } else {
            
            //
            // failed on the first phase
            //
            DBG_PRINT_ERROR(("the first phase of the shadowing failed\n"));
            shadowCompletionRC = KERN_FAILURE;
        }
        
        if( KERN_SUCCESS != shadowCompletionRC && gSecuritySettings.securitySettings.disableOnShadowErrors ){
            
            //
            // the shadowing failed so must the request
            //
            DBG_PRINT_ERROR(("shadowing failed\n"));
            disable = true;
        }
        
        //
        // the SCSI operation completion status is reported by a completion callback hook, see shadowSCSIOperation()
        //
        
    }// end if( isShadowedUser )
    
    
__exit:
    assert( dldSCSIDev );
    dldSCSIDev->release();
    
    if( disable )
        DldSCSITaskCompleteAccessDenied( request );
    
    return (!disable);
}