// SPDX-License-Identifier: LGPL-2.0-only // SPDX-FileCopyrightText: 2023 Oleksij Rempel #include #include #include #include #include #include #include #include #include "isobusfs_srv.h" #include "isobusfs_cmn_fa.h" static struct isobusfs_srv_handles * isobusfs_srv_walk_handles(struct isobusfs_srv_priv *priv, const char *path) { unsigned int i; for (i = 0; i < ARRAY_SIZE(priv->handles); i++) { if (priv->handles[i].path == NULL) continue; if (!strcmp(priv->handles[i].path, path)) return &priv->handles[i]; } return NULL; } static int isobusfs_srv_add_file(struct isobusfs_srv_priv *priv, const char *path, int fd, DIR *dir) { unsigned int j; if (priv->handles_count >= (int)ARRAY_SIZE(priv->handles)) { pr_err("too many handles"); return -ENOSPC; } for (j = 0; j < ARRAY_SIZE(priv->handles); j++) { if (priv->handles[j].path == NULL) break; } priv->handles[j].path = strdup(path); priv->handles[j].fd = fd; priv->handles[j].dir = dir; priv->handles_count++; return j; } static int isobusfs_srv_add_client_to_file(struct isobusfs_srv_handles *file, struct isobusfs_srv_client *client) { unsigned int j; for (j = 0; j < ARRAY_SIZE(file->clients); j++) { if (file->clients[j] == client) return 0; } for (j = 0; j < ARRAY_SIZE(file->clients); j++) { if (file->clients[j] == NULL) { file->clients[j] = client; file->refcount++; return 0; } } pr_err("%s: can't add client to file", __func__); return -ENOENT; } static int isobusfs_srv_request_file(struct isobusfs_srv_priv *priv, struct isobusfs_srv_client *client, const char *path, int fd, DIR *dir) { struct isobusfs_srv_handles *file; int handle, ret; file = isobusfs_srv_walk_handles(priv, path); if (!file) { handle = isobusfs_srv_add_file(priv, path, fd, dir); if (handle < 0) return handle; file = &priv->handles[handle]; } else { handle = file - priv->handles; } ret = isobusfs_srv_add_client_to_file(file, client); if (ret < 0) return ret; return handle; } static struct isobusfs_srv_handles * isobusfs_srv_get_handle(struct isobusfs_srv_priv *priv, int handle) { if (handle < 0 || handle >= (int)ARRAY_SIZE(priv->handles)) return NULL; return &priv->handles[handle]; } static int isobusfs_srv_release_handle(struct isobusfs_srv_priv *priv, struct isobusfs_srv_client *client, int handle) { struct isobusfs_srv_handles *hdl = isobusfs_srv_get_handle(priv, handle); unsigned int client_index; if (!hdl) { pr_warn("%s: invalid handle %d", __func__, handle); return -ENOENT; } /* Find the client in the hdl's client list and remove it */ for (client_index = 0; client_index < ARRAY_SIZE(hdl->clients); client_index++) { if (hdl->clients[client_index] == client) { hdl->clients[client_index] = NULL; hdl->refcount--; pr_debug("%s: client %p removed from handle %d", __func__, client, handle); /* If refcount is 0, close the hdl and remove it from the list */ if (hdl->refcount == 0) { pr_debug("%s: closing handle %d", __func__, handle); /* fd will be automatically closed when * closedir(3) is called. */ if (hdl->dir) closedir(hdl->dir); else close(hdl->fd); memset(hdl, 0, sizeof(*hdl)); priv->handles_count--; } return 0; } } pr_err("%s: client %p not found in handle %d", __func__, client, handle); return -ENOENT; } void isobusfs_srv_remove_client_from_handles(struct isobusfs_srv_priv *priv, struct isobusfs_srv_client *client) { unsigned int handle; unsigned int client_index; for (handle = 0; handle < ARRAY_SIZE(priv->handles); handle++) { struct isobusfs_srv_handles *hdl = &priv->handles[handle]; if (hdl->path == NULL) continue; for (client_index = 0; client_index < ARRAY_SIZE(hdl->clients); client_index++) { if (hdl->clients[client_index] == client) { hdl->clients[client_index] = NULL; hdl->refcount--; if (hdl->refcount == 0) { close(hdl->fd); memset(hdl, 0, sizeof(*hdl)); priv->handles_count--; } break; } } } } static int isobusfs_srv_fa_open_directory(struct isobusfs_srv_priv *priv, struct isobusfs_srv_client *client, const char *path, size_t path_len, uint8_t *handle) { char linux_path[ISOBUSFS_SRV_MAX_PATH_LEN]; struct isobusfs_srv_handles *hdl; struct stat file_stat; int file_index, fd; DIR *dir; int ret; ret = isobusfs_path_to_linux_path(priv, path, path_len, linux_path, sizeof(linux_path)); if (ret < 0) return ret; hdl = isobusfs_srv_walk_handles(priv, linux_path); if (hdl) { pr_err("%s: Path %s is already opened\n", __func__, linux_path); return ISOBUSFS_ERR_OTHER; } dir = opendir(linux_path); if (!dir) { pr_err("%s: Error opening directory %s. Error %d (%s)\n", __func__, linux_path, errno, strerror(errno)); switch (errno) { case EACCES: return ISOBUSFS_ERR_ACCESS_DENIED; case ENOENT: return ISOBUSFS_ERR_FILE_ORPATH_NOT_FOUND; case ENOMEM: return ISOBUSFS_ERR_OUT_OF_MEM; default: return ISOBUSFS_ERR_OTHER; } } fd = dirfd(dir); if (fd < 0) { pr_err("%s: Error getting file descriptor for directory %s. Error %d (%s)\n", __func__, linux_path, errno, strerror(errno)); closedir(dir); return ISOBUSFS_ERR_OTHER; } if (fstat(fd, &file_stat) < 0 || !S_ISDIR(file_stat.st_mode)) { pr_err("%s: Path %s is not a directory\n", __func__, linux_path); closedir(dir); return ISOBUSFS_ERR_INVALID_ACCESS; } file_index = isobusfs_srv_request_file(priv, client, linux_path, fd, dir); if (file_index < 0) { closedir(dir); return file_index; } *handle = (uint8_t)file_index; return 0; } static int isobusfs_srv_fa_open_file(struct isobusfs_srv_priv *priv, struct isobusfs_srv_client *client, const char *path, size_t path_len, uint8_t flags, uint8_t *handle) { char linux_path[ISOBUSFS_SRV_MAX_PATH_LEN]; struct isobusfs_srv_handles *hdl; struct stat file_stat; int open_flags = 0; int file_index; int ret, fd; ret = isobusfs_path_to_linux_path(priv, path, path_len, linux_path, sizeof(linux_path)); if (ret < 0) return ret; pr_debug("convert ISOBUS FS path to linux path: %.*s -> %s", path_len, path, linux_path); /* Determine open flags based on the requested access type */ switch (flags & ISOBUSFS_FA_OPEN_MASK) { case ISOBUSFS_FA_OPEN_FILE_RO: open_flags |= O_RDONLY; break; case ISOBUSFS_FA_OPEN_FILE_WO: open_flags |= O_WRONLY; break; case ISOBUSFS_FA_OPEN_FILE_WR: open_flags |= O_RDWR; if (!(flags & ISOBUSFS_FA_OPEN_APPEND)) open_flags |= O_TRUNC; break; default: return ISOBUSFS_ERR_INVALID_ACCESS; } if (flags & ISOBUSFS_FA_OPEN_APPEND) open_flags |= O_APPEND; /* Check if the file is already opened */ hdl = isobusfs_srv_walk_handles(priv, linux_path); if (hdl) { pr_warn("Handle: %s is already opened by client: %x\n", linux_path, client->addr); fd = hdl->fd; } else { /* Open the file if not already opened */ fd = open(linux_path, open_flags); if (fd < 0) { switch (errno) { case EACCES: return ISOBUSFS_ERR_ACCESS_DENIED; case EINVAL: return ISOBUSFS_ERR_INVALID_ACCESS; case EMFILE: case ENFILE: return ISOBUSFS_ERR_TOO_MANY_FILES_OPEN; case ENOENT: return ISOBUSFS_ERR_FILE_ORPATH_NOT_FOUND; case ENOMEM: return ISOBUSFS_ERR_OUT_OF_MEM; default: return ISOBUSFS_ERR_OTHER; } } /* Check if the opened path is a regular file */ if (fstat(fd, &file_stat) < 0) { close(fd); return ISOBUSFS_ERR_OTHER; } if (!S_ISREG(file_stat.st_mode)) { close(fd); /* Invalid access (not a regular file) */ return ISOBUSFS_ERR_INVALID_ACCESS; } } /* Request the file, which also handles refcount and client list * updates */ file_index = isobusfs_srv_request_file(priv, client, linux_path, fd, NULL); if (file_index < 0) { close(fd); return file_index; } *handle = (uint8_t)file_index; return 0; } static int isobusfs_srv_fa_open_file_req(struct isobusfs_srv_priv *priv, struct isobusfs_msg *msg) { struct isobusfs_fa_openf_req *req = (struct isobusfs_fa_openf_req *)msg->buf; uint16_t name_len = le16toh(req->name_len); struct isobusfs_srv_client *client; struct isobusfs_fa_openf_res res; uint8_t error_code = 0; uint8_t access_type; size_t abs_path_len; uint8_t handle = 0; char *abs_path; int ret = 0; client = isobusfs_srv_get_client_by_msg(priv, msg); if (!client) { pr_warn("client not found"); error_code = ISOBUSFS_ERR_OTHER; goto send_response; } if (name_len > msg->len - sizeof(*req)) { error_code = ISOBUSFS_ERR_INVALID_ACCESS; goto send_response; } /* Perform checks on the received request, e.g., validate path length */ if (name_len > ISOBUSFS_MAX_PATH_NAME_LENGTH) { error_code = ISOBUSFS_ERR_INVALID_ACCESS; goto send_response; } 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; } if (client->current_dir[0] == '\0') isobusfs_srv_set_default_current_dir(priv, client); /* 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) { error_code = ISOBUSFS_ERR_FILE_ORPATH_NOT_FOUND; goto send_response; } pr_debug("< rx: Open File Request. from client 0x%2x: %.*s. Current directory: %s", client->addr, req->name_len, req->name, client->current_dir); access_type = FIELD_GET(ISOBUSFS_FA_OPEN_MASK, req->flags); if (access_type == ISOBUSFS_FA_OPEN_DIR) { error_code = isobusfs_srv_fa_open_directory(priv, client, abs_path, abs_path_len, &handle); } else { error_code = isobusfs_srv_fa_open_file(priv, client, abs_path, abs_path_len, req->flags, &handle); } send_response: res.fs_function = isobusfs_cg_function_to_buf(ISOBUSFS_CG_FILE_ACCESS, ISOBUSFS_FA_F_OPEN_FILE_RES); res.tan = req->tan; res.error_code = error_code; res.handle = handle; 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 err; } pr_debug("> tx: Open File Response. Error code: %d (%s).", error_code, isobusfs_error_to_str(error_code)); err: return ret; } static int isobusfs_srv_read_file(struct isobusfs_srv_handles *handle, uint8_t *buffer, size_t count, ssize_t *readed_size) { *readed_size = read(handle->fd, buffer, count); if (*readed_size < 0) { switch (errno) { case EBADF: return ISOBUSFS_ERR_INVALID_HANDLE; case EFAULT: return ISOBUSFS_ERR_OUT_OF_MEM; case EIO: return ISOBUSFS_ERR_ON_READ; default: return ISOBUSFS_ERR_OTHER; } } return 0; } static uint16_t convert_to_file_date(time_t time_val) { struct tm *timeinfo = localtime(&time_val); int year, month, day; if (!timeinfo) return 0; year = timeinfo->tm_year + 1900 - 1980; month = timeinfo->tm_mon + 1; day = timeinfo->tm_mday; if (year < 0 || year > 127) return 0; return (year << 9) | (month << 5) | day; } static uint16_t convert_to_file_time(time_t time_val) { struct tm *timeinfo = localtime(&time_val); int hours, minutes, seconds; uint16_t time; if (!timeinfo) return 0; hours = timeinfo->tm_hour; minutes = timeinfo->tm_min; seconds = timeinfo->tm_sec / 2; time = (hours << 11) | (minutes << 5) | seconds; return time; } static int check_access_with_base(const char *base_dir, const char *relative_path, int mode) { char full_path[ISOBUSFS_SRV_MAX_PATH_LEN]; if (snprintf(full_path, sizeof(full_path), "%s/%s", base_dir, relative_path) >= (int)sizeof(full_path)) { return -ENAMETOOLONG; } return access(full_path, mode); } static int isobusfs_srv_read_directory(struct isobusfs_srv_handles *handle, uint8_t *buffer, size_t count, ssize_t *readed_size) { DIR *dir = handle->dir; struct dirent *entry; size_t pos = 0; /* * Position the directory stream to the previously stored offset (handle->dir_pos). * * Handling Changes in Directory Contents: * - If the directory contents change between reads (e.g., files/directories added or deleted), * handle->dir_pos may not point to the expected entry. * - To ensure consistency, implement checks (e.g., compare inode numbers) to verify the correct * entry position. * * Detecting End of Directory: * - If readdir() returns NULL before reaching handle->dir_pos, it indicates the end of the * directory with no more entries to read. * - This situation should be handled appropriately, such as by resetting handle->dir_pos and * either returning an error or restarting from the beginning of the directory, depending * on the application's requirements. */ for (int i = 0; i < handle->dir_pos && (entry = readdir(dir)) != NULL; i++) { /* Iterating to the desired position */ } /* * Directory Entry Layout: * 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. * * The layout of each directory entry in the buffer is as follows: * - Byte 1: Filename Length (as per ISO 11783-13:2021 B.22). * Represents the length of the filename that follows. * * - Byte 2–n: Filename (as per ISO 11783-13:2021 B.23). * The actual name of the file or directory. * * - Byte n + 1: Attributes (as per ISO 11783-13:2021 B.15). * * - Bytes n + 2, n + 3: File Date (as per ISO 11783-13:2021 B.24). * Encoded file date, using a 16-bit format derived from file_stat.st_mtime. * * - Bytes n + 4, n + 5: File Time (as per ISO 11783-13:2021 B.25). * Encoded file time, using a 16-bit format derived from file_stat.st_mtime. * * - Bytes n + 6 … n + 9: Size (as per ISO 11783-13:2021 B.26). * The size of the file in bytes, encoded in a 32-bit little-endian format. * * The handle->dir_pos is incremented after processing each entry, marking * the current position in the directory stream for subsequent reads. */ while ((entry = readdir(dir)) != NULL) { size_t entry_name_len, entry_total_len; __le16 file_date, file_time; uint8_t attributes = 0; struct stat file_stat; __le32 size; if (check_access_with_base(handle->path, entry->d_name, R_OK) != 0) continue; /* Skip this entry if it's not readable */ 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; entry_total_len = 1 + entry_name_len + 1 + 2 + 2 + 4; if (pos + entry_total_len > count) break; buffer[pos++] = (uint8_t)entry_name_len; memcpy(buffer + pos, entry->d_name, 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; file_date = htole16(convert_to_file_date(file_stat.st_mtime)); memcpy(buffer + pos, &file_date, sizeof(file_date)); pos += sizeof(file_date); file_time = htole16(convert_to_file_time(file_stat.st_mtime)); memcpy(buffer + pos, &file_time, sizeof(file_time)); pos += sizeof(file_time); size = htole32(file_stat.st_size); memcpy(buffer + pos, &size, sizeof(size)); pos += sizeof(size); } *readed_size = pos; return 0; } static int isobusfs_srv_fa_rf_req(struct isobusfs_srv_priv *priv, struct isobusfs_msg *msg) { uint8_t res_fail[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; struct isobusfs_read_file_response *res; struct isobusfs_srv_handles *handle; struct isobusfs_srv_client *client; struct isobusfs_fa_readf_req *req; ssize_t readed_size = 0; uint8_t error_code = 0; ssize_t send_size; int ret = 0; int count; req = (struct isobusfs_fa_readf_req *)msg->buf; count = le16toh(req->count); pr_debug("< rx: Read File Request. tan: %d, handle: %d, count: %d", req->tan, req->handle, count); /* C.3.5.1 Read File, General: * The requested data (excluding the other parameters) is sent in * the response (up to 1 780 bytes when TP is used, up to 65 530 bytes * when ETP is used). The number of data bytes read can be less than * requested if the end of the file is reached. * TODO: currently we are not able to detect support transport mode, * 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); if (!client) { pr_warn("client not found"); error_code = ISOBUSFS_ERR_OTHER; goto send_response; } handle = isobusfs_srv_get_handle(priv, req->handle); if (!handle) { pr_warn("failed to find file with handle: %x", req->handle); error_code = ISOBUSFS_ERR_FILE_ORPATH_NOT_FOUND; } /* Determine whether to read a file or a directory */ if (handle->dir) { ret = isobusfs_srv_read_directory(handle, res->data, count, &readed_size); } else { ret = isobusfs_srv_read_file(handle, res->data, count, &readed_size); } if (ret < 0) { error_code = ret; readed_size = 0; } else if (count != 0 && readed_size == 0) { error_code = ISOBUSFS_ERR_END_OF_FILE; } send_response: res->fs_function = isobusfs_cg_function_to_buf(ISOBUSFS_CG_FILE_ACCESS, ISOBUSFS_FA_F_READ_FILE_RES); res->tan = req->tan; res->error_code = error_code; res->count = htole16(readed_size); send_size = sizeof(*res) + readed_size; if (send_size < ISOBUSFS_MIN_TRANSFER_LENGH) send_size = ISOBUSFS_MIN_TRANSFER_LENGH; /* send to socket */ ret = isobusfs_srv_sendto(priv, msg, res, send_size); if (ret < 0) { pr_warn("can't send Read File Response"); goto free_res; } pr_debug("> tx: Read File Response. Error code: %d (%s), readed size: %d", error_code, isobusfs_error_to_str(error_code), readed_size); free_res: free(res); return ret; } static int isobusfs_srv_seek(struct isobusfs_srv_priv *priv, struct isobusfs_srv_handles *handle, int32_t offset, uint8_t position_mode) { int whence; off_t offs; switch (position_mode) { case ISOBUSFS_FA_SEEK_SET: whence = SEEK_SET; if (offset < 0) { pr_warn("Invalid offset. Offset must be positive."); return ISOBUSFS_ERR_INVALID_REQUESTED_LENGHT; } break; case ISOBUSFS_FA_SEEK_CUR: whence = SEEK_CUR; if (offset < 0 && handle->offset < -offset) { pr_warn("Invalid offset. Negative offset is too big."); return ISOBUSFS_ERR_INVALID_REQUESTED_LENGHT; } break; case ISOBUSFS_FA_SEEK_END: whence = SEEK_END; if (offset > 0) { pr_warn("Invalid offset. Offset must be negative"); return ISOBUSFS_ERR_INVALID_REQUESTED_LENGHT; } break; default: pr_warn("invalid position mode"); return ISOBUSFS_ERR_OTHER; } /* seek file */ offs = lseek(handle->fd, offset, whence); if (offs < 0) { pr_warn("Failed to seek file"); switch (offs) { case EBADF: return ISOBUSFS_ERR_INVALID_HANDLE; case EINVAL: return ISOBUSFS_ERR_INVALID_REQUESTED_LENGHT; case ENXIO: return ISOBUSFS_ERR_END_OF_FILE; case EOVERFLOW: return ISOBUSFS_ERR_OUT_OF_MEM; case ESPIPE: return ISOBUSFS_ERR_ACCESS_DENIED; default: return ISOBUSFS_ERR_OTHER; } } handle->offset = offs; return ISOBUSFS_ERR_SUCCESS; } static int isobusfs_srv_seek_directory(struct isobusfs_srv_handles *handle, int32_t offset) { DIR *dir = fdopendir(handle->fd); if (!dir) return ISOBUSFS_ERR_OTHER; rewinddir(dir); for (int32_t i = 0; i < offset; i++) { if (readdir(dir) == NULL) return ISOBUSFS_ERR_END_OF_FILE; } handle->dir_pos = offset; return ISOBUSFS_ERR_SUCCESS; } static int isobusfs_srv_fa_sf_req(struct isobusfs_srv_priv *priv, struct isobusfs_msg *msg) { struct isobusfs_fa_seekf_res res = {0}; struct isobusfs_srv_client *client; struct isobusfs_fa_seekf_req *req; struct isobusfs_srv_handles *handle; int32_t offset_out = 0; uint8_t error_code = 0; int ret; req = (struct isobusfs_fa_seekf_req *)msg->buf; pr_debug("< rx: Seek File Request. Handle: %x, offset: %d, position mode: %d", req->handle, le32toh(req->offset), req->position_mode); client = isobusfs_srv_get_client_by_msg(priv, msg); if (!client) { pr_warn("client not found"); error_code = ISOBUSFS_ERR_OTHER; goto send_response; } handle = isobusfs_srv_get_handle(priv, req->handle); if (!handle) { pr_warn("failed to find handle: %x", req->handle); error_code = ISOBUSFS_ERR_INVALID_HANDLE; goto send_response; } if (handle->dir) { error_code = isobusfs_srv_seek_directory(handle, le32toh(req->offset)); res.position = htole32(handle->dir_pos); } else { error_code = isobusfs_srv_seek(priv, handle, le32toh(req->offset), req->position_mode); res.position = htole32(handle->offset); } send_response: res.fs_function = isobusfs_cg_function_to_buf(ISOBUSFS_CG_FILE_ACCESS, ISOBUSFS_FA_F_SEEK_FILE_RES); res.tan = req->tan; res.error_code = error_code; /* send to socket */ ret = isobusfs_srv_sendto(priv, msg, &res, sizeof(res)); if (ret < 0) { pr_warn("can't send seek file response"); return ret; } pr_debug("> tx: Seek File Response. Error code: %d, offset: %d", error_code, offset_out); return 0; } static int isobusfs_srv_fa_cf_req(struct isobusfs_srv_priv *priv, struct isobusfs_msg *msg) { struct isobusfs_close_file_request *req; struct isobusfs_close_file_res res; struct isobusfs_srv_client *client; uint8_t error_code = 0; int ret; req = (struct isobusfs_close_file_request *)msg->buf; client = isobusfs_srv_get_client_by_msg(priv, msg); if (!client) { pr_warn("client not found"); error_code = ISOBUSFS_ERR_OTHER; goto send_response; } ret = isobusfs_srv_release_handle(priv, client, req->handle); if (ret < 0) { pr_warn("failed to release handle: %x", req->handle); switch (ret) { case -ENOENT: error_code = ISOBUSFS_ERR_FILE_ORPATH_NOT_FOUND; break; default: error_code = ISOBUSFS_ERR_OTHER; } } send_response: res.fs_function = isobusfs_cg_function_to_buf(ISOBUSFS_CG_FILE_ACCESS, ISOBUSFS_FA_F_CLOSE_FILE_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 err; } pr_debug("> tx: Close File Response. Error code: %d", error_code); err: return ret; } /* Command group: file access */ int isobusfs_srv_rx_cg_fa(struct isobusfs_srv_priv *priv, struct isobusfs_msg *msg) { int func = isobusfs_buf_to_function(msg->buf); int ret = 0; switch (func) { case ISOBUSFS_FA_F_OPEN_FILE_REQ: ret = isobusfs_srv_fa_open_file_req(priv, msg); break; case ISOBUSFS_FA_F_CLOSE_FILE_REQ: ret = isobusfs_srv_fa_cf_req(priv, msg); break; case ISOBUSFS_FA_F_READ_FILE_REQ: ret = isobusfs_srv_fa_rf_req(priv, msg); break; case ISOBUSFS_FA_F_SEEK_FILE_REQ: ret = isobusfs_srv_fa_sf_req(priv, msg); break; case ISOBUSFS_FA_F_WRITE_FILE_REQ: /* fall through */ default: pr_warn("%s: unsupported function: %i", __func__, func); isobusfs_srv_send_error(priv, msg, ISOBUSFS_ERR_FUNC_NOT_SUPPORTED); } return ret; }