/*
- * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
struct update_state {
struct cat_desc * s_desc;
struct cat_attr * s_attr;
- struct cat_fork * s_datafork;
- struct cat_fork * s_rsrcfork;
+ const struct cat_fork * s_datafork;
+ const struct cat_fork * s_rsrcfork;
struct hfsmount * s_hfsmp;
};
static int cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp);
static int cat_update_internal(struct hfsmount *hfsmp, int update_hardlink, struct cat_desc *descp, struct cat_attr *attrp,
- struct cat_fork *dataforkp, struct cat_fork *rsrcforkp);
+ const struct cat_fork *dataforkp, const struct cat_fork *rsrcforkp);
* catalog by checking the ID hash table.
*/
int
-cat_acquire_cnid (struct hfsmount *hfsmp, cnid_t *new_cnid) {
-
+cat_acquire_cnid (struct hfsmount *hfsmp, cnid_t *new_cnid)
+{
uint32_t nextCNID;
struct BTreeIterator *iterator;
FSBufferDescriptor btdata;
} else {
hfsmp->vcbNxtCNID++;
}
- MarkVCBDirty(hfsmp);
+ hfs_note_header_minor_change(hfsmp);
/* First check that there are not any entries pending in the hash table with this ID */
if (cat_check_idhash (hfsmp, nextCNID)) {
cnid = getcnid(recp);
if (cnid == 0) {
/* If ths CNID == 0, it's invalid. Mark as corrupt */
- hfs_mark_volume_inconsistent (hfsmp);
+ hfs_mark_inconsistent (hfsmp, HFS_INCONSISTENCY_DETECTED);
err = EINVAL;
}
else {
cnid = getcnid(recp);
if (cnid == 0) {
/* CNID of 0 is invalid. Mark as corrupt */
- hfs_mark_volume_inconsistent (hfsmp);
+ hfs_mark_inconsistent (hfsmp, HFS_INCONSISTENCY_DETECTED);
result = EINVAL;
goto exit;
}
* than that which is in its extent records.
*/
- (void) hfs_mark_volume_inconsistent (hfsmp);
+ (void) hfs_mark_inconsistent (hfsmp, HFS_INCONSISTENCY_DETECTED);
forkp->cf_blocks = validblks;
if (attrp != NULL) {
* volume inconsistent
*/
printf ("hfs: cat_create() failed to delete thread record id=%u on vol=%s\n", new_fileid, hfsmp->vcbVN);
- hfs_mark_volume_inconsistent(hfsmp);
+ hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
}
}
goto exit;
/* Get the CNID after calling searchrecord */
cnid = getcnid (recp);
if (cnid == 0) {
- hfs_mark_volume_inconsistent(hfsmp);
+ hfs_mark_inconsistent(hfsmp, HFS_INCONSISTENCY_DETECTED);
result = EINVAL;
goto exit;
}
err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
if (err) {
printf("hfs: cat_create: could not undo (BTInsert = %d)\n", err);
- hfs_mark_volume_inconsistent(hfsmp);
+ hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
result = err;
goto exit;
}
err = BTDeleteRecord(fcb, to_iterator);
if (err) {
printf("hfs: cat_create: could not undo (BTDelete = %d)\n", err);
- hfs_mark_volume_inconsistent(hfsmp);
+ hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
result = err;
goto exit;
}
if (BTDeleteRecord(fcb, iterator)) {
if (!std_hfs) {
printf ("hfs: cat_delete() failed to delete thread record id=%u on vol=%s\n", cnid, hfsmp->vcbVN);
- hfs_mark_volume_inconsistent(hfsmp);
+ hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
}
}
*/
static int
cat_update_internal(struct hfsmount *hfsmp, int update_hardlink, struct cat_desc *descp, struct cat_attr *attrp,
- struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
+ const struct cat_fork *dataforkp, const struct cat_fork *rsrcforkp)
{
FCB * fcb;
BTreeIterator * iterator;
*/
int
cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
- struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
+ const struct cat_fork *dataforkp, const struct cat_fork *rsrcforkp)
{
return cat_update_internal(hfsmp, false, descp, attrp, dataforkp, rsrcforkp);
}
{
struct cat_desc *descp;
struct cat_attr *attrp;
- struct cat_fork *forkp;
+ const struct cat_fork *forkp;
struct hfsmount *hfsmp;
long blksize;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
/*
- * Get the next CNID. We can change it since we hold the catalog lock.
+ * Get the next CNID. Note that we are currently holding catalog lock.
*/
- nextCNID = hfsmp->vcbNxtCNID;
- if (nextCNID == 0xFFFFFFFF) {
- hfs_lock_mount (hfsmp);
- hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
- hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
- hfs_unlock_mount(hfsmp);
- } else {
- hfsmp->vcbNxtCNID++;
+ result = cat_acquire_cnid(hfsmp, &nextCNID);
+ if (result) {
+ return result;
}
- MarkVCBDirty(hfsmp);
/* Get space for iterator, key and data */
MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
/* This is our only chance to set the encoding (other than a rename). */
encoding = hfs_pickencoding(bto->key.nodeName.unicode, bto->key.nodeName.length);
- /* Insert the thread record first. */
+ /*
+ * Insert the thread record first.
+ */
datalen = buildthread((void*)&bto->key, &bto->data, 0, 0);
btdata.bufferAddress = &bto->data;
btdata.itemSize = datalen;
btdata.itemCount = 1;
-
- for (;;) {
- buildthreadkey(nextCNID, 0, (CatalogKey *) &bto->iterator.key);
-
- /*
- * If the CNID wraparound bit is set, then we need to validate if there
- * is a cnode in the hash already with this ID (even if it no longer exists
- * on disk). If so, then just skip this ID and move on to the next one.
- */
- if (!std_hfs && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
- /* Verify that the CNID does not already exist in the cnode hash... */
- if (hfs_chash_snoop (hfsmp, nextCNID, 1, NULL, NULL) == 0) {
- /* It was found in the cnode hash!*/
- result = btExists;
- }
- }
- if (result == 0) {
- result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
- }
-
- if ((result == btExists) && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
- /*
- * Allow CNIDs on HFS Plus volumes to wrap around
- */
- if (++nextCNID < kHFSFirstUserCatalogNodeID) {
- nextCNID = kHFSFirstUserCatalogNodeID;
- }
- continue;
- }
- if (result == 0) {
- thread_inserted = 1;
- }
- break;
- }
- if (result)
+ buildthreadkey(nextCNID, 0, (CatalogKey *) &bto->iterator.key);
+ result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
+ if (result) {
goto exit;
-
- /*
- * CNID is now established. If we have wrapped then
- * update the vcbNxtCNID.
- */
- if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
- hfsmp->vcbNxtCNID = nextCNID + 1;
- if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
- hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
- }
}
+ thread_inserted = 1;
/*
* Now insert the link record.
buildthreadkey(nextCNID, 0, (CatalogKey *)&bto->iterator.key);
if (BTDeleteRecord(fcb, &bto->iterator)) {
- printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp->vcbVN);
- hfs_mark_volume_inconsistent(hfsmp);
+ printf("hfs: 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) {
cnid_t dir_cnid;
int stdhfs;
int error;
+ int reached_eof;
};
static int
#endif
if (parentcnid != state->dir_cnid) {
state->error = ENOENT;
+ state->reached_eof = 1;
return (0); /* stop */
}
break;
* Note: index is zero relative
*/
int
-cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list)
+cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list, int *reachedeof)
{
FCB* fcb;
CatalogKey * key;
int index;
int have_key;
int result = 0;
+ int reached_eof = 0;
ce_list->realentries = 0;
std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
parentcnid = dirhint->dh_desc.cd_parentcnid;
+ bzero (&state, sizeof(struct readattr_state));
+
state.hfsmp = hfsmp;
state.list = ce_list;
state.dir_cnid = parentcnid;
result = ps.error;
else
result = MacToVFSError(result);
+
if (result) {
+ /*
+ * Note: the index may now point to EOF if the directory
+ * was modified in between system calls. We will return
+ * ENOENT from cat_findposition if this is the case, and
+ * when we bail out with an error, our caller (hfs_readdirattr_internal)
+ * will suppress the error and indicate EOF to its caller.
+ */
result = MacToVFSError(result);
goto exit;
}
result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
(IterateCallBackProcPtr)getentriesattr_callback, &state);
- if (state.error)
+ if (state.error) {
result = state.error;
- else if (ce_list->realentries == 0)
+ reached_eof = state.reached_eof;
+ }
+ else if (ce_list->realentries == 0) {
result = ENOENT;
- else
+ reached_eof = 1;
+ }
+ else {
result = MacToVFSError(result);
+ }
if (std_hfs)
goto exit;
cep->ce_rsrcblks = filerec.resourceFork.totalBlocks;
}
}
+
exit:
FREE(iterator, M_TEMP);
-
+ *reachedeof = reached_eof;
return MacToVFSError(result);
}
result = MacToVFSError(result);
if (result) {
result = MacToVFSError(result);
+ if (result == ENOENT) {
+ /*
+ * ENOENT means we've hit the EOF.
+ * suppress the error, and set the eof flag.
+ */
+ result = 0;
+ dirhint->dh_desc.cd_flags |= CD_EOF;
+ *eofflag = 1;
+ }
goto cleanup;
}
}
/* Make sure parent directory didn't change */
if (state->parentID != curID) {
- state->error = EINVAL;
+ /*
+ * The parent ID is different from curID this means we've hit
+ * the EOF for the directory.
+ */
+ state->error = ENOENT;
return (0); /* stop */
}
case S_IFBLK:
attrp->ca_rdev = bsd->special.rawDevice;
break;
-
- case S_IFDIR: /* fall through */
+ case S_IFIFO:
+ case S_IFSOCK:
+ case S_IFDIR:
case S_IFREG:
/* Pick up the hard link count */
if (bsd->special.linkCount > 0)
}
if (error) {
printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id, error);
- hfs_mark_volume_inconsistent(hfsmp);
+ hfs_mark_inconsistent(hfsmp, HFS_INCONSISTENCY_DETECTED);
goto out;
}
/* Just for sanity, make sure that id in catalog record and thread record match */
if ((outdescp != NULL) && (dirlink_id != outdescp->cd_cnid)) {
printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id, outdescp->cd_cnid);
- hfs_mark_volume_inconsistent(hfsmp);
+ hfs_mark_inconsistent(hfsmp, HFS_INCONSISTENCY_DETECTED);
error = ENOENT;
}
}
}
+void hfs_fork_copy(struct cat_fork *dst, const struct cat_fork *src,
+ HFSPlusExtentDescriptor *extents)
+{
+ /* Copy everything but the extents into the dest fork */
+ memcpy(dst, src, offsetof(struct cat_fork, cf_extents));
+ /* Then copy the supplied extents into the fork */
+ memcpy(dst->cf_extents, extents, sizeof(HFSPlusExtentRecord));
+}