HFSCatalogNodeID newFolder(const char* pathName, Volume* volume) { HFSPlusCatalogFolder* parentFolder; HFSPlusCatalogFolder folder; HFSPlusCatalogKey key; HFSPlusCatalogThread thread; uint32_t newFolderID; int threadLength; char* path; char* name; char* curChar; char* lastSeparator; path = strdup(pathName); curChar = path; lastSeparator = NULL; while((*curChar) != '\0') { if((*curChar) == '/') lastSeparator = curChar; curChar++; } if(lastSeparator == NULL) { parentFolder = (HFSPlusCatalogFolder*) getRecordFromPath("/", volume, NULL, NULL); name = path; } else { name = lastSeparator + 1; *lastSeparator = '\0'; parentFolder = (HFSPlusCatalogFolder*) getRecordFromPath(path, volume, NULL, NULL); } if(parentFolder == NULL || parentFolder->recordType != kHFSPlusFolderRecord) { free(path); free(parentFolder); return FALSE; } newFolderID = volume->volumeHeader->nextCatalogID++; volume->volumeHeader->folderCount++; folder.recordType = kHFSPlusFolderRecord; folder.flags = kHFSHasFolderCountMask; folder.valence = 0; folder.folderID = newFolderID; folder.createDate = UNIX_TO_APPLE_TIME(time(NULL)); folder.contentModDate = folder.createDate; folder.attributeModDate = folder.createDate; folder.accessDate = folder.createDate; folder.backupDate = folder.createDate; folder.permissions.ownerID = parentFolder->permissions.ownerID; folder.permissions.groupID = parentFolder->permissions.groupID; folder.permissions.adminFlags = 0; folder.permissions.ownerFlags = 0; folder.permissions.fileMode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; folder.permissions.special.iNodeNum = 0; memset(&folder.userInfo, 0, sizeof(folder.userInfo)); memset(&folder.finderInfo, 0, sizeof(folder.finderInfo)); folder.textEncoding = 0; folder.folderCount = 0; key.parentID = parentFolder->folderID; ASCIIToUnicode(name, &key.nodeName); key.keyLength = sizeof(key.parentID) + STR_SIZE(key.nodeName); thread.recordType = kHFSPlusFolderThreadRecord; thread.reserved = 0; thread.parentID = parentFolder->folderID; ASCIIToUnicode(name, &thread.nodeName); threadLength = sizeof(thread.recordType) + sizeof(thread.reserved) + sizeof(thread.parentID) + STR_SIZE(thread.nodeName); flipCatalogThread(&thread, TRUE); flipCatalogFolder(&folder); ASSERT(addToBTree(volume->catalogTree, (BTKey*)(&key), sizeof(HFSPlusCatalogFolder), (unsigned char *)(&folder)), "addToBTree"); key.nodeName.length = 0; key.parentID = newFolderID; key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); ASSERT(addToBTree(volume->catalogTree, (BTKey*)(&key), threadLength, (unsigned char *)(&thread)), "addToBTree"); parentFolder->folderCount++; parentFolder->valence++; updateCatalog(volume, (HFSPlusCatalogRecord*) parentFolder); updateVolume(volume); free(parentFolder); free(path); return newFolderID; }
int move(const char* source, const char* dest, Volume* volume) { HFSPlusCatalogRecord* srcRec; HFSPlusCatalogFolder* srcFolderRec; HFSPlusCatalogFolder* destRec; char* destPath; char* destName; char* curChar; char* lastSeparator; int i; int threadLength; HFSPlusCatalogKey srcKey; HFSPlusCatalogKey destKey; HFSPlusCatalogThread* thread; srcRec = getRecordFromPath3(source, volume, NULL, &srcKey, TRUE, FALSE, kHFSRootFolderID); if(srcRec == NULL) { free(srcRec); return FALSE; } srcFolderRec = (HFSPlusCatalogFolder*) getRecordByCNID(srcKey.parentID, volume); if(srcFolderRec == NULL || srcFolderRec->recordType != kHFSPlusFolderRecord) { free(srcRec); free(srcFolderRec); return FALSE; } destPath = strdup(dest); curChar = destPath; lastSeparator = NULL; while((*curChar) != '\0') { if((*curChar) == '/') lastSeparator = curChar; curChar++; } if(lastSeparator == NULL) { destRec = (HFSPlusCatalogFolder*) getRecordFromPath("/", volume, NULL, NULL); destName = destPath; } else { destName = lastSeparator + 1; *lastSeparator = '\0'; destRec = (HFSPlusCatalogFolder*) getRecordFromPath(destPath, volume, NULL, NULL); if(destRec == NULL || destRec->recordType != kHFSPlusFolderRecord) { free(destPath); free(srcRec); free(destRec); free(srcFolderRec); return FALSE; } } removeFromBTree(volume->catalogTree, (BTKey*)(&srcKey)); srcKey.nodeName.length = 0; if(srcRec->recordType == kHFSPlusFolderRecord) { srcKey.parentID = ((HFSPlusCatalogFolder*)srcRec)->folderID; } else if(srcRec->recordType == kHFSPlusFileRecord) { srcKey.parentID = ((HFSPlusCatalogFile*)srcRec)->fileID; } else { /* unexpected */ return FALSE; } srcKey.keyLength = sizeof(srcKey.parentID) + sizeof(srcKey.nodeName.length); removeFromBTree(volume->catalogTree, (BTKey*)(&srcKey)); destKey.nodeName.length = strlen(destName); threadLength = sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint16_t) + (sizeof(uint16_t) * destKey.nodeName.length); thread = (HFSPlusCatalogThread*) malloc(threadLength); thread->reserved = 0; destKey.parentID = destRec->folderID; thread->parentID = destKey.parentID; thread->nodeName.length = destKey.nodeName.length; for(i = 0; i < destKey.nodeName.length; i++) { destKey.nodeName.unicode[i] = destName[i]; thread->nodeName.unicode[i] = destName[i]; } destKey.keyLength = sizeof(uint32_t) + sizeof(uint16_t) + (sizeof(uint16_t) * destKey.nodeName.length); switch(srcRec->recordType) { case kHFSPlusFolderRecord: thread->recordType = kHFSPlusFolderThreadRecord; flipCatalogFolder((HFSPlusCatalogFolder*)srcRec); addToBTree(volume->catalogTree, (BTKey*)(&destKey), sizeof(HFSPlusCatalogFolder), (unsigned char *)(srcRec)); break; case kHFSPlusFileRecord: thread->recordType = kHFSPlusFileThreadRecord; flipCatalogFile((HFSPlusCatalogFile*)srcRec); addToBTree(volume->catalogTree, (BTKey*)(&destKey), sizeof(HFSPlusCatalogFile), (unsigned char *)(srcRec)); break; } destKey.nodeName.length = 0; destKey.parentID = srcKey.parentID; destKey.keyLength = sizeof(destKey.parentID) + sizeof(destKey.nodeName.length); flipCatalogThread(thread, TRUE); addToBTree(volume->catalogTree, (BTKey*)(&destKey), threadLength, (unsigned char *)(thread)); /* adjust valence */ srcFolderRec->valence--; updateCatalog(volume, (HFSPlusCatalogRecord*) srcFolderRec); destRec->valence++; updateCatalog(volume, (HFSPlusCatalogRecord*) destRec); free(thread); free(destPath); free(srcRec); free(destRec); free(srcFolderRec); return TRUE; }
int removeFile(const char* fileName, Volume* volume) { HFSPlusCatalogRecord* record; HFSPlusCatalogKey key; io_func* io; HFSPlusCatalogFolder* parentFolder; record = getRecordFromPath3(fileName, volume, NULL, &key, TRUE, FALSE, kHFSRootFolderID); if(record != NULL) { parentFolder = (HFSPlusCatalogFolder*) getRecordByCNID(key.parentID, volume); if(parentFolder != NULL) { if(parentFolder->recordType != kHFSPlusFolderRecord) { ASSERT(FALSE, "parent not folder"); free(parentFolder); return FALSE; } } else { ASSERT(FALSE, "can't find parent"); return FALSE; } if(record->recordType == kHFSPlusFileRecord) { io = openRawFile(((HFSPlusCatalogFile*)record)->fileID, &((HFSPlusCatalogFile*)record)->dataFork, record, volume); allocate((RawFile*)io->data, 0); CLOSE(io); removeFromBTree(volume->catalogTree, (BTKey*)(&key)); key.nodeName.length = 0; key.parentID = ((HFSPlusCatalogFile*)record)->fileID; key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); removeFromBTree(volume->catalogTree, (BTKey*)(&key)); volume->volumeHeader->fileCount--; } else { if(((HFSPlusCatalogFolder*)record)->valence > 0) { free(record); free(parentFolder); ASSERT(FALSE, "folder not empty"); return FALSE; } else { removeFromBTree(volume->catalogTree, (BTKey*)(&key)); key.nodeName.length = 0; key.parentID = ((HFSPlusCatalogFolder*)record)->folderID; key.keyLength = sizeof(key.parentID) + sizeof(key.nodeName.length); removeFromBTree(volume->catalogTree, (BTKey*)(&key)); } parentFolder->folderCount--; volume->volumeHeader->folderCount--; } parentFolder->valence--; updateCatalog(volume, (HFSPlusCatalogRecord*) parentFolder); updateVolume(volume); free(record); free(parentFolder); return TRUE; } else { free(parentFolder); ASSERT(FALSE, "cannot find record"); return FALSE; } }
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, ((uint64_t)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; }