summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiles Bader <miles@gnu.org>1995-10-15 16:09:01 +0000
committerMiles Bader <miles@gnu.org>1995-10-15 16:09:01 +0000
commitc3c9caac8205564b17181e2a6cbf2864d46bf58c (patch)
tree0d3e054d22c4e973b09f6335e589d63c53f18ad3
parent1b0513e15b86efd940ead554066225dd5c47ecb7 (diff)
Formerly rdwr.c.~3~
-rw-r--r--libstore/rdwr.c126
1 files changed, 111 insertions, 15 deletions
diff --git a/libstore/rdwr.c b/libstore/rdwr.c
index e14601e3..020f4f2f 100644
--- a/libstore/rdwr.c
+++ b/libstore/rdwr.c
@@ -20,14 +20,15 @@
with this program; if not, write to the Free Software Foundation, Inc.,
675 Mass Ave, Cambridge, MA 02139, USA. */
-/* 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). */
+/* Write LEN bytes from BUF to STORE at ADDR. If AMOUNT is NULL, returns EIO
+ if less than LEN bytes were written, otherwise returns the amount written
+ 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);
{
- error_t err = 0;
- size_t request_len = len;
+ error_t err = EIO; /* What happens if we run off the end */
+ size_t total_written = 0;
off_t *runs = store->runs;
unsigned runs_len = store->runs_len;
store_write_meth_t write = store->meths->write;
@@ -38,30 +39,38 @@ store_write (struct store *store,
binary search or something to find the starting run. */
while (runs_len)
{
- off_t run_offs = runs[0];
- off_t run_len = runs[1];
+ off_t run_addr = runs[0];
+ off_t run_blocks = runs[1];
- if (run_len <= addr)
+ if (run_blocks <= addr)
/* Not to the right place yet, move on... */
- addr -= run_len;
- else if (run_offs < 0)
- /* A hole! Can't write here. Must stop. */
+ addr -= run_blocks;
+ else if (run_addr < 0)
+ /* A hole! Can't write here. Must stop. If no data's been written
+ so far, ERR will still contain EIO, otherwise it will contain 0
+ and we'll just return a short write. */
break;
else
/* Ok, we can write in this run, at least a bit. */
{
size_t written, blocks_written;
- off_t end = addr + len;
+ off_t run_len = (run_blocks << block_shift);
+ off_t end = (addr << block_shift) + len;
off_t seg_len = end > run_len ? run_len - addr : len;
/* Write to the actual object at the correct address. */
- err = (*write)(store, run_offs + addr, buf, seg_len, &written);
+ err = (*write)(store, run_addr + addr, buf, seg_len, &written);
if (err)
/* Ack */
break;
- buf += written;
+ total_written += written;
+
len -= written;
+ if (len == 0)
+ break; /* All data written */
+
+ buf += written;
blocks_written = written >> block_shift;
if ((blocks_written << block_shift) != seg_amount)
@@ -75,8 +84,17 @@ store_write (struct store *store,
runs_len -= 2;
}
- if (!err)
- *amount = request_len - len;
+ if (amount)
+ /* The user wants to know about short writes. */
+ {
+ if (total_written)
+ err = 0; /* return a short write */
+ *amount = total_written;
+ }
+ else if (!err)
+ /* Since there's no way to return the amount actually written, signal an
+ error. */
+ err = EIO;
return err;
}
@@ -85,4 +103,82 @@ error_t
store_read (struct store *store,
off_t addr, size_t amount, char **buf, size_t *len)
{
+ error_t err = EIO; /* What happens if we run off the end */
+ size_t total_read = 0;
+ off_t *runs = store->runs;
+ unsigned runs_len = store->runs_len;
+ store_read_meth_t read = store->meths->read;
+ int block_shift = store->log2_block_size;
+
+ /* XXX: this isn't going to be very efficient if RUNS is very complex...
+ But it should do dandy if it's short. For long run lists, we could do a
+ binary search or something to find the starting run. */
+ while (runs_len)
+ {
+ off_t run_addr = runs[0];
+ off_t run_blocks = runs[1];
+
+ if (run_blocks <= addr)
+ /* Not to the right place yet, move on... */
+ addr -= run_blocks;
+ else if (run_addr < 0)
+ /* A hole! Can't read here. Must stop. If no data's been read
+ so far, ERR will still contain EIO, otherwise it will contain 0
+ and we'll just return a short read. */
+ break;
+ else
+ /* Ok, we can read in this run, at least a bit. */
+ {
+ size_t seg_read, blocks_read;
+ off_t run_len = (run_blocks << block_shift);
+ off_t end = (addr << block_shift) + amount;
+ off_t seg_amount = end > run_len ? run_len - addr : amount;
+
+ /* Read to the actual object at the correct address. */
+ if (total_read)
+ /* Some stuff has already been read, so we have to worry about
+ coalescing the return buffers. */
+ {
+
+ }
+ else
+ {
+ err = (*read)(store, run_addr + addr, seg_amount, buf, len);
+ if (err)
+ /* Ack */
+ break;
+ seg_read = len;
+ }
+
+ total_read += seg_read;
+
+ amount -= seg_read;
+ if (amount == 0)
+ break; /* All data read */
+
+ blocks_read = seg_read >> block_shift;
+ if ((blocks_read << block_shift) != seg_amount)
+ /* A non-block multiple amount was read!? What do we do? */
+ break;
+
+ addr += blocks_read;
+ }
+
+ runs += 2;
+ runs_len -= 2;
+ }
+
+ if (amount)
+ /* The user wants to know about short reads. */
+ {
+ if (total_read)
+ err = 0; /* return a short read!! */
+ *amount = total_read;
+ }
+ else if (!err)
+ /* Since there's no way to return the amount actually read, signal an
+ error. */
+ err = EIO;
+
+ return err;
}