mirror of
https://github.com/linux-can/can-utils.git
synced 2026-11-04 15:31:10 +01:00
isobusfs: fix directory read and seek semantics to match ISO 11783-13
Fix directory operations to comply with ISO 11783-13:2021 (sections
C.3.4.2 and C.3.5.2), which specifies that directory operations differ
significantly from standard file operations:
- The Count parameter in a Read File request represents the number of
directory entries to read, not the number of bytes.
- The Offset parameter in a Seek File request represents the logical
entry index, not a byte offset.
The previous implementation treated directories strictly as files,
using byte-based offsets and counts. This resulted in incorrect seeking
behavior and protocol violations when listing directories.
Align the implementation with the standard by:
1. Server side:
- Introduce isobusfs_srv_dir_entry_visible() to consistently
filter out invalid (unreadable, hidden, oversized) entries. This
ensures that entry indices remain stable.
- Implement isobusfs_srv_dir_skip_entries() to advance the
directory stream by logical visible entries rather than bytes.
- Update the Read File handler to interpret count as the
maximum number of entries and return the number of entries read
in the response header.
- Update the Seek File handler to seek by entry index.
2. Client side:
- Calculate the request count based on the number of minimal-size
entries that fit into the maximum data length.
- Interpret the response count as the number of entries received
rather than the byte length of the payload.
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
This commit is contained in:
+75
-28
@@ -16,6 +16,12 @@
|
||||
#define MAX_COMMAND_LENGTH 256
|
||||
|
||||
#define MAX_DISPLAY_FILENAME_LENGTH 100
|
||||
/*
|
||||
* ISO 11783-13:2021 B.21 minimal directory entry payload size in bytes:
|
||||
* 1 (name length) + 1 (min name byte) + 1 (attributes) +
|
||||
* 2 (date) + 2 (time) + 4 (size).
|
||||
*/
|
||||
#define ISOBUSFS_MIN_DIR_ENTRY_SIZE (1 + 1 + 1 + 2 + 2 + 4)
|
||||
|
||||
struct command_mapping {
|
||||
const char *command;
|
||||
@@ -510,8 +516,12 @@ isobusfs_cli_ls_handle_open_dir_sent(struct isobusfs_priv *priv,
|
||||
|
||||
ctx->handle = res->handle;
|
||||
|
||||
ctx->offset = 0;
|
||||
ctx->entry_count = 0;
|
||||
|
||||
ret = isobusfs_cli_send_and_register_fa_sf_event(priv, ctx->handle,
|
||||
0, ctx->entry_count,
|
||||
ISOBUSFS_FA_SEEK_SET,
|
||||
ctx->offset,
|
||||
cb, ctx);
|
||||
if (ret)
|
||||
pr_int("Failed to send seek file request: %i\n", ret);
|
||||
@@ -530,7 +540,7 @@ isobusfs_cli_ls_handle_seek_dir_sent(struct isobusfs_priv *priv,
|
||||
{
|
||||
isobusfs_event_callback cb = isobusfs_cli_ls_event_callback;
|
||||
struct isobusfs_fa_seekf_res *res =
|
||||
(struct isobusfs_fa_seekf_res *)msg;
|
||||
(struct isobusfs_fa_seekf_res *)msg->buf;
|
||||
uint16_t count;
|
||||
int ret;
|
||||
|
||||
@@ -543,8 +553,10 @@ isobusfs_cli_ls_handle_seek_dir_sent(struct isobusfs_priv *priv,
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* set max possible number fitting in to 16bits */
|
||||
count = UINT16_MAX;
|
||||
/* ISO 11783-13:2021 C.3.5.2: count is number of directory entries. */
|
||||
count = ISOBUSFS_MAX_DATA_LENGH / ISOBUSFS_MIN_DIR_ENTRY_SIZE;
|
||||
if (!count)
|
||||
count = 1;
|
||||
ctx->request_count = count;
|
||||
|
||||
ret = isobusfs_cli_send_and_register_fa_rf_event(priv, ctx->handle,
|
||||
@@ -616,8 +628,8 @@ static bool isobusfs_cli_extract_directory_entry(const uint8_t *buffer,
|
||||
uint16_t *file_time,
|
||||
uint32_t *file_size)
|
||||
{
|
||||
size_t entry_total_len, copy_len;
|
||||
uint8_t filename_length;
|
||||
size_t entry_total_len;
|
||||
|
||||
if (*pos + 2 > buffer_length) {
|
||||
pr_int("Error: Incomplete data in buffer\n");
|
||||
@@ -633,8 +645,14 @@ static bool isobusfs_cli_extract_directory_entry(const uint8_t *buffer,
|
||||
}
|
||||
|
||||
(*pos)++;
|
||||
strncpy(filename, (const char *)buffer + *pos, filename_length);
|
||||
filename[filename_length] = '\0';
|
||||
|
||||
if (filename_length > ISOBUSFS_MAX_DIR_ENTRY_NAME_LENGTH)
|
||||
copy_len = ISOBUSFS_MAX_DIR_ENTRY_NAME_LENGTH;
|
||||
else
|
||||
copy_len = filename_length;
|
||||
|
||||
strncpy(filename, (const char *)buffer + *pos, copy_len);
|
||||
filename[copy_len] = '\0';
|
||||
*pos += filename_length;
|
||||
if (filename_length > MAX_DISPLAY_FILENAME_LENGTH) {
|
||||
/* Truncate the filename and replace the last character
|
||||
@@ -688,15 +706,17 @@ isobusfs_cli_print_directory_entry(struct isobusfs_cli_ls_context *ctx,
|
||||
static void
|
||||
isobusfs_cli_print_directory_entries(struct isobusfs_cli_ls_context *ctx,
|
||||
const uint8_t *buffer,
|
||||
size_t buffer_length)
|
||||
size_t buffer_length,
|
||||
uint16_t max_entries)
|
||||
{
|
||||
char filename[ISOBUSFS_MAX_DIR_ENTRY_NAME_LENGTH + 1];
|
||||
uint16_t file_date, file_time;
|
||||
uint32_t file_size;
|
||||
uint8_t attributes;
|
||||
size_t pos = 0;
|
||||
uint16_t entries = 0;
|
||||
|
||||
while (pos < buffer_length) {
|
||||
while (pos < buffer_length && entries < max_entries) {
|
||||
if (!isobusfs_cli_extract_directory_entry(buffer, buffer_length,
|
||||
&pos, filename,
|
||||
&attributes,
|
||||
@@ -709,6 +729,7 @@ isobusfs_cli_print_directory_entries(struct isobusfs_cli_ls_context *ctx,
|
||||
file_date, file_time,
|
||||
file_size);
|
||||
ctx->entry_count++;
|
||||
entries++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,26 +742,42 @@ isobusfs_cli_ls_handle_read_dir_sent(struct isobusfs_priv *priv,
|
||||
(struct isobusfs_read_file_response *)msg->buf;
|
||||
size_t buffer_length = msg->len - sizeof(*res);
|
||||
isobusfs_event_callback cb;
|
||||
size_t entries_before;
|
||||
size_t entries_in_message;
|
||||
uint16_t count;
|
||||
int ret;
|
||||
|
||||
pr_debug("< rx: Read File Response. Error code: %i", res->error_code);
|
||||
|
||||
if (isobusfs_cli_int_is_error(priv, 0, res->error_code, res->tan))
|
||||
goto error;
|
||||
|
||||
count = le16toh(res->count);
|
||||
if (count && count != buffer_length) {
|
||||
pr_int("Buffer length mismatch: %u != %zu\n", count,
|
||||
buffer_length);
|
||||
goto error;
|
||||
if (count && buffer_length) {
|
||||
entries_before = ctx->entry_count;
|
||||
isobusfs_cli_print_directory_entries(ctx, res->data,
|
||||
buffer_length, count);
|
||||
entries_in_message = ctx->entry_count - entries_before;
|
||||
} else {
|
||||
entries_in_message = 0;
|
||||
}
|
||||
|
||||
if (count)
|
||||
isobusfs_cli_print_directory_entries(ctx, res->data,
|
||||
buffer_length);
|
||||
if (count != entries_in_message) {
|
||||
pr_warn("Directory entry count mismatch: server sent %u, parsed %zu\n",
|
||||
count, entries_in_message);
|
||||
/* Continue processing if we parsed at least some entries.
|
||||
* Strict validation would reject valid responses if parsing
|
||||
* fails partway through due to corruption.
|
||||
*/
|
||||
if (entries_in_message == 0 && count > 0) {
|
||||
pr_int("Error: Failed to parse any directory entries\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
cb = isobusfs_cli_ls_event_callback;
|
||||
if (count) {
|
||||
|
||||
if (res->error_code == ISOBUSFS_ERR_END_OF_FILE) {
|
||||
ret = isobusfs_cli_send_and_register_fa_cf_event(priv,
|
||||
ctx->handle,
|
||||
cb, ctx);
|
||||
@@ -750,21 +787,31 @@ isobusfs_cli_ls_handle_read_dir_sent(struct isobusfs_priv *priv,
|
||||
}
|
||||
|
||||
ctx->state = ISOBUSFS_CLI_LS_STATE_CLOSE_DIR_SENT;
|
||||
} else {
|
||||
ctx->offset = ctx->entry_count;
|
||||
ret = isobusfs_cli_send_and_register_fa_sf_event(priv,
|
||||
ctx->handle, 0,
|
||||
ctx->offset,
|
||||
cb, ctx);
|
||||
if (ret)
|
||||
pr_int("Failed to send seek file request: %i\n", ret);
|
||||
|
||||
ctx->state = ISOBUSFS_CLI_LS_STATE_SEEK_DIR_SENT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
pr_int("Error: zero-length read without EOF\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Directory seek offset is entry index, not byte offset.
|
||||
* Server side seek rewinds and skips "offset" entries.
|
||||
*/
|
||||
ctx->offset = ctx->entry_count;
|
||||
|
||||
ret = isobusfs_cli_send_and_register_fa_sf_event(priv,
|
||||
ctx->handle, 0,
|
||||
ctx->offset,
|
||||
cb, ctx);
|
||||
if (ret)
|
||||
pr_int("Failed to send seek file request: %i\n", ret);
|
||||
|
||||
ctx->state = ISOBUSFS_CLI_LS_STATE_SEEK_DIR_SENT;
|
||||
|
||||
return;
|
||||
error:
|
||||
|
||||
ctx->state = ISOBUSFS_CLI_LS_STATE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user