Logo Search packages:      
Sourcecode: hfsprogs version File versions  Download package

BlockCache.c

/*
 * Copyright (c) 2000-2003, 2005 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 *
 * This 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 OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

#include "SRuntime.h"
#include "Scavenger.h"
#include "../cache.h"



extern OSErr MapFileBlockC (
      SVCB *   vcb,
      SFCB *   fcb,
      UInt32   numberOfBytes,
      UInt32   sectorOffset,
      UInt64 * startSector,
      UInt32 * availableBytes
);

extern Cache_t fscache;


static OSStatus  ReadFragmentedBlock (SFCB *file, UInt32 blockNum, BlockDescriptor *block);
static OSStatus  WriteFragmentedBlock(    SFCB *file, 
                                                            BlockDescriptor *block, 
                                                            int age, 
                                                            uint32_t writeOptions );
static OSStatus  ReleaseFragmentedBlock (SFCB *file, BlockDescriptor *block, int age);


void
InitBlockCache(SVCB *volume)
{
      volume->vcbBlockCache = (void *) &fscache;
}


/*
 *  kGetBlock
 *  kForceReadBlock
 *  kGetEmptyBlock
 *  kSkipEndianSwap
 */
OSStatus
GetVolumeBlock (SVCB *volume, UInt64 blockNum, GetBlockOptions options, BlockDescriptor *block)
{
      UInt32  blockSize;
      SInt64  offset;
      UInt16  signature;
      OSStatus result;  
      Buf_t *   buffer;
      Cache_t * cache;

      buffer = NULL;
      cache  = (Cache_t *) volume->vcbBlockCache;
      blockSize = 512;

      offset = (SInt64) ((UInt64) blockNum) << kSectorShift;

      result = CacheRead (cache, offset, blockSize, &buffer);

      if (result == 0) {
            block->blockHeader = buffer;
            block->buffer = buffer->Buffer;
            block->blockNum = blockNum;
            block->blockSize = blockSize;
            block->blockReadFromDisk = 0;
            block->fragmented = 0;
      } else {
            block->blockHeader = NULL;
            block->buffer = NULL;
      }
      
      if (!(options & kSkipEndianSwap) && (result == 0)) {
            HFSMasterDirectoryBlock *mdb;

            mdb = (HFSMasterDirectoryBlock *)block->buffer;
            signature = SWAP_BE16(mdb->drSigWord);
            if (signature == kHFSPlusSigWord || signature == kHFSXSigWord)
                  SWAP_HFSPLUSVH(block->buffer);
            else if (signature == kHFSSigWord)
                  SWAP_HFSMDB(block->buffer);
      }
      return (result);
}


/*
 *  kReleaseBlock
 *  kForceWriteBlock
 *  kMarkBlockDirty
 *  kTrashBlock
 *  kSkipEndianSwap
 */
OSStatus
ReleaseVolumeBlock (SVCB *volume, BlockDescriptor *block, ReleaseBlockOptions options)
{
      OSStatus  result = 0;
      Cache_t * cache;
      Buf_t *   buffer;
      int       age;
      UInt16  signature;

      cache  = (Cache_t *) volume->vcbBlockCache;
      buffer = (Buf_t *) block->blockHeader;
      age    = ((options & kTrashBlock) != 0);

      /*
       * Always leave the blocks in the cache in big endian
       */
      if (!(options & kSkipEndianSwap)) {
            signature = ((HFSMasterDirectoryBlock *)block->buffer)->drSigWord;
            if (signature == kHFSPlusSigWord || signature == kHFSXSigWord)
                  SWAP_HFSPLUSVH(block->buffer);
            else if (signature == kHFSSigWord)
                  SWAP_HFSMDB(block->buffer);
      }

      if (options & (kMarkBlockDirty | kForceWriteBlock)) {
            result = CacheWrite(cache, buffer, age, 0);
      } else { /* not dirty */
            result = CacheRelease (cache, buffer, age);
      }
      return (result);
}


