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; }
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; }
/* 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; }
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; }
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( ¶m, 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( ¶m ); { ::isAccessAllowed( ¶m ); 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( ¶m.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( ¶m ); 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( ¶m, 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( ¶m ); ::DldIsShadowedUser( ¶m ); ::DldReleaseResources( ¶m ); 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); }