isobusfs: fix directory read and seek semantics to match ISO 11783-13
According to ISO 11783-13:2021 (sections C.3.4.2 and C.3.5.2), 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.
Previously, the implementation treated directories strictly as files,
using byte-based offsets and counts. This resulted in incorrect seeking
behavior and protocol violations when listing directories.
This patch aligns the implementation with the standard by:
1. Server side:
- Introducing `isobusfs_srv_dir_entry_visible()` to consistently
filter out invalid (unreadable, hidden, oversized) entries. This
ensures that entry indices remain stable.
- Implementing `isobusfs_srv_dir_skip_entries()` to advance the
directory stream by logical visible entries rather than bytes.
- Updating the "Read File" handler to interpret `count` as the
maximum number of entries and return the number of entries read
in the response header.
- Updating the "Seek File" handler to seek by entry index.
2. Client side:
- Calculating the request count based on the number of minimal-size
entries that fit into the maximum data length.
- Interpreting 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>
pull/615/head
parent
cdc00bacf0
commit
4855df9af7
|
|
@ -16,6 +16,12 @@
|
||||||
#define MAX_COMMAND_LENGTH 256
|
#define MAX_COMMAND_LENGTH 256
|
||||||
|
|
||||||
#define MAX_DISPLAY_FILENAME_LENGTH 100
|
#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 {
|
struct command_mapping {
|
||||||
const char *command;
|
const char *command;
|
||||||
|
|
@ -510,8 +516,12 @@ isobusfs_cli_ls_handle_open_dir_sent(struct isobusfs_priv *priv,
|
||||||
|
|
||||||
ctx->handle = res->handle;
|
ctx->handle = res->handle;
|
||||||
|
|
||||||
|
ctx->offset = 0;
|
||||||
|
ctx->entry_count = 0;
|
||||||
|
|
||||||
ret = isobusfs_cli_send_and_register_fa_sf_event(priv, ctx->handle,
|
ret = isobusfs_cli_send_and_register_fa_sf_event(priv, ctx->handle,
|
||||||
0, ctx->entry_count,
|
ISOBUSFS_FA_SEEK_SET,
|
||||||
|
ctx->offset,
|
||||||
cb, ctx);
|
cb, ctx);
|
||||||
if (ret)
|
if (ret)
|
||||||
pr_int("Failed to send seek file request: %i\n", 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;
|
isobusfs_event_callback cb = isobusfs_cli_ls_event_callback;
|
||||||
struct isobusfs_fa_seekf_res *res =
|
struct isobusfs_fa_seekf_res *res =
|
||||||
(struct isobusfs_fa_seekf_res *)msg;
|
(struct isobusfs_fa_seekf_res *)msg->buf;
|
||||||
uint16_t count;
|
uint16_t count;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
|
@ -543,8 +553,10 @@ isobusfs_cli_ls_handle_seek_dir_sent(struct isobusfs_priv *priv,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set max possible number fitting in to 16bits */
|
/* ISO 11783-13:2021 C.3.5.2: count is number of directory entries. */
|
||||||
count = UINT16_MAX;
|
count = ISOBUSFS_MAX_DATA_LENGH / ISOBUSFS_MIN_DIR_ENTRY_SIZE;
|
||||||
|
if (!count)
|
||||||
|
count = 1;
|
||||||
ctx->request_count = count;
|
ctx->request_count = count;
|
||||||
|
|
||||||
ret = isobusfs_cli_send_and_register_fa_rf_event(priv, ctx->handle,
|
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,
|
uint16_t *file_time,
|
||||||
uint32_t *file_size)
|
uint32_t *file_size)
|
||||||
{
|
{
|
||||||
|
size_t entry_total_len, copy_len;
|
||||||
uint8_t filename_length;
|
uint8_t filename_length;
|
||||||
size_t entry_total_len;
|
|
||||||
|
|
||||||
if (*pos + 2 > buffer_length) {
|
if (*pos + 2 > buffer_length) {
|
||||||
pr_int("Error: Incomplete data in buffer\n");
|
pr_int("Error: Incomplete data in buffer\n");
|
||||||
|
|
@ -633,7 +645,13 @@ static bool isobusfs_cli_extract_directory_entry(const uint8_t *buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
(*pos)++;
|
(*pos)++;
|
||||||
strncpy(filename, (const char *)buffer + *pos, filename_length);
|
|
||||||
|
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[filename_length] = '\0';
|
filename[filename_length] = '\0';
|
||||||
*pos += filename_length;
|
*pos += filename_length;
|
||||||
if (filename_length > MAX_DISPLAY_FILENAME_LENGTH) {
|
if (filename_length > MAX_DISPLAY_FILENAME_LENGTH) {
|
||||||
|
|
@ -688,15 +706,17 @@ isobusfs_cli_print_directory_entry(struct isobusfs_cli_ls_context *ctx,
|
||||||
static void
|
static void
|
||||||
isobusfs_cli_print_directory_entries(struct isobusfs_cli_ls_context *ctx,
|
isobusfs_cli_print_directory_entries(struct isobusfs_cli_ls_context *ctx,
|
||||||
const uint8_t *buffer,
|
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];
|
char filename[ISOBUSFS_MAX_DIR_ENTRY_NAME_LENGTH + 1];
|
||||||
uint16_t file_date, file_time;
|
uint16_t file_date, file_time;
|
||||||
uint32_t file_size;
|
uint32_t file_size;
|
||||||
uint8_t attributes;
|
uint8_t attributes;
|
||||||
size_t pos = 0;
|
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,
|
if (!isobusfs_cli_extract_directory_entry(buffer, buffer_length,
|
||||||
&pos, filename,
|
&pos, filename,
|
||||||
&attributes,
|
&attributes,
|
||||||
|
|
@ -709,6 +729,7 @@ isobusfs_cli_print_directory_entries(struct isobusfs_cli_ls_context *ctx,
|
||||||
file_date, file_time,
|
file_date, file_time,
|
||||||
file_size);
|
file_size);
|
||||||
ctx->entry_count++;
|
ctx->entry_count++;
|
||||||
|
entries++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -721,26 +742,35 @@ isobusfs_cli_ls_handle_read_dir_sent(struct isobusfs_priv *priv,
|
||||||
(struct isobusfs_read_file_response *)msg->buf;
|
(struct isobusfs_read_file_response *)msg->buf;
|
||||||
size_t buffer_length = msg->len - sizeof(*res);
|
size_t buffer_length = msg->len - sizeof(*res);
|
||||||
isobusfs_event_callback cb;
|
isobusfs_event_callback cb;
|
||||||
|
size_t entries_before;
|
||||||
|
size_t entries_in_message;
|
||||||
uint16_t count;
|
uint16_t count;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pr_debug("< rx: Read File Response. Error code: %i", res->error_code);
|
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))
|
if (isobusfs_cli_int_is_error(priv, 0, res->error_code, res->tan))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
count = le16toh(res->count);
|
count = le16toh(res->count);
|
||||||
if (count && count != buffer_length) {
|
if (count && buffer_length) {
|
||||||
pr_int("Buffer length mismatch: %u != %zu\n", count,
|
entries_before = ctx->entry_count;
|
||||||
buffer_length);
|
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 != entries_in_message) {
|
||||||
|
pr_int("Directory entry count mismatch: %u != %zu\n", count,
|
||||||
|
entries_in_message);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count)
|
|
||||||
isobusfs_cli_print_directory_entries(ctx, res->data,
|
|
||||||
buffer_length);
|
|
||||||
|
|
||||||
cb = isobusfs_cli_ls_event_callback;
|
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,
|
ret = isobusfs_cli_send_and_register_fa_cf_event(priv,
|
||||||
ctx->handle,
|
ctx->handle,
|
||||||
cb, ctx);
|
cb, ctx);
|
||||||
|
|
@ -750,8 +780,20 @@ isobusfs_cli_ls_handle_read_dir_sent(struct isobusfs_priv *priv,
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->state = ISOBUSFS_CLI_LS_STATE_CLOSE_DIR_SENT;
|
ctx->state = ISOBUSFS_CLI_LS_STATE_CLOSE_DIR_SENT;
|
||||||
} else {
|
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;
|
ctx->offset = ctx->entry_count;
|
||||||
|
|
||||||
ret = isobusfs_cli_send_and_register_fa_sf_event(priv,
|
ret = isobusfs_cli_send_and_register_fa_sf_event(priv,
|
||||||
ctx->handle, 0,
|
ctx->handle, 0,
|
||||||
ctx->offset,
|
ctx->offset,
|
||||||
|
|
@ -760,11 +802,9 @@ isobusfs_cli_ls_handle_read_dir_sent(struct isobusfs_priv *priv,
|
||||||
pr_int("Failed to send seek file request: %i\n", ret);
|
pr_int("Failed to send seek file request: %i\n", ret);
|
||||||
|
|
||||||
ctx->state = ISOBUSFS_CLI_LS_STATE_SEEK_DIR_SENT;
|
ctx->state = ISOBUSFS_CLI_LS_STATE_SEEK_DIR_SENT;
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
error:
|
error:
|
||||||
|
|
||||||
ctx->state = ISOBUSFS_CLI_LS_STATE_ERROR;
|
ctx->state = ISOBUSFS_CLI_LS_STATE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -495,15 +495,119 @@ static int check_access_with_base(const char *base_dir,
|
||||||
return access(full_path, mode);
|
return access(full_path, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ISO 11783-13:2021 B.21 and B.15:
|
||||||
|
* Filters directory entries that can be returned in a Read File response
|
||||||
|
* for directory handles while collecting the attributes and name length.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* isobusfs_srv_dir_entry_visible() - Filter visible directory entries.
|
||||||
|
* @handle: Directory handle for access checks.
|
||||||
|
* @entry: Directory entry to inspect.
|
||||||
|
* @file_stat: Stat buffer to fill for the entry.
|
||||||
|
* @entry_name_len: Returns entry name length on success.
|
||||||
|
* @attributes: Returns computed attributes on success.
|
||||||
|
*
|
||||||
|
* ISO 11783-13:2021 B.21 and B.15 define the directory entry layout and
|
||||||
|
* attributes. This helper skips entries that are not readable or too long
|
||||||
|
* and returns attributes for the entry that will be serialized.
|
||||||
|
*
|
||||||
|
* Return: true when the entry should be emitted, false otherwise.
|
||||||
|
*/
|
||||||
|
static bool isobusfs_srv_dir_entry_visible(struct isobusfs_srv_handles *handle,
|
||||||
|
const struct dirent *entry,
|
||||||
|
struct stat *file_stat,
|
||||||
|
size_t *entry_name_len,
|
||||||
|
uint8_t *attributes)
|
||||||
|
{
|
||||||
|
if (check_access_with_base(handle->path, entry->d_name, R_OK) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (fstatat(handle->fd, entry->d_name, file_stat, 0) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*entry_name_len = strlen(entry->d_name);
|
||||||
|
if (*entry_name_len > ISOBUSFS_MAX_DIR_ENTRY_NAME_LENGTH)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (S_ISDIR(file_stat->st_mode))
|
||||||
|
*attributes |= ISOBUSFS_ATTR_DIRECTORY;
|
||||||
|
if (check_access_with_base(handle->path, entry->d_name, W_OK) != 0)
|
||||||
|
*attributes |= ISOBUSFS_ATTR_READ_ONLY;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ISO 11783-13:2021 C.3.4.2 and C.3.5.2:
|
||||||
|
* Directory offsets/counts are in entries, so advance the directory stream
|
||||||
|
* by visible entries only and report EOF if the entry offset is past the end.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* isobusfs_srv_dir_skip_entries() - Advance directory stream by entries.
|
||||||
|
* @handle: Directory handle to reposition.
|
||||||
|
* @offset: Entry offset to seek to.
|
||||||
|
*
|
||||||
|
* ISO 11783-13:2021 C.3.4.2 and C.3.5.2 state directory offsets/counts are in
|
||||||
|
* entries. This helper advances by visible entries only.
|
||||||
|
*
|
||||||
|
* Return: ISOBUSFS_ERR_SUCCESS or a protocol error code.
|
||||||
|
*/
|
||||||
|
static int isobusfs_srv_dir_skip_entries(struct isobusfs_srv_handles *handle,
|
||||||
|
int32_t offset)
|
||||||
|
{
|
||||||
|
struct dirent *entry;
|
||||||
|
int32_t skipped = 0;
|
||||||
|
|
||||||
|
rewinddir(handle->dir);
|
||||||
|
|
||||||
|
while (skipped < offset && (entry = readdir(handle->dir)) != NULL) {
|
||||||
|
struct stat file_stat;
|
||||||
|
size_t entry_name_len = 0;
|
||||||
|
uint8_t attributes = 0;
|
||||||
|
|
||||||
|
if (!isobusfs_srv_dir_entry_visible(handle, entry, &file_stat,
|
||||||
|
&entry_name_len,
|
||||||
|
&attributes))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
skipped++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset > 0 && skipped < offset)
|
||||||
|
return ISOBUSFS_ERR_END_OF_FILE;
|
||||||
|
|
||||||
|
return ISOBUSFS_ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isobusfs_srv_read_directory() - Read directory entries into a response buffer.
|
||||||
|
* @handle: Directory handle to read.
|
||||||
|
* @buffer: Output buffer for directory entries.
|
||||||
|
* @max_bytes: Maximum payload bytes allowed in the response.
|
||||||
|
* @max_entries: Maximum number of entries to return.
|
||||||
|
* @readed_size: Returns payload size in bytes.
|
||||||
|
* @entries_read: Returns number of entries serialized.
|
||||||
|
*
|
||||||
|
* ISO 11783-13:2021 C.3.5.2: directory Count is entry based. This function
|
||||||
|
* encodes up to @max_entries entries while respecting @max_bytes.
|
||||||
|
*
|
||||||
|
* Return: ISOBUSFS_ERR_SUCCESS or a protocol error code.
|
||||||
|
*/
|
||||||
static int isobusfs_srv_read_directory(struct isobusfs_srv_handles *handle,
|
static int isobusfs_srv_read_directory(struct isobusfs_srv_handles *handle,
|
||||||
uint8_t *buffer, size_t count,
|
uint8_t *buffer, size_t max_bytes,
|
||||||
ssize_t *readed_size)
|
uint16_t max_entries,
|
||||||
|
ssize_t *readed_size,
|
||||||
|
uint16_t *entries_read)
|
||||||
{
|
{
|
||||||
DIR *dir = handle->dir;
|
DIR *dir = handle->dir;
|
||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* ISO 11783-13:2021 C.3.5.2:
|
||||||
|
* Directory offsets are entry indices, not byte positions.
|
||||||
* Position the directory stream to the previously stored offset (handle->dir_pos).
|
* Position the directory stream to the previously stored offset (handle->dir_pos).
|
||||||
*
|
*
|
||||||
* Handling Changes in Directory Contents:
|
* Handling Changes in Directory Contents:
|
||||||
|
|
@ -519,13 +623,12 @@ static int isobusfs_srv_read_directory(struct isobusfs_srv_handles *handle,
|
||||||
* either returning an error or restarting from the beginning of the directory, depending
|
* either returning an error or restarting from the beginning of the directory, depending
|
||||||
* on the application's requirements.
|
* on the application's requirements.
|
||||||
*/
|
*/
|
||||||
for (int i = 0; i < handle->dir_pos &&
|
ret = isobusfs_srv_dir_skip_entries(handle, handle->dir_pos);
|
||||||
(entry = readdir(dir)) != NULL; i++) {
|
if (ret != ISOBUSFS_ERR_SUCCESS)
|
||||||
/* Iterating to the desired position */
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Directory Entry Layout:
|
* Directory Entry Layout (ISO 11783-13:2021 B.21):
|
||||||
* This loop reads directory entries and encodes them into a buffer.
|
* This loop reads directory entries and encodes them into a buffer.
|
||||||
* Each entry in the buffer follows the format specified in ISO 11783-13:2021.
|
* Each entry in the buffer follows the format specified in ISO 11783-13:2021.
|
||||||
*
|
*
|
||||||
|
|
@ -550,26 +653,23 @@ static int isobusfs_srv_read_directory(struct isobusfs_srv_handles *handle,
|
||||||
* The handle->dir_pos is incremented after processing each entry, marking
|
* The handle->dir_pos is incremented after processing each entry, marking
|
||||||
* the current position in the directory stream for subsequent reads.
|
* the current position in the directory stream for subsequent reads.
|
||||||
*/
|
*/
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
*entries_read = 0;
|
||||||
|
while ((entry = readdir(dir)) != NULL &&
|
||||||
|
(*entries_read) < max_entries) {
|
||||||
size_t entry_name_len, entry_total_len;
|
size_t entry_name_len, entry_total_len;
|
||||||
__le16 file_date, file_time;
|
__le16 file_date, file_time;
|
||||||
uint8_t attributes = 0;
|
|
||||||
struct stat file_stat;
|
struct stat file_stat;
|
||||||
|
uint8_t attributes = 0;
|
||||||
__le32 size;
|
__le32 size;
|
||||||
|
|
||||||
if (check_access_with_base(handle->path, entry->d_name, R_OK) != 0)
|
if (!isobusfs_srv_dir_entry_visible(handle, entry, &file_stat,
|
||||||
continue; /* Skip this entry if it's not readable */
|
&entry_name_len,
|
||||||
|
&attributes))
|
||||||
if (fstatat(handle->fd, entry->d_name, &file_stat, 0) < 0)
|
|
||||||
continue; /* Skip this entry on error */
|
|
||||||
|
|
||||||
entry_name_len = strlen(entry->d_name);
|
|
||||||
if (entry_name_len > ISOBUSFS_MAX_DIR_ENTRY_NAME_LENGTH)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
entry_total_len = 1 + entry_name_len + 1 + 2 + 2 + 4;
|
entry_total_len = 1 + entry_name_len + 1 + 2 + 2 + 4;
|
||||||
|
|
||||||
if (pos + entry_total_len > count)
|
if (pos + entry_total_len > max_bytes)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
buffer[pos++] = (uint8_t)entry_name_len;
|
buffer[pos++] = (uint8_t)entry_name_len;
|
||||||
|
|
@ -577,10 +677,6 @@ static int isobusfs_srv_read_directory(struct isobusfs_srv_handles *handle,
|
||||||
memcpy(buffer + pos, entry->d_name, entry_name_len);
|
memcpy(buffer + pos, entry->d_name, entry_name_len);
|
||||||
pos += entry_name_len;
|
pos += entry_name_len;
|
||||||
|
|
||||||
if (S_ISDIR(file_stat.st_mode))
|
|
||||||
attributes |= ISOBUSFS_ATTR_DIRECTORY;
|
|
||||||
if (check_access_with_base(handle->path, entry->d_name, W_OK) != 0)
|
|
||||||
attributes |= ISOBUSFS_ATTR_READ_ONLY;
|
|
||||||
buffer[pos++] = attributes;
|
buffer[pos++] = attributes;
|
||||||
|
|
||||||
file_date = htole16(convert_to_file_date(file_stat.st_mtime));
|
file_date = htole16(convert_to_file_date(file_stat.st_mtime));
|
||||||
|
|
@ -594,9 +690,11 @@ static int isobusfs_srv_read_directory(struct isobusfs_srv_handles *handle,
|
||||||
size = htole32(file_stat.st_size);
|
size = htole32(file_stat.st_size);
|
||||||
memcpy(buffer + pos, &size, sizeof(size));
|
memcpy(buffer + pos, &size, sizeof(size));
|
||||||
pos += sizeof(size);
|
pos += sizeof(size);
|
||||||
|
(*entries_read)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
*readed_size = pos;
|
*readed_size = pos;
|
||||||
|
handle->dir_pos += *entries_read;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -609,13 +707,18 @@ static int isobusfs_srv_fa_rf_req(struct isobusfs_srv_priv *priv,
|
||||||
struct isobusfs_srv_client *client;
|
struct isobusfs_srv_client *client;
|
||||||
struct isobusfs_fa_readf_req *req;
|
struct isobusfs_fa_readf_req *req;
|
||||||
ssize_t readed_size = 0;
|
ssize_t readed_size = 0;
|
||||||
|
uint16_t entries_read = 0;
|
||||||
uint8_t error_code = 0;
|
uint8_t error_code = 0;
|
||||||
ssize_t send_size;
|
ssize_t send_size;
|
||||||
|
bool res_allocated = false;
|
||||||
|
size_t max_bytes = 0;
|
||||||
|
uint16_t count = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int count;
|
bool is_dir = false;
|
||||||
|
|
||||||
req = (struct isobusfs_fa_readf_req *)msg->buf;
|
req = (struct isobusfs_fa_readf_req *)msg->buf;
|
||||||
count = le16toh(req->count);
|
count = le16toh(req->count);
|
||||||
|
res = (struct isobusfs_read_file_response *)&res_fail[0];
|
||||||
|
|
||||||
pr_debug("< rx: Read File Request. tan: %d, handle: %d, count: %d",
|
pr_debug("< rx: Read File Request. tan: %d, handle: %d, count: %d",
|
||||||
req->tan, req->handle, count);
|
req->tan, req->handle, count);
|
||||||
|
|
@ -627,17 +730,6 @@ static int isobusfs_srv_fa_rf_req(struct isobusfs_srv_priv *priv,
|
||||||
* TODO: currently we are not able to detect support transport mode,
|
* TODO: currently we are not able to detect support transport mode,
|
||||||
* so ETP is assumed.
|
* so ETP is assumed.
|
||||||
*/
|
*/
|
||||||
if (count > ISOBUSFS_MAX_DATA_LENGH)
|
|
||||||
count = ISOBUSFS_MAX_DATA_LENGH;
|
|
||||||
|
|
||||||
res = malloc(sizeof(*res) + count);
|
|
||||||
if (!res) {
|
|
||||||
pr_warn("failed to allocate memory");
|
|
||||||
res = (struct isobusfs_read_file_response *)&res_fail[0];
|
|
||||||
error_code = ISOBUSFS_ERR_OUT_OF_MEM;
|
|
||||||
goto send_response;
|
|
||||||
}
|
|
||||||
|
|
||||||
client = isobusfs_srv_get_client_by_msg(priv, msg);
|
client = isobusfs_srv_get_client_by_msg(priv, msg);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
pr_warn("client not found");
|
pr_warn("client not found");
|
||||||
|
|
@ -649,12 +741,33 @@ static int isobusfs_srv_fa_rf_req(struct isobusfs_srv_priv *priv,
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
pr_warn("failed to find file with handle: %x", req->handle);
|
pr_warn("failed to find file with handle: %x", req->handle);
|
||||||
error_code = ISOBUSFS_ERR_FILE_ORPATH_NOT_FOUND;
|
error_code = ISOBUSFS_ERR_FILE_ORPATH_NOT_FOUND;
|
||||||
|
goto send_response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine whether to read a file or a directory */
|
/* Determine whether to read a file or a directory */
|
||||||
if (handle->dir) {
|
if (handle->dir) {
|
||||||
ret = isobusfs_srv_read_directory(handle, res->data, count,
|
/* ISO 11783-13:2021 C.3.5.2: count is entry count for directories. */
|
||||||
&readed_size);
|
is_dir = true;
|
||||||
|
max_bytes = ISOBUSFS_MAX_DATA_LENGH;
|
||||||
|
} else {
|
||||||
|
if (count > ISOBUSFS_MAX_DATA_LENGH)
|
||||||
|
count = ISOBUSFS_MAX_DATA_LENGH;
|
||||||
|
max_bytes = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = malloc(sizeof(*res) + max_bytes);
|
||||||
|
if (!res) {
|
||||||
|
pr_warn("failed to allocate memory");
|
||||||
|
res = (struct isobusfs_read_file_response *)&res_fail[0];
|
||||||
|
error_code = ISOBUSFS_ERR_OUT_OF_MEM;
|
||||||
|
goto send_response;
|
||||||
|
}
|
||||||
|
res_allocated = true;
|
||||||
|
|
||||||
|
if (is_dir) {
|
||||||
|
ret = isobusfs_srv_read_directory(handle, res->data, max_bytes,
|
||||||
|
count, &readed_size,
|
||||||
|
&entries_read);
|
||||||
} else {
|
} else {
|
||||||
ret = isobusfs_srv_read_file(handle, res->data, count,
|
ret = isobusfs_srv_read_file(handle, res->data, count,
|
||||||
&readed_size);
|
&readed_size);
|
||||||
|
|
@ -663,7 +776,10 @@ static int isobusfs_srv_fa_rf_req(struct isobusfs_srv_priv *priv,
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_code = ret;
|
error_code = ret;
|
||||||
readed_size = 0;
|
readed_size = 0;
|
||||||
} else if (count != 0 && readed_size == 0) {
|
entries_read = 0;
|
||||||
|
} else if (count != 0 &&
|
||||||
|
((is_dir && entries_read == 0) ||
|
||||||
|
(!is_dir && readed_size == 0))) {
|
||||||
error_code = ISOBUSFS_ERR_END_OF_FILE;
|
error_code = ISOBUSFS_ERR_END_OF_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -673,6 +789,9 @@ send_response:
|
||||||
ISOBUSFS_FA_F_READ_FILE_RES);
|
ISOBUSFS_FA_F_READ_FILE_RES);
|
||||||
res->tan = req->tan;
|
res->tan = req->tan;
|
||||||
res->error_code = error_code;
|
res->error_code = error_code;
|
||||||
|
if (is_dir)
|
||||||
|
res->count = htole16(entries_read);
|
||||||
|
else
|
||||||
res->count = htole16(readed_size);
|
res->count = htole16(readed_size);
|
||||||
|
|
||||||
send_size = sizeof(*res) + readed_size;
|
send_size = sizeof(*res) + readed_size;
|
||||||
|
|
@ -690,6 +809,7 @@ send_response:
|
||||||
error_code, isobusfs_error_to_str(error_code), readed_size);
|
error_code, isobusfs_error_to_str(error_code), readed_size);
|
||||||
|
|
||||||
free_res:
|
free_res:
|
||||||
|
if (res_allocated)
|
||||||
free(res);
|
free(res);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -754,19 +874,33 @@ static int isobusfs_srv_seek(struct isobusfs_srv_priv *priv,
|
||||||
return ISOBUSFS_ERR_SUCCESS;
|
return ISOBUSFS_ERR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isobusfs_srv_seek_directory() - Seek a directory by entry index.
|
||||||
|
* @handle: Directory handle to seek.
|
||||||
|
* @offset: Entry index to seek to.
|
||||||
|
*
|
||||||
|
* ISO 11783-13:2021 C.3.4.2 defines directory offsets as entry indices.
|
||||||
|
*
|
||||||
|
* Return: ISOBUSFS_ERR_SUCCESS or a protocol error code.
|
||||||
|
*/
|
||||||
static int isobusfs_srv_seek_directory(struct isobusfs_srv_handles *handle,
|
static int isobusfs_srv_seek_directory(struct isobusfs_srv_handles *handle,
|
||||||
int32_t offset)
|
int32_t offset)
|
||||||
{
|
{
|
||||||
DIR *dir = fdopendir(handle->fd);
|
int32_t current_pos = handle->dir_pos;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!dir)
|
if (!handle->dir)
|
||||||
return ISOBUSFS_ERR_OTHER;
|
return ISOBUSFS_ERR_OTHER;
|
||||||
|
|
||||||
rewinddir(dir);
|
/*
|
||||||
|
* ISO 11783-13:2021 C.3.4.2:
|
||||||
for (int32_t i = 0; i < offset; i++) {
|
* Directory offsets are entry indices. If we fail to seek, restore
|
||||||
if (readdir(dir) == NULL)
|
* the previous entry position since the position shall not change on error.
|
||||||
return ISOBUSFS_ERR_END_OF_FILE;
|
*/
|
||||||
|
ret = isobusfs_srv_dir_skip_entries(handle, offset);
|
||||||
|
if (ret != ISOBUSFS_ERR_SUCCESS) {
|
||||||
|
isobusfs_srv_dir_skip_entries(handle, current_pos);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle->dir_pos = offset;
|
handle->dir_pos = offset;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue