aboutsummaryrefslogtreecommitdiff
path: root/libstore/rdwr.c
diff options
context:
space:
mode:
Diffstat (limited to 'libstore/rdwr.c')
-rw-r--r--libstore/rdwr.c152
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;
}
-