From 7adaf79dc3954591b7b5257adb62451f47e2573d Mon Sep 17 00:00:00 2001 From: Apple Date: Wed, 29 Mar 2017 20:15:56 +0000 Subject: [PATCH] hfs-366.50.19.tar.gz --- CopyHFSMeta/SparseBundle.c | 144 +++++--- CopyHFSMeta/main.c | 11 +- core/hfs.h | 47 ++- core/hfs_catalog.c | 19 +- core/hfs_chash.c | 494 +-------------------------- core/hfs_cnode.c | 18 +- core/hfs_cnode.h | 2 - core/hfs_cprotect.c | 19 ++ core/hfs_endian.c | 10 +- core/hfs_iokit.cpp | 2 + core/hfs_readwrite.c | 3 + core/hfs_vfsops.c | 37 +- core/hfs_vfsutils.c | 73 ++-- core/hfs_vnops.c | 24 +- fs/Info.plist | 30 ++ fsck_hfs/dfalib/BlockCache.c | 12 +- fsck_hfs/dfalib/CatalogCheck.c | 3 +- fsck_hfs/dfalib/SDevice.c | 12 +- fsck_hfs/dfalib/SRebuildBTree.c | 4 +- fsck_hfs/dfalib/SRepair.c | 14 +- fsck_hfs/dfalib/SVerify2.c | 7 +- fsck_hfs/dfalib/fsck_journal.c | 7 +- fsck_hfs/dfalib/hfs_endian.c | 22 +- hfs.xcodeproj/project.pbxproj | 4 + tests/cases/test-hard-links.m | 2 +- tests/cases/test-raw-dev-unaligned.c | 93 +++++ tests/cases/test-throttled-io.c | 119 ++++--- tests/hfs-tests.entitlements | 4 + tests/hfs-tests.mm | 2 +- 29 files changed, 544 insertions(+), 694 deletions(-) create mode 100644 tests/cases/test-raw-dev-unaligned.c diff --git a/CopyHFSMeta/SparseBundle.c b/CopyHFSMeta/SparseBundle.c index 455903e..2d966c4 100644 --- a/CopyHFSMeta/SparseBundle.c +++ b/CopyHFSMeta/SparseBundle.c @@ -73,19 +73,23 @@ doSparseRead(struct IOWrapper *context, off_t offset, void *buffer, off_t len) { struct SparseBundleContext *ctx = context->context; off_t blockSize = ctx->bandSize; - size_t nread = 0; + ssize_t nread = 0; ssize_t retval = -1; while (nread < len) { off_t bandNum = (offset + nread) / blockSize; // Which band file to use off_t bandOffset = (offset + nread) % blockSize; // how far to go into the file - size_t amount = MIN(len - nread, blockSize - bandOffset); // How many bytes to write in this band file - struct stat sbuf; + ssize_t amount = MIN(len - nread, blockSize - bandOffset); // How many bytes to write in this band file char *bandName; - ssize_t n;; + ssize_t n; int fd; - asprintf(&bandName, "%s/bands/%x", ctx->pathname, bandNum); + asprintf(&bandName, "%s/bands/%llx", ctx->pathname, bandNum); + if (!bandName) { + warn("Cannot allocate memory for path '%s/bands/%llx'", ctx->pathname, bandNum); + retval = -1; + goto done; + } fd = open(bandName, O_RDONLY); if (fd == -1) { if (errno == ENOENT) { @@ -100,15 +104,19 @@ doSparseRead(struct IOWrapper *context, off_t offset, void *buffer, off_t len) free(bandName); goto done; } + n = pread(fd, (char*)buffer + nread, amount, bandOffset); if (n == -1) { - warn("Cannot write to band file %s/band/%x for offset %llu for amount %zu", ctx->pathname, bandNum, offset+nread, amount); + warn("Cannot read from band file %s for offset %llu for amount %zu", bandName, offset+nread, amount); close(fd); + free(bandName); goto done; } if (n < amount) { // hit EOF, pad out with zeroes memset(buffer + nread + amount, 0, amount - n); } + free(bandName); + close(fd); nread += n; } retval = nread; @@ -121,29 +129,38 @@ done: * Write a chunk of data to a bundle. */ static ssize_t -doSparseWrite(IOWrapper_t *context, off_t offset, void *buffer, size_t len) +doSparseWrite(IOWrapper_t *context, off_t offset, void *buffer, off_t len) { struct SparseBundleContext *ctx = context->context; off_t blockSize = ctx->bandSize; - size_t written = 0; + ssize_t written = 0; ssize_t retval = -1; while (written < len) { off_t bandNum = (offset + written) / blockSize; // Which band file to use off_t bandOffset = (offset + written) % blockSize; // how far to go into the file size_t amount = MIN(len - written, blockSize - bandOffset); // How many bytes to write in this band file - char *bandName; ssize_t nwritten; int fd; if (ctx->cfd == -1 || ctx->cBandNum != bandNum) { + char *bandName = NULL; + if (ctx->cfd != -1) { close(ctx->cfd); + ctx->cfd = -1; } - asprintf(&bandName, "%s/bands/%x", ctx->pathname, bandNum); + asprintf(&bandName, "%s/bands/%llx", ctx->pathname, bandNum); + if (!bandName) { + warnx("Cannot allocate memory for band %s/bands/%llx", ctx->pathname, bandNum); + retval = -1; + goto done; + } + fd = open(bandName, O_WRONLY | O_CREAT, 0666); if (fd == -1) { warn("Cannot open band file %s for offset %llu", bandName, offset + written); + free(bandName); retval = -1; goto done; } @@ -165,7 +182,7 @@ doSparseWrite(IOWrapper_t *context, off_t offset, void *buffer, size_t len) } nwritten = pwrite(fd, (char*)buffer + written, amount, bandOffset); if (nwritten == -1) { - warn("Cannot write to band file %s/band/%x for offset %llu for amount %zu", ctx->pathname, bandNum, offset+written, amount); + warn("Cannot write to band file %s/band/%llx for offset %llu for amount %zu", ctx->pathname, bandNum, offset+written, amount); close(fd); ctx->cfd = -1; retval = -1; @@ -188,7 +205,7 @@ done: static ssize_t WriteExtentToSparse(struct IOWrapper * context, DeviceInfo_t *devp, off_t start, off_t len, void (^bp)(off_t)) { - const size_t bufSize = 1024 * 1024; + const ssize_t bufSize = 1024 * 1024; uint8_t *buffer = NULL; ssize_t retval = 0; off_t total = 0; @@ -204,7 +221,7 @@ WriteExtentToSparse(struct IOWrapper * context, DeviceInfo_t *devp, off_t start, while (total < len) { ssize_t nread; ssize_t nwritten; - size_t amt = MIN(bufSize, len - total); + ssize_t amt = MIN(bufSize, len - total); nread = UnalignedRead(devp, buffer, amt, start + total); if (nread == -1) { warn("Cannot read from device at offset %lld", start + total); @@ -251,7 +268,7 @@ GetSizesFromPlist(const char *path, size_t *bandSize, off_t *devSize) long long tmpLL; - inFileURL = CFURLCreateFromFileSystemRepresentation(NULL, path, strlen(path), FALSE); + inFileURL = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)path, strlen(path), FALSE); if (inFileURL == NULL) { if (debug) warn("Cannot create url from pathname %s", path); goto done; @@ -398,10 +415,10 @@ struct IOWrapper * InitSparseBundle(const char *path, DeviceInfo_t *devp) { struct SparseBundleContext ctx = { 0 }; - struct SparseBundleContext *retctx = NULL; - IOWrapper_t *retval = NULL; + IOWrapper_t *wrapper = NULL; struct stat sb; - char tmpname[strlen(path) + sizeof("Info.plist") + 2]; // '/' + NUL + char *tmpname = NULL; + int tmplen; if (strstr(path, ".sparsebundle") == NULL) { asprintf(&ctx.pathname, "%s.sparsebundle", path); @@ -409,6 +426,10 @@ InitSparseBundle(const char *path, DeviceInfo_t *devp) ctx.pathname = strdup(path); } + if (ctx.pathname == NULL) { + return NULL; + } + if (lstat(ctx.pathname, &sb) == -1) { if (errno != ENOENT) { warn("cannot check sparse bundle %s", ctx.pathname); @@ -422,7 +443,14 @@ InitSparseBundle(const char *path, DeviceInfo_t *devp) warnx("sparse bundle object %s is not a directory", ctx.pathname); goto done; } - sprintf(tmpname, "%s/Info.plist", ctx.pathname); + /* NB! All names we create inside the bundle directory are shorter + * then "Info.plist", we re-use the buffer allocated here for + * multiple file names. */ + tmplen = asprintf(&tmpname, "%s/Info.plist", ctx.pathname); + if (tmpname == NULL) { + goto done; + } + if (stat(tmpname, &sb) != -1) { size_t bandSize = 0; off_t devSize = 0; @@ -445,46 +473,64 @@ InitSparseBundle(const char *path, DeviceInfo_t *devp) goto done; } ctx.bandSize = kBandSize; - fprintf(fp, bundlePrototype, kBandSize, devp->size); - fclose(fp); - sprintf(tmpname, "%s/Info.bckup", ctx.pathname); - fp = fopen(tmpname, "w"); - if (fp) { - fprintf(fp, bundlePrototype, kBandSize, devp->size); - fclose(fp); + if (fprintf(fp, bundlePrototype, kBandSize, devp->size) < 0) { + warn("failed to set bundle information in %s", tmpname); + fclose(fp); /* eat error return */ + goto done; + } + if (fclose(fp) != 0) { + warn("failed to update bundle information in %s", tmpname); + goto done; } - sprintf(tmpname, "%s/bands", ctx.pathname); - if (mkdir(tmpname, 0777) == -1) { + snprintf(tmpname, tmplen + 1, "%s/Info.bckup", ctx.pathname); + if ((int)strlen(tmpname) == tmplen) { /* Only update backup if we get the full path */ + /* Failure to create a backup is not fatal */ + fp = fopen(tmpname, "w"); + if (fp) { + if ((fprintf(fp, bundlePrototype, kBandSize, devp->size) < 0) || + (fclose(fp) < 0)) { + unlink(tmpname); + } + } + } else { + warn("Cannot create backup bundle information file in %s", tmpname); + } + if ((snprintf(tmpname, tmplen + 1, "%s/bands", ctx.pathname) > tmplen) || + (mkdir(tmpname, 0777) == -1)) { warn("cannot create bands directory in sparse bundle %s", ctx.pathname); goto done; } - sprintf(tmpname, "%s/token", ctx.pathname); - close(open(tmpname, O_CREAT | O_TRUNC, 0666)); - } - - retval = malloc(sizeof(*retval)); - if (retval == NULL) { - free(retval); - retval = NULL; - goto done; + if (snprintf(tmpname, tmplen + 1, "%s/token", ctx.pathname) <= tmplen) { + close(open(tmpname, O_CREAT | O_TRUNC, 0666)); + } } - retctx = malloc(sizeof(*retctx)); - if (retctx) { - *retctx = ctx; - retctx->cfd = -1; + wrapper = malloc(sizeof(*wrapper)); + if (wrapper) { + struct SparseBundleContext *wrapped_ctx; + + wrapped_ctx = malloc(sizeof(*wrapped_ctx)); + if (wrapped_ctx) { + *wrapped_ctx = ctx; + wrapped_ctx->cfd = -1; + + wrapper->writer = &WriteExtentToSparse; + wrapper->reader = &doSparseRead; + wrapper->getprog = &GetProgress; + wrapper->setprog = &SetProgress; + wrapper->cleanup = &doCleanup; + wrapper->context = wrapped_ctx; + } else { + free(wrapper); + wrapper = NULL; + } } - retval->writer = &WriteExtentToSparse; - retval->reader = &doSparseRead; - retval->getprog = &GetProgress; - retval->setprog = &SetProgress; - retval->cleanup = &doCleanup; - - retval->context = retctx; done: - if (retval == NULL) { + if (wrapper == NULL) { if (ctx.pathname) free(ctx.pathname); } - return retval; + + free(tmpname); + return wrapper; } diff --git a/CopyHFSMeta/main.c b/CopyHFSMeta/main.c index d562d7f..0954cf8 100644 --- a/CopyHFSMeta/main.c +++ b/CopyHFSMeta/main.c @@ -58,7 +58,7 @@ usage(const char *progname) errx(kBadExit, "usage: %s [-vdpS] [-g gatherFile] [-C] [-r ] ", progname); } - +int main(int ac, char **av) { char *src = NULL; @@ -66,7 +66,6 @@ main(int ac, char **av) DeviceInfo_t *devp = NULL; VolumeDescriptor_t *vdp = NULL; VolumeObjects_t *vop = NULL; - IOWrapper_t *wrapper = NULL; int ch; off_t restart = 0; int printEstimate = 0; @@ -148,10 +147,12 @@ main(int ac, char **av) * If we're given a destination, initialize it. */ if (dst) { - wrapper = InitSparseBundle(dst, devp); - } + IOWrapper_t *wrapper = InitSparseBundle(dst, devp); + + if (wrapper == NULL) { + err(kBadExit, "cannot initialize destination container %s", dst); + } - if (wrapper) { // See if we're picking up from a previous copy if (restart == 0) { restart = wrapper->getprog(wrapper); diff --git a/core/hfs.h b/core/hfs.h index d91f479..f726cd3 100644 --- a/core/hfs.h +++ b/core/hfs.h @@ -61,6 +61,7 @@ #include #include #include +#include #include "../hfs_encodings/hfs_encodings.h" @@ -95,7 +96,6 @@ struct cprotect; // multiple separate transactions) #define HFS_BIGFILE_SIZE (400LL * 1024LL * 1024LL) - enum { kMDBSize = 512 }; /* Size of I/O transfer to read entire MDB */ enum { kMasterDirectoryBlock = 2 }; /* MDB offset on disk in 512-byte blocks */ @@ -399,6 +399,9 @@ typedef struct hfsmount { #endif + /* the full UUID of the volume, not the one stored in finderinfo */ + uuid_t hfs_full_uuid; + /* Per mount cnode hash variables: */ lck_mtx_t hfs_chash_mutex; /* protects access to cnode hash table */ u_long hfs_cnodehash; /* size of cnode hash table - 1 */ @@ -430,21 +433,6 @@ typedef struct hfsmount { }; // These lists are not sorted like a range list usually is struct rl_head hfs_reserved_ranges[2]; - - // cnode zone - struct cnode_zone { - int elem_size; - int chunk_size; - int alloc_count; - int gcd; - int y; - struct chunk_hdr **heap; - int heap_max_count, heap_count; - lck_mtx_t alloc_mtx; - bool allocating : 1; - bool waiting : 1; - struct chunk_hdr *spare; - } z; } hfsmount_t; /* @@ -822,6 +810,9 @@ int hfs_truncatefs(struct hfsmount *hfsmp, u_int64_t newsize, vfs_context_t cont /***************************************************************************** Functions from hfs_vfsops.c ******************************************************************************/ + +extern void hfs_getvoluuid(struct hfsmount *hfsmp, uuid_t result); + /* used as a callback by the journaling code */ extern void hfs_sync_metadata(void *arg); @@ -993,6 +984,30 @@ int unicode_to_hfs(ExtendedVCB *vcb, ByteCount srcLen, u_int16_t* srcStr, Str31 void *hfs_malloc(size_t size); void hfs_free(void *ptr, size_t size); void *hfs_mallocz(size_t size); + +typedef enum { + HFS_CNODE_ZONE, + HFS_FILEFORK_ZONE, + HFS_DIRHINT_ZONE, + HFS_NUM_ZONES +} hfs_zone_kind_t; + +typedef struct hfs_zone_entry { + hfs_zone_kind_t hze_kind; + size_t hze_elem_size; + const char * hze_name; + boolean_t hze_noencrypt; +} hfs_zone_entry_t; + +typedef struct hfs_zone { + zone_t hz_zone; + size_t hz_elem_size; +} hfs_zone_t; + +void hfs_init_zones(void); +void *hfs_zalloc(hfs_zone_kind_t type); +void hfs_zfree(void *ptr, hfs_zone_kind_t type); + void hfs_sysctl_register(void); void hfs_sysctl_unregister(void); diff --git a/core/hfs_catalog.c b/core/hfs_catalog.c index a70d32e..8a2009b 100644 --- a/core/hfs_catalog.c +++ b/core/hfs_catalog.c @@ -3038,7 +3038,6 @@ exit: } #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8) -#define MAX_LINKINFO_ENTRIES 3000 /* * Callback to pack directory entries. @@ -3518,12 +3517,13 @@ cat_getdirentries(struct hfsmount *hfsmp, u_int32_t entrycnt, directoryhint_t *d } fcb = hfsmp->hfs_catalog_cp->c_datafork; + #define MAX_LINKINFO_ENTRIES 275 /* - * 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. + * Get a buffer for link info array, btree iterator and a direntry. + * + * We impose an cap of 275 link entries when trying to compute + * the total number of hardlink entries that we'll allow in the + * linkinfo array, as this has been shown to noticeably impact performance. * * Note that in the case where there are very few hardlinks, * this does not restrict or prevent us from vending out as many entries @@ -3532,8 +3532,11 @@ cat_getdirentries(struct hfsmount *hfsmp, u_int32_t entrycnt, directoryhint_t *d * this MALLOC'd array. It also limits itself to maxlinks of hardlinks. */ - /* Now compute the maximum link array size */ - maxlinks = MIN (entrycnt, MAX_LINKINFO_ENTRIES); + // This value cannot underflow: both entrycnt and the rhs are unsigned 32-bit + // ints, so the worst-case MIN of them is 0. + maxlinks = MIN (entrycnt, (u_int32_t)(uio_resid(uio) / SMALL_DIRENTRY_SIZE)); + // Prevent overflow. + maxlinks = MIN (maxlinks, MAX_LINKINFO_ENTRIES); bufsize = MAXPATHLEN + (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator); if (extended) { diff --git a/core/hfs_chash.c b/core/hfs_chash.c index 5796f78..5fe0bc3 100644 --- a/core/hfs_chash.c +++ b/core/hfs_chash.c @@ -81,13 +81,8 @@ lck_grp_t * chash_lck_grp; lck_grp_attr_t * chash_lck_grp_attr; lck_attr_t * chash_lck_attr; -static cnode_t *hfs_cnode_alloc(hfsmount_t *hfsmp, bool *unlocked); - #define CNODEHASH(hfsmp, inum) (&hfsmp->hfs_cnodehashtbl[(inum) & hfsmp->hfs_cnodehash]) -static void hfs_cnode_zone_init(hfsmount_t *hfsmp); -static void hfs_cnode_zone_free(hfsmount_t *hfsmp); - /* * Initialize cnode hash table. */ @@ -125,8 +120,6 @@ hfs_chashinit_finish(struct hfsmount *hfsmp) lck_mtx_init(&hfsmp->hfs_chash_mutex, chash_lck_grp, chash_lck_attr); hfsmp->hfs_cnodehashtbl = hashinit(desiredvnodes / 4, M_TEMP, &hfsmp->hfs_cnodehash); - - hfs_cnode_zone_init(hfsmp); } void @@ -135,8 +128,6 @@ hfs_delete_chash(struct hfsmount *hfsmp) lck_mtx_destroy(&hfsmp->hfs_chash_mutex, chash_lck_grp); FREE(hfsmp->hfs_cnodehashtbl, M_TEMP); - - hfs_cnode_zone_free(hfsmp); } @@ -347,7 +338,7 @@ loop_with_lock: * this cnode and add it to the hash * just dump our allocation */ - hfs_cnode_free(hfsmp, ncp); + hfs_zfree(ncp, HFS_CNODE_ZONE); ncp = NULL; } @@ -401,12 +392,17 @@ loop_with_lock: panic("%s - should never get here when skiplock is set \n", __FUNCTION__); if (ncp == NULL) { - bool unlocked; - - ncp = hfs_cnode_alloc(hfsmp, &unlocked); - - if (unlocked) - goto loop_with_lock; + hfs_chash_unlock(hfsmp); + + ncp = hfs_zalloc(HFS_CNODE_ZONE); + + /* + * since we dropped the chash lock, + * we need to go back and re-verify + * that this node hasn't come into + * existence... + */ + goto loop; } hfs_chash_lock_convert(hfsmp); @@ -580,469 +576,3 @@ hfs_chash_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid) return retval; } - -// -- Cnode Memory Allocation -- - -/* - * We allocate chunks but to minimise fragmentation, we store the - * chunks in a heap ordered such that the chunk with the least number - * of free elements is at the top. At the end of each chunk we store - * a pointer to a header somewhere within the chunk. When all - * elements in a chunk are in use, the pointer is NULL. Given that - * chunks will always be aligned to a page, we can compute the element - * index in the chunk using this equation: - * - * y * (uintptr_t)el % PAGE_SIZE / gcd % (PAGE_SIZE / gcd) - * - * where gcd is the greatest common divisor of elem_size and - * PAGE_SIZE, (which we calculate using the Euclidean algorithm) and y - * also falls out of the Euclidean algorithm -- see - * hfs_cnode_zone_init below. The proof for this is left as an - * exercise for the reader. Hint: start with the equation: - * - * elem_ndx * elem_size = PAGE_SIZE * elem_pg + elem_addr % PAGE_SIZE - * - * Now realise that can be made to look like a Diophantine equation - * (ax + by = c) which is solvable using the Extended Euclidean - * algorithm and from there you will arrive at the equation above. - */ - -enum { - CHUNK_HDR_MAGIC = 0x6b6e6863, // chnk - CNODE_MAGIC = 0x65646f6e63736668, // hfscnode - ELEMENT_ALIGNMENT = 8, - - /* - * We store the pointer to the header for a chunk 8 bytes in from - * the end of the chunk. - */ - CHUNK_TAIL_LEN = 8, -}; - -typedef struct chunk_hdr -{ - void *next_free; - uint32_t magic; // = CHUNK_HDR_MAGIC - uint16_t free_count; - uint16_t heap_ndx; - struct chunk_hdr **phdr; -} chunk_hdr_t; - -static void hfs_cnode_zone_init(hfsmount_t *hfsmp) -{ - struct cnode_zone *z = &hfsmp->z; - - int elem_size = sizeof(cnode_t); - - elem_size = roundup(elem_size, ELEMENT_ALIGNMENT); - - z->elem_size = elem_size; - - // Figure out chunk_size - int npages, chunk_size, waste; - - for (npages = 1;; ++npages) { - chunk_size = npages * page_size; - waste = (chunk_size - CHUNK_TAIL_LEN) % elem_size; - - // If waste is less than 1%, that's good enough - if (waste < chunk_size / 100) - break; - } - - z->chunk_size = chunk_size; - z->alloc_count = (chunk_size - CHUNK_TAIL_LEN) / elem_size; - - // Compute the GCD of elem_size and page_size using Euclidean algorithm - int t = 1, last_t = 0, r = z->elem_size, last_r = PAGE_SIZE; - - while (r != 0) { - int q = last_r / r; - int next_r = last_r - q * r; - last_r = r; - r = next_r; - int next_t = last_t - q * t; - last_t = t; - t = next_t; - } - - z->gcd = last_r; - z->y = last_t; - - z->heap_max_count = 128 / sizeof(void *); - z->heap = hfs_malloc(z->heap_max_count * sizeof(void *)); - -#if DEBUG - printf("hfs: cnode size %d, chunk size %d, # per chunk %d, waste %d\n", - elem_size, chunk_size, z->alloc_count, waste); -#endif -} - -static void hfs_cnode_zone_free(hfsmount_t *hfsmp) -{ - // Check that all heap chunks are completely free - struct cnode_zone *z = &hfsmp->z; - - for (int i = 0; i < z->heap_count; ++i) { -#if DEBUG - hfs_assert(z->heap[i]->free_count == z->alloc_count); -#else - if (z->heap[i]->free_count != z->alloc_count) { - printf("hfs: cnodes leaked!\n"); - continue; - } -#endif - void *chunk = (void *)((uintptr_t)z->heap[i]->phdr - - (z->chunk_size - CHUNK_TAIL_LEN)); - hfs_free(chunk, z->chunk_size); - } - - hfs_free(z->heap, z->heap_max_count * sizeof(void *)); -} - -static void *zel(struct cnode_zone *z, void *chunk, int i) -{ - return chunk + i * z->elem_size; -} - -static chunk_hdr_t **zphdr(struct cnode_zone *z, void *chunk) -{ - return chunk + z->chunk_size - CHUNK_TAIL_LEN; -} - -#if 0 -static void hfs_check_heap(hfsmount_t *hfsmp, void *just_removed) -{ - struct cnode_zone *z = &hfsmp->z; - - for (int i = 0; i < z->heap_count; ++i) { - hfs_assert(z->heap[i]->magic == CHUNK_HDR_MAGIC); - hfs_assert(z->heap[i]->free_count > 0 - && z->heap[i]->free_count <= z->alloc_count); - hfs_assert(z->heap[i]->heap_ndx == i); - void *max_el = z->heap[i]->phdr; - void *min_el = (void *)((uintptr_t)z->heap[i]->phdr - + CHUNK_TAIL_LEN - z->chunk_size); - int count = 1; - hfs_assert(z->heap[i] != just_removed); - void *el = z->heap[i]->next_free; - size_t bitmap_size = (z->alloc_count + 7) / 8; - uint8_t bitmap[bitmap_size]; - bzero(bitmap, bitmap_size); - int ndx = (int)((void *)z->heap[i] - min_el) / z->elem_size; - bitmap[ndx / 8] |= 0x80 >> ndx % 8; - while (el) { - hfs_assert(el != just_removed); - hfs_assert(el >= min_el - && el < max_el && (el - min_el) % z->elem_size == 0); - int n = (int)(el - min_el) / z->elem_size; - hfs_assert(!(bitmap[n / 8] & (0x80 >> n % 8))); - bitmap[n / 8] |= 0x80 >> n % 8; - el = *(void **)el; - ++count; - } - hfs_assert(count == z->heap[i]->free_count); - if (i) - hfs_assert(z->heap[(i + 1) / 2 - 1]->free_count <= z->heap[i]->free_count); - } -} -#else -#define hfs_check_heap(a, b) (void)0 -#endif - -static void hfs_cnode_set_magic(cnode_t *cp, uint64_t v) -{ -#if HFS_MALLOC_DEBUG - cp->magic = v; -#endif -} - -static cnode_t *hfs_cnode_alloc(hfsmount_t *hfsmp, bool *unlocked) -{ - hfs_check_heap(hfsmp, NULL); - - *unlocked = false; - - struct cnode_zone *z = &hfsmp->z; - void *el; - - while (!z->heap_count) { - if (z->spare) { - /* - * We have a spare chunk we can use so initialise it, add - * it to the heap and return one element from it. - */ - chunk_hdr_t *chunk_hdr = z->spare; - z->spare = NULL; - void *last = NULL; - for (int i = z->alloc_count - 2; i > 0; --i) { - void *p = zel(z, chunk_hdr, i); - *(void **)p = last; - hfs_cnode_set_magic(p, 0); - last = p; - } - hfs_cnode_set_magic((void *)chunk_hdr, 0); - chunk_hdr->phdr = zphdr(z, chunk_hdr); - chunk_hdr->next_free = last; - chunk_hdr->free_count = z->alloc_count - 1; - chunk_hdr->heap_ndx = 0; - // Set the tail to the index of the chunk_hdr - *zphdr(z, chunk_hdr) = chunk_hdr; - z->heap[0] = chunk_hdr; - z->heap_count = 1; - el = zel(z, chunk_hdr, z->alloc_count - 1); - hfs_cnode_set_magic(el, 0); - goto exit; - } - - *unlocked = true; - - if (z->allocating) { - z->waiting = true; - msleep(z, &hfsmp->hfs_chash_mutex, PINOD | PSPIN, - "hfs_cnode_alloc", NULL); - } else { - z->allocating = true; - lck_mtx_unlock(&hfsmp->hfs_chash_mutex); - chunk_hdr_t *chunk_hdr = hfs_malloc(z->chunk_size); - chunk_hdr->magic = CHUNK_HDR_MAGIC; - hfs_assert(!((uintptr_t)chunk_hdr & PAGE_MASK)); - lck_mtx_lock_spin(&hfsmp->hfs_chash_mutex); - z->allocating = false; - if (z->waiting) { - wakeup(z); - z->waiting = false; - } - hfs_assert(!z->spare); - z->spare = chunk_hdr; - } - } - - chunk_hdr_t *chunk_hdr = z->heap[0]; - if (chunk_hdr->next_free) { - /* - * Not the last element in this chunk, so just pull an element - * off and return it. This chunk should remain at the top of - * the heap. - */ - el = chunk_hdr->next_free; - chunk_hdr->next_free = *(void **)chunk_hdr->next_free; - --chunk_hdr->free_count; - - goto exit; - } - - /* - * This is the last element in the chunk so we need to fix up the - * heap. - */ - el = chunk_hdr; - - *chunk_hdr->phdr = NULL; - chunk_hdr->magic = ~CHUNK_HDR_MAGIC; - - if (!--z->heap_count) - goto exit; - - chunk_hdr_t *v = z->heap[z->heap_count]; - - // Fix heap downwards; offset by one to make easier - int k = 1; - chunk_hdr_t **a = &z->heap[0] - 1; - int N = z->heap_count; - - for (;;) { - // Look at the next level down - int j = k * 2; - if (j > N) - break; - // Pick the smaller of the two that we're looking at - if (j < N && a[j]->free_count > a[j+1]->free_count) - ++j; - if (v->free_count <= a[j]->free_count) - break; - // Shift element at j to k - a[k] = a[j]; - a[k]->heap_ndx = k - 1; - - // Descend - k = j; - } - - a[k] = v; - a[k]->heap_ndx = k - 1; - -exit: - - hfs_check_heap(hfsmp, el); - -#if HFS_MALLOC_DEBUG - if (((cnode_t *)el)->magic == CNODE_MAGIC) { -#if __x86_64__ - asm("int $3\n"); -#elif __arm64__ - asm("svc 0\n"); -#else - asm("trap\n"); -#endif - } -#endif // HFS_MALLOC_DEBUG - - hfs_cnode_set_magic(el, CNODE_MAGIC); - - return el; -} - -void hfs_cnode_free(hfsmount_t *hfsmp, cnode_t *cp) -{ - void *el = cp; - void *old_heap = NULL; - size_t old_heap_size = 0; - struct cnode_zone *z = &hfsmp->z; - - int ndx_in_chunk = (z->y * (uintptr_t)el % PAGE_SIZE - / z->gcd % (PAGE_SIZE / z->gcd)); - - void *free_chunk = NULL, *chunk = el - ndx_in_chunk * z->elem_size; - - lck_mtx_lock_spin(&hfsmp->hfs_chash_mutex); - -#if HFS_MALLOC_DEBUG - hfs_assert(cp->magic == CNODE_MAGIC); - cp->magic = ~CNODE_MAGIC; -#endif - -loop: - - hfs_check_heap(hfsmp, NULL); - - chunk_hdr_t *hdr = *zphdr(z, chunk); - - int k, orig_k; - - if (hdr) { - /* - * This chunk already has some free elements in it so we chain this - * element on and then fix up the heap. - */ - hfs_assert(hdr->magic == CHUNK_HDR_MAGIC); - - *(void **)el = hdr->next_free; - hdr->next_free = el; - hfs_assert(hdr->next_free != hdr); - - chunk_hdr_t *v; - orig_k = k = hdr->heap_ndx + 1; - - chunk_hdr_t **a = &z->heap[0] - 1; - - if (++hdr->free_count == z->alloc_count) { - // Chunk is totally free - - // Always keep at least one chunk on the heap - if (z->heap_count == 1) - goto exit; - - // Remove the chunk - free_chunk = chunk; - --z->heap_count; - v = z->heap[z->heap_count]; - - if (k > 1 && a[k / 2]->free_count > v->free_count) { - do { - a[k] = a[k / 2]; - a[k]->heap_ndx = k - 1; - k = k / 2; - } while (k > 1 && a[k / 2]->free_count > v->free_count); - a[k] = v; - a[k]->heap_ndx = k - 1; - goto exit; - } - } else - v = hdr; - - // Fix up the heap - int N = z->heap_count; - - for (;;) { - // Look at the next level down - int j = k * 2; - if (j > N) - break; - // Pick the smaller of the two that we're looking at - if (j < N && a[j]->free_count > a[j+1]->free_count) - ++j; - if (v->free_count <= a[j]->free_count) - break; - // Shift element at j to k - a[k] = a[j]; - a[k]->heap_ndx = k - 1; - - // Descend - k = j; - } - - a[k] = v; - a[k]->heap_ndx = k - 1; - } else { - // This element is the first that's free in this chunk - - if (z->heap_count == z->heap_max_count) { - // We need to resize the heap - int new_max_count = z->heap_max_count * 2; - lck_mtx_unlock(&hfsmp->hfs_chash_mutex); - if (old_heap) - hfs_free(old_heap, old_heap_size); - struct chunk_hdr **new_heap = hfs_malloc(new_max_count * sizeof(void *)); - lck_mtx_lock_spin(&hfsmp->hfs_chash_mutex); - // Check to see if someone beat us to it - if (new_max_count > z->heap_max_count) { - memcpy(new_heap, z->heap, z->heap_count * sizeof(void *)); - old_heap_size = z->heap_max_count * sizeof(void *); - z->heap_max_count = new_max_count; - old_heap = z->heap; - z->heap = new_heap; - } else { - old_heap = new_heap; - old_heap_size = new_max_count * sizeof(void *); - } - // Lock was dropped so we have to go around again - goto loop; - } - - hdr = (chunk_hdr_t *)el; - *zphdr(z, chunk) = hdr; - hdr->free_count = 1; - hdr->next_free = NULL; - hdr->heap_ndx = 0; - hdr->phdr = zphdr(z, chunk); - hdr->magic = CHUNK_HDR_MAGIC; - - // Fix heap upwards; offset by one to make easier - chunk_hdr_t **a = &z->heap[0] - 1; - - if (z->heap_count++) { - k = z->heap_count; - chunk_hdr_t *v = z->heap[0]; - while (k > 3 && a[k / 2]->free_count > v->free_count) { - a[k] = a[k / 2]; - a[k]->heap_ndx = k - 1; - k = k / 2; - } - a[k] = v; - a[k]->heap_ndx = k - 1; - } - - z->heap[0] = hdr; - } - -exit: - - hfs_check_heap(hfsmp, NULL); - lck_mtx_unlock(&hfsmp->hfs_chash_mutex); - - if (old_heap) - hfs_free(old_heap, old_heap_size); - if (free_chunk) - hfs_free(free_chunk, z->chunk_size); -} diff --git a/core/hfs_cnode.c b/core/hfs_cnode.c index 84520fb..12b126c 100644 --- a/core/hfs_cnode.c +++ b/core/hfs_cnode.c @@ -806,7 +806,7 @@ hfs_vnop_reclaim(struct vnop_reclaim_args *ap) hfs_free(fp->ff_symlinkptr, fp->ff_size); } rl_remove_all(&fp->ff_invalidranges); - hfs_free(fp, sizeof(struct filefork)); + hfs_zfree(fp, HFS_FILEFORK_ZONE); } /* @@ -938,8 +938,12 @@ hfs_getnewvnode( issystemfile = (descp->cd_flags & CD_ISMETA) && (vtype == VREG); wantrsrc = flags & GNV_WANTRSRC; - /* Sanity check the vtype and mode */ - if (vtype == VBAD) { + /* Sanity checks: */ + if (vtype == VBAD || + (vtype != VDIR && forkp && + (attrp->ca_blocks < forkp->cf_blocks || + howmany((uint64_t)forkp->cf_size, hfsmp->blockSize) > forkp->cf_blocks || + (vtype == VLNK && (uint64_t)forkp->cf_size > MAXPATHLEN)))) { /* Mark the FS as corrupt and bail out */ hfs_mark_inconsistent(hfsmp, HFS_INCONSISTENCY_DETECTED); retval = EINVAL; @@ -1215,12 +1219,10 @@ hfs_getnewvnode( panic("hfs_getnewvnode: orphaned vnode (data)"); cvpp = &cp->c_vp; } else { - if (forkp && attrp->ca_blocks < forkp->cf_blocks) - panic("hfs_getnewvnode: bad ca_blocks (too small)"); /* * Allocate and initialize a file fork... */ - fp = hfs_malloc(sizeof(struct filefork)); + fp = hfs_zalloc(HFS_FILEFORK_ZONE); fp->ff_cp = cp; if (forkp) bcopy(forkp, &fp->ff_data, sizeof(struct cat_fork)); @@ -1381,7 +1383,7 @@ hfs_getnewvnode( else cp->c_rsrcfork = NULL; - hfs_free(fp, sizeof(struct filefork)); + hfs_zfree(fp, HFS_FILEFORK_ZONE); } /* * If this is a newly created cnode or a vnode reclaim @@ -1524,7 +1526,7 @@ hfs_reclaim_cnode(hfsmount_t *hfsmp, struct cnode *cp) (void)hfsmp; // Prevent compiler warning #endif - hfs_cnode_free(hfsmp, cp); + hfs_zfree(cp, HFS_CNODE_ZONE); } diff --git a/core/hfs_cnode.h b/core/hfs_cnode.h index e6a83aa..dd426b8 100644 --- a/core/hfs_cnode.h +++ b/core/hfs_cnode.h @@ -468,8 +468,6 @@ extern int hfs_valid_cnode(struct hfsmount *hfsmp, struct vnode *dvp, struct com extern int hfs_chash_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid); -void hfs_cnode_free(struct hfsmount *hfsmp, cnode_t *cp); - /* * HFS cnode lock functions. * diff --git a/core/hfs_cprotect.c b/core/hfs_cprotect.c index bc1b2c3..3fa485b 100644 --- a/core/hfs_cprotect.c +++ b/core/hfs_cprotect.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "hfs.h" #include "hfs_cnode.h" @@ -2499,19 +2500,37 @@ cpnew_fail: return error; } + /* Initialize the aks_cred_t structure passed to AKS */ static void cp_init_access(aks_cred_t access, struct cnode *cp) { vfs_context_t context = vfs_context_current(); kauth_cred_t cred = vfs_context_ucred(context); proc_t proc = vfs_context_proc(context); + struct hfsmount *hfsmp; + struct vnode *vp; + uuid_t hfs_uuid; bzero(access, sizeof(*access)); + vp = CTOV(cp, 0); + if (vp == NULL) { + /* is it a rsrc */ + vp = CTOV(cp,1); + if (vp == NULL) { + //leave the struct bzeroed. + return; + } + } + + hfsmp = VTOHFS(vp); + hfs_getvoluuid(hfsmp, hfs_uuid); + /* Note: HFS uses 32-bit fileID, even though inode is a 64-bit value */ access->inode = cp->c_fileid; access->pid = proc_pid(proc); access->uid = kauth_cred_getuid(cred); + uuid_copy (access->volume_uuid, hfs_uuid); if (cp->c_cpentry) access->key_revision = cp->c_cpentry->cp_key_revision; diff --git a/core/hfs_endian.c b/core/hfs_endian.c index 22b716f..bdc9c7a 100644 --- a/core/hfs_endian.c +++ b/core/hfs_endian.c @@ -204,7 +204,8 @@ hfs_swap_BTNode ( (allow_empty_node == false) && (srcOffs[i] == 0)) || (srcOffs[i] < sizeof(BTNodeDescriptor) && srcOffs[i] != 0) || (srcOffs[i] > (src->blockSize - 2 * (srcDesc->numRecords + 1)))) { - printf("hfs_swap_BTNode: record #%d invalid offset (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]); + printf("hfs_swap_BTNode: offset #%d invalid (0x%04X) (blockSize 0x%x numRecords %d)\n", + i, srcOffs[i], src->blockSize, srcDesc->numRecords); error = fsBTInvalidHeaderErr; goto fail; } @@ -215,7 +216,7 @@ hfs_swap_BTNode ( */ if ((i != 0) && (srcOffs[i] >= srcOffs[i-1])) { printf("hfs_swap_BTNode: offsets %d and %d out of order (0x%04X, 0x%04X)\n", - srcDesc->numRecords-i-1, srcDesc->numRecords-i, srcOffs[i], srcOffs[i-1]); + i, i-1, srcOffs[i], srcOffs[i-1]); error = fsBTInvalidHeaderErr; goto fail; } @@ -359,7 +360,8 @@ hfs_swap_BTNode ( ((allow_empty_node == false) && (srcOffs[i] == 0)) || (srcOffs[i] < sizeof(BTNodeDescriptor) && srcOffs[i] != 0) || (srcOffs[i] > (src->blockSize - 2 * (srcDesc->numRecords + 1)))) { - panic("hfs_UNswap_BTNode: record #%d invalid offset (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]); + panic("hfs_UNswap_BTNode: offset #%d invalid (0x%04X) (blockSize 0x%x numRecords %d)\n", + i, srcOffs[i], src->blockSize, srcDesc->numRecords); error = fsBTInvalidHeaderErr; goto fail; } @@ -370,7 +372,7 @@ hfs_swap_BTNode ( */ if ((i < srcDesc->numRecords) && (srcOffs[i+1] >= srcOffs[i])) { panic("hfs_UNswap_BTNode: offsets %d and %d out of order (0x%04X, 0x%04X)\n", - srcDesc->numRecords-i-2, srcDesc->numRecords-i-1, srcOffs[i+1], srcOffs[i]); + i+1, i, srcOffs[i+1], srcOffs[i]); error = fsBTInvalidHeaderErr; goto fail; } diff --git a/core/hfs_iokit.cpp b/core/hfs_iokit.cpp index ac9a43d..5908364 100644 --- a/core/hfs_iokit.cpp +++ b/core/hfs_iokit.cpp @@ -118,6 +118,8 @@ bool com_apple_filesystems_hfs::start(IOService *provider) return false; } + hfs_init_zones(); + hfs_sysctl_register(); return true; diff --git a/core/hfs_readwrite.c b/core/hfs_readwrite.c index f3a0827..879fe0a 100644 --- a/core/hfs_readwrite.c +++ b/core/hfs_readwrite.c @@ -49,6 +49,7 @@ #include #include #include +#include #include @@ -2530,6 +2531,8 @@ fail_change_next_allocation: } hfs_lock_mount (hfsmp); bcopy(ap->a_data, &hfsmp->vcbFndrInfo, sizeof(hfsmp->vcbFndrInfo)); + /* Null out the cached UUID, to be safe */ + uuid_clear (hfsmp->hfs_full_uuid); hfs_unlock_mount (hfsmp); (void) hfs_flushvolumeheader(hfsmp, HFS_FVH_WAIT); break; diff --git a/core/hfs_vfsops.c b/core/hfs_vfsops.c index 7144459..10e5f53 100644 --- a/core/hfs_vfsops.c +++ b/core/hfs_vfsops.c @@ -479,6 +479,10 @@ hfs_mount(struct mount *mp, vnode_t devvp, user_addr_t data, vfs_context_t conte } } else /* not an update request */ { + if (devvp == NULL) { + retval = EINVAL; + goto out; + } /* Set the mount flag to indicate that we support volfs */ vfs_setflags(mp, (u_int64_t)((unsigned int)MNT_DOVOLFS)); @@ -4190,22 +4194,31 @@ err_exit: * Creates a UUID from a unique "name" in the HFS UUID Name space. * See version 3 UUID. */ -static void -hfs_getvoluuid(struct hfsmount *hfsmp, uuid_t result) +void +hfs_getvoluuid(struct hfsmount *hfsmp, uuid_t result_uuid) { - MD5_CTX md5c; - uint8_t rawUUID[8]; - ((uint32_t *)rawUUID)[0] = hfsmp->vcbFndrInfo[6]; - ((uint32_t *)rawUUID)[1] = hfsmp->vcbFndrInfo[7]; + if (uuid_is_null(hfsmp->hfs_full_uuid)) { + uuid_t result; - MD5Init( &md5c ); - MD5Update( &md5c, HFS_UUID_NAMESPACE_ID, sizeof( uuid_t ) ); - MD5Update( &md5c, rawUUID, sizeof (rawUUID) ); - MD5Final( result, &md5c ); + MD5_CTX md5c; + uint8_t rawUUID[8]; + + ((uint32_t *)rawUUID)[0] = hfsmp->vcbFndrInfo[6]; + ((uint32_t *)rawUUID)[1] = hfsmp->vcbFndrInfo[7]; + + MD5Init( &md5c ); + MD5Update( &md5c, HFS_UUID_NAMESPACE_ID, sizeof( uuid_t ) ); + MD5Update( &md5c, rawUUID, sizeof (rawUUID) ); + MD5Final( result, &md5c ); + + result[6] = 0x30 | ( result[6] & 0x0F ); + result[8] = 0x80 | ( result[8] & 0x3F ); + + uuid_copy(hfsmp->hfs_full_uuid, result); + } + uuid_copy (result_uuid, hfsmp->hfs_full_uuid); - result[6] = 0x30 | ( result[6] & 0x0F ); - result[8] = 0x80 | ( result[8] & 0x3F ); } /* diff --git a/core/hfs_vfsutils.c b/core/hfs_vfsutils.c index 0e73c58..e841c41 100644 --- a/core/hfs_vfsutils.c +++ b/core/hfs_vfsutils.c @@ -57,6 +57,7 @@ /* for parsing boot-args */ #include #include +#include #include "hfs_iokit.h" #include "hfs.h" @@ -815,7 +816,14 @@ OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, /* reset retval == 0. we don't care about errors in volname conversion */ retval = 0; - + /* + * pull in the volume UUID while we are still single-threaded. + * This brings the volume UUID into the cached one dangling off of the HFSMP + * Otherwise it would have to be computed on first access. + */ + uuid_t throwaway; + hfs_getvoluuid (hfsmp, throwaway); + /* * We now always initiate a full bitmap scan even if the volume is read-only because this is * our only shot to do I/Os of dramaticallly different sizes than what the buffer cache ordinarily @@ -2323,7 +2331,7 @@ hfs_getdirhint(struct cnode *dcp, int index, int detach) need_init = true; if (dcp->c_dirhintcnt < HFS_MAXDIRHINTS) { /* we don't need recycling */ /* Create a default directory hint */ - hint = hfs_malloc(sizeof(directoryhint_t)); + hint = hfs_zalloc(HFS_DIRHINT_ZONE); ++dcp->c_dirhintcnt; need_remove = false; } else { /* recycle the last (i.e., the oldest) hint */ @@ -2387,7 +2395,7 @@ hfs_reldirhint(struct cnode *dcp, directoryhint_t * relhint) relhint->dh_desc.cd_flags &= ~CD_HASBUF; vfs_removename((const char *)name); } - hfs_free(relhint, sizeof(*relhint)); + hfs_zfree(relhint, HFS_DIRHINT_ZONE); } /* @@ -2418,7 +2426,7 @@ hfs_reldirhints(struct cnode *dcp, int stale_hints_only) } prev = TAILQ_PREV(hint, hfs_hinthead, dh_link); /* must save this pointer before calling FREE_ZONE on this node */ TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link); - hfs_free(hint, sizeof(*hint)); + hfs_zfree(hint, HFS_DIRHINT_ZONE); --dcp->c_dirhintcnt; } } @@ -4251,7 +4259,6 @@ bool hfs_dump_allocations(void) HFS_SYSCTL(QUAD, _vfs_generic_hfs, OID_AUTO, allocated, CTLFLAG_RD | CTLFLAG_LOCKED, &hfs_allocated, "Memory allocated") -// Any allocation >= PAGE_SIZE will be page aligned void *hfs_malloc(size_t size) { #if HFS_MALLOC_DEBUG @@ -4260,11 +4267,7 @@ void *hfs_malloc(size_t size) struct alloc_debug_header *hdr; void *ptr; - - if (size >= PAGE_SIZE) - ptr = IOMallocAligned(size + sizeof(*hdr), PAGE_SIZE); - else - ptr = kalloc(size + sizeof(*hdr)); + ptr = kalloc(size + sizeof(*hdr)); hdr = ptr + size; @@ -4281,10 +4284,7 @@ void *hfs_malloc(size_t size) hdr->chain.le_prev = NULL; #else void *ptr; - if (size >= PAGE_SIZE) - ptr = IOMallocAligned(size, PAGE_SIZE); - else - ptr = kalloc(size); + ptr = kalloc(size); #endif OSAddAtomic64(size, &hfs_allocated); @@ -4313,15 +4313,9 @@ void hfs_free(void *ptr, size_t size) lck_mtx_unlock(hfs_alloc_mtx); } - if (size >= PAGE_SIZE) - IOFreeAligned(ptr, size + sizeof(*hdr)); - else - kfree(ptr, size + sizeof(*hdr)); + kfree(ptr, size + sizeof(*hdr)); #else - if (size >= PAGE_SIZE) - IOFreeAligned(ptr, size); - else - kfree(ptr, size); + kfree(ptr, size); #endif } @@ -4332,6 +4326,41 @@ void *hfs_mallocz(size_t size) return ptr; } +// -- Zone allocator-related structures and routines -- + +hfs_zone_entry_t hfs_zone_entries[HFS_NUM_ZONES] = { + { HFS_CNODE_ZONE, sizeof(struct cnode), "HFS node", true }, + { HFS_FILEFORK_ZONE, sizeof(struct filefork), "HFS fork", true }, + { HFS_DIRHINT_ZONE, sizeof(struct directoryhint), "HFS dirhint", true } +}; + +hfs_zone_t hfs_zones[HFS_NUM_ZONES]; + +void hfs_init_zones(void) { + for (int i = 0; i < HFS_NUM_ZONES; i++) { + hfs_zones[i].hz_zone = zinit(hfs_zone_entries[i].hze_elem_size, 1024 * 1024, PAGE_SIZE, hfs_zone_entries[i].hze_name); + hfs_zones[i].hz_elem_size = hfs_zone_entries[i].hze_elem_size; + + zone_change(hfs_zones[i].hz_zone, Z_CALLERACCT, false); + if (hfs_zone_entries[i].hze_noencrypt) + zone_change(hfs_zones[i].hz_zone, Z_NOENCRYPT, true); + } +} + +void *hfs_zalloc(hfs_zone_kind_t zone) +{ + OSAddAtomic64(hfs_zones[zone].hz_elem_size, &hfs_allocated); + + return zalloc(hfs_zones[zone].hz_zone); +} + +void hfs_zfree(void *ptr, hfs_zone_kind_t zone) +{ + OSAddAtomic64(-(int64_t)hfs_zones[zone].hz_elem_size, &hfs_allocated); + + zfree(hfs_zones[zone].hz_zone, ptr); +} + struct hfs_sysctl_chain *sysctl_list; void hfs_sysctl_register(void) diff --git a/core/hfs_vnops.c b/core/hfs_vnops.c index 2eae260..316a657 100644 --- a/core/hfs_vnops.c +++ b/core/hfs_vnops.c @@ -1334,7 +1334,15 @@ hfs_vnop_setattr(struct vnop_setattr_args *ap) * NOTE: HFS COMPRESSION depends on the data_size being set *before* the bsd flags are updated */ VATTR_SET_SUPPORTED(vap, va_data_size); - if (VATTR_IS_ACTIVE(vap, va_data_size) && !vnode_islnk(vp)) { + if (VATTR_IS_ACTIVE(vap, va_data_size)) { + if (!vnode_isreg(vp)) { + if (vnode_isdir(vp)) { + return EISDIR; + } + //otherwise return EINVAL + return EINVAL; + } + #if HFS_COMPRESSION /* keep the compressed state locked until we're done truncating the file */ decmpfs_cnode *dp = VTOCMP(vp); @@ -3874,7 +3882,8 @@ hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, * The resource fork vnode & filefork did not exist. * Create a temporary one for use in this function only. */ - temp_rsrc_fork = hfs_mallocz(sizeof(struct filefork)); + temp_rsrc_fork = hfs_zalloc(HFS_FILEFORK_ZONE); + bzero(temp_rsrc_fork, sizeof(struct filefork)); temp_rsrc_fork->ff_cp = cp; rl_init(&temp_rsrc_fork->ff_invalidranges); } @@ -3886,7 +3895,7 @@ hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, error = cat_lookup (hfsmp, &desc, 1, 0, (struct cat_desc*) NULL, (struct cat_attr*) NULL, &temp_rsrc_fork->ff_data, NULL); if (error) { - hfs_free(temp_rsrc_fork, sizeof(struct filefork)); + hfs_zfree(temp_rsrc_fork, HFS_FILEFORK_ZONE); hfs_systemfile_unlock (hfsmp, lockflags); goto out; } @@ -3895,7 +3904,7 @@ hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, if (!skip_reserve) { if ((error = cat_preflight(hfsmp, CAT_DELETE, NULL, 0))) { if (temp_rsrc_fork) { - hfs_free(temp_rsrc_fork, sizeof(struct filefork)); + hfs_zfree(temp_rsrc_fork, HFS_FILEFORK_ZONE); } hfs_systemfile_unlock(hfsmp, lockflags); goto out; @@ -3924,7 +3933,7 @@ hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, if (error) { if (temp_rsrc_fork) { - hfs_free(temp_rsrc_fork, sizeof(struct filefork)); + hfs_zfree(temp_rsrc_fork, HFS_FILEFORK_ZONE); } goto out; } @@ -3997,7 +4006,7 @@ hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, /* Get rid of the temporary rsrc fork */ if (temp_rsrc_fork) { - hfs_free(temp_rsrc_fork, sizeof(struct filefork)); + hfs_zfree(temp_rsrc_fork, HFS_FILEFORK_ZONE); } cp->c_flag |= C_NOEXISTS; @@ -5179,7 +5188,10 @@ hfs_vnop_symlink(struct vnop_symlink_args *ap) return (EINVAL); hfsmp = VTOHFS(dvp); + len = strlen(ap->a_target); + if (len > MAXPATHLEN) + return (ENAMETOOLONG); /* Check for free space */ if (((u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize) < len) { diff --git a/fs/Info.plist b/fs/Info.plist index 18bcc30..c8a77b1 100644 --- a/fs/Info.plist +++ b/fs/Info.plist @@ -161,6 +161,12 @@ -n FSVerificationExecutable fsck_hfs + FSfileObjectsAreCasePreserving + + FSfileObjectsAreCaseSensitive + + FSvolumeNameIsCasePreserving + HFS+ @@ -192,6 +198,12 @@ -fn FSVerificationExecutable fsck_hfs + FSfileObjectsAreCasePreserving + + FSfileObjectsAreCaseSensitive + + FSvolumeNameIsCasePreserving + Journaled HFS+ @@ -227,6 +239,12 @@ -l FSCoreStorageEncryptionName Mac OS Extended (Journaled, Encrypted) + FSfileObjectsAreCasePreserving + + FSfileObjectsAreCaseSensitive + + FSvolumeNameIsCasePreserving + Case-sensitive HFS+ @@ -258,6 +276,12 @@ -fn FSVerificationExecutable fsck_hfs + FSfileObjectsAreCasePreserving + + FSfileObjectsAreCaseSensitive + + FSvolumeNameIsCasePreserving + Case-sensitive Journaled HFS+ @@ -293,6 +317,12 @@ -l FSCoreStorageEncryptionName Mac OS Extended (Case-sensitive, Journaled, Encrypted) + FSfileObjectsAreCasePreserving + + FSfileObjectsAreCaseSensitive + + FSvolumeNameIsCasePreserving + diff --git a/fsck_hfs/dfalib/BlockCache.c b/fsck_hfs/dfalib/BlockCache.c index cb58f89..132139e 100644 --- a/fsck_hfs/dfalib/BlockCache.c +++ b/fsck_hfs/dfalib/BlockCache.c @@ -285,7 +285,9 @@ ReadFragmentedBlock (SFCB *file, UInt32 blockNum, BlockDescriptor *block) if (result) goto ErrorExit; if (bufs[i]->Length != fragSize) { - plog("ReadFragmentedBlock: cache failure (Length != fragSize)\n"); + if (debug) { + plog("ReadFragmentedBlock: cache failure (Length != fragSize)\n"); + } result = -1; goto ErrorExit; } @@ -337,7 +339,9 @@ WriteFragmentedBlock( SFCB *file, BlockDescriptor *block, int age, uint32_t writ bufEnd = buffer + file->fcbBlockSize; if (bufs == NULL) { - plog("WriteFragmentedBlock: NULL bufs list!\n"); + if (debug) { + plog("WriteFragmentedBlock: NULL bufs list!\n"); + } return (-1); } @@ -384,7 +388,9 @@ ReleaseFragmentedBlock (SFCB *file, BlockDescriptor *block, int age) bufs = (Buf_t **) block->blockHeader; if (bufs == NULL) { - plog("ReleaseFragmentedBlock: NULL buf list!\n"); + if (debug) { + plog("ReleaseFragmentedBlock: NULL buf list!\n"); + } return (-1); } diff --git a/fsck_hfs/dfalib/CatalogCheck.c b/fsck_hfs/dfalib/CatalogCheck.c index ae0c983..beb332f 100644 --- a/fsck_hfs/dfalib/CatalogCheck.c +++ b/fsck_hfs/dfalib/CatalogCheck.c @@ -1035,7 +1035,8 @@ CheckFile(const HFSPlusCatalogKey * key, const HFSPlusCatalogFile * file) uint8_t *dataBuffer = malloc(file->dataFork.totalBlocks * gScavGlobals->calculatedVCB->vcbBlockSize + 1); if (dataBuffer == NULL) { - plog("Unable to allocate %llu bytes for reading symlink", file->dataFork.logicalSize); + if (debug) + plog("Unable to allocate %llu bytes for reading symlink", file->dataFork.logicalSize); } else { char *curPtr = (char*)dataBuffer; size_t nread = 0; diff --git a/fsck_hfs/dfalib/SDevice.c b/fsck_hfs/dfalib/SDevice.c index d1c7d1f..c8225a6 100644 --- a/fsck_hfs/dfalib/SDevice.c +++ b/fsck_hfs/dfalib/SDevice.c @@ -47,12 +47,12 @@ OSErr GetDeviceSize(int driveRefNum, UInt64 *numBlocks, UInt32 *blockSize) int devBlockSize = 0; if (ioctl(driveRefNum, DKIOCGETBLOCKCOUNT, &devBlockCount) < 0) { - plog("ioctl(DKIOCGETBLOCKCOUNT) for fd %d: %s\n", driveRefNum, strerror(errno)); + if (debug) plog("ioctl(DKIOCGETBLOCKCOUNT) for fd %d: %s\n", driveRefNum, strerror(errno)); return (-1); } if (ioctl(driveRefNum, DKIOCGETBLOCKSIZE, &devBlockSize) < 0) { - plog("ioctl(DKIOCGETBLOCKSIZE) for fd %d: %s\n", driveRefNum, strerror(errno)); + if (debug) plog("ioctl(DKIOCGETBLOCKSIZE) for fd %d: %s\n", driveRefNum, strerror(errno)); return (-1); } @@ -193,7 +193,7 @@ OSErr DeviceRead(int device, int drive, void* buffer, SInt64 offset, UInt32 reqB seek_off = lseek(device, offset, SEEK_SET); if (seek_off == -1) { - plog("# DeviceRead: lseek(%qd) failed with %d\n", offset, errno); + if (debug) plog("# DeviceRead: lseek(%qd) failed with %d\n", offset, errno); return (errno); } @@ -201,7 +201,7 @@ OSErr DeviceRead(int device, int drive, void* buffer, SInt64 offset, UInt32 reqB if (nbytes == -1) return (errno); if (nbytes == 0) { - plog("CANNOT READ: BLK %ld\n", (long)offset/512); + if (debug) plog("CANNOT READ: BLK %ld\n", (long)offset/512); return (5); } @@ -245,7 +245,7 @@ OSErr DeviceWrite(int device, int drive, void* buffer, SInt64 offset, UInt32 req seek_off = lseek(device, offset, SEEK_SET); if (seek_off == -1) { - plog("# DeviceRead: lseek(%qd) failed with %d\n", offset, errno); + if (debug) plog("# DeviceRead: lseek(%qd) failed with %d\n", offset, errno); return (errno); } @@ -254,7 +254,7 @@ OSErr DeviceWrite(int device, int drive, void* buffer, SInt64 offset, UInt32 req return (errno); } if (nbytes == 0) { - plog("CANNOT WRITE: BLK %ld\n", (long)offset/512); + if (debug) plog("CANNOT WRITE: BLK %ld\n", (long)offset/512); return (5); } diff --git a/fsck_hfs/dfalib/SRebuildBTree.c b/fsck_hfs/dfalib/SRebuildBTree.c index 79ef408..9951168 100755 --- a/fsck_hfs/dfalib/SRebuildBTree.c +++ b/fsck_hfs/dfalib/SRebuildBTree.c @@ -27,12 +27,12 @@ Written by: Jerry Cottingham - Copyright: © 1986, 1990, 1992-2002 by Apple Computer, Inc., all rights reserved. + Copyright: � 1986, 1990, 1992-2002 by Apple Computer, Inc., all rights reserved. */ #define SHOW_ELAPSED_TIMES 0 -#define DEBUG_REBUILD 1 +#define DEBUG_REBUILD 0 extern void MyIndirectLog(const char *); diff --git a/fsck_hfs/dfalib/SRepair.c b/fsck_hfs/dfalib/SRepair.c index 4a80776..f1d32cd 100644 --- a/fsck_hfs/dfalib/SRepair.c +++ b/fsck_hfs/dfalib/SRepair.c @@ -2899,7 +2899,7 @@ static OSErr FixBadExtent(SGlobPtr GPtr, RepairOrderPtr p) /* Lookup record in catalog BTree */ err = GetCatalogRecord(GPtr, fileID, isHFSPlus, &catKey, &catRecord, &recordSize); if (err) { - plog("%s: Could not get catalog record for fileID %u\n", __FUNCTION__, fileID); + if (debug) plog("%s: Could not get catalog record for fileID %u\n", __FUNCTION__, fileID); goto out; } @@ -2935,7 +2935,7 @@ static OSErr FixBadExtent(SGlobPtr GPtr, RepairOrderPtr p) err = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, kNoHint, &catRecord, recordSize, &hint); if (err) { - plog("%s: Could not replace catalog record for fileID %u\n", __FUNCTION__, fileID); + if (debug) plog("%s: Could not replace catalog record for fileID %u\n", __FUNCTION__, fileID); goto out; } didRepair = true; @@ -2952,7 +2952,8 @@ static OSErr FixBadExtent(SGlobPtr GPtr, RepairOrderPtr p) err = SearchBTreeRecord (GPtr->calculatedExtentsFCB, &extentKey, kNoHint, &extentKey, &extentRecord, &recordSize, &hint); if (err) { - plog("%s: Could not get overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__, fileID, forkType, extentStartBlock); + if (debug) plog("%s: Could not get overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__, fileID, forkType, + extentStartBlock); goto out; } @@ -2973,7 +2974,8 @@ static OSErr FixBadExtent(SGlobPtr GPtr, RepairOrderPtr p) err = ReplaceBTreeRecord(GPtr->calculatedExtentsFCB, &extentKey, hint, &extentRecord, recordSize, &hint); if (err) { - plog("%s: Could not replace overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__, fileID, forkType, extentStartBlock); + if (debug) plog("%s: Could not replace overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__, fileID, + forkType, extentStartBlock); goto out; } didRepair = true; @@ -3599,7 +3601,7 @@ static OSErr GetCatalogRecord(SGlobPtr GPtr, UInt32 fileID, Boolean isHFSPlus, C BuildCatalogKey(fileID, NULL, isHFSPlus, &catThreadKey); err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catThreadKey, kNoHint, catKey, catRecord, recordSize, &hint); if (err) { - plog ("%s: No matching catalog thread record found\n", __FUNCTION__); + if (debug) plog ("%s: No matching catalog thread record found\n", __FUNCTION__); goto out; } @@ -3625,7 +3627,7 @@ static OSErr GetCatalogRecord(SGlobPtr GPtr, UInt32 fileID, Boolean isHFSPlus, C BuildCatalogKey(catRecord->hfsPlusThread.parentID, &catalogName, isHFSPlus, catKey); err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, catKey, kNoHint, catKey, catRecord, recordSize, &hint); if (err) { - plog ("%s: No matching catalog record found\n", __FUNCTION__); + if (debug) plog ("%s: No matching catalog record found\n", __FUNCTION__); if (cur_debug_level & d_dump_record) { plog ("Searching for key:\n"); diff --git a/fsck_hfs/dfalib/SVerify2.c b/fsck_hfs/dfalib/SVerify2.c index 4280b9c..1249cf7 100644 --- a/fsck_hfs/dfalib/SVerify2.c +++ b/fsck_hfs/dfalib/SVerify2.c @@ -27,7 +27,7 @@ Version: xxx put version here xxx - Copyright: © 1997-1999 by Apple Computer, Inc., all rights reserved. + Copyright: � 1997-1999 by Apple Computer, Inc., all rights reserved. */ #include @@ -1307,8 +1307,9 @@ static int BTKeyChk( SGlobPtr GPtr, NodeDescPtr nodeP, BTreeControlBlock *btcb ) } else { - RcdError( GPtr, E_KeyOrd ); - plog("Records %d and %d (0-based); offsets 0x%04X and 0x%04X\n", index-1, index, (long)prevkeyP - (long)nodeP, (long)keyPtr - (long)nodeP); + RcdError( GPtr, E_KeyOrd ); + if (fsckGetVerbosity(GPtr->context) > 0) + plog("Records %d and %d (0-based); offsets 0x%04X and 0x%04X\n", index-1, index, (long)prevkeyP - (long)nodeP, (long)keyPtr - (long)nodeP); result = E_KeyOrd; } } diff --git a/fsck_hfs/dfalib/fsck_journal.c b/fsck_hfs/dfalib/fsck_journal.c index 6c5b0e4..d144106 100644 --- a/fsck_hfs/dfalib/fsck_journal.c +++ b/fsck_hfs/dfalib/fsck_journal.c @@ -506,8 +506,11 @@ journal_open(int jfd, * XXX * I am not sure about this; this behaviour is not in TN1150 at all, * but I _think_ this is what the kernel is doing. - */ - plog("Journal sequence number is 0, is going into the end okay?\n"); + */ +#if DEBUG_JOURNAL + if (debug) + plog("Journal sequence number is 0, is going into the end okay?\n"); +#endif } into_the_weeds = 1; #if DEBUG_JOURNAL diff --git a/fsck_hfs/dfalib/hfs_endian.c b/fsck_hfs/dfalib/hfs_endian.c index 74c66e6..8fdd66d 100755 --- a/fsck_hfs/dfalib/hfs_endian.c +++ b/fsck_hfs/dfalib/hfs_endian.c @@ -202,13 +202,13 @@ hfs_swap_BTNode ( #ifdef ENDIAN_DEBUG if (direction == kSwapBTNodeBigToHost) { - plog ("BE -> Native Swap\n"); + if (debug) plog ("BE -> Native Swap\n"); } else if (direction == kSwapBTNodeHostToBig) { - plog ("Native -> BE Swap\n"); + if (debug) plog ("Native -> BE Swap\n"); } else if (direction == kSwapBTNodeHeaderRecordOnly) { - plog ("Not swapping descriptors\n"); + if (debug) plog ("Not swapping descriptors\n"); } else { - plog ("hfs_swap_BTNode: This is impossible"); + if (debug) plog ("hfs_swap_BTNode: This is impossible"); exit(99); } #endif @@ -272,7 +272,8 @@ hfs_swap_BTNode ( * This is why we allow the record offset to be zero. */ if ((srcOffs[i] & 1) || (srcOffs[i] < sizeof(BTNodeDescriptor) && srcOffs[i] != 0) || (srcOffs[i] >= src->blockSize)) { - if (debug) plog("hfs_swap_BTNode: record #%d invalid offset (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]); + if (debug) plog("hfs_swap_BTNode: offset #%d invalid (0x%04X) (blockSize 0x%x numRecords %d)\n", + i, srcOffs[i], src->blockSize, srcDesc->numRecords); WriteError(fcb->fcbVolume->vcbGPtr, E_BadNode, fcb->fcbFileID, src->blockNum); error = E_BadNode; goto fail; @@ -284,7 +285,7 @@ hfs_swap_BTNode ( */ if ((i != 0) && (srcOffs[i] >= srcOffs[i-1])) { if (debug) plog("hfs_swap_BTNode: offsets %d and %d out of order (0x%04X, 0x%04X)\n", - srcDesc->numRecords-i-1, srcDesc->numRecords-i, srcOffs[i], srcOffs[i-1]); + i, i-1, srcOffs[i], srcOffs[i-1]); WriteError(fcb->fcbVolume->vcbGPtr, E_BadNode, fcb->fcbFileID, src->blockNum); error = E_BadNode; goto fail; @@ -395,7 +396,8 @@ hfs_swap_BTNode ( * This is why we allow the record offset to be zero. */ if ((srcOffs[i] & 1) || (srcOffs[i] < sizeof(BTNodeDescriptor) && srcOffs[i] != 0) || (srcOffs[i] >= src->blockSize)) { - if (debug) plog("hfs_UNswap_BTNode: record #%d invalid offset (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]); + if (debug) plog("hfs_UNswap_BTNode: offset #%d invalid (0x%04X) (blockSize 0x%x numRecords %d)\n", + i, srcOffs[i], src->blockSize, srcDesc->numRecords); WriteError(fcb->fcbVolume->vcbGPtr, E_BadNode, fcb->fcbFileID, src->blockNum); error = E_BadNode; goto fail; @@ -407,7 +409,7 @@ hfs_swap_BTNode ( */ if ((i < srcDesc->numRecords) && (srcOffs[i+1] >= srcOffs[i])) { if (debug) plog("hfs_UNswap_BTNode: offsets %d and %d out of order (0x%04X, 0x%04X)\n", - srcDesc->numRecords-i-2, srcDesc->numRecords-i-1, srcOffs[i+1], srcOffs[i]); + i+1, i, srcOffs[i+1], srcOffs[i]); WriteError(fcb->fcbVolume->vcbGPtr, E_BadNode, fcb->fcbFileID, src->blockNum); error = E_BadNode; goto fail; @@ -860,7 +862,7 @@ hfs_swap_HFSPlusBTInternalNode ( srcRec->recordType = SWAP_BE32(srcRec->recordType); } } else { - plog("hfs_swap_HFSPlusBTInternalNode: fileID %u is not a system B-tree\n", fileID); + if (debug) plog("hfs_swap_HFSPlusBTInternalNode: fileID %u is not a system B-tree\n", fileID); exit(99); } @@ -1111,7 +1113,7 @@ hfs_swap_HFSBTInternalNode ( } } else { - plog("hfs_swap_HFSBTInternalNode: fileID %u is not a system B-tree\n", fileID); + if (debug) plog("hfs_swap_HFSBTInternalNode: fileID %u is not a system B-tree\n", fileID); exit(99); } diff --git a/hfs.xcodeproj/project.pbxproj b/hfs.xcodeproj/project.pbxproj index 71a9ec7..ef8da41 100644 --- a/hfs.xcodeproj/project.pbxproj +++ b/hfs.xcodeproj/project.pbxproj @@ -136,6 +136,7 @@ 0703A0541CD826160035BCFD /* test-defrag.c in Sources */ = {isa = PBXBuildFile; fileRef = 0703A0531CD826160035BCFD /* test-defrag.c */; }; 07C2BF891CB43F5E00D8327D /* test-renamex.c in Sources */ = {isa = PBXBuildFile; fileRef = 07C2BF881CB43F5E00D8327D /* test-renamex.c */; }; 2A386A3B1C22209C007FEDAC /* test-list-ids.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A386A3A1C221E67007FEDAC /* test-list-ids.c */; }; + 2A84DBD41D9E15F2007964B8 /* test-raw-dev-unaligned.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A84DBD31D9E1179007964B8 /* test-raw-dev-unaligned.c */; }; 2A9399951BDFEB5200FB075B /* test-access.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A9399941BDFEA6E00FB075B /* test-access.c */; }; 2A9399981BDFF7E500FB075B /* test-chflags.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A9399961BDFEF3900FB075B /* test-chflags.c */; }; 2A93999D1BE0146E00FB075B /* test-class-roll.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A93999B1BE0146000FB075B /* test-class-roll.c */; }; @@ -718,6 +719,7 @@ 0703A0531CD826160035BCFD /* test-defrag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-defrag.c"; sourceTree = ""; }; 07C2BF881CB43F5E00D8327D /* test-renamex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-renamex.c"; sourceTree = ""; }; 2A386A3A1C221E67007FEDAC /* test-list-ids.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-list-ids.c"; sourceTree = ""; }; + 2A84DBD31D9E1179007964B8 /* test-raw-dev-unaligned.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-raw-dev-unaligned.c"; sourceTree = ""; }; 2A9399941BDFEA6E00FB075B /* test-access.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-access.c"; sourceTree = ""; }; 2A9399961BDFEF3900FB075B /* test-chflags.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-chflags.c"; sourceTree = ""; }; 2A93999B1BE0146000FB075B /* test-class-roll.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-class-roll.c"; sourceTree = ""; }; @@ -1383,6 +1385,7 @@ FB55AE551B7CD89600701D03 /* cases */ = { isa = PBXGroup; children = ( + 2A84DBD31D9E1179007964B8 /* test-raw-dev-unaligned.c */, 07C2BF881CB43F5E00D8327D /* test-renamex.c */, 2A386A3A1C221E67007FEDAC /* test-list-ids.c */, 2A9399D41BE2C14800FB075B /* test-unicode-file-names.c */, @@ -2455,6 +2458,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 2A84DBD41D9E15F2007964B8 /* test-raw-dev-unaligned.c in Sources */, 2A386A3B1C22209C007FEDAC /* test-list-ids.c in Sources */, 2ABDCEA71BF3DAA100CFC70C /* test-journal-toggle.c in Sources */, FBE1B1D41BD6E41D00CEB443 /* test-move-data-extents.c in Sources */, diff --git a/tests/cases/test-hard-links.m b/tests/cases/test-hard-links.m index d38c669..5702561 100644 --- a/tests/cases/test-hard-links.m +++ b/tests/cases/test-hard-links.m @@ -167,7 +167,7 @@ static void run(void) ssize_t res; do { res = fsgetpath(path, sizeof(path), &sfs.f_fsid, (uint64_t)sb.st_ino); - } while (res != -1 || errno == EAGAIN); + } while (res != -1); assert_with_errno(errno == ENOENT); }); diff --git a/tests/cases/test-raw-dev-unaligned.c b/tests/cases/test-raw-dev-unaligned.c new file mode 100644 index 0000000..8e5bfd7 --- /dev/null +++ b/tests/cases/test-raw-dev-unaligned.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hfs-tests.h" +#include "test-utils.h" +#include "disk-image.h" + +TEST(raw_dev_unaligned, .run_as_root = 1); + +int run_raw_dev_unaligned(__unused test_ctx_t *ctx) +{ + struct statfs sfs; + + assert(statfs("/tmp", &sfs) == 0); + if (strcmp(sfs.f_fstypename, "hfs")) { + printf("raw_dev_aligned needs hfs as root file system - skipping.\n"); + return 0; + } + + int fd = open("/tmp/raw-dev-unaligned", O_CREAT | O_RDWR | O_TRUNC, 0666); + + assert_with_errno(fd >= 0); + + assert_no_err(ftruncate(fd, 4096)); + + assert_no_err(fcntl(fd, F_FULLFSYNC)); + + struct log2phys l2p = {}; + + assert_no_err(fcntl(fd, F_LOG2PHYS_EXT, &l2p)); + + assert(!strncmp(sfs.f_mntfromname, "/dev/disk", 9)); + + char *rdev; + asprintf(&rdev, "/dev/rdisk%s", sfs.f_mntfromname + 9); + + int raw_fd; + assert_with_errno((raw_fd = open(rdev, O_RDWR)) >= 0); + + void *buf = malloc(16384); + + off_t offset = l2p.l2p_devoffset; + + // Make p non-aligned + char *p = (char *)((((uintptr_t)buf + 64) & ~63) + 8); + + unsigned bs = 4096; + if (bs < sfs.f_bsize) + bs = sfs.f_bsize; + + check_io(pread(raw_fd, p, bs, offset), bs); + + // Make a change + check_io(pwrite(fd, &fd, 4, 0), 4); + + assert_no_err(fcntl(fd, F_FULLFSYNC)); + + char *state = malloc(bs); + + /* + * Make sure it changed on the raw device so we know we've got the + * correct location. We can't actually check the contents because + * it's encrypted on iOS. + */ + check_io(pread(raw_fd, state, bs, offset), bs); + + assert(memcmp(state, p, bs)); + + assert_no_err(close(fd)); + + for (int i = 0; i < 3000; ++i) { + for (unsigned i = 0; i < bs; ++i) + state[i] ^= 0xff; + memcpy(p, state, bs); + check_io(pwrite(raw_fd, p, bs, offset), bs); + check_io(pread(raw_fd, p, bs, offset), bs); + assert(!memcmp(p, state, bs)); + } + + // cleanup + assert_no_err(close(raw_fd)); + assert_no_err(unlink("/tmp/raw-dev-unaligned")); + free(buf); + free(state); + + return 0; +} diff --git a/tests/cases/test-throttled-io.c b/tests/cases/test-throttled-io.c index 803c0b7..2d38181 100644 --- a/tests/cases/test-throttled-io.c +++ b/tests/cases/test-throttled-io.c @@ -20,55 +20,72 @@ TEST(throttled_io) -static disk_image_t *di; -static char *file1, *file2, *file3; +static disk_image_t *gDI; +static char *gFile1, *gFile2, *gFile3; -static pid_t pid; -static void *buf; -static const size_t buf_size = 64 * 1024 * 1024; +static pid_t gPID = 0; +static void *gBuf; +static const size_t gBuf_size = 64 * 1024 * 1024; -static int run_test1(void) +static void start_background_io(void) { char *of; - asprintf(&of, "of=%s", file1); + asprintf(&of, "of=%s", gFile1); + assert_no_err(posix_spawn(&gPID, "/bin/dd", NULL, NULL, + (char *[]){ "/bin/dd", "if=/dev/random", + of, NULL }, + NULL)); +} + +static void end_background_io(void) +{ + if ( gPID != 0 ) + { + kill(gPID, SIGKILL); + int stat; + wait(&stat); + gPID = 0; + } +} + +static int run_test1(void) +{ + // Kick off another process to ensure we get throttled - assert_no_err(posix_spawn(&pid, "/bin/dd", NULL, NULL, - (char *[]){ "/bin/dd", "if=/dev/random", - of, NULL }, - NULL)); + start_background_io(); assert_no_err(setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE)); int fd, fd2; assert_with_errno((fd = open("/dev/random", O_RDONLY)) >= 0); - assert_with_errno((fd2 = open(file2, + assert_with_errno((fd2 = open(gFile2, O_RDWR | O_CREAT | O_TRUNC, 0666)) >= 0); assert_no_err(fcntl(fd2, F_SINGLE_WRITER, 1)); assert_no_err(fcntl(fd2, F_NOCACHE, 1)); - buf = valloc(buf_size); + gBuf = valloc(gBuf_size); CC_SHA1_CTX ctx; CC_SHA1_Init(&ctx); - ssize_t res = check_io(read(fd, buf, buf_size), buf_size); + ssize_t res = check_io(read(fd, gBuf, gBuf_size), gBuf_size); - CC_SHA1_Update(&ctx, buf, (CC_LONG)res); + CC_SHA1_Update(&ctx, gBuf, (CC_LONG)res); - res = check_io(write(fd2, buf, res), res); + res = check_io(write(fd2, gBuf, res), res); - bzero(buf, buf_size); + bzero(gBuf, gBuf_size); CC_SHA1_CTX ctx2; CC_SHA1_Init(&ctx2); lseek(fd2, 0, SEEK_SET); - res = check_io(read(fd2, buf, buf_size), buf_size); + res = check_io(read(fd2, gBuf, gBuf_size), gBuf_size); - CC_SHA1_Update(&ctx2, buf, (CC_LONG)res); + CC_SHA1_Update(&ctx2, gBuf, (CC_LONG)res); uint8_t digest1[CC_SHA1_DIGEST_LENGTH], digest2[CC_SHA1_DIGEST_LENGTH]; CC_SHA1_Final(digest1, &ctx); @@ -76,6 +93,11 @@ static int run_test1(void) assert(!memcmp(digest1, digest2, CC_SHA1_DIGEST_LENGTH)); + assert_no_err (close(fd)); + assert_no_err (close(fd2)); + + end_background_io(); + return 0; } @@ -84,10 +106,10 @@ static volatile bool done; static void test2_thread(void) { - int fd = open(file3, O_RDONLY); + int fd = open(gFile3, O_RDONLY); assert(fd >= 0); - void *b = buf + buf_size / 2; + void *b = gBuf + gBuf_size / 2; uLong seq = crc32(0, Z_NULL, 0); uint32_t offset = 0; @@ -95,7 +117,7 @@ static void test2_thread(void) ssize_t res; do { - res = check_io(pread(fd, b, buf_size / 2, offset), -1); + res = check_io(pread(fd, b, gBuf_size / 2, offset), -1); } while (res == 0 && !done); assert (res % 4 == 0); @@ -111,11 +133,16 @@ static void test2_thread(void) continue; OSMemoryBarrier(); } while (!done); + + assert_no_err (close(fd)); + } static int run_test2(void) { - int fd = open(file3, O_RDWR | O_CREAT | O_TRUNC, 0666); + start_background_io(); + + int fd = open(gFile3, O_RDWR | O_CREAT | O_TRUNC, 0666); assert(fd >= 0); assert_no_err(fcntl(fd, F_SINGLE_WRITER, 1)); @@ -126,13 +153,13 @@ static int run_test2(void) uLong seq = crc32(0, Z_NULL, 0); for (int i = 0; i < 4; ++i) { - uLong *p = buf; - for (unsigned i = 0; i < buf_size / 2 / sizeof(uLong); ++i) { + uLong *p = gBuf; + for (unsigned i = 0; i < gBuf_size / 2 / sizeof(uLong); ++i) { seq = crc32(Z_NULL, (void *)&seq, 4); p[i] = seq; } - ssize_t res = check_io(write(fd, buf, buf_size / 2), buf_size / 2); + ssize_t res = check_io(write(fd, gBuf, gBuf_size / 2), gBuf_size / 2); written += res; } @@ -143,45 +170,47 @@ static int run_test2(void) pthread_join(thread, NULL); + assert_no_err (close(fd)); + + end_background_io(); + return 0; } static bool clean_up(void) { - kill(pid, SIGKILL); - int stat; - wait(&stat); + end_background_io(); - unlink(file1); - unlink(file2); - unlink(file3); + unlink(gFile1); + unlink(gFile2); + unlink(gFile3); + + free(gFile1); + free(gFile2); + free(gFile3); return true; } int run_throttled_io(__unused test_ctx_t *ctx) { + gDI = disk_image_get(); + + asprintf(&gFile1, "%s/throttled_io.1", gDI->mount_point); + asprintf(&gFile2, "%s/throttled_io.2", gDI->mount_point); + asprintf(&gFile3, "%s/throttled_io.3", gDI->mount_point); + test_cleanup(^ bool { return clean_up(); }); - di = disk_image_get(); - asprintf(&file1, "%s/throttled_io.1", di->mount_point); - asprintf(&file2, "%s/throttled_io.2", di->mount_point); - asprintf(&file3, "%s/throttled_io.3", di->mount_point); - int res = run_test1(); if (res) - goto out; - + return res; + res = run_test2(); if (res) - goto out; - -out: - free(file1); - free(file2); - free(file3); + return res; return res; } diff --git a/tests/hfs-tests.entitlements b/tests/hfs-tests.entitlements index 9b864ae..994efb8 100644 --- a/tests/hfs-tests.entitlements +++ b/tests/hfs-tests.entitlements @@ -2,6 +2,10 @@ + com.apple.rootless.internal-installer-equivalent + + com.apple.private.security.disk-device-access + com.apple.keystore.device com.apple.managedconfiguration.mdmd-access diff --git a/tests/hfs-tests.mm b/tests/hfs-tests.mm index 054d1f6..7ece91a 100644 --- a/tests/hfs-tests.mm +++ b/tests/hfs-tests.mm @@ -217,7 +217,7 @@ int main(int argc, char *argv[]) else { test_t *test = it->second; - int ret = systemx(progname, "--test", test->name, "--no-spawn", "run", NULL); + ret = systemx(progname, "--test", test->name, "--no-spawn", "run", NULL); if (ret) std::cout << "[FAIL] " << test->name << std::endl; -- 2.45.2