{
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) {
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;
* 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;
}
}
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;
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;
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);
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;
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);
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);
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;
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;
}
errx(kBadExit, "usage: %s [-vdpS] [-g gatherFile] [-C] [-r <bytes>] <src device> <destination>", progname);
}
-
+int
main(int ac, char **av)
{
char *src = NULL;
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;
* 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);
#include <kern/locks.h>
#include <vm/vm_kern.h>
#include <sys/sysctl.h>
+#include <uuid/uuid.h>
#include "../hfs_encodings/hfs_encodings.h"
// 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 */
#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 */
};
// 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;
/*
/*****************************************************************************
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);
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);
}
#define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
-#define MAX_LINKINFO_ENTRIES 3000
/*
* Callback to pack directory entries.
}
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
* 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) {
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.
*/
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
lck_mtx_destroy(&hfsmp->hfs_chash_mutex, chash_lck_grp);
FREE(hfsmp->hfs_cnodehashtbl, M_TEMP);
-
- hfs_cnode_zone_free(hfsmp);
}
* 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;
}
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);
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);
-}
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);
}
/*
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;
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));
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
(void)hfsmp; // Prevent compiler warning
#endif
- hfs_cnode_free(hfsmp, cp);
+ hfs_zfree(cp, HFS_CNODE_ZONE);
}
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.
*
#include <sys/kauth.h>
#include <sys/sysctl.h>
#include <sys/ubc.h>
+#include <uuid/uuid.h>
#include "hfs.h"
#include "hfs_cnode.h"
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;
(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;
}
*/
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;
}
((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;
}
*/
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;
}
return false;
}
+ hfs_init_zones();
+
hfs_sysctl_register();
return true;
#include <sys/fsctl.h>
#include <sys/ubc.h>
#include <sys/fsevents.h>
+#include <uuid/uuid.h>
#include <libkern/OSDebug.h>
}
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;
}
} 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));
* 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 );
}
/*
/* for parsing boot-args */
#include <pexpert/pexpert.h>
#include <kern/kalloc.h>
+#include <kern/zalloc.h>
#include "hfs_iokit.h"
#include "hfs.h"
/* 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
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 */
relhint->dh_desc.cd_flags &= ~CD_HASBUF;
vfs_removename((const char *)name);
}
- hfs_free(relhint, sizeof(*relhint));
+ hfs_zfree(relhint, HFS_DIRHINT_ZONE);
}
/*
}
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;
}
}
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
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;
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);
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
}
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)
* 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);
* 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);
}
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;
}
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;
if (error) {
if (temp_rsrc_fork) {
- hfs_free(temp_rsrc_fork, sizeof(struct filefork));
+ hfs_zfree(temp_rsrc_fork, HFS_FILEFORK_ZONE);
}
goto out;
}
/* 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;
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) {
<string>-n</string>
<key>FSVerificationExecutable</key>
<string>fsck_hfs</string>
+ <key>FSfileObjectsAreCasePreserving</key>
+ <true/>
+ <key>FSfileObjectsAreCaseSensitive</key>
+ <false/>
+ <key>FSvolumeNameIsCasePreserving</key>
+ <true/>
</dict>
<key>HFS+</key>
<dict>
<string>-fn</string>
<key>FSVerificationExecutable</key>
<string>fsck_hfs</string>
+ <key>FSfileObjectsAreCasePreserving</key>
+ <true/>
+ <key>FSfileObjectsAreCaseSensitive</key>
+ <false/>
+ <key>FSvolumeNameIsCasePreserving</key>
+ <true/>
</dict>
<key>Journaled HFS+</key>
<dict>
<string>-l</string>
<key>FSCoreStorageEncryptionName</key>
<string>Mac OS Extended (Journaled, Encrypted)</string>
+ <key>FSfileObjectsAreCasePreserving</key>
+ <true/>
+ <key>FSfileObjectsAreCaseSensitive</key>
+ <false/>
+ <key>FSvolumeNameIsCasePreserving</key>
+ <true/>
</dict>
<key>Case-sensitive HFS+</key>
<dict>
<string>-fn</string>
<key>FSVerificationExecutable</key>
<string>fsck_hfs</string>
+ <key>FSfileObjectsAreCasePreserving</key>
+ <true/>
+ <key>FSfileObjectsAreCaseSensitive</key>
+ <true/>
+ <key>FSvolumeNameIsCasePreserving</key>
+ <true/>
</dict>
<key>Case-sensitive Journaled HFS+</key>
<dict>
<string>-l</string>
<key>FSCoreStorageEncryptionName</key>
<string>Mac OS Extended (Case-sensitive, Journaled, Encrypted)</string>
+ <key>FSfileObjectsAreCasePreserving</key>
+ <true/>
+ <key>FSfileObjectsAreCaseSensitive</key>
+ <true/>
+ <key>FSvolumeNameIsCasePreserving</key>
+ <true/>
</dict>
</dict>
</dict>
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;
}
bufEnd = buffer + file->fcbBlockSize;
if (bufs == NULL) {
- plog("WriteFragmentedBlock: NULL bufs list!\n");
+ if (debug) {
+ plog("WriteFragmentedBlock: NULL bufs list!\n");
+ }
return (-1);
}
bufs = (Buf_t **) block->blockHeader;
if (bufs == NULL) {
- plog("ReleaseFragmentedBlock: NULL buf list!\n");
+ if (debug) {
+ plog("ReleaseFragmentedBlock: NULL buf list!\n");
+ }
return (-1);
}
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;
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);
}
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);
}
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);
}
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);
}
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);
}
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 *);
/* 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;
}
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;
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;
}
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;
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;
}
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");
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 <sys/ioctl.h>
}
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;
}
}
* 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
#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
* 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;
*/
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;
* 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;
*/
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;
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);
}
}
} 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);
}
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 */; };
0703A0531CD826160035BCFD /* test-defrag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-defrag.c"; sourceTree = "<group>"; };
07C2BF881CB43F5E00D8327D /* test-renamex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-renamex.c"; sourceTree = "<group>"; };
2A386A3A1C221E67007FEDAC /* test-list-ids.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-list-ids.c"; sourceTree = "<group>"; };
+ 2A84DBD31D9E1179007964B8 /* test-raw-dev-unaligned.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-raw-dev-unaligned.c"; sourceTree = "<group>"; };
2A9399941BDFEA6E00FB075B /* test-access.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-access.c"; sourceTree = "<group>"; };
2A9399961BDFEF3900FB075B /* test-chflags.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-chflags.c"; sourceTree = "<group>"; };
2A93999B1BE0146000FB075B /* test-class-roll.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-class-roll.c"; sourceTree = "<group>"; };
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 */,
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 */,
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);
});
--- /dev/null
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#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;
+}
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);
assert(!memcmp(digest1, digest2, CC_SHA1_DIGEST_LENGTH));
+ assert_no_err (close(fd));
+ assert_no_err (close(fd2));
+
+ end_background_io();
+
return 0;
}
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;
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);
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));
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;
}
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;
}
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
+ <key>com.apple.rootless.internal-installer-equivalent</key>
+ <true/>
+ <key>com.apple.private.security.disk-device-access</key>
+ <true/>
<key>com.apple.keystore.device</key>
<true/>
<key>com.apple.managedconfiguration.mdmd-access</key>
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;