/*
 *  kGetBlock
 *  kForceReadBlock
 *  kGetEmptyBlock
 */
OSStatus
GetFileBlock (SFCB *file, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block)
{
      UInt64      diskBlock;
      UInt32      contiguousBytes;
      SInt64  offset;

      OSStatus result;  
      Buf_t *   buffer;
      Cache_t * cache;

      buffer = NULL;
      block->buffer = NULL;
      block->blockHeader = NULL;
      cache  = (Cache_t *)file->fcbVolume->vcbBlockCache;

      /* Map file block to volume block */
      result = MapFileBlockC(file->fcbVolume, file, file->fcbBlockSize,
                  (((UInt64)blockNum * (UInt64)file->fcbBlockSize) >> kSectorShift),
                  &diskBlock, &contiguousBytes);
      if (result) return (result);

      if (contiguousBytes < file->fcbBlockSize)
            return ( ReadFragmentedBlock(file, blockNum, block) );

      offset = (SInt64) ((UInt64) diskBlock) << kSectorShift;

      result = CacheRead (cache, offset, file->fcbBlockSize, &buffer);
      if (result)  return (result);

      block->blockHeader = buffer;
      block->buffer = buffer->Buffer;
      block->blockNum = blockNum;
      block->blockSize = file->fcbBlockSize;
      block->blockReadFromDisk = 0;
      block->fragmented = 0;
      
      return (noErr);
}


/*
 *  kReleaseBlock
 *  kForceWriteBlock
 *  kMarkBlockDirty
 *  kTrashBlock
 */
OSStatus
ReleaseFileBlock (SFCB *file, BlockDescriptor *block, ReleaseBlockOptions options)
{
      OSStatus  result = 0;
      Cache_t * cache;
      Buf_t *   buffer;
      int       age;
      uint32_t  writeOptions = 0;

      cache  = (Cache_t *)file->fcbVolume->vcbBlockCache;
      buffer = (Buf_t *) block->blockHeader;
      age    = ((options & kTrashBlock) != 0);

      if ( (options & kForceWriteBlock) == 0 )
            /* only write if we're forced to */
            writeOptions |= kLazyWrite;

      if (options & (kMarkBlockDirty | kForceWriteBlock)) {
            if (block->fragmented)
                  result = WriteFragmentedBlock(file, block, age, writeOptions);
            else
                  result = CacheWrite(cache, buffer, age, writeOptions);
      } else { /* not dirty */

            if (block->fragmented)
                  result = ReleaseFragmentedBlock(file, block, age);
            else
                  result = CacheRelease (cache, buffer, age);
      }
      return (result);
}


/*
 *
 */
OSStatus
SetFileBlockSize (SFCB *file, ByteCount blockSize)
{
      file->fcbBlockSize = blockSize;

      return (0);
}


/*
 * Read a block that is fragmented across 2 or more allocation blocks
 *
 *  - a block descriptor buffer is allocated here
 *  - the blockHeader field holds a list of Buf_t buffers.
 *  - the fragmented flag is set
 */
