#include "lf_hfs.h"
#include "lf_hfs_catalog.h"
#include "lf_hfs_utils.h"
#include "lf_hfs_vfsutils.h"
#include "lf_hfs_vfsops.h"
#include "lf_hfs_unicode_wrappers.h"
#include "lf_hfs_sbunicode.h"
#include "lf_hfs_btrees_internal.h"
#include <sys/stat.h>
#include "lf_hfs_vnops.h"
#include <UserFS/UserVFS.h>
#include "lf_hfs_dirops_handler.h"
#include "lf_hfs_endian.h"
#include "lf_hfs_btree.h"
#include "lf_hfs_xattr.h"
#include "lf_hfs_chash.h"
#include <sys/types.h>
#include <sys/mount.h>
#include "lf_hfs_chash.h"
#include "lf_hfs_generic_buf.h"
#include "lf_hfs_journal.h"
#define HFS_LOOKUP_SYSFILE 0x1
#define HFS_LOOKUP_HARDLINK 0x2
#define HFS_LOOKUP_CASESENSITIVE 0x4
#define SMALL_DIRENTRY_SIZE (UVFS_DIRENTRY_RECLEN(1))
u_char modetodirtype[16] = {
UVFS_FA_TYPE_FILE, 0, 0, 0,
UVFS_FA_TYPE_DIR, 0, 0, 0,
UVFS_FA_TYPE_FILE, 0, UVFS_FA_TYPE_SYMLINK, 0,
0, 0, 0, 0
};
#define MODE_TO_TYPE(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
#define BDINIT(bd, addr) { \
(bd).bufferAddress = (addr); \
(bd).itemSize = sizeof(*(addr)); \
(bd).itemCount = 1; \
}
#define IDHASH(hfsmp, inum) (&hfsmp->hfs_idhashtbl[(inum) & hfsmp->hfs_idhash])
static int isadir(const CatalogRecord *crp);
static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid,
u_int32_t hint, u_int32_t encoding, int isdir, struct cat_desc *descp);
static int buildkey(struct cat_desc *descp, HFSPlusCatalogKey *key);
static int resolvelinkid(struct hfsmount *hfsmp, u_int32_t linkref, ino_t *ino);
static int cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp);
struct linkinfo {
u_int32_t link_ref;
caddr_t dirent_addr;
};
typedef struct linkinfo linkinfo_t;
struct btobj {
BTreeIterator iterator;
HFSPlusCatalogKey key;
CatalogRecord data;
};
enum {
kHFSAliasSize = 0x1d0,
kHFSAliasVolTypeEjectable = 0x5,
kHFSAliasVolCreateDateOffset = 0x12a,
kHFSAliasVolTypeOffset = 0x130,
kHFSAliasParentIDOffset = 0x132,
kHFSAliasTargetIDOffset = 0x176,
};
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
};
struct packdirentry_state {
int cbs_flags;
u_int32_t cbs_parentID;
u_int32_t cbs_index;
ReadDirBuff_t cbs_psReadDirBuffer;
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;
UVFSDirEntry * cbs_direntry;
UVFSDirEntry * cbs_prevdirentry;
UVFSDirEntry * cbs_lastinsertedentry;
u_int32_t cbs_previlinkref;
Boolean cbs_hasprevdirentry;
Boolean cbs_haslastinsertedentry;
Boolean cbs_eof;
};
struct position_state {
int error;
u_int32_t count;
u_int32_t index;
u_int32_t parentID;
struct hfsmount *hfsmp;
};
void
hfs_idhash_init (struct hfsmount *hfsmp) {
hfsmp->hfs_idhashtbl = hashinit(HFS_IDHASH_DEFAULT, &hfsmp->hfs_idhash);
}
void
hfs_idhash_destroy (struct hfsmount *hfsmp) {
hashDeinit(hfsmp->hfs_idhashtbl);
}
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;
}
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;
result = UnicodeBinaryCompare (str1, length1, str2, length2);
}
return result;
}
void
cat_releasedesc(struct cat_desc *descp)
{
if (descp == NULL)
return;
if ((descp->cd_flags & CD_HASBUF) && (descp->cd_nameptr != NULL)) {
hfs_free( (void*)descp->cd_nameptr );
}
descp->cd_nameptr = NULL;
descp->cd_namelen = 0;
descp->cd_flags &= ~CD_HASBUF;
}
static cnid_t
getcnid(const CatalogRecord *crp)
{
cnid_t cnid = 0;
switch (crp->recordType) {
case kHFSPlusFolderRecord:
cnid = crp->hfsPlusFolder.folderID;
break;
case kHFSPlusFileRecord:
cnid = crp->hfsPlusFile.fileID;
break;
default:
LFHFS_LOG(LEVEL_ERROR, "getcnid: unknown recordType=%d\n", crp->recordType);
break;
}
return (cnid);
}
static u_int32_t
getencoding(const CatalogRecord *crp)
{
u_int32_t encoding;
if (crp->recordType == kHFSPlusFolderRecord)
encoding = crp->hfsPlusFolder.textEncoding;
else if (crp->recordType == kHFSPlusFileRecord)
encoding = crp->hfsPlusFile.textEncoding;
else
encoding = 0;
return (encoding);
}
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 & (S_IRWXU|S_IRWXG|S_IRWXO));
} else {
attrp->ca_mode = S_IFREG | (hfsmp->hfs_file_mask & (S_IRWXU|S_IRWXG|S_IRWXO));
}
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_IFIFO:
case S_IFSOCK:
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;
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 int
builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_int32_t hint, u_int32_t encoding,
int isdir, struct cat_desc *descp)
{
int result = 0;
unsigned char * nameptr;
size_t bufsize;
size_t utf8len;
bufsize = (3 * key->nodeName.length) + 1;
nameptr = hfs_malloc(bufsize);
if (nameptr == NULL)
return ENOMEM;
memset(nameptr,0,bufsize);
result = utf8_encodestr(key->nodeName.unicode,
key->nodeName.length * sizeof(UniChar),
nameptr, (size_t *)&utf8len,
bufsize, ':', UTF_ADD_NULL_TERM);
if (result == ENAMETOOLONG) {
hfs_free(nameptr);
bufsize = 1 + utf8_encodelen(key->nodeName.unicode,
key->nodeName.length * sizeof(UniChar),
':', UTF_ADD_NULL_TERM);
nameptr = hfs_malloc(bufsize);
result = utf8_encodestr(key->nodeName.unicode,
key->nodeName.length * sizeof(UniChar),
nameptr, (size_t *)&utf8len,
bufsize, ':', UTF_ADD_NULL_TERM);
}
descp->cd_parentcnid = key->parentID;
descp->cd_nameptr = nameptr;
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;
return result;
}
static int
cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t hint, int wantrsrc,
struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid)
{
BTreeIterator * iterator = NULL;
FSBufferDescriptor btdata = {0};
CatalogRecord * recp = NULL;
u_int16_t datasize = 0;
int result = 0;
u_int32_t ilink = 0;
cnid_t cnid = 0;
u_int32_t encoding = 0;
cnid_t parentid = 0;
recp = hfs_malloc(sizeof(CatalogRecord));
BDINIT(btdata, recp);
iterator = hfs_mallocz(sizeof(*iterator));
iterator->hint.nodeNum = hint;
bcopy(keyp, &iterator->key, sizeof(CatalogKey));
FCB *filePtr = VTOF(HFSTOVCB(hfsmp)->catalogRefNum);
result = BTSearchRecord(filePtr, iterator,
&btdata, &datasize, iterator);
if (result)
goto exit;
cnid = getcnid(recp);
if (cnid == 0) {
hfs_mark_inconsistent (hfsmp, HFS_INCONSISTENCY_DETECTED);
result = EINVAL;
goto exit;
}
parentid = keyp->hfsPlus.parentID;
encoding = getencoding(recp);
hint = iterator->hint.nodeNum;
if ( IsEntryAJnlFile(hfsmp, cnid) && !(flags & HFS_LOOKUP_SYSFILE))
{
result = HFS_ERESERVEDNAME;
goto exit;
}
if ( (attrp || forkp)
&& (recp->recordType == kHFSPlusFileRecord)
&& ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_itime) ||
(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) && !(flags & HFS_LOOKUP_HARDLINK)) {
ilink = recp->hfsPlusFile.hl_linkReference;
(void) cat_resolvelink(hfsmp, ilink, isdirlink, (struct HFSPlusCatalogFile *)recp);
}
}
if (attrp != NULL) {
getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
if (ilink) {
attrp->ca_linkref = ilink;
}
if (ilink)
{
if (!(attrp->ca_recflags & kHFSHasLinkChainMask))
{
LFHFS_LOG(LEVEL_DEBUG, "cat_lookupbykey: set hardlink bit on vol=%s cnid=%u inoid=%u\n", hfsmp->vcbVN, cnid, ilink);
attrp->ca_recflags |= kHFSHasLinkChainMask;
}
}
else
{
if ((parentid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
(parentid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid))
{
if (attrp->ca_recflags & kHFSHasLinkChainMask)
{
LFHFS_LOG(LEVEL_DEBUG, "cat_lookupbykey: clear hardlink bit on vol=%s cnid=%u\n", hfsmp->vcbVN, cnid);
attrp->ca_recflags &= ~kHFSHasLinkChainMask;
}
if (S_ISREG(attrp->ca_mode) && (attrp->ca_linkcount > 1))
{
LFHFS_LOG(LEVEL_DEBUG, "cat_lookupbykey: set linkcount=1 on vol=%s cnid=%u old=%u\n", hfsmp->vcbVN, cnid, attrp->ca_linkcount);
attrp->ca_linkcount = 1;
}
}
}
}
if (forkp != NULL) {
if (isadir(recp)) {
bzero(forkp, sizeof(*forkp));
}
else if (wantrsrc) {
forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
forkp->cf_new_size = 0;
forkp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
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_new_size = 0;
forkp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
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;
(void) hfs_mark_inconsistent (hfsmp, HFS_INCONSISTENCY_DETECTED);
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;
pluskey = (HFSPlusCatalogKey *)&iterator->key;
builddesc(pluskey, cnid, hint, encoding, isadir(recp), descp);
}
if (desc_cnid != NULL) {
*desc_cnid = cnid;
}
exit:
hfs_free(iterator);
hfs_free(recp);
return MacToVFSError(result);
}
static int
isadir(const CatalogRecord *crp)
{
if (crp->recordType == kHFSPlusFolderRecord)
{
return 1;
}
return 0;
}
static int
buildthread(void *keyp, void *recp, int directory)
{
int size = 0;
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, CatalogKey *key)
{
key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength;
key->hfsPlus.parentID = parentID;
key->hfsPlus.nodeName.length = 0;
}
int
cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
{
BTreeIterator *iterator = NULL;
CatalogRecord * recp = NULL;
FSBufferDescriptor btdata;
CatalogKey * keyp;
int isdir = 0;
int result;
iterator = hfs_mallocz(sizeof(BTreeIterator));
if (iterator == NULL)
{
result = ENOMEM;
goto exit;
}
buildthreadkey(cnid, (CatalogKey *)&iterator->key);
iterator->hint.nodeNum = 0;
recp = hfs_malloc(sizeof(CatalogRecord));
if (recp == NULL)
{
result = ENOMEM;
goto exit;
}
memset(recp,0,sizeof(CatalogRecord));
BDINIT(btdata, recp);
result = BTSearchRecord(VTOF(hfsmp->hfs_catalog_vp), iterator, &btdata, NULL, NULL);
if (result)
goto exit;
switch (recp->recordType)
{
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;
}
builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp);
exit:
if (recp)
hfs_free(recp);
if (iterator)
hfs_free(iterator);
return result;
}
bool IsEntryAJnlFile(struct hfsmount *hfsmp, cnid_t cnid)
{
return (((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))) &&
((cnid == hfsmp->hfs_jnlfileid) || (cnid == hfsmp->hfs_jnlinfoblkid)));
}
static bool IsEntryADirectoryLink(struct hfsmount *hfsmp, const CatalogRecord *crp,time_t itime)
{
return ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
(SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator) &&
(crp->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
(crp->hfsPlusFile.bsdInfo.special.iNodeNum >= kHFSFirstUserCatalogNodeID) &&
((itime == (time_t)hfsmp->hfs_itime) || (itime == (time_t)hfsmp->hfs_metadata_createdate)));
}
static bool IsEntryAHardLink(struct hfsmount *hfsmp, const CatalogRecord *crp,time_t itime)
{
return((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)));
}
static int
getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp, struct packdirentry_state *state)
{
UVFSDirEntry* entry = NULL;
const CatalogName *cnp;
OSErr result;
u_int32_t ilinkref = 0;
u_int32_t curlinkref = 0;
cnid_t cnid;
u_int8_t type = 0;
time_t itime;
caddr_t uiobase = NULL;
size_t namelen = 0;
size_t maxnamelen;
size_t uiosize = 0;
caddr_t uioaddr;
Boolean bIsLastRecordInDir = false;
Boolean bToHide = false;
Boolean bIsLink = false;
Boolean bIsMangled = false;
struct hfsmount *hfsmp = state->cbs_hfsmp;
cnid_t curID = ckp->hfsPlus.parentID;
if (state->cbs_parentID != curID)
{
if (state->cbs_hasprevdirentry)
{
bIsLastRecordInDir = true;
}
else
{
state->cbs_eof = true;
state->cbs_result = ENOENT;
return (0);
}
}
entry = state->cbs_direntry;
u_int8_t* nameptr = (u_int8_t *)&entry->de_name;
if (state->cbs_flags & VNODE_READDIR_NAMEMAX)
{
maxnamelen = NAME_MAX + 1;
}
else
{
maxnamelen = UVFS_DIRENTRY_RECLEN(MAX_UTF8_NAME_LENGTH);
}
if (bIsLastRecordInDir)
{
cnid = INT_MAX;
}
else
{
if (crp == NULL)
return (0);
switch(crp->recordType)
{
case kHFSPlusFolderRecord:
type = UVFS_FA_TYPE_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)
{
bToHide = true;
}
}
break;
case kHFSPlusFileRecord:
itime = to_bsd_time(crp->hfsPlusFile.createDate);
type = MODE_TO_TYPE(crp->hfsPlusFile.bsdInfo.fileMode);
cnid = crp->hfsPlusFile.fileID;
if (IsEntryAHardLink(hfsmp, crp, itime))
{
if (crp->hfsPlusFile.flags & kHFSHasLinkChainMask)
{
cnid = crp->hfsPlusFile.bsdInfo.special.iNodeNum;
}
else
{
ilinkref = crp->hfsPlusFile.bsdInfo.special.iNodeNum;
}
bIsLink =1;
}
else if (IsEntryADirectoryLink(hfsmp, crp,itime))
{
type = UVFS_FA_TYPE_DIR;
cnid = crp->hfsPlusFile.bsdInfo.special.iNodeNum;
bIsLink = true;
}
if ((curID == kHFSRootFolderID) && IsEntryAJnlFile(hfsmp, cnid))
{
bToHide = 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, ':', UTF_ADD_NULL_TERM);
}
if (result == ENAMETOOLONG)
{
cnid_t linkid = cnid;
if (bIsLink)
{
linkid = crp->hfsPlusFile.fileID;
}
result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar), cnp->ustr.unicode, maxnamelen, (ByteCount*)&namelen, nameptr, linkid);
if (result) return (0);
bIsMangled = 1;
}
}
state->cbs_prevdirentry->de_nextcookie = (state->cbs_index + 3) | ((u_int64_t)cnid << 32);
uiosize = state->cbs_prevdirentry->de_reclen;
if ((UVFS_DIRENTRY_RECLEN(namelen) + uiosize) > state->cbs_psReadDirBuffer->uBufferResid)
{
state->cbs_prevdirentry->de_reclen = 0;
}
if (bIsLastRecordInDir)
{
state->cbs_prevdirentry->de_reclen = 0;
state->cbs_prevdirentry->de_nextcookie = UVFS_DIRCOOKIE_EOF;
}
uioaddr = (caddr_t) state->cbs_prevdirentry;
if (ilinkref || state->cbs_previlinkref)
{
uiobase = uioaddr;
}
if ((uiosize > (user_size_t)state->cbs_psReadDirBuffer->uBufferResid) || (ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks))
{
return (0);
}
if (state->cbs_hasprevdirentry)
{
if (state->cbs_prevdirentry->de_fileid != 0)
{
memcpy(state->cbs_psReadDirBuffer->pvBuffer + READDIR_BUF_OFFSET(state->cbs_psReadDirBuffer), uioaddr, uiosize);
state->cbs_lastinsertedentry = state->cbs_psReadDirBuffer->pvBuffer + READDIR_BUF_OFFSET(state->cbs_psReadDirBuffer);
state->cbs_haslastinsertedentry = true;
state->cbs_psReadDirBuffer->uBufferResid -= uiosize;
}
else if (state->cbs_haslastinsertedentry && bIsLastRecordInDir)
{
state->cbs_lastinsertedentry->de_reclen = 0;
state->cbs_lastinsertedentry->de_nextcookie = UVFS_DIRCOOKIE_EOF;
}
++state->cbs_index;
state->cbs_desc->cd_cnid = cnid;
if (type == UVFS_FA_TYPE_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 (!bIsMangled)
{
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);
new_nameptr = hfs_mallocz(bufsize);
result = utf8_encodestr(cnp->ustr.unicode, cnp->ustr.length * sizeof(UniChar), new_nameptr, &tmp_namelen, bufsize, ':', UTF_ADD_NULL_TERM);
if (result)
{
hfs_free(new_nameptr);
return (0);
}
state->cbs_desc->cd_namelen = tmp_namelen;
bcopy(new_nameptr, state->cbs_namebuf, tmp_namelen + 1);
hfs_free(new_nameptr);
}
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 (bIsLastRecordInDir)
{
state->cbs_eof = true;
return (0);
}
entry->de_filetype = type;
entry->de_namelen = namelen;
entry->de_reclen = UVFS_DIRENTRY_RECLEN(namelen);
entry->de_fileid = bToHide ? 0 : cnid;
UVFSDirEntry* 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 && state->cbs_psReadDirBuffer->uBufferResid >= SMALL_DIRENTRY_SIZE);
}
static int
cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp, struct position_state *state)
{
cnid_t curID = 0;
curID = ckp->hfsPlus.parentID;
if (state->parentID != curID) {
state->error = ENOENT;
return (0);
}
switch(crp->recordType)
{
case kHFSPlusFolderRecord:
case kHFSPlusFileRecord:
++state->count;
break;
default:
LFHFS_LOG(LEVEL_ERROR, "cat_findposition: invalid record type %d in dir %d\n", crp->recordType, curID);
state->error = EINVAL;
return (0);
};
return (state->count < state->index);
}
int
cat_getdirentries(struct hfsmount *hfsmp, u_int32_t entrycnt, directoryhint_t *dirhint, ReadDirBuff_s* psReadDirBuffer, int flags, int *items, bool *eofflag, UVFSDirEntry* psDotDotEntry)
{
FCB* fcb;
BTreeIterator * iterator = NULL;
CatalogKey * key;
struct packdirentry_state state;
int result = 0;
int index;
int have_key;
int extended;
extended = flags & VNODE_READDIR_EXTENDED;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
#define MAX_LINKINFO_ENTRIES 275
int maxlinks = min (entrycnt, (u_int32_t)(psReadDirBuffer->uBufferResid / SMALL_DIRENTRY_SIZE));
maxlinks = MIN (maxlinks, MAX_LINKINFO_ENTRIES);
int bufsize = MAXPATHLEN + (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator);
if (extended)
{
bufsize += 2 * (sizeof(UVFSDirEntry) + sizeof(char)*MAX_UTF8_NAME_LENGTH);
}
void* buffer = hfs_mallocz(bufsize);
state.cbs_flags = flags;
state.cbs_hasprevdirentry = false;
state.cbs_haslastinsertedentry = (psDotDotEntry != NULL);
state.cbs_lastinsertedentry = psDotDotEntry;
state.cbs_previlinkref = 0;
state.cbs_nlinks = 0;
state.cbs_maxlinks = maxlinks;
state.cbs_linkinfo = (linkinfo_t *)((char *)buffer + MAXPATHLEN);
state.cbs_eof = false;
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 = (UVFSDirEntry *)((char *)iterator + sizeof(BTreeIterator));
state.cbs_prevdirentry = (UVFSDirEntry *) ((uint8_t*) state.cbs_direntry + sizeof(UVFSDirEntry) + sizeof(char)*MAX_UTF8_NAME_LENGTH);
}
if (dirhint->dh_desc.cd_namelen != 0)
{
if (buildkey(&dirhint->dh_desc, (HFSPlusCatalogKey *)key) == 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, key);
iterator->hint.nodeNum = dirhint->dh_threadhint;
iterator->hint.index = 0;
have_key = 1;
}
if (!have_key)
{
buildthreadkey(dirhint->dh_desc.cd_parentcnid, 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);
if (result == ENOENT) {
result = 0;
dirhint->dh_desc.cd_flags |= CD_EOF;
*eofflag = true;
}
goto cleanup;
}
}
}
state.cbs_index = index;
state.cbs_hfsmp = hfsmp;
state.cbs_psReadDirBuffer = psReadDirBuffer;
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;
hfs_free((void*) 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;
result = BTIterateRecords(fcb, op, iterator, (IterateCallBackProcPtr)getdirentries_callback, &state);
if (extended && (result == fsBTRecordNotFoundErr))
{
CatalogKey ckp;
bzero(&ckp, sizeof(ckp));
result = getdirentries_callback(&ckp, NULL, &state);
}
*items = state.cbs_index - index;
index = state.cbs_index;
if (state.cbs_eof)
{
dirhint->dh_desc.cd_flags |= CD_EOF;
*eofflag = true;
}
if (*items == 0 && psDotDotEntry!= NULL)
{
if (state.cbs_eof)
{
psDotDotEntry->de_nextcookie = UVFS_DIRCOOKIE_EOF;
psDotDotEntry->de_nextrec = 0;
}
else
{
psDotDotEntry->de_nextrec = 0;
}
}
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 = lf_hfs_utils_allocate_and_copy_string( (char *)buffer, dirhint->dh_desc.cd_namelen );
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)
{
ino_t fileid = 0;
caddr_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 (extended)
{
ino64_t fileid_64 = (ino64_t)fileid;
memcpy(&fileid_64, (void*) address, sizeof(fileid_64));
}
else
{
memcpy(&fileid, (void*) address, sizeof(fileid));
}
}
}
if (state.cbs_result)
result = state.cbs_result;
else
result = MacToVFSError(result);
if (result == ENOENT)
{
result = 0;
}
cleanup:
hfs_free(buffer);
return (result);
}
int
cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files, int wantrsrc,
struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
{
BTreeIterator * iterator = NULL;
FSBufferDescriptor btdata = {0};
u_int16_t datasize = 0;
CatalogKey * keyp = NULL;
CatalogRecord * recp = NULL;
int result = 0;
iterator = hfs_mallocz(sizeof(*iterator));
if (iterator == NULL)
return MacToVFSError(ENOMEM);
buildthreadkey(cnid, (CatalogKey *)&iterator->key);
recp = hfs_malloc(sizeof(CatalogRecord));
BDINIT(btdata, recp);
result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
&btdata, &datasize, iterator);
if (result)
goto exit;
switch (recp->recordType) {
case kHFSPlusFileThreadRecord:
case kHFSPlusFolderThreadRecord:
keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
if (keyp->hfsPlus.nodeName.length == 0) {
result = ENOENT;
goto exit;
}
keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
(keyp->hfsPlus.nodeName.length * 2);
break;
default:
result = ENOENT;
goto exit;
}
result = cat_lookupbykey(hfsmp, keyp,
((allow_system_files != 0) ? HFS_LOOKUP_SYSFILE : 0),
0, wantrsrc, outdescp, attrp, forkp, NULL);
if (result == 0 && outdescp) {
cnid_t dcnid = outdescp->cd_cnid;
if (cnid != dcnid)
{
LFHFS_LOG(LEVEL_ERROR, "cat_idlookup: Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid, cnid, dcnid, dcnid);
result = ENOENT;
}
}
exit:
hfs_free(recp);
hfs_free(iterator);
return MacToVFSError(result);
}
static int
buildkey(struct cat_desc *descp, HFSPlusCatalogKey *key)
{
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);
}
return (0);
}
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 = NULL;
int result;
int flags = 0;
keyp = hfs_malloc(sizeof(CatalogKey));
if ( keyp == NULL )
{
result = ENOMEM;
goto exit;
}
result = buildkey(descp, (HFSPlusCatalogKey *)keyp);
if (result)
goto exit;
result = cat_lookupbykey(hfsmp, keyp, flags, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid);
if (result == ENOENT) {
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);
}
}
exit:
hfs_free(keyp);
return (result);
}
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;
u_int8_t utf8[NAME_MAX + 1];
ByteCount utf8len;
u_int16_t unicode[kHFSPlusMaxFileNameChars + 1];
size_t unicodelen;
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, 0, outdescp, attrp, forkp);
if (result)
return (ENOENT);
if (descp->cd_parentcnid != outdescp->cd_parentcnid)
goto falsematch;
result = utf8_decodestr(outdescp->cd_nameptr, outdescp->cd_namelen,
unicode, &unicodelen, sizeof(unicode), ':', 0);
if (result) {
goto falsematch;
}
result = ConvertUnicodeToUTF8Mangled(unicodelen, unicode,
sizeof(utf8), &utf8len, utf8, fileID);
if ((result != 0) ||
((u_int16_t)descp->cd_namelen != utf8len) ||
(bcmp(descp->cd_nameptr, utf8, utf8len) != 0)) {
goto falsematch;
}
return (0);
falsematch:
cat_releasedesc(outdescp);
return (ENOENT);
}
struct readattr_state {
struct hfsmount *hfsmp;
struct cat_entrylist *list;
cnid_t dir_cnid;
int error;
int reached_eof;
};
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 = key->hfsPlus.parentID;
switch(rec->recordType)
{
case kHFSPlusFolderRecord:
case kHFSPlusFileRecord:
if (parentcnid != state->dir_cnid)
{
state->error = btNotFound;
state->reached_eof = 1;
return (0);
}
break;
case kHFSPlusFolderThreadRecord:
case kHFSPlusFileThreadRecord:
list->skipentries++;
if (parentcnid != state->dir_cnid)
{
state->error = btNotFound;
state->reached_eof = 1;
return (0);
}
else
return (1);
break;
default:
state->error = btNotFound;
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 ((rec->recordType == kHFSPlusFileRecord) && IsEntryAJnlFile(hfsmp, rec->hfsPlusFile.fileID))
{
list->skipentries++;
return (1);
}
}
cep = &list->entry[list->realentries++];
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);
}
int
cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list, int *reachedeof)
{
FCB* fcb;
CatalogKey * key;
BTreeIterator * iterator = NULL;
struct readattr_state state;
cnid_t parentcnid;
int i;
int index;
bool bHaveKey = false;
int result = 0;
int reached_eof = 0;
ce_list->realentries = 0;
fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
parentcnid = dirhint->dh_desc.cd_parentcnid;
bzero (&state, sizeof(struct readattr_state));
state.hfsmp = hfsmp;
state.list = ce_list;
state.dir_cnid = parentcnid;
state.error = 0;
iterator = hfs_mallocz(sizeof(*iterator));
key = (CatalogKey *)&iterator->key;
iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
index = dirhint->dh_index + 1;
if (dirhint->dh_desc.cd_namelen != 0)
{
if (buildkey(&dirhint->dh_desc, (HFSPlusCatalogKey *)key) == 0)
{
bHaveKey = true;
}
}
if ((index == 0) || !bHaveKey)
{
buildthreadkey(dirhint->dh_desc.cd_parentcnid, 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;
reached_eof = state.reached_eof;
}
else if (ce_list->realentries == 0)
{
result = btNotFound;
reached_eof = 1;
}
else
{
result = MacToVFSError(result);
}
for (i = 0; i < (int)ce_list->realentries; ++i)
{
struct FndrFileInfo *fip;
struct cat_entry *cep;
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) {
struct HFSPlusCatalogFile filerec;
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:
if (iterator)
hfs_free(iterator);
*reachedeof = reached_eof;
return MacToVFSError(result);
}
int cat_check_idhash (struct hfsmount *hfsmp, cnid_t test_fileid) {
cat_preflightid_t *preflight;
int found = 0;
for (preflight = IDHASH(hfsmp, test_fileid)->lh_first; preflight ; preflight = preflight->id_hash.le_next)
{
if (preflight->fileid == test_fileid)
{
found = 1;
break;
}
}
return found;
}
int
cat_acquire_cnid (struct hfsmount *hfsmp, cnid_t *new_cnid)
{
uint32_t nextCNID;
BTreeIterator *iterator;
FSBufferDescriptor btdata;
uint16_t datasize;
CatalogRecord *recp;
int result = 0;
int wrapped = 0;
nextid:
nextCNID = hfsmp->vcbNxtCNID;
if (nextCNID == 0xFFFFFFFF) {
wrapped++;
if (wrapped > 1) {
return ENOSPC;
}
hfs_lock_mount (hfsmp);
hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
hfs_unlock_mount (hfsmp);
} else {
hfsmp->vcbNxtCNID++;
}
hfs_note_header_minor_change(hfsmp);
if (cat_check_idhash (hfsmp, nextCNID))
{
goto nextid;
}
iterator = hfs_mallocz(sizeof(BTreeIterator));
if (iterator == NULL)
return ENOMEM;
buildthreadkey(nextCNID, (CatalogKey *)&iterator->key);
recp = hfs_malloc(sizeof(CatalogRecord));
BDINIT(btdata, recp);
result = BTSearchRecord(hfsmp->hfs_catalog_cp->c_datafork, iterator, &btdata, &datasize, iterator);
hfs_free(recp);
hfs_free(iterator);
if (result == btNotFound) {
result = file_attribute_exist (hfsmp, nextCNID);
if (result == EEXIST) {
goto nextid;
}
if (result) {
return result;
}
if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask))
{
if (hfs_chash_snoop (hfsmp, nextCNID, 1, NULL, NULL) == 0)
{
goto nextid;
}
}
*new_cnid = nextCNID;
}
else if (result == noErr) {
goto nextid;
}
else {
return EIO;
}
return 0;
}
int
cat_preflight(struct hfsmount *hfsmp, uint32_t ops, cat_cookie_t *cookie)
{
int lockflags = 0;
int result;
if (hfsmp->hfs_catalog_cp->c_lockowner != pthread_self())
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);
}
void
cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie)
{
int lockflags = 0;
if (hfsmp->hfs_catalog_cp->c_lockowner != pthread_self())
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);
}
static cnid_t
getparentcnid(const CatalogRecord *recp)
{
cnid_t cnid = 0;
switch (recp->recordType)
{
case kHFSPlusFileThreadRecord:
case kHFSPlusFolderThreadRecord:
cnid = recp->hfsPlusThread.parentID;
break;
default:
LFHFS_LOG(LEVEL_ERROR, "getparentcnid: unknown recordType (crp @ %p)\n", recp);
hfs_assert(0);
break;
}
return (cnid);
}
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 )
{
int result = 0;
FSBufferDescriptor btdata;
ExtendedVCB * vcb = HFSTOVCB(hfsmp);
FCB * fcb = GetFileControlBlock(vcb->catalogRefNum);
u_int16_t datasize;
int sourcegone = 0;
int skipthread = 0;
int directory = from_cdp->cd_flags & CD_ISDIR;
int is_dirlink = 0;
u_int32_t encoding = 0;
if (from_cdp->cd_namelen == 0 || to_cdp->cd_namelen == 0)
{
return (EINVAL);
}
CatalogRecord* recp = NULL;
BTreeIterator* to_iterator = NULL;
BTreeIterator* from_iterator = (BTreeIterator*) hfs_mallocz(sizeof(BTreeIterator));
if (from_iterator == NULL)
{
return (ENOMEM);
}
if ((result = buildkey(from_cdp, (HFSPlusCatalogKey*) &from_iterator->key)))
{
goto exit;
}
to_iterator = hfs_mallocz(sizeof(*to_iterator));
if (to_iterator == NULL)
{
result = ENOMEM;
goto exit;
}
if ((result = buildkey(to_cdp, (HFSPlusCatalogKey*) &to_iterator->key)))
{
goto exit;
}
recp = hfs_malloc(sizeof(CatalogRecord));
if (recp == NULL)
{
result = ENOMEM;
goto exit;
}
BDINIT(btdata, recp);
if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid))
{
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;
}
BTreeIterator* dir_iterator = hfs_mallocz(sizeof(BTreeIterator));
if (dir_iterator == NULL)
{
result = ENOMEM;
goto exit;
}
while (pathcnid > fsRtDirID)
{
buildthreadkey(pathcnid, (CatalogKey *)&dir_iterator->key);
result = BTSearchRecord(fcb, dir_iterator, &btdata, &datasize, NULL);
if (result)
{
hfs_free(dir_iterator);
goto exit;
}
pathcnid = getparentcnid(recp);
if (pathcnid == cnid || pathcnid == 0)
{
result = EINVAL;
hfs_free(dir_iterator);
goto exit;
}
}
hfs_free(dir_iterator);
}
result = BTSearchRecord(fcb, from_iterator, &btdata,
&datasize, from_iterator);
if (result)
{
if (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(&temp_desc, (HFSPlusCatalogKey *)&from_iterator->key);
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 ((directory) && (recp->recordType == kHFSPlusFileRecord) && (recp->hfsPlusFile.flags & kHFSHasLinkChainMask))
{
is_dirlink = 1;
}
if (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 = kTextEncodingMacRoman;
hfs_setencodingbits(hfsmp, encoding);
recp->hfsPlusFile.textEncoding = encoding;
if (out_cdp)
out_cdp->cd_encoding = encoding;
}
result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
if (result == btExists)
{
int fromtype = recp->recordType;
cnid_t cnid = 0;
if (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)
goto exit;
result = BTSearchRecord(fcb, to_iterator, &btdata, &datasize, NULL);
if (result)
goto exit;
cnid = getcnid (recp);
if (cnid == 0)
{
hfs_mark_inconsistent(hfsmp, HFS_INCONSISTENCY_DETECTED);
result = EINVAL;
goto exit;
}
if ((fromtype != recp->recordType) || (from_cdp->cd_cnid != cnid))
{
result = EEXIST;
goto exit;
}
result = BTDeleteRecord(fcb, from_iterator);
if (result)
goto exit;
result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
if (result)
{
{
int err;
err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
if (err)
{
LFHFS_LOG(LEVEL_ERROR, "cat_create: could not undo (BTInsert = %d)\n", err);
hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
result = err;
goto exit;
}
}
goto exit;
}
sourcegone = 1;
}
if (result)
goto exit;
if (!sourcegone)
{
result = BTDeleteRecord(fcb, from_iterator);
if (result)
{
{
int err;
err = BTDeleteRecord(fcb, to_iterator);
if (err)
{
LFHFS_LOG(LEVEL_ERROR, "cat_create: could not undo (BTDelete = %d)\n", err);
hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
result = err;
goto exit;
}
}
goto exit;
}
}
buildthreadkey(from_cdp->cd_cnid, (CatalogKey *)&from_iterator->key);
(void) BTDeleteRecord(fcb, from_iterator);
if (!skipthread)
{
if (is_dirlink)
{
datasize = buildthread(&to_iterator->key, recp, false);
}
else
{
datasize = buildthread(&to_iterator->key, recp, directory);
}
btdata.itemSize = datasize;
buildthreadkey(from_cdp->cd_cnid, (CatalogKey *)&from_iterator->key);
result = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
}
if (out_cdp)
{
HFSPlusCatalogKey * pluskey = NULL;
pluskey = (HFSPlusCatalogKey *)&to_iterator->key;
builddesc(pluskey, from_cdp->cd_cnid, to_iterator->hint.nodeNum, encoding, directory, out_cdp);
}
exit:
(void) BTFlushPath(fcb);
hfs_free(from_iterator);
hfs_free(to_iterator);
hfs_free(recp);
return MacToVFSError(result);
}
struct update_state {
struct cat_desc * s_desc;
struct cat_attr * s_attr;
const struct cat_fork * s_datafork;
const struct cat_fork * s_rsrcfork;
struct hfsmount * s_hfsmp;
};
static int
catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state)
{
struct cat_desc *descp = state->s_desc;
struct cat_attr *attrp = state->s_attr;
const struct cat_fork *forkp;
struct hfsmount *hfsmp = state->s_hfsmp;
long blksize = HFSTOVCB(hfsmp)->blockSize;
switch (crp->recordType)
{
case kHFSPlusFolderRecord:
{
HFSPlusCatalogFolder *dir;
dir = (struct HFSPlusCatalogFolder *)crp;
if (dir->folderID != attrp->ca_fileid)
{
LFHFS_LOG(LEVEL_DEBUG, "catrec_update: id %d != %d, vol=%s\n", dir->folderID, attrp->ca_fileid, hfsmp->vcbVN);
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) || ((HFSTOVFS(hfsmp)->mnt_flag) & 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;
int is_dirlink;
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);
is_dirlink = (file->flags & kHFSHasLinkChainMask) &&
(SWAP_BE32(file->userInfo.fdType) == kHFSAliasType) &&
(SWAP_BE32(file->userInfo.fdCreator) == kHFSAliasCreator);
if (!is_dirlink && ((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) || (((HFSTOVFS(hfsmp)->mnt_flag) & 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 = (u_int32_t) 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 = (u_int32_t) howmany(forkp->cf_bytesread, blksize);
}
if ((file->resourceFork.extents[0].startBlock != 0) &&
(file->resourceFork.extents[0].startBlock == file->dataFork.extents[0].startBlock))
{
LFHFS_LOG(LEVEL_ERROR, "catrec_update: rsrc fork == data fork");
hfs_assert(0);
}
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 > 1 ) || (file->hl_linkCount > 1))
{
file->hl_linkCount = attrp->ca_linkcount;
}
}
break;
}
default:
return (btNotFound);
}
return (0);
}
static int
getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key)
{
FSBufferDescriptor btdata;
u_int16_t datasize;
CatalogKey * keyp = NULL;
CatalogRecord * recp = NULL;
int result = 0;
BTreeIterator* iterator = hfs_mallocz(sizeof(BTreeIterator));
if (iterator == NULL)
{
result = memFullErr;
goto exit;
}
buildthreadkey(cnid, (CatalogKey *)&iterator->key);
recp = hfs_mallocz(sizeof(CatalogRecord));
if (recp == NULL)
{
result = memFullErr;
goto exit;
}
BDINIT(btdata, recp);
result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator, &btdata, &datasize, iterator);
if (result)
goto exit;
switch (recp->recordType)
{
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 = cmNotFound;
break;
}
exit:
hfs_free(iterator);
hfs_free(recp);
return MacToVFSError(result);
}
static int
cat_update_internal(struct hfsmount *hfsmp, int update_hardlink, struct cat_desc *descp, struct cat_attr *attrp,
const struct cat_fork *dataforkp, const struct cat_fork *rsrcforkp)
{
FCB * fcb = hfsmp->hfs_catalog_cp->c_datafork;
BTreeIterator * iterator;
int result = 0;
struct update_state state;
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 ((update_hardlink == false) &&
((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(descp, (HFSPlusCatalogKey *)&iterator->key);
}
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);
}
int
cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
const struct cat_fork *dataforkp, const struct cat_fork *rsrcforkp)
{
return cat_update_internal(hfsmp, false, descp, attrp, dataforkp, rsrcforkp);
}
int
cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
{
FCB * fcb = hfsmp->hfs_catalog_cp->c_datafork;
BTreeIterator *iterator;
cnid_t cnid;
int result = 0;
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(descp, (HFSPlusCatalogKey *)&iterator->key);
cnid = descp->cd_cnid;
}
if (result)
goto exit;
result = BTDeleteRecord(fcb, iterator);
if (result)
{
if (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(&temp_desc, (HFSPlusCatalogKey *)&iterator->key);
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, (CatalogKey *)&iterator->key);
if (BTDeleteRecord(fcb, iterator))
{
LFHFS_LOG(LEVEL_ERROR, "cat_delete: failed to delete thread record id=%u on vol=%s\n", cnid, hfsmp->vcbVN);
hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
}
exit:
(void) BTFlushPath(fcb);
return MacToVFSError(result);
}
static void
buildrecord(struct cat_attr *attrp, cnid_t cnid, 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);
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;
}
int
cat_create(struct hfsmount *hfsmp, cnid_t new_fileid, struct cat_desc *descp, struct cat_attr *attrp, struct cat_desc *out_descp)
{
int result = 0;
FCB * fcb= hfsmp->hfs_catalog_cp->c_datafork;
BTreeIterator* iterator = NULL;
HFSPlusCatalogKey* key = NULL;
CatalogRecord* data = NULL;
FSBufferDescriptor btdata = {0};
u_int32_t datalen;
u_int32_t encoding = kTextEncodingMacRoman;
iterator = hfs_mallocz(sizeof(BTreeIterator));
key = hfs_mallocz(sizeof(HFSPlusCatalogKey));
data = hfs_mallocz(sizeof(CatalogRecord));
if ( (iterator == NULL) || (key == NULL) || (data == NULL) )
{
result =ENOMEM;
goto exit;
}
result = buildkey(descp, key);
if (result)
goto exit;
datalen = buildthread((void*)key, data, S_ISDIR(attrp->ca_mode));
btdata.bufferAddress = data;
btdata.itemSize = datalen;
btdata.itemCount = 1;
buildthreadkey(new_fileid, (CatalogKey *) &iterator->key);
result = BTInsertRecord(fcb, iterator, &btdata, datalen);
if (result)
{
goto exit;
}
buildrecord(attrp, new_fileid, encoding, data, &datalen);
btdata.bufferAddress = data;
btdata.itemSize = datalen;
btdata.itemCount = 1;
bcopy(key, &iterator->key, sizeof(HFSPlusCatalogKey));
result = BTInsertRecord(fcb, iterator, &btdata, datalen);
if (result)
{
if (result == btExists)
result = EEXIST;
buildthreadkey(new_fileid, (CatalogKey *)&iterator->key);
if (BTDeleteRecord(fcb, iterator))
{
LFHFS_LOG(LEVEL_ERROR, "cat_create() failed to delete thread record id=%u on vol=%s\n", new_fileid, hfsmp->vcbVN);
hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
}
goto exit;
}
if (out_descp != NULL)
{
HFSPlusCatalogKey * pluskey = NULL;
pluskey = (HFSPlusCatalogKey *)&iterator->key;
builddesc(pluskey, new_fileid, iterator->hint.nodeNum, encoding, S_ISDIR(attrp->ca_mode), out_descp);
}
attrp->ca_fileid = new_fileid;
exit:
(void) BTFlushPath(fcb);
if (iterator)
hfs_free(iterator);
if (key)
hfs_free(key);
if (data)
hfs_free(data);
return MacToVFSError(result);
}
int
cat_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid)
{
int retval = 0;
int lockflags = 0;
struct cat_desc desc;
struct cat_attr attr = {0};
while ((cnid != kHFSRootFolderID) && (cnid != kHFSRootParentID) &&
(cnid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid)) {
retval = hfs_chash_set_childlinkbit(hfsmp, 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, 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;
}
int
cat_check_link_ancestry(struct hfsmount *hfsmp, cnid_t cnid, cnid_t pointed_at_cnid)
{
FSBufferDescriptor btdata;
HFSPlusCatalogFolder folder;
int invalid = 0;
int result;
BDINIT(btdata, &folder);
BTreeIterator* ip = hfs_mallocz(sizeof(BTreeIterator));
if (ip == NULL)
return ENOMEM;
HFSPlusCatalogKey* keyp = (HFSPlusCatalogKey *)&ip->key;
FCB *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))) {
LFHFS_LOG(LEVEL_ERROR, "cat_check_link_ancestry: getkey failed [%d] id=%u, vol=%s\n", result, cnid, hfsmp->vcbVN);
invalid = 1;
break;
}
if ((result = BTSearchRecord(fcb, ip, &btdata, NULL, NULL))) {
LFHFS_LOG(LEVEL_ERROR, "cat_check_link_ancestry: cannot find id=%u, vol=%s, [%d]\n", cnid, hfsmp->vcbVN, result);
invalid = 1;
break;
}
if (folder.flags & kHFSHasLinkChainMask) {
invalid = 1;
break;
}
cnid = keyp->parentID;
}
hfs_free(ip);
return (invalid);
}
int
cat_resolvelink(struct hfsmount *hfsmp, u_int32_t linkref, int isdirlink, struct HFSPlusCatalogFile *recp)
{
FSBufferDescriptor btdata;
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;
}
iterator = hfs_mallocz(sizeof(BTreeIterator));
if (iterator == NULL)
{
return ENOMEM;
}
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(&idesc, (HFSPlusCatalogKey *)&iterator->key);
result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,&btdata, NULL, NULL);
if (result == 0) {
if (recp->hl_linkCount == 0)
recp->hl_linkCount = 2;
} else {
LFHFS_LOG(LEVEL_ERROR, "cat_resolvelink: can't find inode=%s on vol=%s\n", inodename, hfsmp->vcbVN);
}
hfs_free(iterator);
return (result ? ENOENT : 0);
}
static int
resolvelinkid(struct hfsmount *hfsmp, u_int32_t 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);
}
int
cat_lookup_lastlink(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *lastlink, struct cat_desc *cdesc)
{
FCB * fcb;
BTreeIterator * iterator;
FSBufferDescriptor btdata = {0};
struct HFSPlusCatalogFile file;
int result = 0;
int itercount = 0;
int foundlast = 0;
cnid_t currentlink = linkfileid;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
iterator = hfs_mallocz(sizeof(*iterator));
if (iterator == NULL)
return ENOMEM;
while ((foundlast == 0) && (itercount < HFS_LINK_MAX )) {
itercount++;
bzero(iterator, sizeof(*iterator));
if ((result = getkey(hfsmp, currentlink, (CatalogKey *)&iterator->key))) {
goto exit;
}
BDINIT(btdata, &file);
if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
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;
goto exit;
}
else if (parent == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
currentlink = file.hl_firstLinkID;
if (currentlink == 0) {
result = ENOLINK;
goto exit;
}
}
else {
if (file.hl_nextLinkID == 0) {
foundlast = 1;
*lastlink = currentlink;
builddesc ((HFSPlusCatalogKey*)&iterator->key, currentlink, 0, 0, 0, cdesc);
break;
}
currentlink = file.hl_nextLinkID;
}
}
else {
result = ENOLINK;
goto exit;
}
}
exit:
if (foundlast == 0) {
if (cdesc) {
bzero (cdesc, sizeof(struct cat_desc));
}
if (lastlink) {
*lastlink = 0;
}
}
hfs_free(iterator);
return MacToVFSError(result);
}
int
cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
{
FCB * fcb;
BTreeIterator * iterator;
FSBufferDescriptor btdata;
struct HFSPlusCatalogFile file;
int result;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
iterator = hfs_mallocz(sizeof(*iterator));
if (iterator == NULL)
return ENOMEM;
if ((result = buildkey(descp, (HFSPlusCatalogKey *)&iterator->key))) {
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:
hfs_free(iterator);
return MacToVFSError(result);
}
int
cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp)
{
struct HFSPlusCatalogFile file = {0};
struct cat_attr cattr = {0};
uint32_t totalBlocks;
int result = 0;
cattr.ca_fileid = descp->cd_cnid;
if (descp->cd_flags & CD_ISDIR) {
FCB * fcb;
BTreeIterator * iterator;
FSBufferDescriptor btdata;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
iterator->hint.nodeNum = 0;
if ((result = buildkey(descp, (HFSPlusCatalogKey *)&iterator->key))) {
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 (int 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, 0);
totalBlocks -= file.resourceFork.extents[i].blockCount;
file.resourceFork.extents[i].startBlock = 0;
file.resourceFork.extents[i].blockCount = 0;
}
}
exit:
return (result);
}
struct linkupdate_state {
cnid_t filelinkid;
cnid_t prevlinkid;
cnid_t nextlinkid;
};
static int
update_siblinglinks_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state)
{
HFSPlusCatalogFile *file;
if (crp->recordType != kHFSPlusFileRecord) {
LFHFS_LOG(LEVEL_ERROR, "update_siblinglinks_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 {
LFHFS_LOG(LEVEL_ERROR, "update_siblinglinks_callback: file %d isn't a chain\n", file->fileID);
}
return (0);
}
int
cat_update_siblinglinks(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 = hfs_mallocz(sizeof(*iterator));
if (iterator == NULL)
return ENOMEM;
result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key);
if (result == 0) {
result = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr)update_siblinglinks_callback, &state);
(void) BTFlushPath(fcb);
} else {
LFHFS_LOG(LEVEL_ERROR, "cat_update_siblinglinks: couldn't resolve cnid=%d, vol=%s\n", linkfileid, hfsmp->vcbVN);
}
hfs_free(iterator);
return MacToVFSError(result);
}
void
cat_convertattr(
struct hfsmount *hfsmp,
CatalogRecord * recp,
struct cat_attr *attrp,
struct cat_fork *datafp,
struct cat_fork *rsrcfp)
{
getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
if (isadir(recp))
{
bzero(datafp, sizeof(*datafp));
}else {
datafp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
datafp->cf_new_size = 0;
datafp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
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_new_size = 0;
rsrcfp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
datafp->cf_bytesread = 0;
rsrcfp->cf_vblocks = 0;
bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
&rsrcfp->cf_extents[0], sizeof(HFSPlusExtentRecord));
}
}
static int
cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp)
{
GenericLFBufPtr bp = NULL;
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_logical_block_size;
bzero(rsrcforkp, sizeof(HFSPlusForkData));
result = BlockAllocate(hfsmp, 0, blkcount, blkcount,
HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE,
&rsrcforkp->extents[0].startBlock,
&rsrcforkp->extents[0].blockCount);
if (result == dskFulErr ) {
result = BlockAllocate(hfsmp, 0, blkcount, blkcount,
HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE | HFS_ALLOC_FLUSHTXN,
&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 = lf_hfs_generic_buf_allocate( hfsmp->hfs_devvp, blkno, roundup(kHFSAliasSize, hfsmp->hfs_logical_block_size), 0);
result = lf_hfs_generic_buf_read(bp);
if (result) {
goto exit;
}
if (hfsmp->jnl) {
journal_modify_block_start(hfsmp->jnl, bp);
}
alias = (char *)bp->pvData;
bzero(alias, bp->uDataSize);
bcopy(hfs_dirlink_alias_rsrc, alias, kHFSAliasSize);
valptr = (uint32_t *)(alias + kHFSAliasVolCreateDateOffset);
*valptr = OSSwapHostToBigInt32(hfsmp->localCreateDate);
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 = lf_hfs_generic_buf_write(bp))) {
goto exit;
}
rsrcforkp->logicalSize = kHFSAliasSize;
rsrcforkp->totalBlocks = rsrcforkp->extents[0].blockCount;
exit:
if (bp) {
lf_hfs_generic_buf_release(bp);
}
if (result && rsrcforkp->extents[0].startBlock != 0) {
(void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock, rsrcforkp->extents[0].blockCount, 0);
rsrcforkp->extents[0].startBlock = 0;
rsrcforkp->extents[0].blockCount = 0;
rsrcforkp->logicalSize = 0;
rsrcforkp->totalBlocks = 0;
}
return (result);
}
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;
int thread_inserted = 0;
int alias_allocated = 0;
int result = 0;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
result = cat_acquire_cnid(hfsmp, &nextCNID);
if (result) {
return result;
}
bto = hfs_malloc(sizeof(struct btobj));
bto->iterator.hint.nodeNum = 0;
rsrcforkp = &bto->data.hfsPlusFile.resourceFork;
result = buildkey(descp, &bto->key);
if (result) {
LFHFS_LOG(LEVEL_ERROR, "cat_createlink: err %d from buildkey\n", result);
goto exit;
}
datalen = buildthread((void*)&bto->key, &bto->data, 0);
btdata.bufferAddress = &bto->data;
btdata.itemSize = datalen;
btdata.itemCount = 1;
buildthreadkey(nextCNID, (CatalogKey *) &bto->iterator.key);
result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
if (result) {
goto exit;
}
thread_inserted = 1;
buildrecord(attrp, nextCNID, kTextEncodingMacUnicode, &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) {
LFHFS_LOG(LEVEL_ERROR, "cat_createlink: BTInsertRecord err=%d, vol=%s\n", MacToVFSError(result), hfsmp->vcbVN);
buildthreadkey(nextCNID, (CatalogKey *)&bto->iterator.key);
if (BTDeleteRecord(fcb, &bto->iterator)) {
LFHFS_LOG(LEVEL_ERROR, "cat_createlink: failed to delete thread record on volume %s\n", hfsmp->vcbVN);
hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
}
}
if (alias_allocated && rsrcforkp->extents[0].startBlock != 0) {
(void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock,
rsrcforkp->extents[0].blockCount, 0);
rsrcforkp->extents[0].startBlock = 0;
rsrcforkp->extents[0].blockCount = 0;
}
}
(void) BTFlushPath(fcb);
hfs_free(bto);
return MacToVFSError(result);
}