/*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2003 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
+ * are subject to the Apple Public Source License Version 2.0 (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.
#include <sl.h>
#include <hfs/hfs_format.h>
+#include "hfs.h"
+
#define kBlockSize (0x200)
#define kMDBBaseOffset (2 * kBlockSize)
static CICell gCurrentIH;
static long long gAllocationOffset;
static long gIsHFSPlus;
+static long gCaseSensitive;
static long gBlockSize;
static long gCacheBlockSize;
static char *gBTreeHeaderBuffer;
static char *gHFSPlusHeader;
static HFSPlusVolumeHeader *gHFSPlus;
static char *gLinkTemp;
+static long long gVolID;
static char *gTempStr;
#else /* !__i386__ */
static long long gAllocationOffset;
static long gIsHFSPlus;
static long gBlockSize;
+static long gCaseSensitive;
static long gCacheBlockSize;
static char gBTreeHeaderBuffer[512];
static BTHeaderRec *gBTHeaders[2];
static char gHFSPlusHeader[kBlockSize];
static HFSPlusVolumeHeader *gHFSPlus =(HFSPlusVolumeHeader*)gHFSPlusHeader;
static char gLinkTemp[64];
+static long long gVolID;
#endif /* !__i386__ */
-static long ReadFile(void *file, long *length);
-static long GetCatalogEntryInfo(void *entry, long *flags, long *time);
+static long ReadFile(void *file, unsigned long *length, void *base, long offset);
+static long GetCatalogEntryInfo(void *entry, long *flags, long *time,
+ FinderInfo *finderInfo, long *infoValid);
static long ResolvePathToCatalogEntry(char *filePath, long *flags,
void *entry, long dirID, long *dirIndex);
static long GetCatalogEntry(long *dirIndex, char **name,
- long *flags, long *time);
+ long *flags, long *time,
+ FinderInfo *finderInfo, long *infoValid);
static long ReadCatalogEntry(char *fileName, long dirID, void *entry,
long *dirIndex);
static long ReadExtentsEntry(long fileID, long startBlock, void *entry);
static long CompareHFSExtentsKeys(void *key, void *testKey);
static long CompareHFSPlusExtentsKeys(void *key, void *testKey);
-extern long FastRelString(char *str1, char *str2);
+extern long FastRelString(u_int8_t *str1, u_int8_t *str2);
extern long FastUnicodeCompare(u_int16_t *uniStr1, u_int32_t len1,
u_int16_t *uniStr2, u_int32_t len2);
-extern void utf_encodestr(const u_int16_t *ucsp, int ucslen,
- u_int8_t *utf8p, u_int32_t bufsize);
-extern void utf_decodestr(const u_int8_t *utf8p, u_int16_t *ucsp,
- u_int16_t *ucslen, u_int32_t bufsize);
+extern long BinaryUnicodeCompare(u_int16_t *uniStr1, u_int32_t len1,
+ u_int16_t *uniStr2, u_int32_t len2);
+static void
+SwapFinderInfo(FndrFileInfo *dst, FndrFileInfo *src)
+{
+ dst->fdType = SWAP_BE32(src->fdType);
+ dst->fdCreator = SWAP_BE32(src->fdCreator);
+ dst->fdFlags = SWAP_BE16(src->fdFlags);
+ // Don't bother with location
+}
+
long HFSInitPartition(CICell ih)
{
long extentSize, extentFile, nodeSize;
return 0;
}
- verbose("HFSInitPartition: %x\n", ih);
-
#ifdef __i386__
if (!gTempStr) gTempStr = (char *)malloc(4096);
if (!gLinkTemp) gLinkTemp = (char *)malloc(64);
gAllocationOffset = 0;
gIsHFSPlus = 0;
+ gCaseSensitive = 0;
gBTHeaders[0] = 0;
gBTHeaders[1] = 0;
CacheInit(ih, gCacheBlockSize);
gCurrentIH = ih;
+ // grab the 64 bit volume ID
+ bcopy(&gHFSMDB->drFndrInfo[6], &gVolID, 8);
+
// Get the Catalog BTree node size.
extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec;
extentSize = SWAP_BE32(gHFSMDB->drCTFlSize);
Seek(ih, gAllocationOffset + kMDBBaseOffset);
Read(ih, (long)gHFSPlusHeader, kBlockSize);
- // Not a HFS[+] volume.
- if (SWAP_BE16(gHFSPlus->signature) != kHFSPlusSigWord) return -1;
+ // Not a HFS+ or HFSX volume.
+ if (SWAP_BE16(gHFSPlus->signature) != kHFSPlusSigWord &&
+ SWAP_BE16(gHFSPlus->signature) != kHFSXSigWord) {
+ verbose("HFS signature was not present.\n");
+ gCurrentIH = 0;
+ return -1;
+ }
gIsHFSPlus = 1;
gCacheBlockSize = gBlockSize = SWAP_BE32(gHFSPlus->blockSize);
CacheInit(ih, gCacheBlockSize);
gCurrentIH = ih;
+ // grab the 64 bit volume ID
+ bcopy(&gHFSPlus->finderInfo[24], &gVolID, 8);
+
// Get the Catalog BTree node size.
extent = &gHFSPlus->catalogFile.extents;
extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize);
long HFSLoadFile(CICell ih, char * filePath)
{
- char entry[512];
- long dirID, result, length, flags;
+ return HFSReadFile(ih, filePath, (void *)gFSLoadAddress, 0, 0);
+}
- if (HFSInitPartition(ih) == -1) return -1;
+long HFSReadFile(CICell ih, char * filePath, void *base, unsigned long offset, unsigned long length)
+{
+ char entry[512];
+ long dirID, result, flags;
verbose("Loading HFS%s file: [%s] from %x.\n",
(gIsHFSPlus ? "+" : ""), filePath, ih);
+ if (HFSInitPartition(ih) == -1) return -1;
+
dirID = kHFSRootFolderID;
// Skip a lead '\'. Start in the system folder if there are two.
if (filePath[0] == '/') {
if (filePath[1] == '/') {
if (gIsHFSPlus) dirID = SWAP_BE32(((long *)gHFSPlus->finderInfo)[5]);
else dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]);
- if (dirID == 0) return -1;
+ if (dirID == 0) {
+ return -1;
+ }
filePath++;
}
filePath++;
}
result = ResolvePathToCatalogEntry(filePath, &flags, entry, dirID, 0);
- if ((result == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1;
+ if ((result == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) {
+ return -1;
+ }
-#if 0 // Not yet for Intel. System.config/Default.table will fail this check.
+#if UNUSED
+ // Not yet for Intel. System.config/Default.table will fail this check.
// Check file owner and permissions.
if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) return -1;
#endif
- result = ReadFile(entry, &length);
- if (result == -1) return -1;
+ result = ReadFile(entry, &length, base, offset);
+ if (result == -1) {
+ return -1;
+ }
return length;
}
long HFSGetDirEntry(CICell ih, char * dirPath, long * dirIndex, char ** name,
- long * flags, long * time)
+ long * flags, long * time,
+ FinderInfo * finderInfo, long * infoValid)
{
char entry[512];
long dirID, dirFlags;
if ((dirFlags & kFileTypeMask) != kFileTypeUnknown) return -1;
}
- GetCatalogEntry(dirIndex, name, flags, time);
+ GetCatalogEntry(dirIndex, name, flags, time, finderInfo, infoValid);
if (*dirIndex == 0) *dirIndex = -1;
if ((*flags & kFileTypeMask) == kFileTypeUnknown) return -1;
return 0;
}
+void
+HFSGetDescription(CICell ih, char *str, long strMaxLen)
+{
+
+ UInt16 nodeSize;
+ UInt32 firstLeafNode;
+ long dirIndex;
+ char *name;
+ long flags, time;
+
+ if (HFSInitPartition(ih) == -1) { return; }
+
+ /* Fill some crucial data structures by side effect. */
+ dirIndex = 0;
+ HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0);
+
+ /* Now we can loook up the volume name node. */
+ nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);
+ firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode);
+
+ dirIndex = firstLeafNode * nodeSize;
+
+ GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0);
+
+ strncpy(str, name, strMaxLen);
+ str[strMaxLen] = '\0';
+}
+
+
+long
+HFSGetFileBlock(CICell ih, char *filePath, unsigned long long *firstBlock)
+{
+ char entry[512];
+ long dirID, result, flags;
+ void *extents;
+ HFSCatalogFile *hfsFile = (void *)entry;
+ HFSPlusCatalogFile *hfsPlusFile = (void *)entry;
+
+ if (HFSInitPartition(ih) == -1) return -1;
+
+ dirID = kHFSRootFolderID;
+ // Skip a lead '\'. Start in the system folder if there are two.
+ if (filePath[0] == '/') {
+ if (filePath[1] == '/') {
+ if (gIsHFSPlus) dirID = SWAP_BE32(((long *)gHFSPlus->finderInfo)[5]);
+ else dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]);
+ if (dirID == 0) {
+ return -1;
+ }
+ filePath++;
+ }
+ filePath++;
+ }
+
+ result = ResolvePathToCatalogEntry(filePath, &flags, entry, dirID, 0);
+ if ((result == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) {
+ printf("HFS: Resolve path %s failed\n", filePath);
+ return -1;
+ }
+
+ if (gIsHFSPlus) {
+ extents = &hfsPlusFile->dataFork.extents;
+ } else {
+ extents = &hfsFile->dataExtents;
+ }
+
+#if DEBUG
+ printf("extent start 0x%x\n", (unsigned long)GetExtentStart(extents, 0));
+ printf("block size 0x%x\n", (unsigned long)gBlockSize);
+ printf("Allocation offset 0x%x\n", (unsigned long)gAllocationOffset);
+#endif
+ *firstBlock = ((unsigned long long)GetExtentStart(extents, 0) * (unsigned long long) gBlockSize + gAllocationOffset) / 512ULL;
+ return 0;
+}
+
+long HFSGetUUID(CICell ih, char *uuidStr)
+{
+ if (HFSInitPartition(ih) == -1) return -1;
+ if (gVolID == 0LL) return -1;
+
+ return CreateUUIDString((uint8_t*)(&gVolID), sizeof(gVolID), uuidStr);
+}
+
// Private Functions
-static long ReadFile(void * file, long * length)
+static long ReadFile(void * file, unsigned long * length, void * base, long offset)
{
void *extents;
long fileID;
+ long fileLength;
HFSCatalogFile *hfsFile = file;
HFSPlusCatalogFile *hfsPlusFile = file;
if (gIsHFSPlus) {
fileID = SWAP_BE32(hfsPlusFile->fileID);
- *length = SWAP_BE64(hfsPlusFile->dataFork.logicalSize);
+ fileLength = (long)SWAP_BE64(hfsPlusFile->dataFork.logicalSize);
extents = &hfsPlusFile->dataFork.extents;
} else {
fileID = SWAP_BE32(hfsFile->fileID);
- *length = SWAP_BE32(hfsFile->dataLogicalSize);
+ fileLength = SWAP_BE32(hfsFile->dataLogicalSize);
extents = &hfsFile->dataExtents;
}
+ if (offset > fileLength) {
+ printf("Offset is too large.\n");
+ return -1;
+ }
+
+ if ((*length == 0) || ((offset + *length) > fileLength)) {
+ *length = fileLength - offset;
+ }
+
if (*length > kLoadSize) {
printf("File is too large.\n");
return -1;
}
-#ifdef __i386__
- *length = ReadExtent((char *)extents, *length, fileID,
- 0, *length, (char *)gFSLoadAddress, 0);
-#else
- *length = ReadExtent((char *)extents, *length, fileID,
- 0, *length, (char *)kLoadAddr, 0);
-#endif
+ *length = ReadExtent((char *)extents, fileLength, fileID,
+ offset, *length, (char *)base, 0);
return 0;
}
-static long GetCatalogEntryInfo(void * entry, long * flags, long * time)
+static long GetCatalogEntryInfo(void * entry, long * flags, long * time,
+ FinderInfo * finderInfo, long * infoValid)
{
long tmpTime = 0;
+ long valid = 0;
// Get information about the file.
case kHFSFileRecord :
*flags = kFileTypeFlat;
tmpTime = SWAP_BE32(((HFSCatalogFile *)entry)->modifyDate);
+ if (finderInfo) {
+ SwapFinderInfo((FndrFileInfo *)finderInfo, &((HFSCatalogFile *)entry)->userInfo);
+ valid = 1;
+ }
break;
case kHFSPlusFileRecord :
if (SWAP_BE32(((HFSPlusCatalogFile *)entry)->bsdInfo.ownerID) != 0)
*flags |= kOwnerNotRoot;
tmpTime = SWAP_BE32(((HFSPlusCatalogFile *)entry)->contentModDate);
+ if (finderInfo) {
+ SwapFinderInfo((FndrFileInfo *)finderInfo, &((HFSPlusCatalogFile *)entry)->userInfo);
+ valid = 1;
+ }
break;
case kHFSFileThreadRecord :
// Convert base time from 1904 to 1970.
*time = tmpTime - 2082844800;
}
+ if (infoValid) *infoValid = valid;
return 0;
}
void * entry, long dirID, long * dirIndex)
{
char *restPath;
- long result, cnt, subFolderID, tmpDirIndex;
+ long result, cnt, subFolderID = 0, tmpDirIndex;
HFSPlusCatalogFile *hfsPlusFile;
// Copy the file name to gTempStr
cnt = 0;
while ((filePath[cnt] != '/') && (filePath[cnt] != '\0')) cnt++;
- strncpy(gTempStr, filePath, cnt);
+ strlcpy(gTempStr, filePath, cnt+1);
// Move restPath to the right place.
if (filePath[cnt] != '\0') cnt++;
// restPath is the rest of the path if any.
result = ReadCatalogEntry(gTempStr, dirID, entry, dirIndex);
- if (result == -1) return -1;
+ if (result == -1) {
+ return -1;
+ }
- GetCatalogEntryInfo(entry, flags, 0);
+ GetCatalogEntryInfo(entry, flags, 0, 0, 0);
if ((*flags & kFileTypeMask) == kFileTypeDirectory) {
if (gIsHFSPlus)
subFolderID = SWAP_BE32(((HFSPlusCatalogFolder *)entry)->folderID);
else
subFolderID = SWAP_BE32(((HFSCatalogFolder *)entry)->folderID);
+ }
+ if ((*flags & kFileTypeMask) == kFileTypeDirectory)
result = ResolvePathToCatalogEntry(restPath, flags, entry,
subFolderID, dirIndex);
- }
if (gIsHFSPlus && ((*flags & kFileTypeMask) == kFileTypeFlat)) {
hfsPlusFile = (HFSPlusCatalogFile *)entry;
}
static long GetCatalogEntry(long * dirIndex, char ** name,
- long * flags, long * time)
+ long * flags, long * time,
+ FinderInfo * finderInfo, long * infoValid)
{
long extentSize, nodeSize, curNode, index;
void *extent;
curNode * nodeSize, nodeSize, nodeBuf, 1);
GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &entry);
- GetCatalogEntryInfo(entry, flags, time);
+ GetCatalogEntryInfo(entry, flags, time, finderInfo, infoValid);
// Get the file name.
if (gIsHFSPlus) {
utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode,
SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length),
- gTempStr, 256);
+ (u_int8_t *)gTempStr, 256, OSBigEndian);
} else {
strncpy(gTempStr,
- &((HFSCatalogKey *)testKey)->nodeName[1],
+ (const char *)&((HFSCatalogKey *)testKey)->nodeName[1],
((HFSCatalogKey *)testKey)->nodeName[0]);
}
*name = gTempStr;
hfsPlusKey->parentID = SWAP_BE32(dirID);
length = strlen(fileName);
if (length > 255) length = 255;
- utf_decodestr(fileName, hfsPlusKey->nodeName.unicode,
- &(hfsPlusKey->nodeName.length), 512);
+ utf_decodestr((u_int8_t *)fileName, hfsPlusKey->nodeName.unicode,
+ &(hfsPlusKey->nodeName.length), 512, OSBigEndian);
} else {
hfsKey->parentID = SWAP_BE32(dirID);
length = strlen(fileName);
if (length > 31) length = 31;
hfsKey->nodeName[0] = length;
- strncpy(hfsKey->nodeName + 1, fileName, length);
+ strncpy((char *)(hfsKey->nodeName + 1), fileName, length);
}
return ReadBTreeEntry(kBTreeCatalog, &key, entry, dirIndex);
gBTreeHeaderBuffer + btree * 256, 0);
gBTHeaders[btree] = (BTHeaderRec *)(gBTreeHeaderBuffer + btree * 256 +
sizeof(BTNodeDescriptor));
+ if ((gIsHFSPlus && btree == kBTreeCatalog) &&
+ (gBTHeaders[btree]->keyCompareType == kHFSBinaryCompare)) {
+ gCaseSensitive = 1;
+ }
}
curNode = SWAP_BE32(gBTHeaders[btree]->rootNode);
if ((searchKey->nodeName.length == 0) || (trialKey->nodeName.length == 0))
result = searchKey->nodeName.length - trialKey->nodeName.length;
else
+ if (gCaseSensitive) {
+ result = BinaryUnicodeCompare(&searchKey->nodeName.unicode[0],
+ SWAP_BE16(searchKey->nodeName.length),
+ &trialKey->nodeName.unicode[0],
+ SWAP_BE16(trialKey->nodeName.length));
+ } else {
result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
SWAP_BE16(searchKey->nodeName.length),
&trialKey->nodeName.unicode[0],
SWAP_BE16(trialKey->nodeName.length));
+ }
}
return result;
return result;
}
+