diff options
Diffstat (limited to 'libstore')
-rw-r--r-- | libstore/rdwr.c | 46 |
1 files changed, 32 insertions, 14 deletions
diff --git a/libstore/rdwr.c b/libstore/rdwr.c index 931538bf..64671241 100644 --- a/libstore/rdwr.c +++ b/libstore/rdwr.c @@ -56,7 +56,7 @@ store_find_first_run (struct store *store, off_t addr, } /* Write LEN bytes from BUF to STORE at ADDR. Returns the amount written - in AMOUNT. ADDR is in BLOCKS (as defined by store->block_size). */ + in AMOUNT. ADDR is in BLOCKS (as defined by STORE->block_size). */ error_t store_write (struct store *store, off_t addr, char *buf, size_t len, size_t *amount) @@ -129,6 +129,9 @@ store_write (struct store *store, return err; } +/* Read AMOUNT bytes from STORE at ADDR into BUF & LEN (which following the + usual mach buffer-return semantics) to STORE at ADDR. ADDR is in BLOCKS + (as defined by STORE->block_size). */ error_t store_read (struct store *store, off_t addr, size_t amount, char **buf, size_t *len) @@ -144,16 +147,17 @@ store_read (struct store *store, /* The first run has it all... */ err = (*read)(store, runs[0] + addr, amount, buf, len); else - /* ARGH, we've got to split up the write ... This isn't fun. */ + /* ARGH, we've got to split up the read ... This isn't fun. */ { int all; /* WHOLE_BUF and WHOLE_BUF_LEN will point to a buff that's large enough to hold the entire request. This is initially whatever the user passed in, but we'll change it as necessary. */ char *whole_buf = *buf, *buf_end = whole_buf; - size_t whole_buf_len = *len, buf_left = whole_buf_len; + size_t whole_buf_len = *len; + int block_shift = store->log2_block_size; - /* Read LEN bytes from the store address ADDR into BUF_LEN. BUF_LEN + /* Read LEN bytes from the store address ADDR into BUF_END. BUF_END and AMOUNT are adjusted by the amount actually read. Whether or not the amount read is the same as what was request is returned in ALL. */ inline error_t seg_read (off_t addr, off_t len, int *all) @@ -177,17 +181,18 @@ store_read (struct store *store, return err; } - if (whole_buf_left < amount) + if (whole_buf_len < amount) /* Not enough room in the user's buffer to hold everything, better make room. */ { - whole_buf_left = amount; - err = vm_allocate (mach_task_self (), &whole_buf, amount, 1); + whole_buf_len = amount; + err = vm_allocate (mach_task_self (), + (vm_address_t *)&whole_buf, amount, 1); if (err) return err; /* Punt early, there's nothing to clean up. */ } - err = seg_read (runs[0] + addr, runs[1], &all); + err = seg_read (runs[0] + addr, runs[1] << block_shift, &all); if (!err && all) { @@ -198,7 +203,7 @@ store_read (struct store *store, off_t run_blocks = runs[1]; if (run_addr < 0) - /* A hole! Can't write here. Must stop. */ + /* A hole! Can't read here. Must stop. */ break; else if (amount == 0) break; @@ -213,14 +218,27 @@ store_read (struct store *store, } } - /* The actual amount written. */ - *len = whole_buf_len - amount; XXXXXXXXX + /* The actual amount read. */ + *len = whole_buf + whole_buf_len - buf_end; + if (*len > 0) + err = 0; /* Return a short read instead of an error. */ /* Deallocate any amount of WHOLE_BUF we didn't use. */ if (whole_buf != *buf) - { - *buf = whole_buf; - } + if (err) + vm_deallocate (mach_task_self (), + (vm_address_t)whole_buf, whole_buf_len); + else + { + vm_size_t unused = whole_buf_len - round_page (*len); + if (unused) + vm_deallocate (mach_task_self (), + (vm_address_t)whole_buf + whole_buf_len - unused, + unused); + *buf = whole_buf; + } } + + return err; } |