git commit -m "first commit for v2"
This commit is contained in:
758
Devices/Libraries/Systems/can-utils/isobusfs/isobusfs_srv_dh.c
Executable file
758
Devices/Libraries/Systems/can-utils/isobusfs/isobusfs_srv_dh.c
Executable file
@@ -0,0 +1,758 @@
|
||||
// SPDX-License-Identifier: LGPL-2.0-only
|
||||
// SPDX-FileCopyrightText: 2023 Oleksij Rempel <linux@rempel-privat.de>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "isobusfs_srv.h"
|
||||
#include "isobusfs_cmn_dh.h"
|
||||
|
||||
void isobusfs_srv_set_default_current_dir(struct isobusfs_srv_priv *priv,
|
||||
struct isobusfs_srv_client *client)
|
||||
{
|
||||
snprintf(client->current_dir, ISOBUSFS_SRV_MAX_PATH_LEN, "\\\\%s",
|
||||
priv->default_volume);
|
||||
}
|
||||
|
||||
static const char *isobusfs_srv_get_volume_end(const char *path, size_t path_size)
|
||||
{
|
||||
const char *vol_end = NULL;
|
||||
size_t i;
|
||||
|
||||
if (!path || !path_size)
|
||||
return NULL;
|
||||
|
||||
if (!(path[0] == '\\' && path[1] == '\\' && path[2] != '\0'))
|
||||
return NULL;
|
||||
|
||||
for (i = 2; i < path_size; i++) {
|
||||
if (path[i] == '\\' || path[i] == '\0') {
|
||||
vol_end = &path[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vol_end)
|
||||
vol_end = &path[i];
|
||||
|
||||
return vol_end;
|
||||
}
|
||||
|
||||
int isobusfs_path_to_linux_path(struct isobusfs_srv_priv *priv,
|
||||
const char *isobusfs_path, size_t isobusfs_path_size,
|
||||
char *linux_path, size_t linux_path_size)
|
||||
{
|
||||
struct isobusfs_srv_volume *volume = NULL;
|
||||
size_t isobusfs_path_pos = 0;
|
||||
const char *vol_end;
|
||||
char *ptr;
|
||||
int i;
|
||||
|
||||
if (!priv || !isobusfs_path || !linux_path || !linux_path_size ||
|
||||
!isobusfs_path_size) {
|
||||
pr_err("%s: invalid argument\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vol_end = isobusfs_srv_get_volume_end(isobusfs_path, isobusfs_path_size);
|
||||
if (!vol_end) {
|
||||
pr_err("%s: invalid path %s. Can't find end of volume string\n",
|
||||
__func__, isobusfs_path);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Search for the volume in the priv->volumes array */
|
||||
for (i = 0; i < priv->volume_count; i++) {
|
||||
size_t volume_name_len = vol_end - (isobusfs_path + 2);
|
||||
|
||||
if (volume_name_len == strlen(priv->volumes[i].name) &&
|
||||
memcmp(priv->volumes[i].name, isobusfs_path + 2,
|
||||
volume_name_len) == 0) {
|
||||
volume = &priv->volumes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!volume) {
|
||||
pr_err("%s: invalid path %s. Can't find volume\n",
|
||||
__func__, isobusfs_path);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Copy the volume's Linux path to the output buffer */
|
||||
strncpy(linux_path, volume->path, linux_path_size - 1);
|
||||
linux_path[linux_path_size - 1] = '\0';
|
||||
|
||||
isobusfs_path_pos = vol_end - isobusfs_path;
|
||||
/* Add a forward slash if path ends after volume name */
|
||||
if (*vol_end == '\0' || isobusfs_path_pos == isobusfs_path_size - 1)
|
||||
strncat(linux_path, "/",
|
||||
linux_path_size - strlen(linux_path) - 1);
|
||||
|
||||
|
||||
if (isobusfs_path_pos + 3 < isobusfs_path_size && strncmp(vol_end, "\\~\\", 3) == 0) {
|
||||
strncat(linux_path, "/", linux_path_size - strlen(linux_path) - 1);
|
||||
/* convert tilde to manufacturer-specific directory */
|
||||
strncat(linux_path, priv->mfs_dir,
|
||||
linux_path_size - strlen(linux_path) - 1);
|
||||
vol_end += 2;
|
||||
}
|
||||
|
||||
/* Replace backslashes with forward slashes for the rest of the path */
|
||||
ptr = linux_path + strlen(linux_path);
|
||||
while (vol_end < isobusfs_path + isobusfs_path_size && *vol_end) {
|
||||
if (*vol_end == '\\')
|
||||
*ptr = '/';
|
||||
else
|
||||
*ptr = *vol_end;
|
||||
|
||||
ptr++;
|
||||
vol_end++;
|
||||
if (ptr - linux_path >= (long int)linux_path_size) {
|
||||
/* Ensure null termination */
|
||||
linux_path[linux_path_size - 1] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isobusfs_check_current_dir_access(struct isobusfs_srv_priv *priv,
|
||||
const char *path, size_t path_size)
|
||||
{
|
||||
char linux_path[ISOBUSFS_SRV_MAX_PATH_LEN];
|
||||
int ret;
|
||||
|
||||
ret = isobusfs_path_to_linux_path(priv, path, path_size,
|
||||
linux_path, sizeof(linux_path));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pr_debug("convert ISOBUS FS path to linux path: %.*s -> %s",
|
||||
path_size, path, linux_path);
|
||||
|
||||
ret = isobusfs_cmn_dh_validate_dir_path(linux_path, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* current directory response function */
|
||||
static int isobusfs_srv_dh_current_dir_res(struct isobusfs_srv_priv *priv,
|
||||
struct isobusfs_msg *msg)
|
||||
{
|
||||
struct isobusfs_dh_get_cd_req *req =
|
||||
(struct isobusfs_dh_get_cd_req *)msg->buf;
|
||||
uint8_t error_code = ISOBUSFS_ERR_SUCCESS;
|
||||
struct isobusfs_dh_get_cd_res *res;
|
||||
struct isobusfs_srv_client *client;
|
||||
size_t str_len, buf_size;
|
||||
size_t fixed_res_size;
|
||||
size_t padding_size = 0;
|
||||
int ret;
|
||||
|
||||
client = isobusfs_srv_get_client_by_msg(priv, msg);
|
||||
if (!client) {
|
||||
pr_warn("client not found");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (client->current_dir[0] == '\0')
|
||||
isobusfs_srv_set_default_current_dir(priv, client);
|
||||
|
||||
ret = isobusfs_check_current_dir_access(priv, client->current_dir,
|
||||
sizeof(client->current_dir));
|
||||
if (ret < 0) {
|
||||
switch (ret) {
|
||||
case -ENOENT:
|
||||
error_code = ISOBUSFS_ERR_FILE_ORPATH_NOT_FOUND;
|
||||
case -ENOMEDIUM:
|
||||
error_code = ISOBUSFS_ERR_VOLUME_NOT_INITIALIZED;
|
||||
case -ENOMEM:
|
||||
error_code = ISOBUSFS_ERR_OUT_OF_MEM;
|
||||
default:
|
||||
error_code = ISOBUSFS_ERR_OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
fixed_res_size = sizeof(*res);
|
||||
str_len = strlen(client->current_dir) + 1;
|
||||
buf_size = fixed_res_size + str_len;
|
||||
|
||||
if (buf_size > ISOBUSFS_MAX_TRANSFER_LENGH) {
|
||||
pr_warn("current directory response too long");
|
||||
|
||||
/* Calculate the maximum allowed string length based on the
|
||||
* buffer size
|
||||
*/
|
||||
str_len = ISOBUSFS_MAX_TRANSFER_LENGH - fixed_res_size;
|
||||
|
||||
/* Update the buffer size accordingly */
|
||||
buf_size = fixed_res_size + str_len;
|
||||
|
||||
error_code = ISOBUSFS_ERR_OUT_OF_MEM;
|
||||
|
||||
} else if (buf_size < ISOBUSFS_MIN_TRANSFER_LENGH) {
|
||||
/* Update the buffer size accordingly */
|
||||
padding_size = ISOBUSFS_MIN_TRANSFER_LENGH - buf_size;
|
||||
buf_size = ISOBUSFS_MIN_TRANSFER_LENGH;
|
||||
}
|
||||
|
||||
res = malloc(buf_size);
|
||||
if (!res) {
|
||||
pr_err("failed to allocate memory for current directory response");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
res->fs_function =
|
||||
isobusfs_cg_function_to_buf(ISOBUSFS_CG_DIRECTORY_HANDLING,
|
||||
ISOBUSFS_DH_F_GET_CURRENT_DIR_RES);
|
||||
res->tan = req->tan;
|
||||
res->error_code = error_code;
|
||||
/* TODO: implement total_space and free_space */
|
||||
res->total_space = htole16(0);
|
||||
res->free_space = htole16(0);
|
||||
res->name_len = htole16(str_len);
|
||||
memcpy(res->name, client->current_dir, str_len);
|
||||
|
||||
if (padding_size) {
|
||||
/* Fill the rest of the res structure with 0xff */
|
||||
memset(((uint8_t *)res) + buf_size - padding_size, 0xff,
|
||||
padding_size);
|
||||
}
|
||||
|
||||
/* send to socket */
|
||||
ret = isobusfs_srv_sendto(priv, msg, res, buf_size);
|
||||
if (ret < 0) {
|
||||
pr_warn("can't send current directory response");
|
||||
goto free_res;
|
||||
}
|
||||
|
||||
pr_debug("> tx: current directory response: %s, total space: %i, free space: %i",
|
||||
client->current_dir, le16toh(res->total_space),
|
||||
le16toh(res->free_space));
|
||||
|
||||
free_res:
|
||||
free(res);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* isobusfs_is_forbidden_char() - check if the given character is forbidden
|
||||
* @ch: character to check
|
||||
*
|
||||
* Return: true if the character is forbidden, false otherwise
|
||||
*
|
||||
* The function checks if the given character is forbidden in the ISOBUS FS
|
||||
* as defined in ISO 11783-13:2021, section A.2.2.1 Names:
|
||||
* To avoid incompatibility between different operating systems, the client
|
||||
* shall not create folder/files with names, which only differs in case, and
|
||||
* names shall not end with a '.' or include ‘<’, ‘>’, ‘|’ (the latter three
|
||||
* may cause issues on FAT32).
|
||||
* ....
|
||||
* LongNameChar ::= any single character defined by Unicode/ISO/IEC 10646,
|
||||
* except 0x00 to 0x1f, 0x7f to 0x9f, ‘\’, ‘*’, ‘?’, ‘/’.
|
||||
*/
|
||||
static bool isobusfs_is_forbidden_char(wchar_t ch)
|
||||
{
|
||||
if (ch >= 0x00 && ch <= 0x1f)
|
||||
return true;
|
||||
if (ch >= 0x7f && ch <= 0x9f)
|
||||
return true;
|
||||
if (ch == L'*' || ch == L'?' || ch == L'/' ||
|
||||
ch == L'<' || ch == L'>' || ch == L'|')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int isobusfs_validate_path_chars(const char *path, size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
wchar_t ch = path[i];
|
||||
|
||||
if (isobusfs_is_forbidden_char(ch))
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isobusfs_handle_path_prefix(const char *current_dir,
|
||||
size_t current_dir_len,
|
||||
const char *rel_path,
|
||||
size_t rel_path_size,
|
||||
size_t *rel_path_pos, char *abs_path,
|
||||
size_t abs_path_size,
|
||||
size_t *abs_path_pos)
|
||||
{
|
||||
if (strncmp(rel_path, "~\\", 2) == 0) {
|
||||
size_t vol_len;
|
||||
const char *vol_end;
|
||||
|
||||
vol_end = isobusfs_srv_get_volume_end(current_dir,
|
||||
current_dir_len);
|
||||
if (!vol_end)
|
||||
return -EINVAL;
|
||||
|
||||
vol_len = vol_end - current_dir;
|
||||
strncpy(abs_path, current_dir, vol_len);
|
||||
abs_path[vol_len] = '\\';
|
||||
*abs_path_pos = vol_len + 1;
|
||||
} else if (strncmp(rel_path, "\\\\", 2) == 0) {
|
||||
/* Too many back slashes, drop it. */
|
||||
if (rel_path[2] == '\\')
|
||||
return -EINVAL;
|
||||
|
||||
strncpy(abs_path, rel_path, 2);
|
||||
*abs_path_pos = 2;
|
||||
*rel_path_pos = 2;
|
||||
} else {
|
||||
strncpy(abs_path, current_dir, abs_path_size);
|
||||
*abs_path_pos = current_dir_len;
|
||||
if (abs_path[*abs_path_pos - 1] != '\\') {
|
||||
if (*abs_path_pos < abs_path_size - 1) {
|
||||
abs_path[*abs_path_pos] = '\\';
|
||||
*abs_path_pos += 1;
|
||||
} else {
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
if (rel_path[*rel_path_pos] == '\\')
|
||||
*rel_path_pos += 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_valid_path_char - Check if the current character is valid in the path
|
||||
* @rel_path: The relative path being processed
|
||||
* @rel_path_size: The size of the relative path
|
||||
* @rel_path_pos: Pointer to the current position in the relative path
|
||||
*
|
||||
* Checks if the current character at the position in the relative path
|
||||
* is not the end of the string, not a null character, and not a backslash.
|
||||
*
|
||||
* Return: True if the current character is valid, False otherwise.
|
||||
*/
|
||||
static bool is_valid_path_char(const char *rel_path, size_t rel_path_size,
|
||||
const size_t *rel_path_pos)
|
||||
{
|
||||
return *rel_path_pos < rel_path_size &&
|
||||
rel_path[*rel_path_pos] != '\0' &&
|
||||
rel_path[*rel_path_pos] != '\\';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified number of positions ahead in the relative path
|
||||
* are either the end of the buffer or a backslash.
|
||||
*
|
||||
* @param rel_path The relative path being processed.
|
||||
* @param rel_path_size The size of the relative path.
|
||||
* @param rel_path_pos The current position in the relative path.
|
||||
* @param look_ahead The number of positions ahead to check.
|
||||
* @return True if the specified positions ahead are the end or a backslash,
|
||||
* False otherwise.
|
||||
*/
|
||||
static bool is_end_or_backslash(const char *rel_path, size_t rel_path_size,
|
||||
const size_t *rel_path_pos, size_t look_ahead)
|
||||
{
|
||||
if (*rel_path_pos + look_ahead >= rel_path_size)
|
||||
return true; /* End of buffer */
|
||||
|
||||
return rel_path[*rel_path_pos + look_ahead] == '\\';
|
||||
}
|
||||
|
||||
/**
|
||||
* is_path_separator - Check if the current character is a path separator
|
||||
* @rel_path: The relative path being processed
|
||||
* @rel_path_size: The size of the relative path
|
||||
* @rel_path_pos: Pointer to the current position in the relative path
|
||||
*
|
||||
* Checks if the current character at the position in the relative path
|
||||
* is a backslash and the position is within the string size.
|
||||
*
|
||||
* Return: True if the current character is a backslash, False otherwise.
|
||||
*/
|
||||
static bool is_path_separator(const char *rel_path, size_t rel_path_size,
|
||||
const size_t *rel_path_pos)
|
||||
{
|
||||
return *rel_path_pos < rel_path_size && rel_path[*rel_path_pos] == '\\';
|
||||
}
|
||||
|
||||
static bool isobusfs_is_dot_directive(const char *rel_path,
|
||||
size_t rel_path_size,
|
||||
const size_t *rel_path_pos)
|
||||
{
|
||||
if (rel_path[*rel_path_pos] == '.') {
|
||||
/* Check for '.' followed by a backslash or at the end of the
|
||||
* string
|
||||
*/
|
||||
if (is_end_or_backslash(rel_path, rel_path_size,
|
||||
rel_path_pos, 1) ||
|
||||
rel_path[*rel_path_pos + 1] == '\0') {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check for '..' followed by a backslash or at the end of the
|
||||
* string
|
||||
*/
|
||||
if (rel_path[*rel_path_pos + 1] == '.') {
|
||||
if (is_end_or_backslash(rel_path, rel_path_size,
|
||||
rel_path_pos, 2) ||
|
||||
rel_path[*rel_path_pos + 2] == '\0') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* isobusfs_handle_single_dot - Processes a single dot directive in a relative
|
||||
* path
|
||||
* @rel_path: The relative path being processed
|
||||
* @rel_path_size: The size of the relative path
|
||||
* @rel_path_pos: Pointer to the current position in the relative path
|
||||
*
|
||||
* This function checks if the current segment in the relative path is a single
|
||||
* dot ('.'). A single dot represents the current directory. If the next
|
||||
* character after the dot is either a backslash or the end of the string,
|
||||
* the function advances the path position appropriately. The function returns
|
||||
* true if it processes a single dot, indicating that the current directory
|
||||
* directive was found and handled.
|
||||
*
|
||||
* Return: True if a single dot directive is detected, False otherwise.
|
||||
*/
|
||||
static bool isobusfs_handle_single_dot(const char *rel_path,
|
||||
size_t rel_path_size,
|
||||
size_t *rel_path_pos)
|
||||
{
|
||||
bool is_dot = false;
|
||||
|
||||
if (is_end_or_backslash(rel_path, rel_path_size, rel_path_pos, 1)) {
|
||||
*rel_path_pos += 2;
|
||||
is_dot = true;
|
||||
} else if (rel_path[*rel_path_pos + 1] == '\0') {
|
||||
*rel_path_pos += 1;
|
||||
is_dot = true;
|
||||
}
|
||||
|
||||
return is_dot;
|
||||
}
|
||||
|
||||
/**
|
||||
* isobusfs_handle_double_dots - Processes a double dot directive in a relative
|
||||
* path
|
||||
*
|
||||
* @rel_path: The relative path being processed
|
||||
* @rel_path_size: The size of the relative path
|
||||
* @rel_path_pos: Pointer to the current position in the relative path
|
||||
* @abs_path: Buffer to store the absolute path being constructed
|
||||
* @abs_path_pos: Pointer to the current position in the absolute path buffer
|
||||
*
|
||||
* This function processes the double dot directive ('..') in a relative path.
|
||||
* The double dot represents the parent directory. If the double dot directive
|
||||
* is followed by a backslash or is at the end of the string, the function
|
||||
* advances the path position accordingly. Additionally, it adjusts the
|
||||
* absolute path position to move up one directory in the path hierarchy. The
|
||||
* function ensures that it does not go beyond the root of the absolute path
|
||||
* while moving up the directory hierarchy.
|
||||
*/
|
||||
static void isobusfs_handle_double_dots(const char *rel_path,
|
||||
size_t rel_path_size,
|
||||
size_t *rel_path_pos, char *abs_path,
|
||||
size_t *abs_path_pos)
|
||||
{
|
||||
/* Move the relative path position forward after handling '..' */
|
||||
if (is_end_or_backslash(rel_path, rel_path_size, rel_path_pos, 2))
|
||||
*rel_path_pos += 3;
|
||||
else if (rel_path[*rel_path_pos + 2] == '\0')
|
||||
*rel_path_pos += 2;
|
||||
|
||||
/* Move the absolute path position backward to simulate moving up a
|
||||
* directory
|
||||
*/
|
||||
if (*abs_path_pos > 2 && abs_path[*abs_path_pos - 1] == '\\')
|
||||
*abs_path_pos -= 1;
|
||||
|
||||
while (*abs_path_pos > 2 && abs_path[*abs_path_pos - 1] != '\\')
|
||||
*abs_path_pos -= 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* isobusfs_handle_dot_directive - Processes '.' and '..' directives in a path
|
||||
* @rel_path: The relative path being processed
|
||||
* @rel_path_size: The size of the relative path
|
||||
* @rel_path_pos: Pointer to the current position in the relative path
|
||||
* @abs_path: Buffer to store the absolute path being constructed
|
||||
* @abs_path_pos: Pointer to the current position in the absolute path buffer
|
||||
*
|
||||
* This function processes the dot directives found in a relative path. It
|
||||
* handles both single dot ('.') and double dot ('..') directives. A single dot
|
||||
* represents the current directory, while a double dot represents moving up to
|
||||
* the parent directory.
|
||||
*/
|
||||
static void isobusfs_handle_dot_directive(const char *rel_path,
|
||||
size_t rel_path_size,
|
||||
size_t *rel_path_pos, char *abs_path,
|
||||
size_t *abs_path_pos)
|
||||
{
|
||||
if (rel_path[*rel_path_pos] == '.') {
|
||||
bool is_dot = isobusfs_handle_single_dot(rel_path, rel_path_size,
|
||||
rel_path_pos);
|
||||
|
||||
if (!is_dot && rel_path[*rel_path_pos + 1] == '.') {
|
||||
isobusfs_handle_double_dots(rel_path, rel_path_size,
|
||||
rel_path_pos, abs_path,
|
||||
abs_path_pos);
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip additional backslashes after '.' or '..' */
|
||||
while (is_path_separator(rel_path, rel_path_size, rel_path_pos))
|
||||
*rel_path_pos += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* isobusfs_process_path_segment - Processes normal path segments
|
||||
* @rel_path: The relative path being processed
|
||||
* @rel_path_size: The size of the relative path
|
||||
* @rel_path_pos: Pointer to the current position in the relative path
|
||||
* @abs_path: The buffer to store the absolute path
|
||||
* @abs_path_size: The size of the absolute path buffer
|
||||
* @abs_path_pos: Pointer to the position in the absolute path buffer
|
||||
*
|
||||
* This function processes normal segments of a relative path, copying them
|
||||
* into the absolute path buffer. It handles each character until it encounters
|
||||
* a path separator or reaches the end of the relative path. If a path separator
|
||||
* is found, it adds a single backslash to the absolute path. The function
|
||||
* ensures that the buffer limits are respected to prevent buffer overflows.
|
||||
*
|
||||
* Return: 0 on successful processing of the segment, -ENOMEM if the absolute
|
||||
* path buffer runs out of space.
|
||||
*/
|
||||
static int isobusfs_process_path_segment(const char *rel_path,
|
||||
size_t rel_path_size,
|
||||
size_t *rel_path_pos, char *abs_path,
|
||||
size_t abs_path_size,
|
||||
size_t *abs_path_pos)
|
||||
{
|
||||
/* Process the current character from the relative path */
|
||||
abs_path[*abs_path_pos] = rel_path[*rel_path_pos];
|
||||
*abs_path_pos += 1;
|
||||
*rel_path_pos += 1;
|
||||
|
||||
/* Continue processing until a path separator or end of the string is
|
||||
* reached
|
||||
*/
|
||||
while (is_valid_path_char(rel_path, rel_path_size, rel_path_pos)) {
|
||||
if (*abs_path_pos >= abs_path_size - 1)
|
||||
return -ENOMEM;
|
||||
|
||||
abs_path[*abs_path_pos] = rel_path[*rel_path_pos];
|
||||
*rel_path_pos += 1;
|
||||
*abs_path_pos += 1;
|
||||
}
|
||||
/* Add a single backslash if next character is a backslash */
|
||||
if (is_path_separator(rel_path, rel_path_size, rel_path_pos)) {
|
||||
*rel_path_pos += 1;
|
||||
if (*abs_path_pos < abs_path_size - 1) {
|
||||
abs_path[*abs_path_pos] = '\\';
|
||||
*abs_path_pos += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isobusfs_handle_relative_path(const char *rel_path,
|
||||
size_t rel_path_size,
|
||||
size_t *rel_path_pos, char *abs_path,
|
||||
size_t abs_path_size,
|
||||
size_t *abs_path_pos)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (*abs_path_pos >= abs_path_size - 1)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Check for '.' or '..' followed by a backslash or at the end of the
|
||||
* string
|
||||
*/
|
||||
if (isobusfs_is_dot_directive(rel_path, rel_path_size, rel_path_pos)) {
|
||||
isobusfs_handle_dot_directive(rel_path, rel_path_size,
|
||||
rel_path_pos, abs_path,
|
||||
abs_path_pos);
|
||||
} else {
|
||||
/* Process normally for filenames or directories */
|
||||
ret = isobusfs_process_path_segment(rel_path, rel_path_size,
|
||||
rel_path_pos, abs_path,
|
||||
abs_path_size,
|
||||
abs_path_pos);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isobusfs_convert_relative_to_absolute(struct isobusfs_srv_priv *priv,
|
||||
const char *current_dir,
|
||||
const char *rel_path,
|
||||
size_t rel_path_size, char *abs_path,
|
||||
size_t abs_path_size)
|
||||
{
|
||||
size_t abs_path_pos = 0;
|
||||
size_t rel_path_pos = 0;
|
||||
size_t current_dir_len;
|
||||
int ret;
|
||||
|
||||
if (!current_dir || !rel_path || !abs_path || !rel_path_size ||
|
||||
!abs_path_size)
|
||||
return -EINVAL;
|
||||
|
||||
ret = isobusfs_validate_path_chars(rel_path, rel_path_size);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
current_dir_len = strlen(current_dir);
|
||||
if (current_dir_len >= abs_path_size)
|
||||
return -ENOMEM;
|
||||
if (current_dir_len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = isobusfs_handle_path_prefix(current_dir, current_dir_len,
|
||||
rel_path, rel_path_size,
|
||||
&rel_path_pos, abs_path,
|
||||
abs_path_size, &abs_path_pos);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (rel_path_pos < rel_path_size && rel_path[rel_path_pos] != '\0') {
|
||||
ret = isobusfs_handle_relative_path(rel_path, rel_path_size,
|
||||
&rel_path_pos, abs_path,
|
||||
abs_path_size,
|
||||
&abs_path_pos);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
abs_path[abs_path_pos] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* change current directory response function */
|
||||
static int isobusfs_srv_dh_ccd_res(struct isobusfs_srv_priv *priv,
|
||||
struct isobusfs_msg *msg)
|
||||
{
|
||||
struct isobusfs_dh_ccd_req *req =
|
||||
(struct isobusfs_dh_ccd_req *)msg->buf;
|
||||
uint8_t error_code = ISOBUSFS_ERR_SUCCESS;
|
||||
struct isobusfs_srv_client *client;
|
||||
struct isobusfs_dh_ccd_res res;
|
||||
size_t abs_path_len;
|
||||
char *abs_path;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We assime, the relative path stored in res->name is not longer
|
||||
* than absolue path
|
||||
*/
|
||||
if (req->name_len > ISOBUSFS_SRV_MAX_PATH_LEN) {
|
||||
pr_warn("path too long");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
client = isobusfs_srv_get_client_by_msg(priv, msg);
|
||||
if (!client) {
|
||||
pr_warn("client not found");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
abs_path_len = ISOBUSFS_SRV_MAX_PATH_LEN;
|
||||
abs_path = malloc(abs_path_len);
|
||||
if (!abs_path) {
|
||||
pr_warn("failed to allocate memory");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_debug("< rx change current directory request from client 0x%2x: %.*s. Current directory: %s",
|
||||
client->addr, req->name_len, req->name, client->current_dir);
|
||||
/* Normalize provided string and convert it to absolute ISOBUS FS path */
|
||||
ret = isobusfs_convert_relative_to_absolute(priv, client->current_dir,
|
||||
(char *)req->name, req->name_len,
|
||||
abs_path, abs_path_len);
|
||||
if (ret < 0)
|
||||
goto process_error;
|
||||
|
||||
pr_debug("converted relative to absolute ISOBUS FS internal path: %s", abs_path);
|
||||
ret = isobusfs_check_current_dir_access(priv, abs_path, abs_path_len);
|
||||
process_error:
|
||||
if (ret < 0) {
|
||||
/* linux_error_to_isobusfs_error() can't distinguish between
|
||||
* -EINVAL vor SRC and DST, so we have to do it manually.
|
||||
*/
|
||||
if (ret == -EINVAL)
|
||||
error_code = ISOBUSFS_ERR_INVALID_DST_NAME;
|
||||
else
|
||||
error_code = linux_error_to_isobusfs_error(ret);
|
||||
} else {
|
||||
/* change current directory */
|
||||
strncpy(client->current_dir, abs_path, ISOBUSFS_SRV_MAX_PATH_LEN);
|
||||
}
|
||||
|
||||
res.fs_function =
|
||||
isobusfs_cg_function_to_buf(ISOBUSFS_CG_DIRECTORY_HANDLING,
|
||||
ISOBUSFS_DH_F_CHANGE_CURRENT_DIR_RES);
|
||||
res.tan = req->tan;
|
||||
res.error_code = error_code;
|
||||
memset(&res.reserved[0], 0xff, sizeof(res.reserved));
|
||||
|
||||
/* send to socket */
|
||||
ret = isobusfs_srv_sendto(priv, msg, &res, sizeof(res));
|
||||
if (ret < 0) {
|
||||
pr_warn("can't send current directory response");
|
||||
goto free_abs_path;
|
||||
}
|
||||
|
||||
pr_debug("> tx: ccd response. Error code: %d", error_code);
|
||||
free_abs_path:
|
||||
free(abs_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* current directory response function */
|
||||
/* Command group: directory handling */
|
||||
int isobusfs_srv_rx_cg_dh(struct isobusfs_srv_priv *priv,
|
||||
struct isobusfs_msg *msg)
|
||||
{
|
||||
int func = isobusfs_buf_to_function(msg->buf);
|
||||
int ret = 0;
|
||||
|
||||
switch (func) {
|
||||
case ISOBUSFS_DH_F_GET_CURRENT_DIR_REQ:
|
||||
return isobusfs_srv_dh_current_dir_res(priv, msg);
|
||||
case ISOBUSFS_DH_F_CHANGE_CURRENT_DIR_REQ:
|
||||
return isobusfs_srv_dh_ccd_res(priv, msg);
|
||||
default:
|
||||
goto not_supported;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
not_supported:
|
||||
isobusfs_srv_send_error(priv, msg, ISOBUSFS_ERR_FUNC_NOT_SUPPORTED);
|
||||
|
||||
pr_warn("%s: unsupported function: %i", __func__, func);
|
||||
|
||||
/* Not a critical error */
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user