X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/39236c6e673c41db228275375ab7fdb0f837b292..7e41aa883dd258f888d0470250eead40a53ef1f5:/bsd/hfs/hfs_catalog.c diff --git a/bsd/hfs/hfs_catalog.c b/bsd/hfs/hfs_catalog.c index c7aa7b38e..0069ac2d4 100644 --- a/bsd/hfs/hfs_catalog.c +++ b/bsd/hfs/hfs_catalog.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2013 Apple Inc. All rights reserved. + * Copyright (c) 2000-2014 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -67,8 +67,8 @@ struct btobj { 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; }; @@ -139,7 +139,7 @@ static int buildthread(void *keyp, void *recp, int std_hfs, int directory); static int cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp); 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); @@ -233,8 +233,8 @@ int cat_remove_idhash (cat_preflightid_t *preflight) { * 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; @@ -267,7 +267,7 @@ nextid: } 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)) { @@ -466,7 +466,7 @@ cat_convertkey( 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 { @@ -898,7 +898,7 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t h 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; } @@ -1088,7 +1088,7 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t h * 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) { @@ -1225,7 +1225,7 @@ cat_create(struct hfsmount *hfsmp, cnid_t new_fileid, struct cat_desc *descp, st * 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; @@ -1466,7 +1466,7 @@ cat_rename ( /* 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; } @@ -1492,7 +1492,7 @@ cat_rename ( 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; } @@ -1519,7 +1519,7 @@ cat_rename ( 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; } @@ -1685,7 +1685,7 @@ cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr 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); } } @@ -1704,7 +1704,7 @@ exit: */ 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; @@ -1764,7 +1764,7 @@ exit: */ 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); } @@ -1778,7 +1778,7 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st { struct cat_desc *descp; struct cat_attr *attrp; - struct cat_fork *forkp; + const struct cat_fork *forkp; struct hfsmount *hfsmp; long blksize; @@ -2460,18 +2460,12 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr * 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); @@ -2487,59 +2481,20 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr * /* 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. @@ -2579,8 +2534,8 @@ exit: 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) { @@ -2832,6 +2787,7 @@ struct readattr_state { cnid_t dir_cnid; int stdhfs; int error; + int reached_eof; }; static int @@ -2857,6 +2813,7 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec, #endif if (parentcnid != state->dir_cnid) { state->error = ENOENT; + state->reached_eof = 1; return (0); /* stop */ } break; @@ -2941,7 +2898,7 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec, * 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; @@ -2953,6 +2910,7 @@ cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_ int index; int have_key; int result = 0; + int reached_eof = 0; ce_list->realentries = 0; @@ -2960,6 +2918,8 @@ cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_ 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; @@ -3016,7 +2976,15 @@ cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_ 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; } @@ -3027,12 +2995,17 @@ cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_ 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; @@ -3076,13 +3049,15 @@ cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_ cep->ce_rsrcblks = filerec.resourceFork.totalBlocks; } } + exit: FREE(iterator, M_TEMP); - + *reachedeof = reached_eof; return MacToVFSError(result); } #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8) +#define MAX_LINKINFO_ENTRIES 3000 /* * Callback to pack directory entries. @@ -3549,12 +3524,13 @@ cat_getdirentries(struct hfsmount *hfsmp, u_int32_t entrycnt, directoryhint_t *d struct packdirentry_state state; void * buffer; int bufsize; + int maxlinks; int result; int index; int have_key; int extended; - + extended = flags & VNODE_READDIR_EXTENDED; if (extended && (hfsmp->hfs_flags & HFS_STANDARD)) { @@ -3563,10 +3539,23 @@ cat_getdirentries(struct hfsmount *hfsmp, u_int32_t entrycnt, directoryhint_t *d fcb = hfsmp->hfs_catalog_cp->c_datafork; /* - * Get a buffer for link info array, btree iterator and a direntry: + * Get a buffer for link info array, btree iterator and a direntry. + * + * We impose an cap of 3000 link entries when trying to compute + * the total number of hardlink entries that we'll allow in the + * linkinfo array. + * + * Note that in the case where there are very few hardlinks, + * this does not restrict or prevent us from vending out as many entries + * as we can to the uio_resid, because the getdirentries callback + * uiomoves the directory entries to the uio itself and does not use + * this MALLOC'd array. It also limits itself to maxlinks of hardlinks. */ - maxlinks = MIN(entrycnt, (u_int32_t)(uio_resid(uio) / SMALL_DIRENTRY_SIZE)); - bufsize = MAXPATHLEN + (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator); + + /* Now compute the maximum link array size */ + maxlinks = MIN (entrycnt, MAX_LINKINFO_ENTRIES); + + bufsize = MAXPATHLEN + (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator); if (extended) { bufsize += 2*sizeof(struct direntry); } @@ -3653,6 +3642,15 @@ cat_getdirentries(struct hfsmount *hfsmp, u_int32_t entrycnt, directoryhint_t *d 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; } } @@ -3814,7 +3812,11 @@ cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp, /* 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 */ } @@ -4372,8 +4374,9 @@ getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct 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) @@ -4791,13 +4794,13 @@ cat_lookup_dirlink(struct hfsmount *hfsmp, cnid_t dirlink_id, } 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; } @@ -4825,3 +4828,11 @@ cat_update_dirlink(struct hfsmount *hfsmp, u_int8_t forktype, } } +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)); +}