diff --git a/inc/ToastyFS.h b/inc/ToastyFS.h index 88cf923..f78fa71 100644 --- a/inc/ToastyFS.h +++ b/inc/ToastyFS.h @@ -159,6 +159,7 @@ typedef struct { ToastyResultType type; ToastyListing listing; void *user; + int bytes_read; // For read operations: actual number of bytes read } ToastyResult; // If the operation specified by "handle" is complete, diff --git a/src/client.c b/src/client.c index 4d67fc9..b49c01a 100644 --- a/src/client.c +++ b/src/client.c @@ -116,6 +116,7 @@ typedef struct { int ranges_head; int ranges_count; int num_pending; + int actual_bytes; // For reads: actual number of bytes that can be read // Write fields SHA256 *hashes; @@ -1006,12 +1007,22 @@ static void process_event_for_read(ToastyFS *toasty, return; } + // Read actual bytes that can be read + uint32_t actual_bytes; + if (!binary_read(&reader, &actual_bytes, sizeof(actual_bytes))) { + toasty->operations[opidx].result = (ToastyResult) { .type=TOASTY_RESULT_READ_ERROR, .user=toasty->operations[opidx].user }; + return; + } + + // Store actual_bytes for later use + toasty->operations[opidx].actual_bytes = actual_bytes; + // Calculate which chunks we need int off = toasty->operations[opidx].off; int len = toasty->operations[opidx].len; if (len == 0) { - toasty->operations[opidx].result = (ToastyResult) { .type=TOASTY_RESULT_READ_SUCCESS, .user=toasty->operations[opidx].user }; + toasty->operations[opidx].result = (ToastyResult) { .type=TOASTY_RESULT_READ_SUCCESS, .user=toasty->operations[opidx].user, .bytes_read=0 }; return; } @@ -1166,7 +1177,7 @@ static void process_event_for_read(ToastyFS *toasty, } else { // No chunks to download sys_free(ranges); - toasty->operations[opidx].result = (ToastyResult) { .type=TOASTY_RESULT_READ_SUCCESS, .user=toasty->operations[opidx].user }; + toasty->operations[opidx].result = (ToastyResult) { .type=TOASTY_RESULT_READ_SUCCESS, .user=toasty->operations[opidx].user, .bytes_read=toasty->operations[opidx].actual_bytes }; } } else { @@ -1241,7 +1252,7 @@ static void process_event_for_read(ToastyFS *toasty, if (toasty->operations[opidx].num_pending == 0) { sys_free(toasty->operations[opidx].ranges); toasty->operations[opidx].ranges = NULL; - toasty->operations[opidx].result = (ToastyResult) { .type=TOASTY_RESULT_READ_SUCCESS, .user=toasty->operations[opidx].user }; + toasty->operations[opidx].result = (ToastyResult) { .type=TOASTY_RESULT_READ_SUCCESS, .user=toasty->operations[opidx].user, .bytes_read=toasty->operations[opidx].actual_bytes }; } } } @@ -2360,8 +2371,9 @@ int toasty_read(ToastyFS *toasty, ToastyString path, return -1; } + int bytes_read = result.bytes_read; toasty_free_result(&result); - return 0; // TODO: return the number of bytes read? + return bytes_read; } int toasty_write(ToastyFS *toasty, ToastyString path, diff --git a/src/file_tree.c b/src/file_tree.c index b8908fa..d25649c 100644 --- a/src/file_tree.c +++ b/src/file_tree.c @@ -411,7 +411,7 @@ int file_tree_write(FileTree *ft, string path, int file_tree_read(FileTree *ft, string path, uint64_t off, uint64_t len, uint64_t *chunk_size, - SHA256 *hashes, int max_hashes) + SHA256 *hashes, int max_hashes, uint64_t *actual_bytes) { int num_comps; string comps[MAX_COMPS]; @@ -432,6 +432,18 @@ int file_tree_read(FileTree *ft, string path, *chunk_size = f->chunk_size; + // Calculate file size (number of chunks * chunk size) + uint64_t file_size = f->num_chunks * f->chunk_size; + + // Calculate actual bytes that can be read + if (off >= file_size) { + *actual_bytes = 0; + } else if (off + len > file_size) { + *actual_bytes = file_size - off; + } else { + *actual_bytes = len; + } + if (len == 0) return 0; diff --git a/src/file_tree.h b/src/file_tree.h index 8785875..6947d11 100644 --- a/src/file_tree.h +++ b/src/file_tree.h @@ -56,7 +56,7 @@ int file_tree_list (FileTree *ft, string path, ListItem *items, int int file_tree_create_entity (FileTree *ft, string path, bool is_dir, uint64_t chunk_size); int file_tree_delete_entity (FileTree *ft, string path); int file_tree_write (FileTree *ft, string path, uint64_t off, uint64_t len, uint32_t num_chunks, uint32_t chunk_size, SHA256 *prev_hashes, SHA256 *hashes, SHA256 *removed_hashes, int *num_removed); -int file_tree_read (FileTree *ft, string path, uint64_t off, uint64_t len, uint64_t *chunk_size, SHA256 *hashes, int max_hashes); +int file_tree_read (FileTree *ft, string path, uint64_t off, uint64_t len, uint64_t *chunk_size, SHA256 *hashes, int max_hashes, uint64_t *actual_bytes); string file_tree_strerror (int code); int file_tree_serialize (FileTree *ft, int (*flush_fn)(char*,int,void*), void *flush_data); int file_tree_deserialize (FileTree *ft, int (*read_fn)(char*,int,void*), void *read_data); diff --git a/src/metadata_server.c b/src/metadata_server.c index 2d45e9b..46da0af 100644 --- a/src/metadata_server.c +++ b/src/metadata_server.c @@ -406,8 +406,9 @@ process_client_read(MetadataServer *state, int conn_idx, ByteView msg) #define MAX_READ_HASHES 128 uint64_t chunk_size; + uint64_t actual_bytes; SHA256 hashes[MAX_READ_HASHES]; - int ret = file_tree_read(&state->file_tree, path, offset, length, &chunk_size, hashes, MAX_READ_HASHES); + int ret = file_tree_read(&state->file_tree, path, offset, length, &chunk_size, hashes, MAX_READ_HASHES, &actual_bytes); if (ret < 0) { @@ -439,6 +440,14 @@ process_client_read(MetadataServer *state, int conn_idx, ByteView msg) uint32_t tmp = chunk_size; message_write(&writer, &tmp, sizeof(tmp)); + // Send the actual number of bytes that can be read + if (actual_bytes > UINT32_MAX) { + message_writer_free(&writer); + return -1; + } + uint32_t tmp_actual = actual_bytes; + message_write(&writer, &tmp_actual, sizeof(tmp_actual)); + uint32_t num_hashes = ret; message_write(&writer, &num_hashes, sizeof(num_hashes)); diff --git a/web/main.c b/web/main.c index 234cac7..dc0302f 100644 --- a/web/main.c +++ b/web/main.c @@ -197,13 +197,13 @@ int main(void) // First, ACK the byte we just read, even if it's // just 0 bytes (every bodybuf must by paired with // a bodyack). - proxied[i].transferred += result.count; - int ack = proxied[i].head_only ? 0 : result.count; + proxied[i].transferred += result.bytes_read; + int ack = proxied[i].head_only ? 0 : result.bytes_read; http_response_builder_bodyack(proxied[i].builder, ack); // If we didn't reach the end of the file, start // a new read. - if (result.count > 0) { + if (result.bytes_read > 0) { // Make sure there is some free space in the buffer int mincap = 1<<10; // TODO: Choose based on overall file size