diff options
Diffstat (limited to 'libstore/rdwr.c')
-rw-r--r-- | libstore/rdwr.c | 152 |
1 files changed, 90 insertions, 62 deletions
diff --git a/libstore/rdwr.c b/libstore/rdwr.c index 9e9d3f84..9737c515 100644 --- a/libstore/rdwr.c +++ b/libstore/rdwr.c @@ -1,9 +1,7 @@ /* Store I/O - Copyright (C) 1995, 1996 Free Software Foundation, Inc. - - Written by Miles Bader <miles@gnu.ai.mit.edu> - + Copyright (C) 1995-1999,2001,2002,2003 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> This file is part of the GNU Hurd. The GNU Hurd is free software; you can redistribute it and/or @@ -18,9 +16,10 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., - 675 Mass Ave, Cambridge, MA 02139, USA. */ + 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ #include <string.h> +#include <sys/mman.h> #include "store.h" @@ -28,13 +27,13 @@ ADDR, and is not a hole, and in RUNS_END a pointer pointing at the end of the run list. Returns the offset within it at which ADDR occurs. Also returns BASE, which should be added to offsets from RUNS. */ -static inline off_t -store_find_first_run (struct store *store, off_t addr, +static inline store_offset_t +store_find_first_run (struct store *store, store_offset_t addr, struct store_run **run, struct store_run **runs_end, - off_t *base, size_t *index) + store_offset_t *base, size_t *index) { struct store_run *tail = store->runs, *tail_end = tail + store->num_runs; - off_t wrap_src = store->wrap_src; + store_offset_t wrap_src = store->wrap_src; if (addr >= wrap_src && addr < store->end) /* Locate the correct position within a repeating pattern of runs. */ @@ -50,7 +49,7 @@ store_find_first_run (struct store *store, off_t addr, binary search or something. */ while (tail < tail_end) { - off_t run_blocks = tail->length; + store_offset_t run_blocks = tail->length; if (run_blocks > addr) { @@ -73,7 +72,7 @@ store_find_first_run (struct store *store, off_t addr, things are still kosher. */ static inline int store_next_run (struct store *store, struct store_run *runs_end, - struct store_run **run, off_t *base, size_t *index) + struct store_run **run, store_offset_t *base, size_t *index) { (*run)++; (*index)++; @@ -94,11 +93,11 @@ store_next_run (struct store *store, struct store_run *runs_end, 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) + store_offset_t addr, const void *buf, size_t len, size_t *amount) { error_t err; size_t index; - off_t base; + store_offset_t base; struct store_run *run, *runs_end; int block_shift = store->log2_block_size; store_write_meth_t write = store->class->write; @@ -106,19 +105,27 @@ store_write (struct store *store, if (store->flags & STORE_READONLY) return EROFS; /* XXX */ + if ((addr << block_shift) + len > store->size) + return EIO; + + if (store->block_size != 0 && (len & (store->block_size - 1)) != 0) + return EINVAL; + addr = store_find_first_run (store, addr, &run, &runs_end, &base, &index); if (addr < 0) err = EIO; - else if ((run->length << block_shift) >= len) + else if ((len >> block_shift) <= run->length - addr) /* The first run has it all... */ err = (*write)(store, base + run->start + addr, index, buf, len, amount); else /* ARGH, we've got to split up the write ... */ { - mach_msg_type_number_t try = run->length << block_shift, written; + mach_msg_type_number_t try, written; /* Write the initial bit in the first run. Errors here are returned. */ - err = (*write)(store, base + run->start + addr, index, buf, try, &written); + try = (run->length - addr) << block_shift; + err = (*write) (store, base + run->start + addr, index, buf, try, + &written); if (!err && written == try) /* Wrote the first bit successfully, now do the rest. Any errors @@ -132,22 +139,25 @@ store_write (struct store *store, /* Ok, we can write in this run, at least a bit. */ { mach_msg_type_number_t seg_written; - off_t run_len = (run->length << block_shift); - try = run_len > len ? len : run_len; + if ((len >> block_shift) <= run->length) + try = len; + else + try = run->length << block_shift; + err = (*write)(store, base + run->start, index, buf, try, &seg_written); if (err) break; /* Ack */ - written += seg_written; + + if (seg_written < try) + break; /* Didn't use up the run, we're done. */ + len -= seg_written; if (len == 0) break; /* Nothing left to write! */ - if (seg_written < run_len) - break; /* Didn't use up the run, we're done. */ - buf += seg_written; } } @@ -158,54 +168,64 @@ store_write (struct store *store, return err; } -/* Read AMOUNT bytes from STORE at ADDR into BUF & LEN (which following the +/* Read AMOUNT bytes from STORE at ADDR into BUF & LEN (which follows 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) + store_offset_t addr, size_t amount, void **buf, size_t *len) { - error_t err; size_t index; - off_t base; + store_offset_t base; struct store_run *run, *runs_end; int block_shift = store->log2_block_size; store_read_meth_t read = store->class->read; addr = store_find_first_run (store, addr, &run, &runs_end, &base, &index); - if (addr < 0) - err = EIO; - else if ((run->length << block_shift) >= amount) + if (addr < 0 || run->start < 0) + return EIO; /* Reading from a hole. */ + + if ((addr << block_shift) + amount > store->size) + amount = store->size - (addr << block_shift); + + if (store->block_size != 0 && (amount & (store->block_size - 1)) != 0) + return EINVAL; + + if ((amount >> block_shift) <= run->length - addr) /* The first run has it all... */ - err = (*read)(store, base + run->start + addr, index, amount, buf, len); + return (*read) (store, base + run->start + addr, index, amount, buf, len); else /* ARGH, we've got to split up the read ... This isn't fun. */ { + error_t err; 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; + void *whole_buf = *buf, *buf_end; size_t whole_buf_len = *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) + inline error_t seg_read (store_offset_t addr, size_t len, int *all) { /* SEG_BUF and SEG_LEN are the buffer for a particular bit of the whole (within one run). */ - char *seg_buf = buf_end; + void *seg_buf = buf_end; size_t seg_buf_len = len; error_t err = (*read)(store, addr, index, len, &seg_buf, &seg_buf_len); if (!err) { /* If for some bizarre reason, the underlying storage chose not - to use the buffer space we so kindly gave it, bcopy it to + to use the buffer space we so kindly gave it, copy it to that space. */ if (seg_buf != buf_end) - bcopy (seg_buf, buf_end, seg_buf_len); + { + memcpy (buf_end, seg_buf, seg_buf_len); + munmap (seg_buf, seg_buf_len); + } buf_end += seg_buf_len; amount -= seg_buf_len; *all = (seg_buf_len == len); @@ -218,30 +238,27 @@ store_read (struct store *store, make room. */ { 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. */ + whole_buf = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + if (whole_buf == (void *) -1) + return errno; /* Punt early, there's nothing to clean up. */ } buf_end = whole_buf; - err = seg_read (base + run->start + addr, run->length << block_shift, &all); + err = seg_read (base + run->start + addr, + (run->length - addr) << block_shift, &all); while (!err && all && amount > 0 && store_next_run (store, runs_end, &run, &base, &index)) { - off_t run_addr = run->start; - off_t run_blocks = run->length; - - if (run_addr < 0) + if (run->start < 0) /* A hole! Can't read here. Must stop. */ break; else - { - off_t run_len = (run_blocks << block_shift); - off_t seg_len = run_len > amount ? amount : run_len; - err = seg_read (base + run_addr, seg_len, &all); - } + err = seg_read (base + run->start, + (amount >> block_shift) <= run->length + ? amount /* This run has the rest. */ + : (run->length << block_shift), /* Whole run. */ + &all); } /* The actual amount read. */ @@ -251,20 +268,31 @@ store_read (struct store *store, /* Deallocate any amount of WHOLE_BUF we didn't use. */ if (whole_buf != *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; - } + { + if (err) + munmap (whole_buf, whole_buf_len); + else + { + vm_size_t unused = whole_buf_len - round_page (*len); + if (unused) + munmap (whole_buf + whole_buf_len - unused, unused); + *buf = whole_buf; + } + } + + return err; } +} + +/* Set STORE's size to NEWSIZE (in bytes). */ +error_t +store_set_size (struct store *store, size_t newsize) +{ + error_t err; + store_set_size_meth_t set_size = store->class->set_size; + + /* Updating the runs list is up to the class set_size method. */ + err = (* set_size) (store, newsize); return err; } - |