#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/dirent.h>
#include <vfs/vfs_support.h>
#include <libkern/libkern.h>
#include <sys/utfconv.h>
#include "hfs.h"
#include "hfs_catalog.h"
#include "hfs_format.h"
#include "hfs_endian.h"
#include "hfscommon/headers/BTreesInternal.h"
#include "hfscommon/headers/BTreesPrivate.h"
#include "hfscommon/headers/HFSUnicodeWrappers.h"
#define BDINIT(bd, addr) { \
(bd).bufferAddress = (addr); \
(bd).itemSize = sizeof(*(addr)); \
(bd).itemCount = 1; \
}
struct btobj {
BTreeIterator iterator;
HFSPlusCatalogKey key;
CatalogRecord data;
};
struct update_state {
struct cat_desc * s_desc;
struct cat_attr * s_attr;
struct cat_fork * s_datafork;
struct cat_fork * s_rsrcfork;
struct hfsmount * s_hfsmp;
};
struct position_state {
int error;
u_int32_t count;
u_int32_t index;
u_int32_t parentID;
struct hfsmount *hfsmp;
};
u_char modetodirtype[16] = {
DT_REG, DT_FIFO, DT_CHR, DT_UNKNOWN,
DT_DIR, DT_UNKNOWN, DT_BLK, DT_UNKNOWN,
DT_REG, DT_UNKNOWN, DT_LNK, DT_UNKNOWN,
DT_SOCK, DT_UNKNOWN, DT_WHT, DT_UNKNOWN
};
#define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files, u_long hint, int wantrsrc,
struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid);
static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp);
static int cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
struct position_state *state);
static int resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino);
static int getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key);
static int buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
HFSPlusCatalogKey *key, int retry);
static void buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key);
static void buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding, CatalogRecord *crp, u_int32_t *recordSize);
static int catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state);
static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
int isdir, struct cat_desc *descp);
static void getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp);
static void promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey, HFSPlusCatalogKey *keyp, u_long *encoding);
static void promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *file, int resource, struct cat_fork * forkp);
static void promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp);
static cnid_t getcnid(const CatalogRecord *crp);
static u_long getencoding(const CatalogRecord *crp);
static cnid_t getparentcnid(const CatalogRecord *recp);
static int isadir(const CatalogRecord *crp);
static int buildthread(void *keyp, void *recp, int std_hfs, int directory);
static int cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp);
__private_extern__
int
cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, __unused proc_t p)
{
int lockflags = 0;
int result;
if (hfsmp->hfs_catalog_cp->c_lockowner != current_thread())
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
result = BTReserveSpace(hfsmp->hfs_catalog_cp->c_datafork, ops, (void*)cookie);
if (lockflags)
hfs_systemfile_unlock(hfsmp, lockflags);
return MacToVFSError(result);
}
__private_extern__
void
cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, __unused proc_t p)
{
int lockflags = 0;
if (hfsmp->hfs_catalog_cp->c_lockowner != current_thread())
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
(void) BTReleaseReserve(hfsmp->hfs_catalog_cp->c_datafork, (void*)cookie);
if (lockflags)
hfs_systemfile_unlock(hfsmp, lockflags);
}
__private_extern__
void
cat_convertattr(
struct hfsmount *hfsmp,
CatalogRecord * recp,
struct cat_attr *attrp,
struct cat_fork *datafp,
struct cat_fork *rsrcfp)
{
int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
if (std_hfs) {
struct HFSPlusCatalogFile cnoderec;
promoteattr(hfsmp, recp, &cnoderec);
getbsdattr(hfsmp, &cnoderec, attrp);
} else {
getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
}
if (isadir(recp))
bzero(datafp, sizeof(*datafp));
else if (std_hfs) {
promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 0, datafp);
promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 1, rsrcfp);
} else {
datafp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
datafp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
if ((hfsmp->hfc_stage == HFC_RECORDING) &&
(attrp->ca_atime >= hfsmp->hfc_timebase)) {
datafp->cf_bytesread =
recp->hfsPlusFile.dataFork.clumpSize *
HFSTOVCB(hfsmp)->blockSize;
} else {
datafp->cf_bytesread = 0;
}
datafp->cf_vblocks = 0;
bcopy(&recp->hfsPlusFile.dataFork.extents[0],
&datafp->cf_extents[0], sizeof(HFSPlusExtentRecord));
rsrcfp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
rsrcfp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
if ((hfsmp->hfc_stage == HFC_RECORDING) &&
(attrp->ca_atime >= hfsmp->hfc_timebase)) {
datafp->cf_bytesread =
recp->hfsPlusFile.resourceFork.clumpSize *
HFSTOVCB(hfsmp)->blockSize;
} else {
datafp->cf_bytesread = 0;
}
rsrcfp->cf_vblocks = 0;
bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
&rsrcfp->cf_extents[0], sizeof(HFSPlusExtentRecord));
}
}
__private_extern__
int
cat_convertkey(
struct hfsmount *hfsmp,
CatalogKey *key,
CatalogRecord * recp,
struct cat_desc *descp)
{
int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
HFSPlusCatalogKey * pluskey = NULL;
u_long encoding;
if (std_hfs) {
MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
} else {
pluskey = (HFSPlusCatalogKey *)key;
encoding = getencoding(recp);
}
builddesc(pluskey, getcnid(recp), 0, encoding, isadir(recp), descp);
if (std_hfs) {
FREE(pluskey, M_TEMP);
}
return (0);
}
__private_extern__
void
cat_releasedesc(struct cat_desc *descp)
{
const u_int8_t * name;
if (descp == NULL)
return;
if ((descp->cd_flags & CD_HASBUF) &&
(descp->cd_nameptr != NULL)) {
name = descp->cd_nameptr;
descp->cd_nameptr = NULL;
descp->cd_namelen = 0;
vfs_removename((const char *)name);
}
descp->cd_nameptr = NULL;
descp->cd_namelen = 0;
descp->cd_flags &= ~CD_HASBUF;
}
__private_extern__
int
cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
struct cat_desc *outdescp, struct cat_attr *attrp,
struct cat_fork *forkp, cnid_t *desc_cnid)
{
CatalogKey * keyp;
int std_hfs;
int result;
std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
MALLOC(keyp, CatalogKey *, sizeof(CatalogKey), M_TEMP, M_WAITOK);
result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)keyp, 1);
if (result)
goto exit;
result = cat_lookupbykey(hfsmp, keyp, 0, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid);
if (result == ENOENT) {
if (!std_hfs) {
struct cat_desc temp_desc;
if (outdescp == NULL) {
bzero(&temp_desc, sizeof(temp_desc));
outdescp = &temp_desc;
}
result = cat_lookupmangled(hfsmp, descp, wantrsrc, outdescp, attrp, forkp);
if (desc_cnid) {
*desc_cnid = outdescp->cd_cnid;
}
if (outdescp == &temp_desc) {
cat_releasedesc(outdescp);
}
} else if (hfsmp->hfs_encoding != kTextEncodingMacRoman) {
}
}
exit:
FREE(keyp, M_TEMP);
return (result);
}
__private_extern__
int
cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
{
struct BTreeIterator *iterator;
struct FSBufferDescriptor file_data;
struct HFSCatalogFile file_rec;
u_int16_t datasize;
FCB *fcb;
int result;
if (HFSTOVCB(hfsmp)->vcbSigWord != kHFSSigWord)
return (EINVAL);
fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
MALLOC(iterator, BTreeIterator *, 2 * sizeof(*iterator), M_TEMP, M_WAITOK);
bzero(&iterator[0], 2* sizeof(*iterator));
result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator[0].key, 0);
if (result)
goto exit;
BDINIT(file_data, &file_rec);
result = BTSearchRecord(fcb, &iterator[0], &file_data, &datasize, &iterator[0]);
if (result)
goto exit;
if (file_rec.recordType != kHFSFileRecord) {
result = EISDIR;
goto exit;
}
if ((file_rec.flags & kHFSThreadExistsMask) == 0) {
struct FSBufferDescriptor thread_data;
struct HFSCatalogThread thread_rec;
file_rec.flags |= kHFSThreadExistsMask;
BDINIT(thread_data, &thread_rec);
thread_data.itemSize = buildthread(&iterator[0].key, &thread_rec, 1, 0);
buildthreadkey(file_rec.fileID, 1, (CatalogKey *)&iterator[1].key);
result = BTInsertRecord(fcb, &iterator[1], &thread_data, thread_data.itemSize);
if (result)
goto exit;
(void) BTReplaceRecord(fcb, &iterator[0], &file_data, datasize);
(void) BTFlushPath(fcb);
}
exit:
(void) BTFlushPath(fcb);
FREE(iterator, M_TEMP);
return MacToVFSError(result);
}
__private_extern__
int
cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
{
struct BTreeIterator * iterator;
FSBufferDescriptor btdata;
CatalogKey * keyp;
CatalogRecord * recp;
int isdir;
int result;
int std_hfs;
isdir = 0;
std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
iterator->hint.nodeNum = 0;
MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
BDINIT(btdata, recp);
result = BTSearchRecord(VTOF(hfsmp->hfs_catalog_vp), iterator, &btdata, NULL, NULL);
if (result)
goto exit;
switch (recp->recordType) {
case kHFSFolderThreadRecord:
isdir = 1;
case kHFSFileThreadRecord:
keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
break;
case kHFSPlusFolderThreadRecord:
isdir = 1;
case kHFSPlusFileThreadRecord:
keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
(keyp->hfsPlus.nodeName.length * 2);
break;
default:
result = ENOENT;
goto exit;
}
if (std_hfs) {
HFSPlusCatalogKey * pluskey = NULL;
u_long encoding;
MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
promotekey(hfsmp, &keyp->hfs, pluskey, &encoding);
builddesc(pluskey, cnid, 0, encoding, isdir, outdescp);
FREE(pluskey, M_TEMP);
} else {
builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp);
}
exit:
FREE(recp, M_TEMP);
FREE(iterator, M_TEMP);
return MacToVFSError(result);
}
__private_extern__
int
cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files,
struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
{
struct BTreeIterator * iterator;
FSBufferDescriptor btdata;
u_int16_t datasize;
CatalogKey * keyp;
CatalogRecord * recp;
int result;
int std_hfs;
std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
bzero(iterator, sizeof(*iterator));
buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
BDINIT(btdata, recp);
result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
&btdata, &datasize, iterator);
if (result)
goto exit;
switch (recp->recordType) {
case kHFSFileThreadRecord:
case kHFSFolderThreadRecord:
keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
break;
case kHFSPlusFileThreadRecord:
case kHFSPlusFolderThreadRecord:
keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
(keyp->hfsPlus.nodeName.length * 2);
break;
default:
result = ENOENT;
goto exit;
}
result = cat_lookupbykey(hfsmp, keyp, allow_system_files, 0, 0, outdescp, attrp, forkp, NULL);
if (result == 0 && outdescp) {
cnid_t dcnid = outdescp->cd_cnid;
if (cnid != dcnid) {
printf("Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid, cnid, dcnid, dcnid);
result = ENOENT;
}
}
exit:
FREE(recp, M_TEMP);
FREE(iterator, M_TEMP);
return MacToVFSError(result);
}
static int
cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
{
cnid_t fileID;
u_int32_t prefixlen;
int result;
if (wantrsrc)
return (ENOENT);
fileID = GetEmbeddedFileID(descp->cd_nameptr, descp->cd_namelen, &prefixlen);
if (fileID < (cnid_t)kHFSFirstUserCatalogNodeID)
return (ENOENT);
if (fileID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
fileID == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid ||
fileID == hfsmp->hfs_jnlfileid ||
fileID == hfsmp->hfs_jnlinfoblkid) {
return (ENOENT);
}
result = cat_idlookup(hfsmp, fileID, 0, outdescp, attrp, forkp);
if (result)
return (ENOENT);
if (descp->cd_parentcnid != outdescp->cd_parentcnid)
goto falsematch;
if (((u_int16_t)outdescp->cd_namelen < prefixlen) ||
bcmp(outdescp->cd_nameptr, descp->cd_nameptr, prefixlen-6) != 0)
goto falsematch;
return (0);
falsematch:
cat_releasedesc(outdescp);
return (ENOENT);
}
static int
cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files, u_long hint, int wantrsrc,
struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid)
{
struct BTreeIterator * iterator;
FSBufferDescriptor btdata;
CatalogRecord * recp;
u_int16_t datasize;
int result;
int std_hfs;
u_long ilink = 0;
cnid_t cnid = 0;
u_long encoding = 0;
std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
BDINIT(btdata, recp);
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
bzero(iterator, sizeof(*iterator));
iterator->hint.nodeNum = hint;
bcopy(keyp, &iterator->key, sizeof(CatalogKey));
result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
&btdata, &datasize, iterator);
if (result)
goto exit;
cnid = getcnid(recp);
encoding = getencoding(recp);
hint = iterator->hint.nodeNum;
if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
((cnid == hfsmp->hfs_jnlfileid) || (cnid == hfsmp->hfs_jnlinfoblkid)) &&
!allow_system_files) {
result = ENOENT;
goto exit;
}
if (!std_hfs
&& (attrp || forkp)
&& (recp->recordType == kHFSPlusFileRecord)
&& ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->vcbCrDate) ||
(to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_metadata_createdate))) {
int isdirlink = 0;
int isfilelink = 0;
if ((SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
(SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) {
isfilelink = 1;
} else if ((recp->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
(SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
(SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
isdirlink = 1;
}
if (isfilelink || isdirlink) {
ilink = recp->hfsPlusFile.hl_linkReference;
(void) cat_resolvelink(hfsmp, ilink, isdirlink, (struct HFSPlusCatalogFile *)recp);
}
}
if (attrp != NULL) {
if (std_hfs) {
struct HFSPlusCatalogFile cnoderec;
promoteattr(hfsmp, recp, &cnoderec);
getbsdattr(hfsmp, &cnoderec, attrp);
} else {
getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
if (ilink)
attrp->ca_linkref = ilink;
}
}
if (forkp != NULL) {
if (isadir(recp)) {
bzero(forkp, sizeof(*forkp));
} else if (std_hfs) {
promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, wantrsrc, forkp);
} else if (wantrsrc) {
forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
forkp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
if ((hfsmp->hfc_stage == HFC_RECORDING) &&
(to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
forkp->cf_bytesread =
recp->hfsPlusFile.resourceFork.clumpSize *
HFSTOVCB(hfsmp)->blockSize;
} else {
forkp->cf_bytesread = 0;
}
forkp->cf_vblocks = 0;
bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
&forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
} else {
int i;
u_int32_t validblks;
forkp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
forkp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
if ((hfsmp->hfc_stage == HFC_RECORDING) &&
(to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
forkp->cf_bytesread =
recp->hfsPlusFile.dataFork.clumpSize *
HFSTOVCB(hfsmp)->blockSize;
} else {
forkp->cf_bytesread = 0;
}
forkp->cf_vblocks = 0;
bcopy(&recp->hfsPlusFile.dataFork.extents[0],
&forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
validblks = 0;
for (i = 0; i < kHFSPlusExtentDensity; ++i) {
if (forkp->cf_extents[i].startBlock + forkp->cf_extents[i].blockCount >= hfsmp->totalBlocks) {
forkp->cf_extents[i].startBlock = 0;
forkp->cf_extents[i].blockCount = 0;
if (attrp != NULL) {
attrp->ca_mode &= S_IFMT | S_IRUSR | S_IRGRP | S_IROTH;
}
} else {
validblks += forkp->cf_extents[i].blockCount;
}
}
if ((validblks < forkp->cf_blocks) && (forkp->cf_extents[7].blockCount == 0)) {
off_t psize;
forkp->cf_blocks = validblks;
if (attrp != NULL) {
attrp->ca_blocks = validblks + recp->hfsPlusFile.resourceFork.totalBlocks;
}
psize = (off_t)validblks * (off_t)hfsmp->blockSize;
if (psize < forkp->cf_size) {
forkp->cf_size = psize;
}
}
}
}
if (descp != NULL) {
HFSPlusCatalogKey * pluskey = NULL;
if (std_hfs) {
MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
promotekey(hfsmp, (HFSCatalogKey *)&iterator->key, pluskey, &encoding);
} else {
pluskey = (HFSPlusCatalogKey *)&iterator->key;
}
builddesc(pluskey, cnid, hint, encoding, isadir(recp), descp);
if (std_hfs) {
FREE(pluskey, M_TEMP);
}
}
if (desc_cnid != NULL) {
*desc_cnid = cnid;
}
exit:
FREE(iterator, M_TEMP);
FREE(recp, M_TEMP);
return MacToVFSError(result);
}
__private_extern__
int
cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
struct cat_desc *out_descp)
{
FCB * fcb;
struct btobj * bto;
FSBufferDescriptor btdata;
u_int32_t nextCNID;
u_int32_t datalen;
int std_hfs;
int result = 0;
u_long encoding = kTextEncodingMacRoman;
int modeformat;
modeformat = attrp->ca_mode & S_IFMT;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
nextCNID = hfsmp->vcbNxtCNID;
if (nextCNID == 0xFFFFFFFF) {
if (std_hfs) {
return (ENOSPC);
} else {
HFS_MOUNT_LOCK(hfsmp, TRUE)
hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
HFS_MOUNT_UNLOCK(hfsmp, TRUE);
}
} else {
hfsmp->vcbNxtCNID++;
}
MarkVCBDirty(hfsmp);
MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
bto->iterator.hint.nodeNum = 0;
result = buildkey(hfsmp, descp, &bto->key, 0);
if (result)
goto exit;
if (!std_hfs) {
encoding = hfs_pickencoding(bto->key.nodeName.unicode,
bto->key.nodeName.length);
hfs_setencodingbits(hfsmp, encoding);
}
if (!std_hfs || (modeformat == S_IFDIR)) {
datalen = buildthread((void*)&bto->key, &bto->data, std_hfs,
S_ISDIR(attrp->ca_mode));
btdata.bufferAddress = &bto->data;
btdata.itemSize = datalen;
btdata.itemCount = 1;
for (;;) {
result = file_attribute_exist(hfsmp, nextCNID);
if (result == EEXIST) {
if (++nextCNID < kHFSFirstUserCatalogNodeID) {
nextCNID = kHFSFirstUserCatalogNodeID;
}
continue;
}
if (result) goto exit;
buildthreadkey(nextCNID, std_hfs, (CatalogKey *) &bto->iterator.key);
result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
if ((result == btExists) && !std_hfs && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
if (++nextCNID < kHFSFirstUserCatalogNodeID) {
nextCNID = kHFSFirstUserCatalogNodeID;
}
continue;
}
break;
}
if (result) goto exit;
}
if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
hfsmp->vcbNxtCNID = nextCNID + 1;
if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
}
}
buildrecord(attrp, nextCNID, std_hfs, encoding, &bto->data, &datalen);
btdata.bufferAddress = &bto->data;
btdata.itemSize = datalen;
btdata.itemCount = 1;
bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key));
result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
if (result) {
if (result == btExists)
result = EEXIST;
if (!std_hfs || S_ISDIR(attrp->ca_mode)) {
buildthreadkey(nextCNID, std_hfs, (CatalogKey *)&bto->iterator.key);
if (BTDeleteRecord(fcb, &bto->iterator)) {
printf ("hfs: cat_create() failed to delete thread record on volume %s\n", hfsmp->vcbVN);
hfs_mark_volume_inconsistent(hfsmp);
}
}
goto exit;
}
if (out_descp != NULL) {
HFSPlusCatalogKey * pluskey = NULL;
if (std_hfs) {
MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
promotekey(hfsmp, (HFSCatalogKey *)&bto->iterator.key, pluskey, &encoding);
} else
pluskey = (HFSPlusCatalogKey *)&bto->iterator.key;
builddesc(pluskey, nextCNID, bto->iterator.hint.nodeNum,
encoding, S_ISDIR(attrp->ca_mode), out_descp);
if (std_hfs) {
FREE(pluskey, M_TEMP);
}
}
attrp->ca_fileid = nextCNID;
exit:
(void) BTFlushPath(fcb);
FREE(bto, M_TEMP);
return MacToVFSError(result);
}
__private_extern__
int
cat_rename (
struct hfsmount * hfsmp,
struct cat_desc * from_cdp,
struct cat_desc * todir_cdp,
struct cat_desc * to_cdp,
struct cat_desc * out_cdp )
{
struct BTreeIterator * to_iterator = NULL;
struct BTreeIterator * from_iterator = NULL;
FSBufferDescriptor btdata;
CatalogRecord * recp = NULL;
HFSPlusCatalogKey * to_key;
ExtendedVCB * vcb;
FCB * fcb;
u_int16_t datasize;
int result = 0;
int sourcegone = 0;
int skipthread = 0;
int directory = from_cdp->cd_flags & CD_ISDIR;
int is_dirlink = 0;
int std_hfs;
u_long encoding = 0;
vcb = HFSTOVCB(hfsmp);
fcb = GetFileControlBlock(vcb->catalogRefNum);
std_hfs = (vcb->vcbSigWord == kHFSSigWord);
if (from_cdp->cd_namelen == 0 || to_cdp->cd_namelen == 0)
return (EINVAL);
MALLOC(from_iterator, BTreeIterator *, sizeof(*from_iterator), M_TEMP, M_WAITOK);
bzero(from_iterator, sizeof(*from_iterator));
if ((result = buildkey(hfsmp, from_cdp, (HFSPlusCatalogKey *)&from_iterator->key, 0)))
goto exit;
MALLOC(to_iterator, BTreeIterator *, sizeof(*to_iterator), M_TEMP, M_WAITOK);
bzero(to_iterator, sizeof(*to_iterator));
if ((result = buildkey(hfsmp, to_cdp, (HFSPlusCatalogKey *)&to_iterator->key, 0)))
goto exit;
to_key = (HFSPlusCatalogKey *)&to_iterator->key;
MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
BDINIT(btdata, recp);
if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)) {
struct BTreeIterator iterator;
cnid_t cnid = from_cdp->cd_cnid;
cnid_t pathcnid = todir_cdp->cd_parentcnid;
if (cnid == fsRtDirID ||
cnid == to_cdp->cd_parentcnid ||
cnid == pathcnid) {
result = EINVAL;
goto exit;
}
bzero(&iterator, sizeof(iterator));
while (pathcnid > fsRtDirID) {
buildthreadkey(pathcnid, std_hfs,
(CatalogKey *)&iterator.key);
result = BTSearchRecord(fcb, &iterator, &btdata,
&datasize, NULL);
if (result) goto exit;
pathcnid = getparentcnid(recp);
if (pathcnid == cnid || pathcnid == 0) {
result = EINVAL;
goto exit;
}
}
}
result = BTSearchRecord(fcb, from_iterator, &btdata,
&datasize, from_iterator);
if (result) {
if (std_hfs || (result != btNotFound))
goto exit;
struct cat_desc temp_desc;
result = cat_lookupmangled(hfsmp, from_cdp, 0, &temp_desc, NULL, NULL);
if (result)
goto exit;
bzero(from_iterator, sizeof(*from_iterator));
result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&from_iterator->key, 0);
if (result) {
cat_releasedesc(&temp_desc);
goto exit;
}
result = BTSearchRecord(fcb, from_iterator, &btdata, &datasize, from_iterator);
if (result) {
cat_releasedesc(&temp_desc);
goto exit;
}
cat_releasedesc(&temp_desc);
}
if ((!std_hfs) &&
(directory) &&
(recp->recordType == kHFSPlusFileRecord) &&
(recp->hfsPlusFile.flags & kHFSHasLinkChainMask)) {
is_dirlink = 1;
}
if (!std_hfs &&
todir_cdp->cd_parentcnid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid &&
todir_cdp->cd_parentcnid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
encoding = hfs_pickencoding(to_key->nodeName.unicode, to_key->nodeName.length);
hfs_setencodingbits(hfsmp, encoding);
recp->hfsPlusFile.textEncoding = encoding;
if (out_cdp)
out_cdp->cd_encoding = encoding;
}
if (std_hfs && !directory &&
!(recp->hfsFile.flags & kHFSThreadExistsMask))
skipthread = 1;
#if 0
if (std_hfs && hfskeycompare(to_key, iter->key) == 0)
goto exit;
if (!std_hfs && hfspluskeycompare(to_key, iter->key) == 0)
goto exit;
#endif
result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
if (result == btExists) {
int fromtype = recp->recordType;
if (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)
goto exit;
result = BTSearchRecord(fcb, to_iterator, &btdata, &datasize, NULL);
if (result)
goto exit;
if ((fromtype != recp->recordType) ||
(from_cdp->cd_cnid != getcnid(recp))) {
result = EEXIST;
goto exit;
}
result = BTDeleteRecord(fcb, from_iterator);
if (result)
goto exit;
result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
if (result) {
#if 1
{
int err;
err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
if (err) {
printf("cat_create: could not undo (BTInsert = %d)", err);
hfs_mark_volume_inconsistent(hfsmp);
result = err;
goto exit;
}
}
#else
(void) BTInsertRecord(fcb, from_iterator, &btdata, datasize);
#endif
goto exit;
}
sourcegone = 1;
}
if (result)
goto exit;
if (!sourcegone) {
result = BTDeleteRecord(fcb, from_iterator);
if (result) {
#if 1
{
int err;
err = BTDeleteRecord(fcb, to_iterator);
if (err) {
printf("cat_create: could not undo (BTDelete = %d)", err);
hfs_mark_volume_inconsistent(hfsmp);
result = err;
goto exit;
}
}
#else
(void) BTDeleteRecord(fcb, to_iterator);
#endif
goto exit;
}
}
buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
(void) BTDeleteRecord(fcb, from_iterator);
if (!skipthread) {
if (is_dirlink) {
datasize = buildthread(&to_iterator->key, recp, std_hfs, false);
} else {
datasize = buildthread(&to_iterator->key, recp, std_hfs, directory);
}
btdata.itemSize = datasize;
buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
result = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
}
if (out_cdp) {
HFSPlusCatalogKey * pluskey = NULL;
if (std_hfs) {
MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
promotekey(hfsmp, (HFSCatalogKey *)&to_iterator->key, pluskey, &encoding);
if (directory && from_cdp->cd_cnid == kHFSRootFolderID) {
u_long realhint;
realhint = hfs_pickencoding(pluskey->nodeName.unicode, pluskey->nodeName.length);
vcb->vcbFndrInfo[4] = SET_HFS_TEXT_ENCODING(realhint);
}
} else
pluskey = (HFSPlusCatalogKey *)&to_iterator->key;
builddesc(pluskey, from_cdp->cd_cnid, to_iterator->hint.nodeNum,
encoding, directory, out_cdp);
if (std_hfs) {
FREE(pluskey, M_TEMP);
}
}
exit:
(void) BTFlushPath(fcb);
if (from_iterator)
FREE(from_iterator, M_TEMP);
if (to_iterator)
FREE(to_iterator, M_TEMP);
if (recp)
FREE(recp, M_TEMP);
return MacToVFSError(result);
}
__private_extern__
int
cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
{
FCB * fcb;
BTreeIterator *iterator;
cnid_t cnid;
int std_hfs;
int result;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
if (descp->cd_cnid < kHFSFirstUserCatalogNodeID ||
descp->cd_parentcnid == kHFSRootParentID)
return (EINVAL);
iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
iterator->hint.nodeNum = 0;
if (descp->cd_namelen == 0) {
result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
cnid = attrp->ca_fileid;
} else {
result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
cnid = descp->cd_cnid;
}
if (result)
goto exit;
result = BTDeleteRecord(fcb, iterator);
if (result) {
if (std_hfs || (result != btNotFound))
goto exit;
struct cat_desc temp_desc;
result = cat_lookupmangled(hfsmp, descp, 0, &temp_desc, attrp, NULL);
if (result)
goto exit;
bzero(iterator, sizeof(*iterator));
result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&iterator->key, 0);
cnid = temp_desc.cd_cnid;
if (result) {
cat_releasedesc(&temp_desc);
goto exit;
}
result = BTDeleteRecord(fcb, iterator);
if (result) {
cat_releasedesc(&temp_desc);
goto exit;
}
cat_releasedesc(&temp_desc);
}
buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
if (BTDeleteRecord(fcb, iterator)) {
if (!std_hfs) {
printf ("hfs: cat_delete() failed to delete thread record on volume %s\n", hfsmp->vcbVN);
hfs_mark_volume_inconsistent(hfsmp);
}
}
exit:
(void) BTFlushPath(fcb);
return MacToVFSError(result);
}
__private_extern__
int
cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
{
FCB * fcb;
BTreeIterator * iterator;
struct update_state state;
int std_hfs;
int result;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
state.s_desc = descp;
state.s_attr = attrp;
state.s_datafork = dataforkp;
state.s_rsrcfork = rsrcforkp;
state.s_hfsmp = hfsmp;
iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
if ((descp->cd_cnid != attrp->ca_fileid) ||
(descp->cd_namelen == 0) ||
(attrp->ca_recflags & kHFSHasLinkChainMask)) {
result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
} else {
result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
}
if (result)
goto exit;
iterator->hint.nodeNum = descp->cd_hint;
result = BTUpdateRecord(fcb, iterator,
(IterateCallBackProcPtr)catrec_update, &state);
if (result)
goto exit;
descp->cd_hint = iterator->hint.nodeNum;
exit:
(void) BTFlushPath(fcb);
return MacToVFSError(result);
}
static int
catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state)
{
struct cat_desc *descp;
struct cat_attr *attrp;
struct cat_fork *forkp;
struct hfsmount *hfsmp;
long blksize;
int i;
descp = state->s_desc;
attrp = state->s_attr;
hfsmp = state->s_hfsmp;
blksize = HFSTOVCB(hfsmp)->blockSize;
switch (crp->recordType) {
case kHFSFolderRecord: {
HFSCatalogFolder *dir;
dir = (struct HFSCatalogFolder *)crp;
if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
(dir->folderID != descp->cd_cnid))
return (btNotFound);
dir->valence = attrp->ca_entries;
dir->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime));
dir->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime));
dir->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime));
bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 16);
bcopy(&attrp->ca_finderinfo[16], &dir->finderInfo, 16);
break;
}
case kHFSFileRecord: {
HFSCatalogFile *file;
file = (struct HFSCatalogFile *)crp;
if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
(file->fileID != attrp->ca_fileid))
return (btNotFound);
file->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime));
file->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime));
file->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime));
bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 16);
bcopy(&attrp->ca_finderinfo[16], &file->finderInfo, 16);
if (state->s_rsrcfork) {
forkp = state->s_rsrcfork;
file->rsrcLogicalSize = forkp->cf_size;
file->rsrcPhysicalSize = forkp->cf_blocks * blksize;
for (i = 0; i < kHFSExtentDensity; ++i) {
file->rsrcExtents[i].startBlock =
(u_int16_t)forkp->cf_extents[i].startBlock;
file->rsrcExtents[i].blockCount =
(u_int16_t)forkp->cf_extents[i].blockCount;
}
}
if (state->s_datafork) {
forkp = state->s_datafork;
file->dataLogicalSize = forkp->cf_size;
file->dataPhysicalSize = forkp->cf_blocks * blksize;
for (i = 0; i < kHFSExtentDensity; ++i) {
file->dataExtents[i].startBlock =
(u_int16_t)forkp->cf_extents[i].startBlock;
file->dataExtents[i].blockCount =
(u_int16_t)forkp->cf_extents[i].blockCount;
}
}
if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE))
file->flags |= kHFSFileLockedMask;
else
file->flags &= ~kHFSFileLockedMask;
break;
}
case kHFSPlusFolderRecord: {
HFSPlusCatalogFolder *dir;
dir = (struct HFSPlusCatalogFolder *)crp;
if (dir->folderID != attrp->ca_fileid) {
printf("catrec_update: id %d != %d\n", dir->folderID, attrp->ca_fileid);
return (btNotFound);
}
dir->flags = attrp->ca_recflags;
dir->valence = attrp->ca_entries;
dir->createDate = to_hfs_time(attrp->ca_itime);
dir->contentModDate = to_hfs_time(attrp->ca_mtime);
dir->backupDate = to_hfs_time(attrp->ca_btime);
dir->accessDate = to_hfs_time(attrp->ca_atime);
attrp->ca_atimeondisk = attrp->ca_atime;
dir->attributeModDate = to_hfs_time(attrp->ca_ctime);
if (ckp->hfsPlus.parentID != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
dir->textEncoding = descp->cd_encoding;
}
dir->folderCount = attrp->ca_dircount;
bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 32);
if ((dir->bsdInfo.fileMode != 0) ||
(attrp->ca_flags != 0) ||
(attrp->ca_uid != hfsmp->hfs_uid) ||
(attrp->ca_gid != hfsmp->hfs_gid) ||
((attrp->ca_mode & ALLPERMS) !=
(hfsmp->hfs_dir_mask & ACCESSPERMS))) {
if ((dir->bsdInfo.fileMode == 0) ||
(((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
dir->bsdInfo.ownerID = attrp->ca_uid;
dir->bsdInfo.groupID = attrp->ca_gid;
}
dir->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
dir->bsdInfo.adminFlags = attrp->ca_flags >> 16;
dir->bsdInfo.fileMode = attrp->ca_mode;
if (attrp->ca_linkcount > 1 || dir->hl_linkCount > 1) {
dir->hl_linkCount = attrp->ca_linkcount;
}
}
break;
}
case kHFSPlusFileRecord: {
HFSPlusCatalogFile *file;
file = (struct HFSPlusCatalogFile *)crp;
if (file->fileID != attrp->ca_fileid)
return (btNotFound);
file->flags = attrp->ca_recflags;
file->createDate = to_hfs_time(attrp->ca_itime);
file->contentModDate = to_hfs_time(attrp->ca_mtime);
file->backupDate = to_hfs_time(attrp->ca_btime);
file->accessDate = to_hfs_time(attrp->ca_atime);
attrp->ca_atimeondisk = attrp->ca_atime;
file->attributeModDate = to_hfs_time(attrp->ca_ctime);
if (ckp->hfsPlus.parentID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
file->hl_firstLinkID = attrp->ca_firstlink;
} else {
file->textEncoding = descp->cd_encoding;
}
bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 32);
if ((file->bsdInfo.fileMode != 0) ||
(attrp->ca_flags != 0) ||
(attrp->ca_uid != hfsmp->hfs_uid) ||
(attrp->ca_gid != hfsmp->hfs_gid) ||
((attrp->ca_mode & ALLPERMS) !=
(hfsmp->hfs_file_mask & ACCESSPERMS))) {
if ((file->bsdInfo.fileMode == 0) ||
(((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
file->bsdInfo.ownerID = attrp->ca_uid;
file->bsdInfo.groupID = attrp->ca_gid;
}
file->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
file->bsdInfo.adminFlags = attrp->ca_flags >> 16;
file->bsdInfo.fileMode = attrp->ca_mode;
}
if (state->s_rsrcfork) {
forkp = state->s_rsrcfork;
file->resourceFork.logicalSize = forkp->cf_size;
file->resourceFork.totalBlocks = forkp->cf_blocks;
bcopy(&forkp->cf_extents[0], &file->resourceFork.extents,
sizeof(HFSPlusExtentRecord));
file->resourceFork.clumpSize =
howmany(forkp->cf_bytesread, blksize);
}
if (state->s_datafork) {
forkp = state->s_datafork;
file->dataFork.logicalSize = forkp->cf_size;
file->dataFork.totalBlocks = forkp->cf_blocks;
bcopy(&forkp->cf_extents[0], &file->dataFork.extents,
sizeof(HFSPlusExtentRecord));
file->dataFork.clumpSize =
howmany(forkp->cf_bytesread, blksize);
}
if ((file->resourceFork.extents[0].startBlock != 0) &&
(file->resourceFork.extents[0].startBlock ==
file->dataFork.extents[0].startBlock)) {
panic("catrec_update: rsrc fork == data fork");
}
if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE))
file->flags |= kHFSFileLockedMask;
else
file->flags &= ~kHFSFileLockedMask;
if (S_ISBLK(attrp->ca_mode) || S_ISCHR(attrp->ca_mode)) {
file->bsdInfo.special.rawDevice = attrp->ca_rdev;
} else if (descp->cd_cnid != attrp->ca_fileid || attrp->ca_linkcount == 2) {
file->hl_linkCount = attrp->ca_linkcount;
}
break;
}
default:
return (btNotFound);
}
return (0);
}
__private_extern__
int
cat_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid)
{
int retval = 0;
int lockflags = 0;
struct cat_desc desc;
struct cat_attr attr;
while ((cnid != kHFSRootFolderID) && (cnid != kHFSRootParentID) &&
(cnid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid)) {
retval = hfs_chash_set_childlinkbit(hfsmp->hfs_raw_dev, cnid);
if (retval == 0) {
break;
}
retval = hfs_start_transaction(hfsmp);
if (retval) {
break;
}
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
retval = cat_idlookup(hfsmp, cnid, 0, &desc, &attr, NULL);
if (retval) {
hfs_systemfile_unlock(hfsmp, lockflags);
hfs_end_transaction(hfsmp);
break;
}
attr.ca_recflags |= kHFSHasChildLinkMask;
retval = cat_update(hfsmp, &desc, &attr, NULL, NULL);
if (retval) {
hfs_systemfile_unlock(hfsmp, lockflags);
hfs_end_transaction(hfsmp);
cat_releasedesc(&desc);
break;
}
hfs_systemfile_unlock(hfsmp, lockflags);
hfs_end_transaction(hfsmp);
cnid = desc.cd_parentcnid;
cat_releasedesc(&desc);
}
return retval;
}
__private_extern__
int
cat_check_link_ancestry(struct hfsmount *hfsmp, cnid_t cnid, cnid_t pointed_at_cnid)
{
HFSPlusCatalogKey *keyp;
BTreeIterator *ip;
FSBufferDescriptor btdata;
HFSPlusCatalogFolder folder;
FCB *fcb;
int invalid;
int result;
invalid = 0;
BDINIT(btdata, &folder);
MALLOC(ip, BTreeIterator *, sizeof(*ip), M_TEMP, M_WAITOK);
keyp = (HFSPlusCatalogKey *)&ip->key;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
while (cnid != kHFSRootParentID) {
if (pointed_at_cnid == cnid) {
invalid = 1;
break;
}
if ((result = getkey(hfsmp, cnid, (CatalogKey *)keyp))) {
printf("cat_check_link_ancestry: getkey for %u failed\n", cnid);
invalid = 1;
break;
}
if ((result = BTSearchRecord(fcb, ip, &btdata, NULL, NULL))) {
printf("cat_check_link_ancestry: cannot find %u\n", cnid);
invalid = 1;
break;
}
if (folder.flags & kHFSHasLinkChainMask) {
invalid = 1;
break;
}
cnid = keyp->parentID;
}
FREE(ip, M_TEMP);
return (invalid);
}
struct linkupdate_state {
cnid_t filelinkid;
cnid_t prevlinkid;
cnid_t nextlinkid;
};
static int
updatelink_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state)
{
HFSPlusCatalogFile *file;
if (crp->recordType != kHFSPlusFileRecord) {
printf("updatelink_callback: unexpected rec type %d\n", crp->recordType);
return (btNotFound);
}
file = (struct HFSPlusCatalogFile *)crp;
if (file->flags & kHFSHasLinkChainMask) {
if (state->prevlinkid != HFS_IGNORABLE_LINK) {
file->hl_prevLinkID = state->prevlinkid;
}
if (state->nextlinkid != HFS_IGNORABLE_LINK) {
file->hl_nextLinkID = state->nextlinkid;
}
} else {
printf("updatelink_callback: file %d isn't a chain\n", file->fileID);
}
return (0);
}
__private_extern__
int
cat_updatelink(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cnid_t nextlinkid)
{
FCB * fcb;
BTreeIterator * iterator;
struct linkupdate_state state;
int result;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
state.filelinkid = linkfileid;
state.prevlinkid = prevlinkid;
state.nextlinkid = nextlinkid;
iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
iterator->hint.nodeNum = 0;
result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key);
if (result == 0) {
result = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr)updatelink_callback, &state);
(void) BTFlushPath(fcb);
} else {
printf("cat_updatelink: couldn't resolve cnid %d\n", linkfileid);
}
return MacToVFSError(result);
}
__private_extern__
int
cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
{
FCB * fcb;
BTreeIterator * iterator;
struct FSBufferDescriptor btdata;
struct HFSPlusCatalogFile file;
int result;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
iterator->hint.nodeNum = 0;
if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
goto exit;
}
BDINIT(btdata, &file);
if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
goto exit;
}
if (file.recordType != kHFSPlusFileRecord) {
result = ENOENT;
goto exit;
}
*linkfileid = file.fileID;
if (file.flags & kHFSHasLinkChainMask) {
*prevlinkid = file.hl_prevLinkID;
*nextlinkid = file.hl_nextLinkID;
} else {
*prevlinkid = 0;
*nextlinkid = 0;
}
exit:
return MacToVFSError(result);
}
__private_extern__
int
cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
{
FCB * fcb;
BTreeIterator * iterator;
struct FSBufferDescriptor btdata;
struct HFSPlusCatalogFile file;
int result;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
iterator->hint.nodeNum = 0;
if ((result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key))) {
printf("cat_lookuplinkbyid: getkey for %d failed %d\n", linkfileid, result);
goto exit;
}
BDINIT(btdata, &file);
if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
printf("cat_lookuplinkbyid: cannot find %d\n", linkfileid);
goto exit;
}
if (file.flags & kHFSHasLinkChainMask) {
cnid_t parent;
parent = ((HFSPlusCatalogKey *)&iterator->key)->parentID;
if (parent == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
result = ENOLINK;
} else if (parent == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
*prevlinkid = 0;
*nextlinkid = file.hl_firstLinkID;
} else {
*prevlinkid = file.hl_prevLinkID;
*nextlinkid = file.hl_nextLinkID;
}
} else {
*prevlinkid = 0;
*nextlinkid = 0;
}
exit:
return MacToVFSError(result);
}
__private_extern__
int
cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
cnid_t nextlinkid, cnid_t *linkfileid)
{
FCB * fcb;
struct btobj * bto;
FSBufferDescriptor btdata;
HFSPlusForkData *rsrcforkp;
u_int32_t nextCNID;
u_int32_t datalen;
u_long encoding;
int thread_inserted = 0;
int alias_allocated = 0;
int result = 0;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
nextCNID = hfsmp->vcbNxtCNID;
if (nextCNID == 0xFFFFFFFF) {
HFS_MOUNT_LOCK(hfsmp, TRUE)
hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
HFS_MOUNT_UNLOCK(hfsmp, TRUE);
} else {
hfsmp->vcbNxtCNID++;
}
MarkVCBDirty(hfsmp);
MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
bto->iterator.hint.nodeNum = 0;
rsrcforkp = &bto->data.hfsPlusFile.resourceFork;
result = buildkey(hfsmp, descp, &bto->key, 0);
if (result) {
printf("cat_createlink: err %d from buildkey\n", result);
goto exit;
}
encoding = hfs_pickencoding(bto->key.nodeName.unicode, bto->key.nodeName.length);
datalen = buildthread((void*)&bto->key, &bto->data, 0, 0);
btdata.bufferAddress = &bto->data;
btdata.itemSize = datalen;
btdata.itemCount = 1;
for (;;) {
buildthreadkey(nextCNID, 0, (CatalogKey *) &bto->iterator.key);
result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
if ((result == btExists) && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
if (++nextCNID < kHFSFirstUserCatalogNodeID) {
nextCNID = kHFSFirstUserCatalogNodeID;
}
continue;
}
if (result == 0) {
thread_inserted = 1;
}
break;
}
if (result)
goto exit;
if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
hfsmp->vcbNxtCNID = nextCNID + 1;
if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
}
}
buildrecord(attrp, nextCNID, 0, encoding, &bto->data, &datalen);
bto->data.hfsPlusFile.hl_prevLinkID = 0;
bto->data.hfsPlusFile.hl_nextLinkID = nextlinkid;
bto->data.hfsPlusFile.hl_linkReference = attrp->ca_linkref;
if (descp->cd_flags & CD_ISDIR) {
if ((result = cat_makealias(hfsmp, attrp->ca_linkref, &bto->data.hfsPlusFile))) {
goto exit;
}
alias_allocated = 1;
}
btdata.bufferAddress = &bto->data;
btdata.itemSize = datalen;
btdata.itemCount = 1;
bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key));
result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
if (result) {
if (result == btExists)
result = EEXIST;
goto exit;
}
if (linkfileid != NULL) {
*linkfileid = nextCNID;
}
exit:
if (result) {
if (thread_inserted) {
printf("cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result));
buildthreadkey(nextCNID, 0, (CatalogKey *)&bto->iterator.key);
if (BTDeleteRecord(fcb, &bto->iterator)) {
printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp->vcbVN);
hfs_mark_volume_inconsistent(hfsmp);
}
}
if (alias_allocated && rsrcforkp->extents[0].startBlock != 0) {
(void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock,
rsrcforkp->extents[0].blockCount);
rsrcforkp->extents[0].startBlock = 0;
rsrcforkp->extents[0].blockCount = 0;
}
}
(void) BTFlushPath(fcb);
FREE(bto, M_TEMP);
return MacToVFSError(result);
}
static const char hfs_dirlink_alias_rsrc[] = {
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
enum {
kHFSAliasSize = 0x1d0,
kHFSAliasVolTypeEjectable = 0x5,
kHFSAliasVolCreateDateOffset = 0x12a,
kHFSAliasVolTypeOffset = 0x130,
kHFSAliasParentIDOffset = 0x132,
kHFSAliasTargetIDOffset = 0x176,
};
static int
cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp)
{
struct buf *bp;
daddr64_t blkno;
u_int32_t blkcount;
int blksize;
int sectorsize;
int result;
HFSPlusForkData *rsrcforkp;
char *alias;
uint32_t *valptr;
rsrcforkp = &(crp->resourceFork);
blksize = hfsmp->blockSize;
blkcount = howmany(kHFSAliasSize, blksize);
sectorsize = hfsmp->hfs_phys_block_size;
bzero(rsrcforkp, sizeof(HFSPlusForkData));
result = BlockAllocate(hfsmp, 0, blkcount, blkcount, 1, 1,
&rsrcforkp->extents[0].startBlock,
&rsrcforkp->extents[0].blockCount);
if (result) {
rsrcforkp->extents[0].startBlock = 0;
goto exit;
}
blkno = ((u_int64_t)rsrcforkp->extents[0].startBlock * (u_int64_t)blksize) / sectorsize;
blkno += hfsmp->hfsPlusIOPosOffset / sectorsize;
bp = buf_getblk(hfsmp->hfs_devvp, blkno, roundup(kHFSAliasSize, hfsmp->hfs_phys_block_size), 0, 0, BLK_META);
if (hfsmp->jnl) {
journal_modify_block_start(hfsmp->jnl, bp);
}
alias = (char *)buf_dataptr(bp);
bzero(alias, buf_size(bp));
bcopy(hfs_dirlink_alias_rsrc, alias, kHFSAliasSize);
valptr = (uint32_t *)(alias + kHFSAliasVolCreateDateOffset);
*valptr = OSSwapHostToBigInt32(hfsmp->localCreateDate);
if (hfsmp->hfs_flags & HFS_VIRTUAL_DEVICE) {
*(uint16_t *)(alias + kHFSAliasVolTypeOffset) =
OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable);
}
valptr = (uint32_t *)(alias + kHFSAliasParentIDOffset);
*valptr = OSSwapHostToBigInt32(hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid);
valptr = (uint32_t *)(alias + kHFSAliasTargetIDOffset);
*valptr = OSSwapHostToBigInt32(inode_num);
if (hfsmp->jnl) {
journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL);
} else if ((result = buf_bwrite(bp))) {
goto exit;
}
rsrcforkp->logicalSize = kHFSAliasSize;
rsrcforkp->totalBlocks = rsrcforkp->extents[0].blockCount;
exit:
if (result && rsrcforkp->extents[0].startBlock != 0) {
(void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock, rsrcforkp->extents[0].blockCount);
rsrcforkp->extents[0].startBlock = 0;
rsrcforkp->extents[0].blockCount = 0;
rsrcforkp->logicalSize = 0;
rsrcforkp->totalBlocks = 0;
}
return (result);
}
__private_extern__
int
cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp)
{
struct HFSPlusCatalogFile file;
struct cat_attr cattr;
uint32_t totalBlocks;
int i;
int result;
bzero(&file, sizeof (file));
bzero(&cattr, sizeof (cattr));
cattr.ca_fileid = descp->cd_cnid;
if (descp->cd_flags & CD_ISDIR) {
FCB * fcb;
BTreeIterator * iterator;
struct FSBufferDescriptor btdata;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
iterator->hint.nodeNum = 0;
if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
goto exit;
}
BDINIT(btdata, &file);
if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
goto exit;
}
}
result = cat_delete(hfsmp, descp, &cattr);
if ((result == 0) &&
(descp->cd_flags & CD_ISDIR) &&
(file.recordType == kHFSPlusFileRecord)) {
totalBlocks = file.resourceFork.totalBlocks;
for (i = 0; (i < 8) && (totalBlocks > 0); i++) {
if ((file.resourceFork.extents[i].blockCount == 0) &&
(file.resourceFork.extents[i].startBlock == 0)) {
break;
}
(void) BlockDeallocate(hfsmp,
file.resourceFork.extents[i].startBlock,
file.resourceFork.extents[i].blockCount);
totalBlocks -= file.resourceFork.extents[i].blockCount;
file.resourceFork.extents[i].startBlock = 0;
file.resourceFork.extents[i].blockCount = 0;
}
}
exit:
return (result);
}
struct readattr_state {
struct hfsmount *hfsmp;
struct cat_entrylist *list;
cnid_t dir_cnid;
int stdhfs;
int error;
};
static int
getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec,
struct readattr_state *state)
{
struct cat_entrylist *list = state->list;
struct hfsmount *hfsmp = state->hfsmp;
struct cat_entry *cep;
cnid_t parentcnid;
if (list->realentries >= list->maxentries)
return (0);
parentcnid = state->stdhfs ? key->hfs.parentID : key->hfsPlus.parentID;
switch(rec->recordType) {
case kHFSPlusFolderRecord:
case kHFSPlusFileRecord:
case kHFSFolderRecord:
case kHFSFileRecord:
if (parentcnid != state->dir_cnid) {
state->error = ENOENT;
return (0);
}
break;
default:
state->error = ENOENT;
return (0);
}
if (parentcnid == kHFSRootFolderID) {
if (rec->recordType == kHFSPlusFolderRecord) {
if (rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
list->skipentries++;
return (1);
}
}
if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
(rec->recordType == kHFSPlusFileRecord) &&
((rec->hfsPlusFile.fileID == hfsmp->hfs_jnlfileid) ||
(rec->hfsPlusFile.fileID == hfsmp->hfs_jnlinfoblkid))) {
list->skipentries++;
return (1);
}
}
cep = &list->entry[list->realentries++];
if (state->stdhfs) {
struct HFSPlusCatalogFile cnoderec;
HFSPlusCatalogKey * pluskey;
u_long encoding;
promoteattr(hfsmp, rec, &cnoderec);
getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
promotekey(hfsmp, (const HFSCatalogKey *)key, pluskey, &encoding);
builddesc(pluskey, getcnid(rec), 0, encoding, isadir(rec), &cep->ce_desc);
FREE(pluskey, M_TEMP);
if (rec->recordType == kHFSFileRecord) {
int blksize = HFSTOVCB(hfsmp)->blockSize;
cep->ce_datasize = rec->hfsFile.dataLogicalSize;
cep->ce_datablks = rec->hfsFile.dataPhysicalSize / blksize;
cep->ce_rsrcsize = rec->hfsFile.rsrcLogicalSize;
cep->ce_rsrcblks = rec->hfsFile.rsrcPhysicalSize / blksize;
}
} else {
getbsdattr(hfsmp, (const struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
builddesc((const HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec),
isadir(rec), &cep->ce_desc);
if (rec->recordType == kHFSPlusFileRecord) {
cep->ce_datasize = rec->hfsPlusFile.dataFork.logicalSize;
cep->ce_datablks = rec->hfsPlusFile.dataFork.totalBlocks;
cep->ce_rsrcsize = rec->hfsPlusFile.resourceFork.logicalSize;
cep->ce_rsrcblks = rec->hfsPlusFile.resourceFork.totalBlocks;
if ((SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
(SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) {
cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
} else if ((rec->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
(SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
(SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
}
}
}
return (list->realentries < list->maxentries);
}
__private_extern__
int
cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list)
{
FCB* fcb;
CatalogKey * key;
BTreeIterator * iterator;
struct readattr_state state;
cnid_t parentcnid;
int i;
int std_hfs;
int index;
int have_key;
int result = 0;
ce_list->realentries = 0;
fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
parentcnid = dirhint->dh_desc.cd_parentcnid;
state.hfsmp = hfsmp;
state.list = ce_list;
state.dir_cnid = parentcnid;
state.stdhfs = std_hfs;
state.error = 0;
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
bzero(iterator, sizeof(*iterator));
key = (CatalogKey *)&iterator->key;
have_key = 0;
iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
index = dirhint->dh_index + 1;
if (dirhint->dh_desc.cd_namelen != 0) {
if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
have_key = 1;
}
}
if ((index == 0) || !have_key) {
buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
if (result) {
result = MacToVFSError(result);
goto exit;
}
if (index > 0) {
struct position_state ps;
ps.error = 0;
ps.count = 0;
ps.index = index;
ps.parentID = dirhint->dh_desc.cd_parentcnid;
ps.hfsmp = hfsmp;
result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
(IterateCallBackProcPtr)cat_findposition, &ps);
if (ps.error)
result = ps.error;
else
result = MacToVFSError(result);
if (result) {
result = MacToVFSError(result);
goto exit;
}
}
}
result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
(IterateCallBackProcPtr)getentriesattr_callback, &state);
if (state.error)
result = state.error;
else if (ce_list->realentries == 0)
result = ENOENT;
else
result = MacToVFSError(result);
if (std_hfs)
goto exit;
for (i = 0; i < (int)ce_list->realentries; ++i) {
struct FndrFileInfo *fip;
struct cat_entry *cep;
struct HFSPlusCatalogFile filerec;
int isdirlink = 0;
int isfilelink = 0;
cep = &ce_list->entry[i];
if (cep->ce_attr.ca_linkref == 0)
continue;
fip = (struct FndrFileInfo *)&cep->ce_attr.ca_finderinfo;
if (S_ISREG(cep->ce_attr.ca_mode) &&
(SWAP_BE32(fip->fdType) == kHardLinkFileType) &&
(SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)) {
isfilelink = 1;
}
if (S_ISREG(cep->ce_attr.ca_mode) &&
(SWAP_BE32(fip->fdType) == kHFSAliasType) &&
(SWAP_BE32(fip->fdCreator) == kHFSAliasCreator) &&
(cep->ce_attr.ca_recflags & kHFSHasLinkChainMask)) {
isdirlink = 1;
}
if (isfilelink || isdirlink) {
if (cat_resolvelink(hfsmp, cep->ce_attr.ca_linkref, isdirlink, &filerec) != 0)
continue;
getbsdattr(hfsmp, &filerec, &cep->ce_attr);
cep->ce_datasize = filerec.dataFork.logicalSize;
cep->ce_datablks = filerec.dataFork.totalBlocks;
cep->ce_rsrcsize = filerec.resourceFork.logicalSize;
cep->ce_rsrcblks = filerec.resourceFork.totalBlocks;
}
}
exit:
FREE(iterator, M_TEMP);
return MacToVFSError(result);
}
#define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
struct linkinfo {
u_long link_ref;
user_addr_t dirent_addr;
};
typedef struct linkinfo linkinfo_t;
struct packdirentry_state {
int cbs_extended;
u_int32_t cbs_parentID;
u_int32_t cbs_index;
uio_t cbs_uio;
ExtendedVCB * cbs_hfsmp;
int cbs_result;
int32_t cbs_nlinks;
int32_t cbs_maxlinks;
linkinfo_t * cbs_linkinfo;
struct cat_desc * cbs_desc;
u_int8_t * cbs_namebuf;
struct direntry * cbs_direntry;
struct direntry * cbs_prevdirentry;
u_int32_t cbs_previlinkref;
Boolean cbs_hasprevdirentry;
Boolean cbs_eof;
};
static int
getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp,
struct packdirentry_state *state)
{
struct hfsmount *hfsmp;
const CatalogName *cnp;
cnid_t curID;
OSErr result;
struct dirent catent;
struct direntry * entry = NULL;
time_t itime;
u_int32_t ilinkref = 0;
u_int32_t curlinkref = 0;
cnid_t cnid;
int hide = 0;
u_int8_t type = DT_UNKNOWN;
u_int8_t is_mangled = 0;
u_int8_t *nameptr;
user_addr_t uiobase = USER_ADDR_NULL;
size_t namelen = 0;
size_t maxnamelen;
size_t uiosize = 0;
caddr_t uioaddr;
Boolean stop_after_pack = false;
hfsmp = state->cbs_hfsmp;
curID = ckp->hfsPlus.parentID;
if (state->cbs_parentID != curID) {
if (state->cbs_extended) {
if (state->cbs_hasprevdirentry) {
stop_after_pack = true;
} else {
state->cbs_result = ENOENT;
return (0);
}
} else {
state->cbs_result = ENOENT;
return (0);
}
}
if (state->cbs_extended) {
entry = state->cbs_direntry;
nameptr = (u_int8_t *)&entry->d_name[0];
maxnamelen = NAME_MAX;
} else {
nameptr = (u_int8_t *)&catent.d_name[0];
maxnamelen = NAME_MAX;
}
if (state->cbs_extended && stop_after_pack) {
cnid = INT_MAX;
} else {
switch(crp->recordType) {
case kHFSPlusFolderRecord:
type = DT_DIR;
cnid = crp->hfsPlusFolder.folderID;
if (curID == kHFSRootFolderID) {
if (cnid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
cnid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
hide = 1;
}
}
break;
case kHFSPlusFileRecord:
itime = to_bsd_time(crp->hfsPlusFile.createDate);
type = MODE_TO_DT(crp->hfsPlusFile.bsdInfo.fileMode);
cnid = crp->hfsPlusFile.fileID;
if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
(SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator) &&
((itime == (time_t)hfsmp->hfs_itime) ||
(itime == (time_t)hfsmp->hfs_metadata_createdate))) {
if (crp->hfsPlusFile.flags & kHFSHasLinkChainMask) {
cnid = crp->hfsPlusFile.hl_linkReference;
} else {
ilinkref = crp->hfsPlusFile.hl_linkReference;
}
} else if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
(SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator) &&
(crp->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
(crp->hfsPlusFile.hl_linkReference >= kHFSFirstUserCatalogNodeID) &&
((itime == (time_t)hfsmp->hfs_itime) ||
(itime == (time_t)hfsmp->hfs_metadata_createdate))) {
type = DT_DIR;
cnid = crp->hfsPlusFile.hl_linkReference;
}
if ((curID == kHFSRootFolderID) &&
((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))) &&
((cnid == hfsmp->hfs_jnlfileid) ||
(cnid == hfsmp->hfs_jnlinfoblkid))) {
hide = 1;
}
break;
default:
return (0);
};
cnp = (const CatalogName*) &ckp->hfsPlus.nodeName;
namelen = cnp->ustr.length;
if ((namelen < maxnamelen) && (crp->hfsPlusFile.textEncoding == 0)) {
int i;
u_int16_t ch;
const u_int16_t *chp;
chp = &cnp->ustr.unicode[0];
for (i = 0; i < (int)namelen; ++i) {
ch = *chp++;
if (ch > 0x007f || ch == 0x0000) {
goto encodestr;
}
nameptr[i] = (ch == '/') ? ':' : (u_int8_t)ch;
}
nameptr[namelen] = '\0';
result = 0;
} else {
encodestr:
result = utf8_encodestr(cnp->ustr.unicode, namelen * sizeof(UniChar),
nameptr, &namelen, maxnamelen + 1, ':', 0);
}
if (result == ENAMETOOLONG) {
result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
cnp->ustr.unicode, maxnamelen + 1,
(ByteCount*)&namelen, nameptr, cnid);
is_mangled = 1;
}
}
if (state->cbs_extended) {
state->cbs_prevdirentry->d_seekoff = (state->cbs_index + 3) | ((u_int64_t)cnid << 32);
uiosize = state->cbs_prevdirentry->d_reclen;
uioaddr = (caddr_t) state->cbs_prevdirentry;
} else {
catent.d_type = type;
catent.d_namlen = namelen;
catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
if (hide)
catent.d_fileno = 0;
else
catent.d_fileno = cnid;
uioaddr = (caddr_t) &catent;
}
if (ilinkref || state->cbs_previlinkref) {
uiobase = uio_curriovbase(state->cbs_uio);
}
if ((uiosize > uio_resid(state->cbs_uio)) ||
(ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks)) {
return (0);
}
if (!state->cbs_extended || state->cbs_hasprevdirentry) {
state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
if (state->cbs_result == 0) {
++state->cbs_index;
state->cbs_desc->cd_cnid = cnid;
if (type == DT_DIR) {
state->cbs_desc->cd_flags |= CD_ISDIR;
} else {
state->cbs_desc->cd_flags &= ~CD_ISDIR;
}
if (state->cbs_desc->cd_nameptr != NULL) {
state->cbs_desc->cd_namelen = 0;
}
#if 0
state->cbs_desc->cd_encoding = xxxx;
#endif
if (!is_mangled) {
state->cbs_desc->cd_namelen = namelen;
bcopy(nameptr, state->cbs_namebuf, namelen + 1);
} else {
u_int8_t *new_nameptr;
size_t bufsize;
size_t tmp_namelen = 0;
cnp = (const CatalogName *)&ckp->hfsPlus.nodeName;
bufsize = 1 + utf8_encodelen(cnp->ustr.unicode,
cnp->ustr.length * sizeof(UniChar),
':', 0);
MALLOC(new_nameptr, u_int8_t *, bufsize, M_TEMP, M_WAITOK);
result = utf8_encodestr(cnp->ustr.unicode,
cnp->ustr.length * sizeof(UniChar),
new_nameptr, &tmp_namelen, bufsize, ':', 0);
state->cbs_desc->cd_namelen = tmp_namelen;
bcopy(new_nameptr, state->cbs_namebuf, tmp_namelen + 1);
FREE(new_nameptr, M_TEMP);
}
}
if (state->cbs_hasprevdirentry) {
curlinkref = ilinkref;
ilinkref = state->cbs_previlinkref;
}
if ((ilinkref != 0) &&
(state->cbs_result == 0) &&
(state->cbs_nlinks < state->cbs_maxlinks)) {
state->cbs_linkinfo[state->cbs_nlinks].dirent_addr = uiobase;
state->cbs_linkinfo[state->cbs_nlinks].link_ref = ilinkref;
state->cbs_nlinks++;
}
if (state->cbs_hasprevdirentry) {
ilinkref = curlinkref;
}
}
if (state->cbs_extended) {
if (stop_after_pack) {
state->cbs_eof = true;
return (0);
}
entry->d_type = type;
entry->d_namlen = namelen;
entry->d_reclen = EXT_DIRENT_LEN(namelen);
if (hide) {
entry->d_fileno = 0;
} else {
entry->d_fileno = cnid;
}
struct direntry * tmp;
tmp = state->cbs_direntry;
state->cbs_direntry = state->cbs_prevdirentry;
state->cbs_prevdirentry = tmp;
state->cbs_hasprevdirentry = true;
state->cbs_previlinkref = ilinkref;
}
return (state->cbs_result == 0 &&
uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
}
static int
getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp,
struct packdirentry_state *state)
{
struct hfsmount *hfsmp;
const CatalogName *cnp;
cnid_t curID;
OSErr result;
struct dirent catent;
cnid_t cnid;
u_int8_t type = DT_UNKNOWN;
u_int8_t *nameptr;
size_t namelen = 0;
size_t maxnamelen;
size_t uiosize = 0;
caddr_t uioaddr;
hfsmp = state->cbs_hfsmp;
curID = ckp->hfs.parentID;
if (state->cbs_parentID != curID) {
state->cbs_result = ENOENT;
return (0);
}
nameptr = (u_int8_t *)&catent.d_name[0];
maxnamelen = NAME_MAX;
switch(crp->recordType) {
case kHFSFolderRecord:
type = DT_DIR;
cnid = crp->hfsFolder.folderID;
break;
case kHFSFileRecord:
type = DT_REG;
cnid = crp->hfsFile.fileID;
break;
default:
return (0);
};
cnp = (const CatalogName*) ckp->hfs.nodeName;
result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen + 1, (ByteCount *)&namelen, nameptr);
if (result) {
result = mac_roman_to_utf8(cnp->pstr, maxnamelen + 1, (ByteCount *)&namelen, nameptr);
}
catent.d_type = type;
catent.d_namlen = namelen;
catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
catent.d_fileno = cnid;
uioaddr = (caddr_t) &catent;
if (uiosize > uio_resid(state->cbs_uio)) {
return (0);
}
state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
if (state->cbs_result == 0) {
++state->cbs_index;
state->cbs_desc->cd_cnid = cnid;
if (type == DT_DIR) {
state->cbs_desc->cd_flags |= CD_ISDIR;
} else {
state->cbs_desc->cd_flags &= ~CD_ISDIR;
}
if (state->cbs_desc->cd_nameptr != NULL) {
state->cbs_desc->cd_namelen = 0;
}
state->cbs_desc->cd_namelen = namelen;
bcopy(nameptr, state->cbs_namebuf, namelen + 1);
}
return (state->cbs_result == 0 && uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
}
__private_extern__
int
cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint,
uio_t uio, int extended, int * items, int * eofflag)
{
FCB* fcb;
BTreeIterator * iterator;
CatalogKey * key;
struct packdirentry_state state;
void * buffer;
int bufsize;
int maxlinks;
int result;
int index;
int have_key;
if (extended && (hfsmp->hfs_flags & HFS_STANDARD)) {
return (ENOTSUP);
}
fcb = hfsmp->hfs_catalog_cp->c_datafork;
maxlinks = MIN(entrycnt, uio_resid(uio) / SMALL_DIRENTRY_SIZE);
bufsize = MAXPATHLEN + (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator);
if (extended) {
bufsize += 2*sizeof(struct direntry);
}
MALLOC(buffer, void *, bufsize, M_TEMP, M_WAITOK);
bzero(buffer, bufsize);
state.cbs_extended = extended;
state.cbs_hasprevdirentry = false;
state.cbs_previlinkref = 0;
state.cbs_nlinks = 0;
state.cbs_maxlinks = maxlinks;
state.cbs_linkinfo = (linkinfo_t *)((char *)buffer + MAXPATHLEN);
iterator = (BTreeIterator *) ((char *)state.cbs_linkinfo + (maxlinks * sizeof(linkinfo_t)));
key = (CatalogKey *)&iterator->key;
have_key = 0;
index = dirhint->dh_index + 1;
if (extended) {
state.cbs_direntry = (struct direntry *)((char *)iterator + sizeof(BTreeIterator));
state.cbs_prevdirentry = state.cbs_direntry + 1;
state.cbs_eof = false;
}
if (dirhint->dh_desc.cd_namelen != 0) {
if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
have_key = 1;
}
}
if (index == 0 && dirhint->dh_threadhint != 0) {
buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
iterator->hint.nodeNum = dirhint->dh_threadhint;
iterator->hint.index = 0;
have_key = 1;
}
if (!have_key) {
buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
if (result) {
result = MacToVFSError(result);
goto cleanup;
}
if (index == 0) {
dirhint->dh_threadhint = iterator->hint.nodeNum;
}
if (index > 0) {
struct position_state ps;
ps.error = 0;
ps.count = 0;
ps.index = index;
ps.parentID = dirhint->dh_desc.cd_parentcnid;
ps.hfsmp = hfsmp;
result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
(IterateCallBackProcPtr)cat_findposition, &ps);
if (ps.error)
result = ps.error;
else
result = MacToVFSError(result);
if (result) {
result = MacToVFSError(result);
goto cleanup;
}
}
}
state.cbs_index = index;
state.cbs_hfsmp = hfsmp;
state.cbs_uio = uio;
state.cbs_desc = &dirhint->dh_desc;
state.cbs_namebuf = (u_int8_t *)buffer;
state.cbs_result = 0;
state.cbs_parentID = dirhint->dh_desc.cd_parentcnid;
if (dirhint->dh_desc.cd_namelen > 0 && dirhint->dh_desc.cd_nameptr != NULL) {
bcopy(dirhint->dh_desc.cd_nameptr, buffer, dirhint->dh_desc.cd_namelen+1);
if (dirhint->dh_desc.cd_flags & CD_HASBUF) {
dirhint->dh_desc.cd_flags &= ~CD_HASBUF;
vfs_removename((const char *)dirhint->dh_desc.cd_nameptr);
}
}
dirhint->dh_desc.cd_nameptr = (u_int8_t *)buffer;
enum BTreeIterationOperations op;
if (extended && index != 0 && have_key)
op = kBTreeCurrentRecord;
else
op = kBTreeNextRecord;
if (hfsmp->hfs_flags & HFS_STANDARD)
result = BTIterateRecords(fcb, op, iterator,
(IterateCallBackProcPtr)getdirentries_std_callback, &state);
else {
result = BTIterateRecords(fcb, op, iterator,
(IterateCallBackProcPtr)getdirentries_callback, &state);
if (state.cbs_extended && (result == fsBTRecordNotFoundErr)) {
CatalogKey ckp;
CatalogRecord crp;
bzero(&ckp, sizeof(ckp));
bzero(&crp, sizeof(crp));
result = getdirentries_callback(&ckp, &crp, &state);
}
}
*items = state.cbs_index - index;
index = state.cbs_index;
if (state.cbs_eof) {
*eofflag = 1;
}
dirhint->dh_desc.cd_hint = iterator->hint.nodeNum;
dirhint->dh_desc.cd_flags |= CD_DECOMPOSED;
dirhint->dh_index = index - 1;
if (dirhint->dh_desc.cd_namelen > 0) {
dirhint->dh_desc.cd_nameptr = (const u_int8_t *)vfs_addname((char *)buffer, dirhint->dh_desc.cd_namelen, 0, 0);
dirhint->dh_desc.cd_flags |= CD_HASBUF;
} else {
dirhint->dh_desc.cd_nameptr = NULL;
dirhint->dh_desc.cd_namelen = 0;
}
if (state.cbs_nlinks > 0) {
u_int32_t fileid = 0;
user_addr_t address;
int i;
for (i = 0; i < state.cbs_nlinks; ++i) {
if (resolvelinkid(hfsmp, state.cbs_linkinfo[i].link_ref, &fileid) != 0)
continue;
address = state.cbs_linkinfo[i].dirent_addr;
if (address == (user_addr_t)0)
continue;
if (uio_isuserspace(uio)) {
if (extended) {
ino64_t fileid_64 = (ino64_t)fileid;
(void) copyout(&fileid_64, address, sizeof(fileid_64));
} else {
(void) copyout(&fileid, address, sizeof(fileid));
}
} else {
if (extended) {
ino64_t fileid_64 = (ino64_t)fileid;
bcopy(&fileid_64, (void*) CAST_DOWN(caddr_t, address), sizeof(fileid_64));
} else {
bcopy(&fileid, (void*) CAST_DOWN(caddr_t, address), sizeof(fileid));
}
}
}
}
if (state.cbs_result)
result = state.cbs_result;
else
result = MacToVFSError(result);
if (result == ENOENT) {
result = 0;
}
cleanup:
FREE(buffer, M_TEMP);
return (result);
}
static int
cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
struct position_state *state)
{
cnid_t curID;
if (state->hfsmp->hfs_flags & HFS_STANDARD)
curID = ckp->hfs.parentID;
else
curID = ckp->hfsPlus.parentID;
if (state->parentID != curID) {
state->error = EINVAL;
return (0);
}
switch(crp->recordType) {
case kHFSPlusFolderRecord:
case kHFSPlusFileRecord:
case kHFSFolderRecord:
case kHFSFileRecord:
++state->count;
break;
default:
printf("cat_findposition: invalid record type %d in dir %d\n",
crp->recordType, curID);
state->error = EINVAL;
return (0);
};
return (state->count < state->index);
}
__private_extern__
int
cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
{
u_int32_t searchParentID, trialParentID;
int result;
searchParentID = searchKey->parentID;
trialParentID = trialKey->parentID;
result = 0;
if (searchParentID > trialParentID) {
++result;
} else if (searchParentID < trialParentID) {
--result;
} else {
u_int16_t * str1 = &searchKey->nodeName.unicode[0];
u_int16_t * str2 = &trialKey->nodeName.unicode[0];
int length1 = searchKey->nodeName.length;
int length2 = trialKey->nodeName.length;
u_int16_t c1, c2;
int length;
if (length1 < length2) {
length = length1;
--result;
} else if (length1 > length2) {
length = length2;
++result;
} else {
length = length1;
}
while (length--) {
c1 = *(str1++);
c2 = *(str2++);
if (c1 > c2) {
result = 1;
break;
}
if (c1 < c2) {
result = -1;
break;
}
}
}
return result;
}
int
CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
{
cnid_t searchParentID, trialParentID;
int result;
searchParentID = searchKey->parentID;
trialParentID = trialKey->parentID;
if (searchParentID > trialParentID)
result = 1;
else if (searchParentID < trialParentID)
result = -1;
else
result = FastRelString(searchKey->nodeName, trialKey->nodeName);
return result;
}
int
CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
{
cnid_t searchParentID, trialParentID;
int result;
searchParentID = searchKey->parentID;
trialParentID = trialKey->parentID;
if (searchParentID > trialParentID) {
result = 1;
}
else if (searchParentID < trialParentID) {
result = -1;
} else {
if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
result = searchKey->nodeName.length - trialKey->nodeName.length;
else
result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
searchKey->nodeName.length,
&trialKey->nodeName.unicode[0],
trialKey->nodeName.length);
}
return result;
}
static int
buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
HFSPlusCatalogKey *key, int retry)
{
int utf8_flags = UTF_ESCAPE_ILLEGAL;
int result = 0;
size_t unicodeBytes = 0;
if (descp->cd_namelen == 0 || descp->cd_nameptr[0] == '\0')
return (EINVAL);
key->parentID = descp->cd_parentcnid;
key->nodeName.length = 0;
if ((descp->cd_flags & CD_DECOMPOSED) == 0)
utf8_flags |= UTF_DECOMPOSED;
result = utf8_decodestr(descp->cd_nameptr, descp->cd_namelen,
key->nodeName.unicode, &unicodeBytes,
sizeof(key->nodeName.unicode), ':', utf8_flags);
key->nodeName.length = unicodeBytes / sizeof(UniChar);
key->keyLength = kHFSPlusCatalogKeyMinimumLength + unicodeBytes;
if (result) {
if (result != ENAMETOOLONG)
result = EINVAL;
return (result);
}
if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) {
HFSCatalogKey hfskey;
bzero(&hfskey, sizeof(hfskey));
hfskey.keyLength = kHFSCatalogKeyMinimumLength;
hfskey.parentID = key->parentID;
hfskey.nodeName[0] = 0;
if (key->nodeName.length > 0) {
if (unicode_to_hfs(HFSTOVCB(hfsmp),
key->nodeName.length * 2,
key->nodeName.unicode,
&hfskey.nodeName[0], retry) != 0) {
return (EINVAL);
}
hfskey.keyLength += hfskey.nodeName[0];
}
bcopy(&hfskey, key, sizeof(hfskey));
}
return (0);
}
__private_extern__
int
cat_resolvelink(struct hfsmount *hfsmp, u_long linkref, int isdirlink, struct HFSPlusCatalogFile *recp)
{
FSBufferDescriptor btdata;
struct BTreeIterator *iterator;
struct cat_desc idesc;
char inodename[32];
cnid_t parentcnid;
int result = 0;
BDINIT(btdata, recp);
if (isdirlink) {
MAKE_DIRINODE_NAME(inodename, sizeof(inodename), (unsigned int)linkref);
parentcnid = hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid;
} else {
MAKE_INODE_NAME(inodename, sizeof(inodename), (unsigned int)linkref);
parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
}
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
bzero(iterator, sizeof(*iterator));
idesc.cd_parentcnid = parentcnid;
idesc.cd_nameptr = (const u_int8_t *)inodename;
idesc.cd_namelen = strlen(inodename);
idesc.cd_flags = 0;
idesc.cd_hint = 0;
idesc.cd_encoding = 0;
(void) buildkey(hfsmp, &idesc, (HFSPlusCatalogKey *)&iterator->key, 0);
result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
&btdata, NULL, NULL);
if (result == 0) {
if (recp->hl_linkCount == 0)
recp->hl_linkCount = 2;
} else {
printf("HFS resolvelink: can't find %s\n", inodename);
}
FREE(iterator, M_TEMP);
return (result ? ENOENT : 0);
}
static int
resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino)
{
struct HFSPlusCatalogFile record;
int error;
error = cat_resolvelink(hfsmp, linkref, 0, &record);
if (error == 0) {
if (record.fileID == 0)
error = ENOENT;
else
*ino = record.fileID;
}
return (error);
}
static int
getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key)
{
struct BTreeIterator * iterator;
FSBufferDescriptor btdata;
u_int16_t datasize;
CatalogKey * keyp;
CatalogRecord * recp;
int result;
int std_hfs;
std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
bzero(iterator, sizeof(*iterator));
buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
BDINIT(btdata, recp);
result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
&btdata, &datasize, iterator);
if (result)
goto exit;
switch (recp->recordType) {
case kHFSFileThreadRecord:
case kHFSFolderThreadRecord:
keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
bcopy(keyp, key, keyp->hfs.keyLength + 1);
break;
case kHFSPlusFileThreadRecord:
case kHFSPlusFolderThreadRecord:
keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
(keyp->hfsPlus.nodeName.length * 2);
bcopy(keyp, key, keyp->hfsPlus.keyLength + 2);
break;
default:
result = ENOENT;
break;
}
exit:
FREE(iterator, M_TEMP);
FREE(recp, M_TEMP);
return MacToVFSError(result);
}
__private_extern__
int
cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp)
{
int result;
result = getkey(hfsmp, cnid, key);
if (result == 0) {
result = cat_lookupbykey(hfsmp, key, 0, 0, 0, NULL, attrp, NULL, NULL);
}
if ((result == 0) &&
(key->hfsPlus.parentID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
(attrp->ca_recflags & kHFSHasLinkChainMask)) {
cnid_t nextlinkid = 0;
cnid_t prevlinkid = 0;
struct cat_desc linkdesc;
if ((cat_lookuplinkbyid(hfsmp, cnid, &prevlinkid, &nextlinkid) == 0) &&
(nextlinkid != 0)) {
if (cat_findname(hfsmp, nextlinkid, &linkdesc) == 0) {
key->hfsPlus.parentID = linkdesc.cd_parentcnid;
cat_releasedesc(&linkdesc);
}
}
}
return MacToVFSError(result);
}
static void
buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding,
CatalogRecord *crp, u_int32_t *recordSize)
{
int type = attrp->ca_mode & S_IFMT;
u_int32_t createtime = to_hfs_time(attrp->ca_itime);
if (std_hfs) {
createtime = UTCToLocal(createtime);
if (type == S_IFDIR) {
bzero(crp, sizeof(HFSCatalogFolder));
crp->recordType = kHFSFolderRecord;
crp->hfsFolder.folderID = cnid;
crp->hfsFolder.createDate = createtime;
crp->hfsFolder.modifyDate = createtime;
bcopy(attrp->ca_finderinfo, &crp->hfsFolder.userInfo, 32);
*recordSize = sizeof(HFSCatalogFolder);
} else {
bzero(crp, sizeof(HFSCatalogFile));
crp->recordType = kHFSFileRecord;
crp->hfsFile.fileID = cnid;
crp->hfsFile.createDate = createtime;
crp->hfsFile.modifyDate = createtime;
bcopy(attrp->ca_finderinfo, &crp->hfsFile.userInfo, 16);
bcopy(&attrp->ca_finderinfo[16], &crp->hfsFile.finderInfo, 16);
*recordSize = sizeof(HFSCatalogFile);
}
} else {
struct HFSPlusBSDInfo * bsdp = NULL;
if (type == S_IFDIR) {
crp->recordType = kHFSPlusFolderRecord;
crp->hfsPlusFolder.flags = attrp->ca_recflags;
crp->hfsPlusFolder.valence = 0;
crp->hfsPlusFolder.folderID = cnid;
crp->hfsPlusFolder.createDate = createtime;
crp->hfsPlusFolder.contentModDate = createtime;
crp->hfsPlusFolder.attributeModDate = createtime;
crp->hfsPlusFolder.accessDate = createtime;
crp->hfsPlusFolder.backupDate = 0;
crp->hfsPlusFolder.textEncoding = encoding;
crp->hfsPlusFolder.folderCount = 0;
bcopy(attrp->ca_finderinfo, &crp->hfsPlusFolder.userInfo, 32);
bsdp = &crp->hfsPlusFolder.bsdInfo;
bsdp->special.linkCount = 1;
*recordSize = sizeof(HFSPlusCatalogFolder);
} else {
crp->recordType = kHFSPlusFileRecord;
crp->hfsPlusFile.flags = attrp->ca_recflags;
crp->hfsPlusFile.reserved1 = 0;
crp->hfsPlusFile.fileID = cnid;
crp->hfsPlusFile.createDate = createtime;
crp->hfsPlusFile.contentModDate = createtime;
crp->hfsPlusFile.accessDate = createtime;
crp->hfsPlusFile.attributeModDate = createtime;
crp->hfsPlusFile.backupDate = 0;
crp->hfsPlusFile.textEncoding = encoding;
crp->hfsPlusFile.reserved2 = 0;
bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
bsdp = &crp->hfsPlusFile.bsdInfo;
if (type == S_IFBLK || type == S_IFCHR) {
bsdp->special.rawDevice = attrp->ca_rdev;
} else {
bsdp->special.linkCount = 1;
}
bzero(&crp->hfsPlusFile.dataFork, 2*sizeof(HFSPlusForkData));
*recordSize = sizeof(HFSPlusCatalogFile);
}
bsdp->ownerID = attrp->ca_uid;
bsdp->groupID = attrp->ca_gid;
bsdp->fileMode = attrp->ca_mode;
bsdp->adminFlags = attrp->ca_flags >> 16;
bsdp->ownerFlags = attrp->ca_flags & 0x000000FF;
}
}
static int
builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
int isdir, struct cat_desc *descp)
{
int result = 0;
unsigned char * nameptr;
size_t bufsize;
size_t utf8len;
unsigned char tmpbuff[128];
bufsize = (3 * key->nodeName.length) + 1;
if (bufsize >= sizeof(tmpbuff) - 1) {
MALLOC(nameptr, unsigned char *, bufsize, M_TEMP, M_WAITOK);
} else {
nameptr = &tmpbuff[0];
}
result = utf8_encodestr(key->nodeName.unicode,
key->nodeName.length * sizeof(UniChar),
nameptr, (size_t *)&utf8len,
bufsize, ':', 0);
if (result == ENAMETOOLONG) {
bufsize = 1 + utf8_encodelen(key->nodeName.unicode,
key->nodeName.length * sizeof(UniChar),
':', 0);
FREE(nameptr, M_TEMP);
MALLOC(nameptr, unsigned char *, bufsize, M_TEMP, M_WAITOK);
result = utf8_encodestr(key->nodeName.unicode,
key->nodeName.length * sizeof(UniChar),
nameptr, (size_t *)&utf8len,
bufsize, ':', 0);
}
descp->cd_parentcnid = key->parentID;
descp->cd_nameptr = (const u_int8_t *)vfs_addname((char *)nameptr, utf8len, 0, 0);
descp->cd_namelen = utf8len;
descp->cd_cnid = cnid;
descp->cd_hint = hint;
descp->cd_flags = CD_DECOMPOSED | CD_HASBUF;
if (isdir)
descp->cd_flags |= CD_ISDIR;
descp->cd_encoding = encoding;
if (nameptr != &tmpbuff[0]) {
FREE(nameptr, M_TEMP);
}
return result;
}
static void
getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp)
{
int isDirectory = (crp->recordType == kHFSPlusFolderRecord);
const struct HFSPlusBSDInfo *bsd = &crp->bsdInfo;
attrp->ca_recflags = crp->flags;
attrp->ca_atime = to_bsd_time(crp->accessDate);
attrp->ca_atimeondisk = attrp->ca_atime;
attrp->ca_mtime = to_bsd_time(crp->contentModDate);
attrp->ca_ctime = to_bsd_time(crp->attributeModDate);
attrp->ca_itime = to_bsd_time(crp->createDate);
attrp->ca_btime = to_bsd_time(crp->backupDate);
if ((bsd->fileMode & S_IFMT) == 0) {
attrp->ca_flags = 0;
attrp->ca_uid = hfsmp->hfs_uid;
attrp->ca_gid = hfsmp->hfs_gid;
if (isDirectory) {
attrp->ca_mode = S_IFDIR | (hfsmp->hfs_dir_mask & ACCESSPERMS);
} else {
attrp->ca_mode = S_IFREG | (hfsmp->hfs_file_mask & ACCESSPERMS);
}
attrp->ca_linkcount = 1;
attrp->ca_rdev = 0;
} else {
attrp->ca_linkcount = 1;
attrp->ca_rdev = 0;
attrp->ca_uid = bsd->ownerID;
attrp->ca_gid = bsd->groupID;
attrp->ca_flags = bsd->ownerFlags | (bsd->adminFlags << 16);
attrp->ca_mode = (mode_t)bsd->fileMode;
switch (attrp->ca_mode & S_IFMT) {
case S_IFCHR:
case S_IFBLK:
attrp->ca_rdev = bsd->special.rawDevice;
break;
case S_IFDIR:
case S_IFREG:
if (bsd->special.linkCount > 0)
attrp->ca_linkcount = bsd->special.linkCount;
break;
}
}
if (isDirectory) {
if (!S_ISDIR(attrp->ca_mode)) {
attrp->ca_mode &= ~S_IFMT;
attrp->ca_mode |= S_IFDIR;
}
attrp->ca_entries = ((const HFSPlusCatalogFolder *)crp)->valence;
attrp->ca_dircount = ((hfsmp->hfs_flags & HFS_FOLDERCOUNT) && (attrp->ca_recflags & kHFSHasFolderCountMask)) ?
((const HFSPlusCatalogFolder *)crp)->folderCount : 0;
if (((const HFSPlusCatalogFolder *)crp)->userInfo.frFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
attrp->ca_flags |= UF_HIDDEN;
} else {
if (crp->flags & kHFSFileLockedMask) {
if ((attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) == 0)
attrp->ca_flags |= UF_IMMUTABLE;
} else {
attrp->ca_flags &= ~(SF_IMMUTABLE | UF_IMMUTABLE);
}
if (crp->userInfo.fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
attrp->ca_flags |= UF_HIDDEN;
attrp->ca_blocks = crp->dataFork.totalBlocks + crp->resourceFork.totalBlocks;
if ((hfsmp->hfs_flags & HFS_STANDARD) == 0)
attrp->ca_recflags |= kHFSThreadExistsMask;
attrp->ca_firstlink = (attrp->ca_recflags & kHFSHasLinkChainMask) ? crp->hl_firstLinkID : 0;
}
attrp->ca_fileid = crp->fileID;
bcopy(&crp->userInfo, attrp->ca_finderinfo, 32);
}
static void
promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey,
HFSPlusCatalogKey *keyp, u_long *encoding)
{
hfs_to_unicode_func_t hfs_get_unicode = hfsmp->hfs_get_unicode;
u_int32_t uniCount;
int error;
*encoding = hfsmp->hfs_encoding;
error = hfs_get_unicode(hfskey->nodeName, keyp->nodeName.unicode,
kHFSPlusMaxFileNameChars, &uniCount);
if (error && hfsmp->hfs_encoding != kTextEncodingMacRoman) {
*encoding = 0;
(void) mac_roman_to_unicode(hfskey->nodeName,
keyp->nodeName.unicode,
kHFSPlusMaxFileNameChars,
&uniCount);
}
keyp->nodeName.length = uniCount;
keyp->parentID = hfskey->parentID;
}
static void
promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep,
int resource, struct cat_fork * forkp)
{
struct HFSPlusExtentDescriptor *xp;
u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
bzero(forkp, sizeof(*forkp));
xp = &forkp->cf_extents[0];
if (resource) {
forkp->cf_size = filep->rsrcLogicalSize;
forkp->cf_blocks = filep->rsrcPhysicalSize / blocksize;
forkp->cf_bytesread = 0;
forkp->cf_vblocks = 0;
xp[0].startBlock = (u_int32_t)filep->rsrcExtents[0].startBlock;
xp[0].blockCount = (u_int32_t)filep->rsrcExtents[0].blockCount;
xp[1].startBlock = (u_int32_t)filep->rsrcExtents[1].startBlock;
xp[1].blockCount = (u_int32_t)filep->rsrcExtents[1].blockCount;
xp[2].startBlock = (u_int32_t)filep->rsrcExtents[2].startBlock;
xp[2].blockCount = (u_int32_t)filep->rsrcExtents[2].blockCount;
} else {
forkp->cf_size = filep->dataLogicalSize;
forkp->cf_blocks = filep->dataPhysicalSize / blocksize;
forkp->cf_bytesread = 0;
forkp->cf_vblocks = 0;
xp[0].startBlock = (u_int32_t)filep->dataExtents[0].startBlock;
xp[0].blockCount = (u_int32_t)filep->dataExtents[0].blockCount;
xp[1].startBlock = (u_int32_t)filep->dataExtents[1].startBlock;
xp[1].blockCount = (u_int32_t)filep->dataExtents[1].blockCount;
xp[2].startBlock = (u_int32_t)filep->dataExtents[2].startBlock;
xp[2].blockCount = (u_int32_t)filep->dataExtents[2].blockCount;
}
}
static void
promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp)
{
u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
if (dataPtr->recordType == kHFSFolderRecord) {
const struct HFSCatalogFolder * folder;
folder = (const struct HFSCatalogFolder *) dataPtr;
crp->recordType = kHFSPlusFolderRecord;
crp->flags = folder->flags;
crp->fileID = folder->folderID;
crp->createDate = LocalToUTC(folder->createDate);
crp->contentModDate = LocalToUTC(folder->modifyDate);
crp->backupDate = LocalToUTC(folder->backupDate);
crp->reserved1 = folder->valence;
crp->reserved2 = 0;
bcopy(&folder->userInfo, &crp->userInfo, 32);
} else {
const struct HFSCatalogFile * file;
file = (const struct HFSCatalogFile *) dataPtr;
crp->recordType = kHFSPlusFileRecord;
crp->flags = file->flags;
crp->fileID = file->fileID;
crp->createDate = LocalToUTC(file->createDate);
crp->contentModDate = LocalToUTC(file->modifyDate);
crp->backupDate = LocalToUTC(file->backupDate);
crp->reserved1 = 0;
crp->reserved2 = 0;
bcopy(&file->userInfo, &crp->userInfo, 16);
bcopy(&file->finderInfo, &crp->finderInfo, 16);
crp->dataFork.totalBlocks = file->dataPhysicalSize / blocksize;
crp->resourceFork.totalBlocks = file->rsrcPhysicalSize / blocksize;
}
crp->textEncoding = 0;
crp->attributeModDate = crp->contentModDate;
crp->accessDate = crp->contentModDate;
bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo));
}
static int
buildthread(void *keyp, void *recp, int std_hfs, int directory)
{
int size = 0;
if (std_hfs) {
HFSCatalogKey *key = (HFSCatalogKey *)keyp;
HFSCatalogThread *rec = (HFSCatalogThread *)recp;
size = sizeof(HFSCatalogThread);
bzero(rec, size);
if (directory)
rec->recordType = kHFSFolderThreadRecord;
else
rec->recordType = kHFSFileThreadRecord;
rec->parentID = key->parentID;
bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1);
} else {
HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)keyp;
HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)recp;
size = sizeof(HFSPlusCatalogThread);
if (directory)
rec->recordType = kHFSPlusFolderThreadRecord;
else
rec->recordType = kHFSPlusFileThreadRecord;
rec->reserved = 0;
rec->parentID = key->parentID;
bcopy(&key->nodeName, &rec->nodeName,
sizeof(UniChar) * (key->nodeName.length + 1));
size -= (sizeof(rec->nodeName.unicode) -
(rec->nodeName.length * sizeof(UniChar)));
}
return (size);
}
static void
buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key)
{
if (std_hfs) {
key->hfs.keyLength = kHFSCatalogKeyMinimumLength;
key->hfs.reserved = 0;
key->hfs.parentID = parentID;
key->hfs.nodeName[0] = 0;
} else {
key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength;
key->hfsPlus.parentID = parentID;
key->hfsPlus.nodeName.length = 0;
}
}
static u_long
getencoding(const CatalogRecord *crp)
{
u_long encoding;
if (crp->recordType == kHFSPlusFolderRecord)
encoding = crp->hfsPlusFolder.textEncoding;
else if (crp->recordType == kHFSPlusFileRecord)
encoding = crp->hfsPlusFile.textEncoding;
else
encoding = 0;
return (encoding);
}
static cnid_t
getcnid(const CatalogRecord *crp)
{
cnid_t cnid = 0;
switch (crp->recordType) {
case kHFSFolderRecord:
cnid = crp->hfsFolder.folderID;
break;
case kHFSFileRecord:
cnid = crp->hfsFile.fileID;
break;
case kHFSPlusFolderRecord:
cnid = crp->hfsPlusFolder.folderID;
break;
case kHFSPlusFileRecord:
cnid = crp->hfsPlusFile.fileID;
break;
default:
panic("hfs: getcnid: unknown recordType (crp @ %p)\n", crp);
break;
}
return (cnid);
}
static cnid_t
getparentcnid(const CatalogRecord *recp)
{
cnid_t cnid = 0;
switch (recp->recordType) {
case kHFSFileThreadRecord:
case kHFSFolderThreadRecord:
cnid = recp->hfsThread.parentID;
break;
case kHFSPlusFileThreadRecord:
case kHFSPlusFolderThreadRecord:
cnid = recp->hfsPlusThread.parentID;
break;
default:
panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp);
break;
}
return (cnid);
}
static int
isadir(const CatalogRecord *crp)
{
return (crp->recordType == kHFSFolderRecord ||
crp->recordType == kHFSPlusFolderRecord);
}