static OSStatus
ReadFragmentedBlock (SFCB *file, UInt32 blockNum, BlockDescriptor *block)
{
      UInt64      sector;
      UInt32      fragSize, blockSize;
      UInt64  fileOffset; 
      SInt64  diskOffset;
      SVCB *  volume;
      int     i, maxFrags;
      OSStatus result;  
      Buf_t **   bufs;   /* list of Buf_t pointers */
      Cache_t * cache;
      char *      buffer;

      volume = file->fcbVolume;
      cache  = (Cache_t *)volume->vcbBlockCache;

      blockSize = file->fcbBlockSize;
      maxFrags = blockSize / volume->vcbBlockSize;
      fileOffset = (UInt64)blockNum * (UInt64)blockSize;
      
      buffer = (char *) AllocateMemory(blockSize);
      bufs = (Buf_t **) AllocateClearMemory(maxFrags * sizeof(Buf_t *));
      if (buffer == NULL || bufs == NULL) {
            result = memFullErr;
            return (result);
      }
      
      block->buffer = buffer;
      block->blockHeader = bufs;
      block->blockNum = blockNum;
      block->blockSize = blockSize;
      block->blockReadFromDisk = false;
      block->fragmented = true;
      
      for (i = 0; (i < maxFrags) && (blockSize > 0); ++i) {
            result = MapFileBlockC (volume, file, blockSize,
                              fileOffset >> kSectorShift,
                              &sector, &fragSize);
            if (result) goto ErrorExit;

            diskOffset = (SInt64) (sector) << kSectorShift;
            result = CacheRead (cache, diskOffset, fragSize, &bufs[i]);
            if (result) goto ErrorExit;
            
            if (bufs[i]->Length != fragSize) {
                  printf("ReadFragmentedBlock: cache failure (Length != fragSize)\n");
                  result = -1;
                  goto ErrorExit;
            }

            CopyMemory(bufs[i]->Buffer, buffer, fragSize);
            buffer     += fragSize;
            fileOffset += fragSize;
            blockSize  -= fragSize;
      }
      
      return (noErr);

ErrorExit:
      i = 0;
      while (bufs[i] != NULL) {
            (void) CacheRelease (cache, bufs[i], true);
            ++i;
      }

      DisposeMemory(block->buffer);
      DisposeMemory(block->blockHeader);

      block->blockHeader = NULL;
      block->buffer = NULL;

      return (result);
}


/*
 * Write a block that is fragmented across 2 or more allocation blocks
 *
 */
static OSStatus
WriteFragmentedBlock( SFCB *file, BlockDescriptor *block, int age, uint32_t writeOptions )
{
      Cache_t * cache;
      Buf_t **  bufs;  /* list of Buf_t pointers */
      char *        buffer;
      char *    bufEnd;
      UInt32        fragSize;
      OSStatus  result;
      int i = 0;

      result = 0;
      cache  = (Cache_t *) file->fcbVolume->vcbBlockCache;
      bufs   = (Buf_t **) block->blockHeader;
      buffer = (char *) block->buffer;
      bufEnd = buffer + file->fcbBlockSize;

      if (bufs == NULL) {
            printf("WriteFragmentedBlock: NULL bufs list!\n");
            return (-1);
      }
      
      while ((bufs[i] != NULL) && (buffer < bufEnd)) {
            fragSize = bufs[i]->Length;
            
            /* copy data for this fragment */
            CopyMemory(buffer, bufs[i]->Buffer, fragSize);
            
            /* write it back to cache */
            result = CacheWrite(cache, bufs[i], age, writeOptions);
            if (result) break;

            buffer += fragSize;
            ++i;
      }
      
      DisposeMemory(block->buffer);
      DisposeMemory(block->blockHeader);

      block->buffer = NULL;
      block->blockHeader = NULL;
      block->fragmented = false;

      return (result);
}


/*
 * Release a block that is fragmented across 2 or more allocation blocks
 *
 */
static OSStatus
ReleaseFragmentedBlock (SFCB *file, BlockDescriptor *block, int age)
{
      Cache_t * cache;
      Buf_t **   bufs;  /* list of Buf_t pointers */
      int i = 0;

      cache = (Cache_t *)file->fcbVolume->vcbBlockCache;
      bufs  = (Buf_t **) block->blockHeader;

      if (bufs == NULL) {
            printf("ReleaseFragmentedBlock: NULL buf list!\n");
            return (-1);
      }

      while (bufs[i] != NULL && bufs[i]->Length) {
            (void) CacheRelease (cache, bufs[i], true);
            ++i;
      }
      
      DisposeMemory(block->buffer);
      DisposeMemory(block->blockHeader);

      block->buffer = NULL;
      block->blockHeader = NULL;
      block->fragmented = false;

      return (noErr);
}


Generated by  Doxygen 1.6.0   Back to index