#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/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, 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);
extern int mac_roman_to_unicode(const Str31 hfs_str, UniChar *uni_str,
UInt32 maxCharLen, UInt32 *unicodeChars);
extern int unicode_to_hfs(ExtendedVCB *vcb, ByteCount srcLen,
const u_int16_t* srcStr, Str31 dstStr, int retry);
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, int *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);
__private_extern__
int
cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, struct proc *p)
{
FCB *fcb;
int lockflags;
int result;
fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
result = BTReserveSpace(fcb, ops, (void*)cookie);
hfs_systemfile_unlock(hfsmp, lockflags);
return MacToVFSError(result);
}
__private_extern__
void
cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, struct proc *p)
{
FCB *fcb;
int lockflags;
fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
(void) BTReleaseReserve(fcb, (void*)cookie);
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)
{
char * 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;
descp->cd_flags &= ~CD_HASBUF;
vfs_removename(name);
}
descp->cd_nameptr = NULL;
descp->cd_namelen = 0;
}
__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, 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;
UInt16 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, struct cat_desc *outdescp,
struct cat_attr *attrp, struct cat_fork *forkp)
{
struct BTreeIterator * iterator;
FSBufferDescriptor btdata;
UInt16 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, 0, 0, outdescp, attrp, forkp, NULL);
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;
int prefixlen;
int result;
if (wantrsrc)
return (ENOENT);
fileID = GetEmbeddedFileID(descp->cd_nameptr, descp->cd_namelen, &prefixlen);
if (fileID < kHFSFirstUserCatalogNodeID)
return (ENOENT);
result = cat_idlookup(hfsmp, fileID, outdescp, attrp, forkp);
if (result)
return (ENOENT);
if (descp->cd_parentcnid != outdescp->cd_parentcnid)
goto falsematch;
if ((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, 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;
UInt16 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))) {
result = ENOENT;
goto exit;
}
if (!std_hfs
&& (attrp || forkp)
&& (recp->recordType == kHFSPlusFileRecord)
&& (SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType)
&& (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)
&& ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)HFSTOVCB(hfsmp)->vcbCrDate) ||
(to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_metadata_createdate))) {
ilink = recp->hfsPlusFile.bsdInfo.special.iNodeNum;
(void) resolvelink(hfsmp, ilink, (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_rdev = 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 {
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));
}
}
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)
{
ExtendedVCB * vcb;
FCB * fcb;
struct btobj * bto;
FSBufferDescriptor btdata;
u_int32_t nextCNID;
u_int32_t datalen;
int std_hfs;
int result = 0;
u_long encoding;
int modeformat;
int mntlock = 0;
modeformat = attrp->ca_mode & S_IFMT;
vcb = HFSTOVCB(hfsmp);
fcb = GetFileControlBlock(vcb->catalogRefNum);
std_hfs = (vcb->vcbSigWord == kHFSSigWord);
HFS_MOUNT_LOCK(hfsmp, TRUE);
mntlock = 1;
nextCNID = hfsmp->vcbNxtCNID;
if (nextCNID == 0xFFFFFFFF) {
if (std_hfs) {
result = ENOSPC;
} else {
hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
}
} else {
hfsmp->vcbNxtCNID++;
}
hfsmp->vcbFlags |= 0xFF00;
if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask) == 0) {
HFS_MOUNT_UNLOCK(hfsmp, TRUE);
mntlock = 0;
if (result)
return (result);
}
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 (;;) {
buildthreadkey(nextCNID, std_hfs, (CatalogKey *) &bto->iterator.key);
result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
if ((result == btExists) && !std_hfs && mntlock) {
if (++nextCNID < kHFSFirstUserCatalogNodeID) {
nextCNID = kHFSFirstUserCatalogNodeID;
}
continue;
}
break;
}
if (result) goto exit;
}
if (mntlock) {
hfsmp->vcbNxtCNID = nextCNID + 1;
if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
}
HFS_MOUNT_UNLOCK(hfsmp, TRUE);
mntlock = 0;
}
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);
(void) BTDeleteRecord(fcb, &bto->iterator);
}
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:
if (mntlock)
HFS_MOUNT_UNLOCK(hfsmp, TRUE);
(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;
UInt16 datasize;
int result = 0;
int sourcegone = 0;
int skipthread = 0;
int directory = from_cdp->cd_flags & CD_ISDIR;
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) {
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) {
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)
panic("cat_create: could not undo (BTInsert = %d)", err);
}
#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)
panic("cat_create: could not undo (BTDelete = %d)", err);
}
#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) {
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)
{
ExtendedVCB * vcb;
FCB * fcb;
BTreeIterator *iterator;
cnid_t cnid;
int std_hfs;
int result;
vcb = HFSTOVCB(hfsmp);
fcb = GetFileControlBlock(vcb->catalogRefNum);
std_hfs = (vcb->vcbSigWord == kHFSSigWord);
if (descp->cd_cnid < kHFSFirstUserCatalogNodeID ||
descp->cd_parentcnid == kHFSRootParentID)
return (EINVAL);
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
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);
(void) BTDeleteRecord(fcb, iterator);
exit:
(void) BTFlushPath(fcb);
FREE(iterator, M_TEMP);
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)
{
ExtendedVCB * vcb;
FCB * fcb;
BTreeIterator * iterator;
struct update_state state;
int std_hfs;
int result;
vcb = HFSTOVCB(hfsmp);
fcb = GetFileControlBlock(vcb->catalogRefNum);
std_hfs = (vcb->vcbSigWord == kHFSSigWord);
state.s_desc = descp;
state.s_attr = attrp;
state.s_datafork = dataforkp;
state.s_rsrcfork = rsrcforkp;
state.s_hfsmp = hfsmp;
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
if ((descp->cd_cnid != attrp->ca_fileid) || (descp->cd_namelen == 0))
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);
FREE(iterator, M_TEMP);
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;
}
}
break;
}
case kHFSPlusFolderRecord: {
HFSPlusCatalogFolder *dir;
dir = (struct HFSPlusCatalogFolder *)crp;
if ((ckp->hfsPlus.parentID != descp->cd_parentcnid) ||
(dir->folderID != descp->cd_cnid))
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);
dir->textEncoding = descp->cd_encoding;
dir->attrBlocks = attrp->ca_attrblks;
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;
}
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);
file->textEncoding = descp->cd_encoding;
file->attrBlocks = attrp->ca_attrblks;
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_nlink == 2)
file->bsdInfo.special.linkCount = attrp->ca_nlink;
break;
}
default:
return (btNotFound);
}
return (0);
}
struct readattr_state {
struct hfsmount *hfsmp;
struct cat_entrylist *list;
cnid_t dir_cnid;
int stdhfs;
int error;
};
static int
cat_readattr(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) &&
(rec->hfsPlusFolder.folderID == hfsmp->hfs_privdir_desc.cd_cnid)) {
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))) {
return (1);
}
}
cep = &list->entry[list->realentries++];
if (state->stdhfs) {
struct HFSPlusCatalogFile cnoderec;
HFSPlusCatalogKey * pluskey;
long encoding;
promoteattr(hfsmp, rec, &cnoderec);
getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
promotekey(hfsmp, (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, (struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
builddesc((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_rdev = 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)cat_readattr, &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;
cep = &ce_list->entry[i];
if (!S_ISREG(cep->ce_attr.ca_mode))
continue;
fip = (struct FndrFileInfo *)&cep->ce_attr.ca_finderinfo;
if ((cep->ce_attr.ca_rdev != 0)
&& (SWAP_BE32(fip->fdType) == kHardLinkFileType)
&& (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)
&& ((cep->ce_attr.ca_itime == (time_t)HFSTOVCB(hfsmp)->vcbCrDate) ||
(cep->ce_attr.ca_itime == (time_t)hfsmp->hfs_metadata_createdate))) {
if (resolvelink(hfsmp, cep->ce_attr.ca_rdev, &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;
struct direntry * cbs_direntry;
struct direntry * cbs_prevdirentry;
u_int32_t cbs_previlinkref;
Boolean cbs_hasprevdirentry;
Boolean cbs_eof;
};
static int
cat_packdirentry(const CatalogKey *ckp, const CatalogRecord *crp,
struct packdirentry_state *state)
{
struct hfsmount *hfsmp;
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;
u_int8_t is_mangled = 0;
char *nameptr;
user_addr_t uiobase;
size_t namelen = 0;
size_t maxnamelen;
size_t uiosize = 0;
caddr_t uioaddr;
Boolean stop_after_pack = false;
hfsmp = state->cbs_hfsmp;
if (hfsmp->hfs_flags & HFS_STANDARD)
curID = ckp->hfs.parentID;
else
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 = &entry->d_name[0];
maxnamelen = NAME_MAX;
} else {
nameptr = &catent.d_name[0];
maxnamelen = NAME_MAX;
}
if (state->cbs_extended && stop_after_pack) {
cnid = INT_MAX;
} else {
if (!(hfsmp->hfs_flags & HFS_STANDARD)) {
switch(crp->recordType) {
case kHFSPlusFolderRecord:
type = DT_DIR;
cnid = crp->hfsPlusFolder.folderID;
if ((curID == kHFSRootFolderID) &&
(cnid == hfsmp->hfs_privdir_desc.cd_cnid)) {
hide = 1;
}
break;
case kHFSPlusFileRecord:
itime = to_bsd_time(crp->hfsPlusFile.createDate);
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))) {
ilinkref = crp->hfsPlusFile.bsdInfo.special.iNodeNum;
}
type = MODE_TO_DT(crp->hfsPlusFile.bsdInfo.fileMode);
cnid = crp->hfsPlusFile.fileID;
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 = (CatalogName*) &ckp->hfsPlus.nodeName;
result = utf8_encodestr(cnp->ustr.unicode, cnp->ustr.length * 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;
}
} else {
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 = (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);
}
}
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;
}
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) {
vfs_removename(state->cbs_desc->cd_nameptr);
}
#if 0
state->cbs_desc->cd_encoding = xxxx;
#endif
if (!is_mangled) {
state->cbs_desc->cd_namelen = namelen;
state->cbs_desc->cd_nameptr = vfs_addname(nameptr, namelen, 0, 0);
} else {
char *new_nameptr;
size_t bufsize;
size_t tmp_namelen = 0;
cnp = (CatalogName *)&ckp->hfsPlus.nodeName;
bufsize = 1 + utf8_encodelen(cnp->ustr.unicode,
cnp->ustr.length * sizeof(UniChar),
':', 0);
MALLOC(new_nameptr, char *, 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;
state->cbs_desc->cd_nameptr = vfs_addname(new_nameptr, tmp_namelen, 0, 0);
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);
}
__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;
fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
maxlinks = MIN(entrycnt, uio_resid(uio) / SMALL_DIRENTRY_SIZE);
bufsize = (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 *) buffer;
iterator = (BTreeIterator *) ((char *)buffer + (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) {
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 cleanup;
}
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_result = 0;
state.cbs_parentID = dirhint->dh_desc.cd_parentcnid;
enum BTreeIterationOperations op;
if (extended && index != 0 && have_key)
op = kBTreeCurrentRecord;
else
op = kBTreeNextRecord;
result = BTIterateRecords(fcb, op, iterator,
(IterateCallBackProcPtr)cat_packdirentry, &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 (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)) {
(void) copyout(&fileid, address,
extended ? sizeof(ino64_t) : sizeof(ino_t));
} else {
ino64_t *inoptr = (ino64_t *)CAST_DOWN(caddr_t, address);
*inoptr = 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 = 0;
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
resolvelink(struct hfsmount *hfsmp, u_long linkref, struct HFSPlusCatalogFile *recp)
{
FSBufferDescriptor btdata;
struct BTreeIterator *iterator;
struct cat_desc idesc;
char inodename[32];
int result = 0;
BDINIT(btdata, recp);
MAKE_INODE_NAME(inodename, linkref);
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
bzero(iterator, sizeof(*iterator));
idesc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
idesc.cd_nameptr = 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->bsdInfo.special.linkCount == 0)
recp->bsdInfo.special.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 = resolvelink(hfsmp, linkref, &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;
UInt16 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, NULL, attrp, NULL, NULL);
}
return MacToVFSError(result);
}
static void
buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding,
CatalogRecord *crp, int *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;
struct FndrFileInfo * fip = NULL;
if (type == S_IFDIR) {
crp->recordType = kHFSPlusFolderRecord;
crp->hfsPlusFolder.flags = 0;
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.attrBlocks = 0;
bcopy(attrp->ca_finderinfo, &crp->hfsPlusFolder.userInfo, 32);
bsdp = &crp->hfsPlusFolder.bsdInfo;
bsdp->special.rawDevice = 0;
*recordSize = sizeof(HFSPlusCatalogFolder);
} else {
crp->recordType = kHFSPlusFileRecord;
crp->hfsPlusFile.flags = kHFSThreadExistsMask;
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.attrBlocks = 0;
bsdp = &crp->hfsPlusFile.bsdInfo;
bsdp->special.rawDevice = 0;
switch(type) {
case S_IFBLK:
case S_IFCHR:
bsdp->special.rawDevice = attrp->ca_rdev;
bzero(&crp->hfsPlusFile.userInfo, 32);
break;
case S_IFREG:
fip = (FndrFileInfo *)&attrp->ca_finderinfo;
if ((SWAP_BE32(fip->fdType) == kHardLinkFileType) &&
(SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)) {
bsdp->special.iNodeNum = attrp->ca_rdev;
}
bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
break;
case S_IFLNK:
bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
break;
}
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;
char * nameptr;
size_t bufsize;
size_t utf8len;
char tmpbuff[128];
bufsize = (3 * key->nodeName.length) + 1;
if (bufsize >= sizeof(tmpbuff) - 1) {
MALLOC(nameptr, 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, 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 = vfs_addname(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_nlink = 1;
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_rdev = 0;
} else {
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_IFREG:
if (bsd->special.linkCount > 0)
attrp->ca_nlink = bsd->special.linkCount;
break;
}
if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
attrp->ca_uid = hfsmp->hfs_uid;
attrp->ca_gid = hfsmp->hfs_gid;
}
}
if (isDirectory) {
if (!S_ISDIR(attrp->ca_mode)) {
attrp->ca_mode &= ~S_IFMT;
attrp->ca_mode |= S_IFDIR;
}
attrp->ca_nlink = 2 + ((HFSPlusCatalogFolder *)crp)->valence;
attrp->ca_entries = ((HFSPlusCatalogFolder *)crp)->valence;
attrp->ca_attrblks = ((HFSPlusCatalogFolder *)crp)->attrBlocks;
} 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);
}
attrp->ca_blocks = crp->dataFork.totalBlocks + crp->resourceFork.totalBlocks;
attrp->ca_attrblks = crp->attrBlocks;
if ((hfsmp->hfs_flags & HFS_STANDARD) == 0)
attrp->ca_recflags |= kHFSThreadExistsMask;
}
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;
UInt32 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) {
struct HFSCatalogFolder * folder;
folder = (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;
bcopy(&folder->userInfo, &crp->userInfo, 32);
} else {
struct HFSCatalogFile * file;
file = (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;
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));
crp->attrBlocks = 0;
}
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:
printf("hfs: getcnid: unknown recordType (crp @ 0x%x)\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 @ 0x%x)\n", recp);
break;
}
return (cnid);
}
static int
isadir(const CatalogRecord *crp)
{
return (crp->recordType == kHFSFolderRecord ||
crp->recordType == kHFSPlusFolderRecord);
}