int allocate()

in hfs/rawfile.c [29:161]


int allocate(RawFile* rawFile, off_t size) {
	unsigned char* zeros;
	Volume* volume;
	HFSPlusForkData* forkData;
	uint32_t blocksNeeded;
	uint32_t blocksToAllocate;
	Extent* extent;
	Extent* lastExtent;

	uint32_t curBlock;

	volume = rawFile->volume;
	forkData = rawFile->forkData;
	extent = rawFile->extents;

	blocksNeeded = ((uint64_t)size / (uint64_t)volume->volumeHeader->blockSize) + (((size % volume->volumeHeader->blockSize) == 0) ? 0 : 1);

	if(blocksNeeded > forkData->totalBlocks) {
		zeros = (unsigned char*) malloc(volume->volumeHeader->blockSize);
		memset(zeros, 0, volume->volumeHeader->blockSize);

		blocksToAllocate = blocksNeeded - forkData->totalBlocks;

		if(blocksToAllocate > volume->volumeHeader->freeBlocks) {
			return FALSE;
		}

		lastExtent = NULL;
		while(extent != NULL) {
			lastExtent = extent;
			extent = extent->next;
		}

		if(lastExtent == NULL) {
			rawFile->extents = (Extent*) malloc(sizeof(Extent));
			lastExtent = rawFile->extents;
			lastExtent->blockCount = 0;
			lastExtent->next = NULL;
			curBlock = volume->volumeHeader->nextAllocation;
		} else {
			curBlock = lastExtent->startBlock + lastExtent->blockCount;
		}

		while(blocksToAllocate > 0) {
			if(isBlockUsed(volume, curBlock)) {
				if(lastExtent->blockCount > 0) {
					lastExtent->next = (Extent*) malloc(sizeof(Extent));
					lastExtent = lastExtent->next;
					lastExtent->blockCount = 0;
					lastExtent->next = NULL;
				}
				curBlock = volume->volumeHeader->nextAllocation;
				volume->volumeHeader->nextAllocation++;
				if(volume->volumeHeader->nextAllocation >= volume->volumeHeader->totalBlocks) {
					volume->volumeHeader->nextAllocation = 0;
				}
			} else {
				if(lastExtent->blockCount == 0) {
					lastExtent->startBlock = curBlock;
				}

				/* zero out allocated block */
				ASSERT(WRITE(volume->image, curBlock * volume->volumeHeader->blockSize, volume->volumeHeader->blockSize, zeros), "WRITE");

				setBlockUsed(volume, curBlock, TRUE);
				volume->volumeHeader->freeBlocks--;
				blocksToAllocate--;
				curBlock++;
				lastExtent->blockCount++;

				if(curBlock >= volume->volumeHeader->totalBlocks) {
					curBlock = volume->volumeHeader->nextAllocation;
				}
			}
		}

		free(zeros);
	} else if(blocksNeeded < forkData->totalBlocks) {
		blocksToAllocate = blocksNeeded;

		lastExtent = NULL;

		while(blocksToAllocate > 0) {
			if(blocksToAllocate > extent->blockCount) {
				blocksToAllocate -= extent->blockCount;
				lastExtent = extent;
				extent = extent->next;
			} else {
				break;
			}
		}


		if(blocksToAllocate == 0 && lastExtent != NULL) {
			// snip the extent list here, since we don't need the rest
			lastExtent->next = NULL;
		} else if(blocksNeeded == 0) {
			rawFile->extents = NULL;
		}

		do {
			for(curBlock = (extent->startBlock + blocksToAllocate); curBlock < (extent->startBlock + extent->blockCount); curBlock++) {
				setBlockUsed(volume, curBlock, FALSE);
				volume->volumeHeader->freeBlocks++;
			}
			lastExtent = extent;
			extent = extent->next;

			if(blocksToAllocate == 0)
			{ 
				free(lastExtent);
			} else {
				lastExtent->next = NULL;
				lastExtent->blockCount = blocksToAllocate;
			}

			blocksToAllocate = 0;
		} while(extent != NULL);
	}

	writeExtents(rawFile);

	forkData->logicalSize = size;
	forkData->totalBlocks = blocksNeeded;

	updateVolume(rawFile->volume);

	if(rawFile->catalogRecord != NULL) {
		updateCatalog(rawFile->volume, rawFile->catalogRecord);
	}

	return TRUE;
}